天天看點

學習面向對象第二節

本節的内容的我自己的主要難點在調用父類構造器

1.Java包

Java預設為源檔案導入java.lang包下的國有類,是以前面在Java程式中使用String和System類都無需使用import來導入這些類,但對于前面介紹的數組時候提到的Array類,則必須使用import語句來導入該類。

使用import可以省略包名,但是使用import static 則可以連類名都省略

2.Java源檔案的大體結構

package語句 //0個或者是1個,必須放在檔案的開始

import | import static語句   //0個或多個,必須放在所有類定義之前

public classDefinition | interfaceDefinition | enumDefinition   //0個或1個public類、接口或枚舉定義

classDefinition | interfaceDefinition | enumDefinition   //0個或多個普通類、接口或枚舉

上面提到了接口定義、枚舉定義,可以暫時把接口、枚舉都當成一種特殊的類

3.Java的常用包

Java的核心類都放在java包下以及其子包下,Java擴充的許多類都放在javax包以及其子包下。這些實用類就是前面所說的API(應用接口),Oracle按這些類的功能分别放在不同的包下。下面幾個包是Java的常用包

1)java.lang:這個包下包含了Java語言的核心類,如String,System,Math,和Thread類等,使用這個包下的類無需使用import導入,系統會自動導入這個包下的所有類

2)java.util:這個包下包含了Java的大量工具類\接口和集合架構類\接口,例如Arrays和list、Set等

3)java.net:這個包下包含了Java網絡程式設計相關的類和接口

4)java.io這個包下包含了一些Java輸入輸出程式設計相關的類和接口

5)java.text:這個包下包含了一些Java格式相關的類

6)java.sql:這個包下包含了一些Java進行JDBC資料庫程式設計的相關類和接口

7)java.awt:這個包下包含了抽象視窗集合(Abstruct Window ToolKits)的相關類\接口,這些主要用于圖形使用者界面(GUI)程式

8)java.swing這個包下包含了Swing圖形使用者界面程式設計的相關類\接口,這些類可用于建構平台無關GUI程式

4.深入構造器

構造器是一個特殊的方法,這個特殊的方法用于建立對象時執行初始化。構造器是建立對象的重要途徑(即使使用工廠模式、反射的等方式建立對象,其實質依然是依賴于構造器),是以,Java類必須包含一個或一個以上的構造神器。

下面定義了一個自定義的構造器,通過這個構造器就可以讓程式員進行及定義的初始化操。

構造器是建立Java對象的重要途徑,通過new關鍵字調用構造器時,構造器也确實傳回了該類的對象,但這個對象并不是完全由構造器負責建立的。實際上,當程式員調用這個構造器時,系統會先為對象配置設定記憶體空間,并為這個對象執行預設初始化,這個對象已經産生了——這些操作在構造器執行之前就完成了。也就是說,當系統開始執行構造器執行體之前,系統已經産生了一個對象,知識這個對象還不能被外部程式通路,隻能在該構造器中通過this類引用。當構造器的執行體執行結束之後,這個對象作為構造器的傳回值傳回,通常還會賦給另一個引用類型變量,進而讓外部程式可以通路。

構造器的重載

public class ConstructorTestOverload {
	public String name;
	public int count;
	//提供無參數的構造器
	public ConstructorTestOverload(){	
	}
	//提供兩個參數的構造器
	//對該構造器傳回的對象執行初始化
	public ConstructorTestOverload(String name,int count){
		this.name=name;
		this.count=count;
	}
	public static void main(String[] args){
		//通過無參構造器構造ConstructorTestOverload對象
		ConstructorTestOverload col=new ConstructorTestOverload();
		ConstructorTestOverload col2=new ConstructorTestOverload("瘋狂Java講義",9000);
		System.out.println(col.count);
		System.out.println(col2.count);
		
	}
		
}
           
public class Apple extends Fruit{
	public String name;
	public String color;
	public double weight;
	public Apple(){
	}
	//兩個參數的構造器
	public Apple(String name,String color){
		this.name=name;
		this.color=color;
	}
	public Apple(String name,String color,double weight){
		this(name,color);
		this.weight=weight;
		//this(name,color);this調用另一個重載的構造器的時候this語句必須放在第一條語句
	}
	public static void main(String[] args){
		/*//建立Apple對象
		Apple a=new Apple();
		//Apple對象本身沒有weight成員變量
		//因為Apple的父類有weight成員變量,也可以通路Apple對象的weight成員變量
		a.weight=56;
		//調用Apple的info()方法
		a.info();*/
		Apple ap1=new Apple();
		Apple ap2=new Apple("蘋果","紅色");
		Apple ap3=new Apple("香蕉","黃色",16);
		System.out.println(ap1.name+","+ap2.name+","+ap3.name);
	}
}
           

使用this調用另一個重載的構造器隻能在構造器中使用,而且必須作為構造器執行體的第一條語句。使用this調用重載的構造器時,系統根據this後括号裡的實參來調用形參與之對應的構造器。

5.重寫父類的方法

方法的重寫要遵循“兩同兩小一大”的原則,“兩同”即方法名相同,形參清單相同;“兩小”值的是子類方法傳回值類型應比父類更小或者相等,“一大”指的是,子類方法的通路權限應比父類方法的通路權限更大或者相等。尤其是要指出的是,覆寫方法和被覆寫的方法要麼都類方法,要麼都是執行個體方法,不能一個類方法,一個是執行個體方法。

如果父類有private通路權限,則該方法對其子類是隐藏的,是以其子類無法通路該方法,也就是無法重寫該方法。如果子類中定義了一個與父類private方法具有相同方法名、相同的形參清單、相同傳回值類型的方法,依然不是重寫,知識在子類中定義了一個新方法。

5.1super限定

如果子類定義了和父類同名的執行個體變量,則會發生子類的執行個體變量隐藏父類執行個體變量的情形。在正常情況下,子類定義的方法直接通路該執行個體變量預設會通路到子類中定義的執行個體變量,無法通路到父類的子類中被隐藏的執行個體變量。在子類定義的執行個體方法中可以e通過super來通路父類中被影藏的執行個體變量,如下代碼:

class BaseClass{
	public int a=5;
	public void fly(){
		System.out.println("父類的方法被調用");
	}
}
public class SubClass extends BaseClass{
	public int a=7;
	public void fly(){
		System.out.println("子類的方法被調用");
	}
	public void accessOwner(){
		System.out.println(a);
	}
	public void accessBase(){
		System.out.println(super.a);
	}
	public void callOverridedMethod(){
		super.fly();
	}
	public static void main(String[] agrs){
		SubClass sc=new SubClass();
		sc.accessOwner();
		sc.accessBase();
		sc.fly();
		sc.callOverridedMethod();
	}
}
           

需要注意的是super關鍵字并不能夠通路父類中被隐藏的成員變量或者私有的方法(private)隻能通路public修飾方法或者是變量。而且super關鍵字不能出現在static方法的程式片中。

6.調用父類構造器

class BaseClass{
	public int a=5;
	public void fly(){
		System.out.println("父類的方法被調用");
	}
}
public class SubClass extends BaseClass{
	public int a=7;
	public void fly(){
		System.out.println("子類的方法被調用");
	}
	public void accessOwner(){
		System.out.println(a);
	}
	public void accessBase(){
		System.out.println(super.a);
	}
	public void callOverridedMethod(){
		super.fly();
	}
	public static void main(String[] agrs){
		SubClass sc=new SubClass();
		sc.accessOwner();
		sc.accessBase();
		sc.fly();
		sc.callOverridedMethod();
	}
}
           
class Creature{
	public Creature(){
		System.out.println("Creature無參數構造器");
	}
}
class Animal extends Creature{
	public Animal(String name){
		System.out.println("Animals帶一個參數的構造器"+"動物的名字"+name);
	}
	public Animal(String name,int age){
		//使用this調用同一個重載構造器
		this(name);
		System.out.println("Animal帶兩個參數的構造器,"+"其age為"+age);
	}
}
public class Wolf extends Animal{
	public Wolf(){
		//顯示調用父類有兩個參數的構造器
		super("灰太狼",3);
		System.out.println("Wolf無參數構造器");
	}
	public static void main(String[] args){
		new Wolf();
	}
}
           

其中父類中已經定義了一個構造方法,在子類中必須要定義一個構造方法,不管構造方法中是否有參數,都需要在子類中建立一個構造方法,否則會報錯,自己的了解,因為如果沒有在的子類中建立一個構造方法,那麼就無法根據構造方法建立,必須要在子類中建立一個構造器。

7.多态

如果編譯時類型和運作時的類型不一緻,就可以出現所謂的多态。

class BaseClass{
    public int book=6;
    public void base(){
        System.out.println("父類的普通方法");
    }
    public void test(){
        System.out.println("父類被覆寫的方法");
    }
    public void Test(){
        System.out.println("測試猜想是否正确");
    }
}
public class SubClass extends BaseClass{
    //重新定義一個book執行個體變量隐藏父類的book執行個體變量
    public String book="輕量級Java EE企業應用實戰";
    public void test(){
        System.out.println("子類覆寫父類的方法");
    }
    public void sub(){
        System.out.println("子類的普通方法");
    }
    public static void main(String[] args){
        //下面編譯時出現類型和運作時類型完全一樣,是以不存在多态
        BaseClass bc=new BaseClass();
        System.out.println(bc.book);
        //下面兩次調用将執行BaseClss方法
        bc.base();
        bc.test();
        //下面編譯時出現類型和運作類型完全一樣,是以不存在多态
        SubClass sc=new SubClass();
        //輸出"輕量級Java EE企業應用實戰"
        System.out.println(sc.book);
        //下面調用從父類繼承的base方法
        sc.base();
        //下面調用執行目前類的test方法
        sc.test();
        //下面編譯時類型和運作時類型不一樣,多台發生
        BaseClass ploymophicBc=new SubClass();
        //輸出6表明通路的是父類對象的執行個體變量
        //子類的不可以new一個父類
        //SubClass ploy=new BaseClass();
        //允許将子類的指針指派給父類
        System.out.println(ploymophicBc.book);
        //下面調用将執行從父類繼承的base()方法
        ploymophicBc.base();
        //下面調用将執行目前類的test方法
        ploymophicBc.test();
        //因為ploymophicBc編譯時類型是BaseClass
        //BaseClass類型沒有提供sub(),方法是以下面的代碼會出現錯誤
        //ploymophicBc.sub();
        ploymophicBc.Test();
    }
}
           

上面程式的main()方法提供了三個引用變量,對于前兩個引用變量bc和sc,他們編譯時類型和運作類型完全相同,是以調用它們的成員變量和方法非常正常,完全沒有任何問題。但第三個引用變量ploymophinBc則比較特殊,他的編譯時類型是BaseClass,而運作類型是SubClass,當調用該引用變量的test()方法時,實際執行的是SubClass類中覆寫後的test()方法,這就實作多态了。

因為子類其實是一種特殊的父類,是以Java允許把一個子類對象直接賦給一個父類引用變量,無須任何類型轉換,或者被稱為向上轉型(upcasting),向上轉型由系統自動完成。