天天看點

面向對象--封裝、繼承、多态面向對象–封裝、繼承、多态

面向對象–封裝、繼承、多态

面向對象思想的本質:

不斷的尋找對象, 建立對象, 指揮對象做事情.

面向對象的特點

  1. 是一種更符合人們思考習慣的思想.
  2. 将複雜的事情簡單化
  3. 将我們從執行者, 變成了指揮者

類和對象

類: 一類事物屬性和行為的集合(抽象), 是Java中的一種引用資料類型

對象: 該類事物的具體展現

建立類,類中具有屬性和方法,對象是将類進行實體化,對象可以調用類中的方法。

成員變量和局部變量:

局部變量 成員變量
位置不同 方法中或方法聲明上 類中方法外
作用域不同 所在的方法中 整個類中
預設值不同 沒有預設值 有預設值
局部變量在使用前必須指派

1.封裝

1.1private關鍵字

  • private(私有) :

    ​ 可以用來修飾類中的成員變量和成員方法.

    ​ 被private修飾之後, 隻能在本類中被通路.

1.2 this

  • this的作用: 用于區分成員變量和局部變量痛的的情況, this.擷取的是成員變量.
  • this: 誰來調用我(我所在的方法), 我就代表誰

1.3.封裝

原則

  1. 将成員變量使用private修飾.
  2. 對外提供公共的通路方式(get/set)

1.4 構造方法

// 構造方法沒有傳回值, 連void都沒有
// 構造方法名和類名一樣

// 空參構造/無參構造
public 構造方法名() {
    
}

// 有參構造
public 構造方法名(參數清單) {
    ... 
}
           

作用

  1. 建立對象
  2. 給類中的成員進行初始化
構造方法的注意事項
  1. 如果類中沒有給出任何的構造方法, 系統會預設提供空參構造
  2. 如果類中給出了任何的構造方法, 系統将不會提供空參構造

1.5标準類

  • 功能類
/*
    成員變量
        使用private修飾

    構造方法
        提供一個無參構造方法
        提供一個帶多個參數的構造方法

    成員方法
        提供每一個成員變量對應的setXxx()/getXxx()
        提供一個顯示對象資訊的show()
 */
           
  • 測試類
/*
    建立對象并為其成員變量指派的兩種方式
        1:無參構造方法建立對象後使用setXxx()指派
        2:使用帶參構造方法直接建立帶有屬性值的對象
*/
           

2.繼承

繼承:就是子類繼承父類的屬性和行為,使得子類對象可以直接具有與父類相同的屬性、相同的行為。 子類可以直接通路父類中的非私有的屬性和行為。

繼承的好處:

  1. 提高代碼的複用性(減少代碼備援,相同代碼重複利用)。
  2. 使類與類之間産生了關系。

2.1繼承的格式

通過 extends 關鍵字,可以聲明一個子類繼承另外一個父類,定義格式如下:

class 父類 {
    ...
}class 子類 extends 父類 {
    ...
}
           
需要注意:Java是單繼承的,一個類隻能繼承一個直接父類,跟現實世界很像,但是Java中的子類是更加強大的。

2.2子類不能繼承的内容

  1. 子類不能繼承父類的構造器,因為子類有自己的構造器。
  2. 值得注意的是子類可以繼承父類的私有成員(成員變量,方法),隻是子類無法直接通路而已,可以通過getter/setter方法通路父類的privat成員變量。

**2.3 **繼承後的特點-成員變量

子父類中出現了同名的成員變量時,子類會優先通路自己對象中的成員變量。如果此時想通路父類成員 變量如何解決呢?我們可以使用super關鍵字。

supre通路父類成員變量

子父類中出現了同名的成員變量時,在子類中需要通路父類中非私有成員變量時,需要使用 super 關 鍵字,修飾父類成員變量,類似于之前學過的 this 。

需要注意的是:super代表的是父類對象的引用,this代表的是目前對象的引用。

2.4繼承後的特點-成員方法

如果子類父類中出現重名的成員方法,則建立子類對象調用該方法的時候,子類對象會優先調用自己的 方法。

方法的重寫

方法重寫 :子類中出現與父類一模一樣的方法時(傳回值類型,方法名和參數清單都相同),會出現覆 蓋效果,也稱為重寫或者複寫。聲明不變,重新實作。

使用場景與案例

發生在子父類之間的關系。 子類繼承了父類的方法,但是子類覺得父類的這方法不足以滿足自己的需 求,子類重新寫了一個與父類同名的方法,以便覆寫父類的該方 法。

public class Animal { 
    public void run(){ 
        System.out.println("動物跑的很快!"); 
    }
    public void cry(){ 
        System.out.println("動物都可以叫~~~"); 
    } 
}
           
public class Cat extends Animal {
	public void cry(){
		System.out.println("我們一起學貓叫,喵喵喵!喵的非常好聽!");
  }
}

public class Test { 
    public static void main(String[] args) { 
        // 建立子類對象 
        Cat ddm = new Cat();
            // 調用父類繼承而來的方法 
            ddm.run(); 
        // 調用子類重寫的方法 
        ddm.cry();
    } 
}
           
注意事項
  1. 方法重寫是發生在子父類之間的關系。
  2. 子類方法覆寫父類方法,必須要保證權限大于等于父類權限。
  3. 子類方法覆寫父類方法,傳回值類型、函數名和參數清單都要一模一樣。

2.5繼承後的特點_構造方法

繼承後子類構造器特點子類所有構造器的第一行都會先調用父類的無參構造器,再執行自己

2.6super()和this()

this.成員變量 -- 本類的 
super.成員變量 -- 父類的 
this.成員方法名() -- 本類的 
super.成員方法名() -- 父類的
           

使用調用構造器格式:

super(...) -- 調用父類的構造器,根據參數比對确認 	 this(...) -- 調用本類的其他構造器,根據參數比對确認
           

注意:

子類的每個構造方法中均有預設的super(),調用父類的空參構造,手動調用父類構造會覆寫預設的super()。

super(…)和this(…)都必須是在構造方法的第一行,是以不能同時出現。

super(…)是根據參數去确定父類哪個構造器的。

this/super可以解決的問題:

this代表所在類的目前對象的引用(位址值),即代表目前對象。

this用在get/set方法中,誰調用這個方法(哪個對象就調用這個方法),this就代表誰(this就代表哪個對象)。

this用在構造器中,代表構造器正在初始化的那個對象

2.7抽象方法

抽象方法的概念:

抽象方法:沒有方法體的方法

抽象類:包含抽象方法的類

抽象類的格式:

修飾符 abstract 傳回值類型 方法名(參數清單)

public abstract void run();

抽象類的子類必須重寫父類所有的抽象方法。否則,該子類也必須聲名為抽象類

父類抽象方法的存在意義:

抽象類存在的意義是為了被子類繼承,否則抽象類将毫無意義,抽象類展現的是模闆思想,模闆是通用的東西抽象類中已經是具體的實作(抽象類中可以有成員變量和實作方法),而模闆中不能決定的東西定義成抽象方法,讓使用模闆(繼承抽象類的類)的類去重寫抽象方法實作需求,這是典型的模闆思想。

final修飾的類/方法/變量的特點:

為了避免子類可以再父類的基礎上改寫父類内容。Java提供了final關鍵字,用于修飾不可改變内容。

被final修飾的類,不能被繼承。 被修飾的方法不能被重寫。 被修飾的變量,不能被重新指派。

引用類型的局部變量,被final修飾後,隻能指向一個對象,位址不能再更改。但是不影響對象内部的成員變量值的修改。

public class FinalDemo2 {
      public static void main(String[] args) {
        // 建立 User 對象
        final  User u = new User();
    // 建立 另一個 User對象
    // u = new User(); // 報錯,指向了新的對象,位址值改變。
    // 調用setName方法
    u.setName("張三"); // 可以修改
	}
}
           

3.接口

3.1什麼是接口

接口,是Java語言中一種引用類型,是方法的集合,如果說類的内部封裝了成員變量、構造方法和成員 方法,那麼接口的内部主要就是封裝了方法,包含抽象方法(JDK 7及以前),預設方法和靜态方法

接口的定義:它與定義類方式相似,但是使用 interface 關鍵字。它也會被編譯成.class檔案,但一定 要明确它并不是類,而是另外一種引用資料類型。

public class 類名.java–>.class

public interface 接口名.java–>.class

引用資料類型:數組,類,接口。

接口的使用,它不能建立對象,但是可以被實作( implements ,類似于被繼承)。一個實作接口的類 (可以看做是接口的子類),需要實作接口中所有的抽象方法,建立該類對象,就可以調用方法了,否

則它必須是一個抽象類。

3.2定義格式

public interface 接口名稱 {
    // 抽象方法 
    // 預設方法 
    // 靜态方法 
}
           

抽象方法:使用 abstract 關鍵字修飾,可以省略,沒有方法體。該方法供子類實作使用。

public interface InterFaceName {
    public abstract void method(); 
}
           

含有預設方法和靜态方法

預設方法:使用 default 修飾,不可省略,供子類調用或者子類重寫。

靜态方法:使用 static 修飾,供接口直接調用。

public interface InterFaceName { 
    public default void method() { 
        // 執行語句 
    }
    public static void method2() {
        // 執行語句
    }
}
           
小結:定義接口時就是将定義類的class改成了interface,并且接口中的内容也有了一些變化

3.3接口的實作

實作的概述

類與接口的關系為實作關系,即類實作接口,該類可以稱為接口的實作類,也可以稱為接口的子類。實作的動作類似繼承,格式相仿,隻是關鍵字不同,實作使用 implements 關鍵字。

非抽象子類實作接口:

  1. 必須重寫接口中所有抽象方法。
  2. 繼承了接口的預設方法,即可以直接調用,也可以重寫。

實作格式:

class 類名 implements 接口名 {
    // 重寫接口中抽象方法【必須】
    // 重寫接口中預設方法【可選】
}
           

接口中的靜态方法與.class檔案相關,隻能使用接口名調用,不可以通過實作類的類名或者實作類的對象調用

4.4接口的多實作

  • 之前學過,在繼承體系中,一個類隻能繼承一個父類。而對于接口而言,一個類是可以實作多個接 口的,這叫做接口的多實作。并且,一個類能繼承一個父類,同時實作多個接口。

實作格式

class 類名 [extends 父類名] implements 接口名1,接口名2,接口名3... { 
    // 重寫接口中抽象方法【必須】 
    // 重寫接口中預設方法【不重名時可選】 
}
           

接口中,有多個抽象方法時,實作類必須重寫所有抽象方法。如果抽象方法有重名的,隻需要重寫一次,有多個預設方法時,實作類都可繼承使用。如果預設方法有重名的,必須重寫一次,存在同名的靜态方法并不會沖突,原因是隻能通過各自接口名通路靜态方法。

優先級的問題

當一個類,既繼承一個父類,又實作若幹個接口時,父類中的成員方法與接口中的預設方法重名,子類 就近選擇執行父類的成員方法。

4.5接口的多繼承

一個接口能繼承另一個或者多個接口,這和類之間的繼承比較相似。接口的繼承使用 extends 關鍵 字,子接口繼承父接口的方法。**如果父接口中的預設方法有重名的,那麼子接口需要重寫一次。**代碼如下:

定義父接口:

interface A {
    public default void method(){ 	 		                System.out.println("AAAAAAAAAAAAAAAAAAA"); 
    } 
}

interface B {
    public default void method(){                             System.out.println("BBBBBBBBBBBBBBBBBBB"); 
    }
}
           

定義子接口:

interface D extends A,B{
    @Override 
    public default void method() {                             System.out.println("DDDDDDDDDDDDDD");
     } 
}
           

小貼士:

子接口重寫預設方法時,default關鍵字可以保留。

子類重寫預設方法時,default關鍵字不可以保留。

小結:接口和接口之間是繼承的關系,而不是實作。一個接口可以繼承多個接口。

4.多态

  • 定義: 是指同一行為,具有多個不同表現形式

多态的前提:

  1. 繼承或者實作【二選一】
  2. 方法的重寫【意義展現:不重寫,無意義】
  3. 父類引用指向子類對象【格式展現】

4.1多态的展現

Fu f = new Zi();	//父類類型 變量名 = new 子類對象;
f.method();			//變量名.方法名();
           

當使用多态方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執行的 是子類重寫後方法。

多态在代碼中的展現為父類引用指向子類對象。

4.2多态的好處

  • 實際開發的過程中,父類類型作為方法形式參數,傳遞子類對象給方法,進行方法的調用,更能體 現出多态的擴充性與便利。

定義父類:

public abstract class Animal { 

	public abstract void eat(); 

} 
           

定義子類:

class Cat extends Animal {
    public void eat() {
        System.out.println("吃魚"); 
    } 
}
	class Dog extends Animal {
        public void eat() {
            System.out.println("吃骨頭");
        }
    }
}
           

定義測試類:

public class Test {
    public static void main(String[] args) {
        // 多态形式,建立對象
        Cat c = new Cat();
        Dog d = new Dog();
        // 調用showCatEat 
        showCatEat(c);
        // 調用showDogEat 
        showDogEat(d);
        /*以上兩個方法, 均可以被showAnimalEat(Animal a)方法所替代 而執行效果一緻 */
        showAnimalEat(c); 
        showAnimalEat(d); 
    }
    
    public static void showCatEat (Cat c){ 
        c.eat(); 
        }
    
    public static void showDogEat (Dog d){ 
        d.eat(); 
    }
    
    public static void showAnimalEat (Animal a){ 
        a.eat();
    } 
}
           

由于多态特性的支援,showAnimalEat方法的Animal類型,是Cat和Dog的父類類型,父類類型接收子 類對象,當然可以把Cat對象和Dog對象,傳遞給方法。

當eat方法執行時,多态規定,執行的是子類重寫的方法,那麼效果自然與showCatEat、showDogEat 方法一緻,是以showAnimalEat完全可以替代以上兩方法。

不僅僅是替代,在擴充性方面,無論之後再多的子類出現,我們都不需要編寫showXxxEat方法了,直 接使用showAnimalEat都可以完成。

是以,多态的好處,展現在,可以使程式編寫的更簡單,并有良好的擴充。

小結:多态的好處是提高程式的靈活性,擴充性

**向上轉型:**多态本身是子類類型向父類類型向上轉換的過程,這個過程是預設的。 當父類引用指向一個子類對象時,便是向上轉型。

父類類型 變量名 = new 子類類型(); 
如:Animal a = new Cat();
           

4.3多态的弊端

當使用多态方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能 調用子類有而父類沒有的方法。編譯都錯誤,更别說運作了。這也是多态給我們帶來的一點"小麻煩"。

定義類:

//父類
abstract class Animal { 
    abstract void eat(); 
}

//子類cat
class Cat extends Animal {
    public void eat() {
        、System.out.println("吃魚"); 
    }
    public void catchMouse() {
        System.out.println("抓老鼠");
    } 
}

//子類dog
class Dog extends Animal { 
    public void eat() { 
        System.out.println("吃骨頭"); 
    }public void watchHouse() {
        System.out.println("看家"); 
    } 
}
           

定義測試類:

public class Test {
    public static void main(String[] args) { 
        // 向上轉型 
        Animal a = new Cat();
        a.eat();		// 調用的是 Cat 的 eat
        
        //從上面定義的類,可以看到catch Mouse()和watch House()方法是子類中的特有的方法,此時再用向上轉型必然會出現錯誤是以想要調用子類特有的方法,就必須做向下轉型
        
        // 向下轉型 
        Cat c = (Cat)a; 
        c.catchMouse();	 // 調用的是 Cat 的 catchMouse 		}
}
           
向下轉型:類似于資料類型的強制轉換

轉型的異常:

public class Test {
    public static void main(String[] args) {
        // 向上轉型 
        Animal a = new Cat(); 
        a.eat();	 // 調用的是 Cat 的 eat 
        
        // 向下轉型 Dog d = (Dog)a; 
        d.watchHouse(); 	// 調用的是 Dog 的 watchHouse 【運作報錯】 
    } 
}
           

這段代碼可以通過編譯,但是運作時,卻報出了 ClassCastException ,類型轉換異常!這是因為, 明明建立了Cat類型對象,運作時,當然不能轉換成Dog對象的。這兩個類型并沒有任何繼承關系,不 符合類型轉換的定義。

  • 為了避免ClassCastException的發生,Java提供了

    instanceof

    關鍵字,給引用變量做類型的校驗,格 式如下:
變量名 instanceof 資料類型 
如果變量屬于該資料類型,傳回true。
如果變量不屬于該資料類型,傳回false。
           

是以,轉換前,我們最好先做一個判斷,代碼如下:

public class Test { 
    public static void main(String[] args) {
        // 向上轉型 
        Animal a = new Cat(); 
        a.eat(); 		// 調用的是 Cat 的 eat 
        
        // 向下轉型 
        if (a instanceof Cat){ 
            Cat c = (Cat)a;
            c.catchMouse(); // 調用的是 Cat 的 catchMouse 
        } else if (a instanceof Dog){
            Dog d = (Dog)a; d.watchHouse(); // 調用的是 Dog 的 watchHouse
        }
    } 
}
           
小結:多态向上轉型是将子類類型轉成父類類型,多态向下轉型是将父類類型轉成子類類型。

5.匿名内部類

  • 匿名内部類:是内部類的簡化寫法。它的本質是一個

    帶具體實作的 父類或者父接口的 匿名的

    子類 對象

開發中,最常用到的内部類就是匿名内部類了。以接口舉例,當你使用一個接口時,似乎得做如下幾步 操作,

  1. 定義子類
  2. 重寫接口中的方法
  3. 建立子類對象
  4. 調用重寫後的方法

我們的目的,最終隻是為了調用方法,那麼能不能簡化一下,把以上四步合成一步呢?匿名内部類就是 做這樣的快捷方式

前提:存在一個類或者接口,這裡的類可以是具體類也可以是抽象類。

普通寫法:

//定義接口
public abstract class FlyAble{
    public abstract void fly(); 
}

//測試類
public class InnerDemo01 {
    public static void main(String[] args) {
        /* 
        	1.等号右邊:定義并建立該接口的子類對象 
        	2.等号左邊:是多态,接口類型引用指向子類對象 
        */
        FlyAble f = new FlyAble(){
            public void fly() {
                System.out.println("我飛了~~~"); 
            } 
        };
    } 
}
           

匿名内部類直接調用方法

public class InnerDemo02 {
    public static void main(String[] args) {
        /*
    	1.等号右邊:定義并建立該接口的子類對象 
        2.等号左邊:是多态,接口類型引用指向子類對象
        */
        new FlyAble(){
            public void fly() {
                System.out.println("我飛了~~~"); 
            } 
        }.fly(); 
    }
}
           

匿名内部類做的事情是建立一個類的子類對象 (沒有給出名字而已)。

5.static關鍵字

5.1概述

static是靜态修飾符,一般修飾成員。被static修飾的成員屬于類,不屬于單個這個類的某個對象。 static修飾的成員被多個對象共享。static修飾的成員屬于類,但是會影響每一個對象。被static修飾的 成員又叫類成員,不叫對象的成員。

  • 類變量:使用static關鍵字修飾的成員變量

static

修飾成員變量時,該變量稱為類變量。該類的每個對象都共享同一個類變量的值。任何對象 都可以更改該類變量的值,但也可以在不建立該類的對象的情況下對類變量進行操作。

靜态方法

當 static 修飾成員方法時,該方法稱為類方法 。靜态方法在聲明中有 static ,建議使用類名來調 用,而不需要建立類的對象。調用方式非常簡單。

  • 類方法:使用 static關鍵字修飾的成員方法,習慣稱為靜态方法。
  • 靜态方法調用的注意事項:
  1. 靜态方法可以直接通路類變量和靜态方法。
  2. 靜态方法不能直接通路普通成員變量或成員方法。
  3. 靜态方法中,不能使用this關鍵字。
靜态隻能通路靜态.

調用格式

被static修飾的成員可以并且建議通過類名直接通路。雖然也可以通過對象名通路靜态成員,原因即多 個對象均屬于一個類,共享使用同一個靜态成員,但是不建議,會出現警告資訊。