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