java筆記(三)[java中的繼承&java中的方法重載,重寫,重構&java中的向上轉型和向下轉型&java中的抽象類和接口&java中的接口向上轉型]

1、在繼承中子類沒有權限去調用父類中的private方法,只能調用父類中修飾爲public或protected的成員方法

2、重載(overloading)在子類中將父類的成員方法的名稱保留,重寫成員方法的實現內容,更改成員方法的存儲權限,或修改成員方法的返回類型,還可以修改方法的修飾權限(只能將修飾權限只能從小的範圍到大的範圍改變eg:從protected->public)還有一種特殊的方法--重寫,子類與父類的成員方法返回值、方法名稱、參數類型及個數完全相同,唯一不同的是方法的實現內容,這種特殊重寫方式稱爲重寫(overriding)。重寫充分體現了java的多態性,不同對象執行自己對應的方法,而又可以通過向上向下轉型來實現。子類重寫父類還可以修改方法的返回值類型,這種重寫方法需要遵循一個原則,即重寫的返回值類型必須是父類中同一方法返回值類型的子類,在實例化子類對象時,java編譯器會在子類構造方法中自動調用父類的無參構造方法。[下面爲該問題例子]

• e.g.

class A
{
	public A(){
		System.out.println("constructor of A");
	}
}
class B extends A
{
	public B(){
		System.out.println("constructor of B");
	}
}
public class Try extends B
{
	public Try(){
		System.out.println("constructor of Try");
	}
		
	public static void main(String[] args){
		Try myTry = new Try();
	}
}

• 運行結果如下:
constructor of A
constructor of B
constructor of Try
• 首先是頂級父類,然後是上一級父類,最後是子類。也就是說實例化子類對象時首先要實例化父類對象,然後再實例化子類對象,所以在子類構造方法訪問父類的構造方法之前,父類已經完成實例化操作。在實例化子類對象時,父類無參構造方法被自動調用,但有參構造方法並不能被自動調用只能依靠super關鍵詞顯示的調用父類的構造方法。即就是必須先構造父類才能構造子類對象。在寫finalize()方法時需要確保之類的最後一個動作是調用父類的finalize()方法。
3、 [對於重載、重寫、重構的區分]
• 方法的重載(Overloading):JAVA中方法的重載是在一個類中發生進行的。當有多個方法具有相同的名字、不同的參數列表時,便產生了方法的重載,此時方法的返回類型與修飾符可相同與不同均無關係。參數列表的不同包括參數的類型,參數的個數,參數的順序這三者之中至少有一項不同。當調用這些重載的方法時根據參數列表的不同來自動匹配方法,這也體現了JAVA的多態性。重載的類型有:參數個數不同,參數類型不同,參數順序不同,而單單返回值類型不同並不能作爲方法區分的條件,方法(函數)區分的條件爲函數名稱,參數[不定長參數的使用e.g. 返回值 方法名(參數類型…參數名稱)  e.g. public static int add(int…args) 方法體中用args.length 來確定傳入參數的多少,就行將參數存入不定長數組相同]
例如:
public class Person{
	public void say(){
	}
	private void say(String name,int age){
		System.out.println("方法的重載1");
	}
	public void say(int age,String name){
		System.out.println("方法的重載2");
	}
	public String say(int age,String name,String sex){
		System.out.println("方法的重載3");
		return name;
	}
}
• 方法的重寫(Overriding):JAVA中方法的重寫時發生在子類繼承父類的方式產生的。當子類繼承父類的方法時相對父類的方法作一定的修改,這時就用到了方法的重寫。在子類中進行方法的重寫時,要求子類中重寫的方法的訪問修飾符,返回類型,方法名,以及參數列表必須與父類的相同,否則編譯將出錯。有一種情況除外:即當子類中重寫的方法的返回類型是父類的返回類型(返回值)的子類,此時也可進行方法的重寫(但此法實行於JAVA5之後),如子類的返回類型爲String,而父類的爲Object。如果子類的返回類型是父類中返回類型的父類或者返回類型是基本數據類型,此時編譯將會報錯。此時若還想用父類的方法,可用super關鍵字來調用。
例如:

public class Person{
	public void say(String name,int age){
		System.out.println("方法的重寫");
	}  
}
public class Student extends Person{
	public void say(int age,String name){
		super.say(name, age);
		System.out.println("測試方法的重寫");
	}
}
  重構:千萬不要把重構理解爲“構造方法的重載”。在軟件工程中,重構是說程序員爲了對已有程序在儘量不改變接口的前提下,進行去除bug,提高效率,增加新的功能等等重新編寫代碼的工作從而調整軟件結構,改進代碼質量。
總結:方法的重載是JAVA的多態性在個類中的體現,而方法的重寫是JAVA的多態性在父類與子類(即類與類)之間的實現,重構是發生在軟件工程中的,不要與構造方法中的重載混淆。

4、[對於向上轉型和向下轉型的區分]對於“向下轉型的父類對象必須是子類對象實例”這句話的理解,以及instanceof的用法

向上轉型總是無害的(安全的),即就是將具有特點,具體的類向其繼承的抽象的,不具體的類轉化,比如平行四邊形類向四邊形類轉化,這種轉型是不會報錯的體現了java的多態性,e.g.:Quadrangle obj = new Parallelogram(); 而向下轉型是將其繼承的抽象的,不具體的類向具有特點,具體的類轉化,這樣會丟失信息,如直接將四邊形當做平行四邊形e.g.:Parallelogram obj = new Quadrangle(); 編譯器會直接報錯,拋出ClassCastException異常,只能使用強制類型轉化 e.g.:Quadrangle q = new Quadrangle(); Parallelogram p = (Parallelogram) q;但是後期將其當做Parallelogram去使用時調用子類Parallelogram中的方法會報錯,所以向下轉型必須滿足“向下轉型的父類對象必須是子類對象實例”這個條件,而判斷的方式就是使用instanceof關鍵詞,如何理解“向下轉型的父類對象必須是子類對象實例”這句話,直接看上去感覺是不可能的,但是其實是,先實例化了子類對象將其向上轉型爲其父類,然後又向下轉型爲子類,此時就很容易理解什麼是“向下轉型的父類對象必須是子類對象實例”這個父類對象本身必須是有子類向上轉型的再將其轉型回去自然不會出錯, 代碼如下: 

Quadrangle q = new Parallelogram();
Parallelogram p = (Parallelogram) q;

這樣就是對的了。 如何使用instanceof這個關鍵詞來判斷,代碼如下: 

Quadrangle q = new Parallelogram(); 
if(q instanceof Parallelogram){
	Parallelogram p = (Parallelogram) q;
}

這樣使用就保證了向下轉型的安全性
5、抽象類不能實例化對象,且往往父類均爲抽象類,使用abstract關鍵詞抽象類的內部必須都是抽象的,且沒有方法體(一般,但有時的抽象類中僅僅是因爲其中的某個方法爲抽象的進而曾格格類被限定爲抽象的,當被繼承後其中的非抽象方法即可在子類實例化對象中使用),這個方法體本身沒有意義,除非其被重寫,而承載這個抽象方法的抽象類必須被繼承。實際上抽象類除了被繼承沒有任何意義。只要類中有一個方法被定義爲抽象的則這個類本身必須被標記爲抽象的。抽象類被繼承後必須實現其中所有被標記爲抽象的方法,哪怕是空實現,因而會出現代碼冗餘的現象,進而出現了接口,將具有特殊性的抽象方法集成在接口中讓抽象父類更具有一般性,且java只允許單繼承多實現,因而使得抽象類中的每個抽象方法變得不可或缺,更具有普遍性,讓接口成爲特殊,讓需要實現接口中這些方法的子類去實現包含有這些特殊方法的接口。
接口是抽象類的延伸,注意接口中的方法不像抽象類中還可以有非抽象的存在,接口中純屬一種規定(強制)必須實現這些方法,因此其中的方法都沒有方法體,且均爲抽象的進而在接口中定義方法時省略了關鍵詞abstract,除此之外在接口中定義的任何字段都被默認當做static和final的。接口定義時使用關鍵詞interface即用該詞替換class實現該接口使用用關鍵詞implements,單繼承類,且接口同樣也是可以向上轉型的,將實現了A接口的類,可以實例化該類對應的實例,並且將其向上轉型爲A接口的實例化對象,並且調用A中規定的方法,此時會去調用該類實現該接口的實現內容,(下面測試下A類實現I接口,B繼承A實現I接口,然後將B轉型爲I接口調用下實現的方法看看調用的是誰,在試試B轉型爲A再轉型爲I接口調用下方法看看這個時候調動的是誰)多實現接口就好比多繼承相同。
測試:
interface I
{
	public void funcI();
}
		
class A implements I
{
	public A(){
		System.out.println("constructor of A");
	}
		
	public void funcI(){
		System.out.println("A class implements the function of interface I");
	}
}
		
class B extends A implements I
{
	public B(){
		super();
		System.out.println("constructor of B");
	}
		
	public void funcI(){
		System.out.println("B class implements the function of interface I");
	}
}
		
public class Try
{
	public static void main(String[] args){
		System.out.println("B類對象實例向上轉型爲I接口實例後調用接口中定義的方法");
		B b = new B();
		I i_b = b;
		i_b.funcI();
		System.out.println("將B類的實例化對象向上轉型爲A類,再向上轉型爲I接口實例,調用接口中定義的方法");
		A a = b;
//		System.out.println("測試下由B向上轉型的A是否爲爲B的實例使用instanceof 關鍵詞"+(a instanceof B));
//		if(a instanceof B){
//			B b_a = (B)a;
//		}//測試下向下轉型
		I i_b_a = a;
		i_b_a.funcI();
		System.out.println("直接生成A的實例化對象並向上轉型爲I接口實例,調用接口中定義的方法");
		a = new A();
		I i_a = a;
		i_a.funcI();
//		System.out.println("測試下由A是否爲B的實例與上面對比使用instanceof 關鍵詞"+(a instanceof B));
		
	}
}
結果:
B類對象實例向上轉型爲I接口實例後調用接口中定義的方法
constructor of A
constructor of B
B class implements the function of interface I
將B類的實例化對象向上轉型爲A類,再向上轉型爲I接口實例,調用接口中定義的方法
B class implements the function of interface I
直接生成A的實例化對象並向上轉型爲I接口實例,調用接口中定義的方法
constructor of A
A class implements the function of interface I
總結:java中的向下或向上轉型(無論是類抑或是接口)都始終以原始的那個實例化對象爲基準,就像例子中將B轉爲A再轉向I並不會去調用A中的實現方法,只有一開始就實例化的是A再向上轉型纔會調用A中實現接口的方法。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章