5繼承
5.1 類、超類和子類
關鍵字extends表示繼承。
Java中的繼承都是公用繼承,沒有C++中的私有繼承和保護繼承。
?
Super class ?Subclass ?來自于集合的術語
Base class ??Derived class
Parent class ?Child class
?
將通用的功能放在超類中,將具有特殊用途的方法放在子類中。
?
子類中可以覆蓋(override)超類中的方法。
子類不能直接訪問超類的私有域。
子類中調用超類方法:super.method()
C++中用 [超類名::方法]的方式調用超類方法。
?
因為子類不能訪問超類私有域,
子類的構造器中,第一條語句要用super(...)調用超類構造器;
如果未提供super()語句,則調用默認無參數的超類構造器;
如果超類沒有默認構造器,則編譯出錯。
?
C++中利用初始化列表語法調用超類構造器。
?
繼承層次:
由一個公共超類派生出來的所有類的集合被稱為繼承層次(inheritance hierarchy)。
從某個特定的類到其祖先的路徑被稱為該類的繼承鏈(inheritance chain)。
一個祖先類可以擁有多個子孫繼承鏈。
Java不支持多繼承。
?
多態:
IS-A規則:子類的每個對象也是超類的對象。
即置換法則,程序中出現超類對象的任何地方都可以用子類對象置換。
超類對象變量可以引用任何其子類對象。
不能將超類的引用賦給子類變量。
?
靜態綁定 static binding:
對于private方法、static方法、final方法或者構造器,編譯器可明確知道應該調用哪個方法。
動態綁定:
調用的方法依賴于隱式參數的實際類型,并且在運行時實現動態綁定。
虛擬機預先為每個類創建一個方法表(method table),其中列出了所有方法的簽名和實際調用的方法。真正調用的時候,虛擬機查找這個表。
?
覆蓋一個方法時,子類方法不能低于超類方法的可見性。
?
阻止繼承:
Final類:不允許被擴展的類。
Final方法:不允許被重寫的方法。
Final類中的方法自動的稱為final方法。
Final域:常量。
Final的主要目的:確保它們不會在子類中改變語義。
String類是final類。
?
如果一個方法沒有被覆蓋并且很短,編譯器進行內聯(inlining)優化處理。因為分支轉移會擾亂指令預取策略。
?
強制類型轉換:
用一對圓括號將目標類名括起來,置于需要轉換的目標引用之前。
唯一原因:在暫時忽視對象的實際類型之后,使用對象的全部功能。
可以將子類引用賦給超類變量,但當超類引用賦給子類變量時,必須進行類型轉換。
在類型轉換之前,進行instanceof檢查。
綜上所述:
1、只能在繼承層次內進行類型轉換;
2、在將超類轉換成子類之前,使用instanceof進行檢查。
?
在一般情況下,應該盡量少用類型轉換和instanceof運算符。
?
C++的類型轉換:Manager* boss = dynamic_cast<Manager*>(staff[1]);
?
抽象類:
包含一個或多個抽象方法的類必須聲明為抽象類。
抽象類可以包含具體數據和具體方法。
?
其子類如果實現部分抽象方法,則也需聲明為抽象類;
子類若實現全部抽象方法,則不必聲明為抽象方法。
?
類即使不包含抽象方法,也可以將其聲明為抽象類。
抽象類不能被實例化。
可以定義一個抽象類的對象變量,但是只能引用非抽象子類的對象。
?
C++中的抽象類:只要包含純虛函數的類就是抽象類。
?
編譯器只允許調用在類中聲明的方法。
?
受保護訪問:
允許子類的方法訪問超類的某個域,或希望超類的某些方法被子類訪問。
protected
最好的示例:Object類中的clone方法。
?
Private——對本類可見;
Public——對所有類可見;
Protected——對本包和所有子類可見;
默認——對本包可見。
?
5.2 Object:所有類的超類
Object類型的變量可以引用任何類型的對象。
在java中,只有基本類型(primitive types)不是對象。
所有數組類型都是對象,擴展于Object類。
?
equals方法:
????public boolean equals( Object other);
Object中,檢測兩個對象變量是否有相同的引用(功能與==一致)。
在子類定義equals方法時,首先調用超類的equals方法。如果檢測失敗,對象就不可能相等。如果超類中的域都相等,就需要比較子類中的實例域。
?
Java要求equals語法具有下面的性質:
自反性
對稱性
傳遞性
一致性
X.equals(null)應該返回false
?
重寫符號:方法前一行加@override
?
重寫equals方法步驟:
1、顯式參數命名為otherObject;
2、檢測this與otherObject是否引用同一個對象;if (this == otherObject) return true;
3、檢測otherObject是否為空;if (otherObject == null) return false;
4、檢測otherObject是否屬于同一類。
a)?如果equals的語義在每個子類中有所改變,用getClass檢測:
if (getClass() != otherObject.getClass()) return false;
b)?如果所有的子類都擁有統一語義,就使用instanceof檢測:
if (!(otherObject instanceof ClassName)) return false;
5、將otherObject轉換為相應的類類型變量:ClassName other = (ClassName) otherObject;
6、對所有需要比較的域進行比較。需在其中包含調用super.equals(other);
?
數組類型的域,用Arrays.equals(a1, a2)檢查是否相等。
?
hashcode方法:
public int hashcode();
定義在Object類中,每個對象都有一個默認的散列碼,其值為對象的存儲地址。
Equals方法必須和hashcode方法一致,即如果a.equals(b)為真,則a和b的散列碼必須相同。
?
Objects.hashCode(Object a); //如果為Null,返回0,否則返回a.hashcode();
Objects.hash(Object... objects); //返回所有對象的散列碼組合。
?
數組類型的域,用Arrays.hashCode(a)計算散列碼。
?
toString方法:
Object類定義了toString()方法,打印輸出對象所屬的類名和散列碼。
絕大多數遵循格式:類的名字[域值]
用getClass().getName()獲得類的名字。
只要對象與字符串通過 + 相連,就自動調用對象的toString()方法。
System.out.println(x); //自動調用x的toString()方法。
?
數組繼承了object類的toString方法。
如果想打印數組的內容,用Arrays.toString(a);多維數組用Arrays.deepToString(a);
?
強烈建議為自定義的每一個類增加toString方法。
?
@SuppressWarnings(“unchecked”)
?
包裝器(wrapper) ?自動裝箱(autoboxing) ?自動拆箱
?
5.3 參數數量可變方法
最后一個參數為 類名... args,實際傳入一個數組。調用時可以傳入多個類對象,甚至一個類數組。
5.4 枚舉類
所有枚舉類型都是Enum類的子類。
比較時,直接用“==”
?
5.6 反射
反射庫(reflection library)提供了一個非常豐富且精心設計的工具集,以便編寫能夠動態操縱java代碼的程序。這項功能被大量地應用于JavaBeans中,它是Java組件的體系結構。
能夠分析類能力的程序稱為反射(reflective)。
?
在程序運行期間,系統保存了所有已加載類的信息,虛擬機用這些信息選擇相應的方法執行。
保存這些信息的類被稱為Class。
獲得類對象的三種方法:
1、Object類中的getClass()方法將會返回一個Class類型的實例。
2、還可以通過調用靜態方法forName獲得類名對應的Class對象。
String className = “java.util.Date”;
Class c1 = Class.forName(className);
當包含main()方法的類被加載時,將會遞歸加載全部所需類。
一種逐步加載方法,調用Class.forName()手工加載其他類。
3、Class cl1 = Data.class;
?
可以利用==運算符實現兩個類對象的比較操作。
?
類對象的newInstance()方法可以快速創建一個類的實例,調用的是默認構造函數。
String s = “java.util.Date”;
Object m = Class.forName(s).newInstance();
?
利用反射分析類的能力:
反射機制最重要的內容——檢查類的結構。
在java.lang.reflect包中用三個類Field、Method、Constructor分別用于描述類的域、方法和構造函數。
Class類中的getFileds()、getMethods()、getConstructors()將返回public域、方法、構造器數組,包括超類的公有成員。
Class類中的getDeclareFiels()、getDeclaredMehtods()、getDeclaredConstructors()返回全部域、方法、構造器數組,不包括超類的公有成員。
?
public class ReflectionTest
{public static void main(String[] args){String name;Scanner in = new Scanner(System.in);System.out.print("Enter class name: ");name = in.next();StringBuilder sb = new StringBuilder();try{Class c1 = Class.forName(name);Class superc1 = c1.getSuperclass();String modifiers =Modifier.toString(c1.getModifiers());if ( modifiers.length() > 0 )sb.append(modifiers + " ");sb.append("class" + name);if (superc1 != null && superc1 != Object.class)sb.append(" extends " + superc1.getName());sb.append("\n{\n");Constructor[]constructors = c1.getDeclaredConstructors();for (Constructor c : constructors){String cname = c.getName();sb.append("\t");String cmodifier =Modifier.toString(c.getModifiers());if (cmodifier.length() != 0)sb.append(cmodifier + " ");sb.append(cname+"(");Class[]cparameters = c.getParameterTypes();for ( int j = 0; j < cparameters.length; j++){if (j > 0)sb.append(",");sb.append(cparameters[j].getName());}sb.append(");\n");}sb.append("\n\n");Method[]methods = c1.getDeclaredMethods();for (Method m : methods){sb.append("\t");String mmodifier =Modifier.toString(m.getModifiers());if (mmodifier.length() != 0)sb.append(mmodifier + " ");String rtype = m.getReturnType().getName();sb.append(rtype + " " + m.getName() + "(");Class[]mparameters = m.getParameterTypes();for (int i = 0; i < mparameters.length; i++){if (i > 0)sb.append(",");sb.append(mparameters[i].getName());}sb.append(")\n");}sb.append("\n\n");Field[]fields = c1.getDeclaredFields();for (Field f : fields){sb.append("\t");String fmodifier =Modifier.toString(f.getModifiers());if( fmodifier.length() !=0 )sb.append(fmodifier + " ");Class type = f.getType();String tname = type.getName();String fname = f.getName();sb.append(tname + " " + fname + ";\n");}sb.append("}");System.out.println(sb);}catch(ClassNotFoundException e){e.printStackTrace();}}
}
利用反射機制查看域值:
f.setAcessible(true);
f.get(obj);
利用反射機制修改域值:
f.get(obj, value);
?
編寫一個可供任意類使用的通用toString方法:
1、使用getDeclaredFileds獲得所有數據域;
2、使用setAccessible將所有的域設置為可訪問的;
3、對每個域,獲得名字和值。
?
setAccessible方法是AccessibleObject類中的一個方法,是Filed、Method、Constructor類的公共超類。
?
Method對象的invoke方法,允許調用包裝在當前Method對象中的方法。
方法簽名:
Object invoke(Object obj, Object... args);
第一個參數為具體對象,其余為這個方法的參數。
對于靜態方法,第一個參數設置為NULL。
Invoke的參數和返回值必須是Object類型的。
使用反射獲得方法指針的代碼要比僅僅直接調用方法明顯慢一些。
?
5.7 繼承設計的技巧
1、將公共操作和域放在超類;
2、不要使用受保護的域;
3、使用繼承體現IS-A關系;
4、除非所有繼承的方法都有意義,否則不要使用繼承;
5、在覆蓋原方法時,不要改變預期的行為;
6、使用多態;
7、不要過多的使用反射——對編寫系統程序及其有用,不適用于應用程序的編寫。
?