java面向?qū)ο蠡A(chǔ)(中)

## 本章學(xué)習(xí)目標(biāo)

- 理解封裝的意義
- 知道public、protected、缺省、private幾種權(quán)限修飾符的區(qū)別
- 知道標(biāo)準(zhǔn)Javabean的要求
- 掌握對(duì)象數(shù)組的聲明、初始化、遍歷
- 掌握增強(qiáng)for循環(huán)的使用
- 理解繼承的意義
- 掌握用extends實(shí)現(xiàn)子類(lèi)繼承父類(lèi)
- 知道Java子類(lèi)繼承父類(lèi)的要求
- 掌握方法重寫(xiě)的概念和要求
- 掌握toString、hashCode和equals方法的重寫(xiě)
- 掌握關(guān)鍵字final的使用
- 了解native關(guān)鍵字
- 掌握用abstract聲明抽象類(lèi)和抽象方法
- 掌握用interface聲明接口
- 掌握用implements實(shí)現(xiàn)接口
- 掌握接口與抽象類(lèi)的區(qū)別

## 6.1 封裝

### 6.1.1 封裝概述

為什么需要封裝?

* 我要用洗衣機(jī),只需要按一下開(kāi)關(guān)和洗滌模式就可以了。有必要了解洗衣機(jī)內(nèi)部的結(jié)構(gòu)嗎?有必要碰電動(dòng)機(jī)嗎?
* 我們使用的電腦,內(nèi)部有CPU、硬盤(pán)、鍵盤(pán)、鼠標(biāo)等等,每一個(gè)部件通過(guò)某種連接方式一起工作,但是各個(gè)部件之間又是獨(dú)立的。
* 現(xiàn)實(shí)生活中,每一個(gè)個(gè)體與個(gè)體之間是有邊界的,每一個(gè)團(tuán)體與團(tuán)體之間是有邊界的,而同一個(gè)個(gè)體、團(tuán)體內(nèi)部的信息是互通的,只是對(duì)外有所隱瞞。

面向?qū)ο缶幊陶Z(yǔ)言是對(duì)客觀世界的模擬,客觀世界里每一個(gè)事物的內(nèi)部信息都是隱藏在對(duì)象內(nèi)部的,外界無(wú)法直接操作和修改,只能通過(guò)指定的方式進(jìn)行訪問(wèn)和修改。封裝可以被認(rèn)為是一個(gè)保護(hù)屏障,防止該類(lèi)的代碼和數(shù)據(jù)被其他類(lèi)隨意訪問(wèn)。適當(dāng)?shù)姆庋b可以讓代碼更容易理解與維護(hù),也加強(qiáng)了代碼的安全性。

隨著我們系統(tǒng)越來(lái)越復(fù)雜,類(lèi)會(huì)越來(lái)越多,那么類(lèi)之間的訪問(wèn)邊界必須把握好,面向?qū)ο蟮拈_(kāi)發(fā)原則要遵循“高內(nèi)聚、低耦合”,而“高內(nèi)聚,低耦合”的體現(xiàn)之一:

* 高內(nèi)聚:類(lèi)的內(nèi)部數(shù)據(jù)操作細(xì)節(jié)自己完成,不允許外部干涉;
* 低耦合:僅對(duì)外暴露少量的方法用于使用

隱藏對(duì)象內(nèi)部的復(fù)雜性,只對(duì)外公開(kāi)簡(jiǎn)單和可控的訪問(wèn)方式,從而提高系統(tǒng)的可擴(kuò)展性、可維護(hù)性。通俗的講,把該隱藏的隱藏起來(lái),該暴露的暴露出來(lái)。這就是封裝性的設(shè)計(jì)思想。

```java
package com.atguigu.oop.encapsulation;

public class Circle {
   private double radius;

   public void setRadius(double radius) {
       if(radius < 0){
           return;
       }
       this.radius = radius;
   }

   public double getRadius() {
       return radius;
   }
}

```

```java
package com.atguigu.oop.encapsulation;

public class TestCircle {
   public static void main(String[] args) {
       double r1 = -1.5;
       double r2 = 2.5;

       Circle c1 = new Circle();
       Circle c2 = new Circle();
//        c1.radius = r1;
//        c2.radius = r2;

       /*
       if(r1>0){
           c1.radius = r1;
       }
       if(r2 > 0){
           c2.radius = r2;
       }*/

       c1.setRadius(r1);
       c2.setRadius(r2);

       System.out.println(c1.getRadius());
       System.out.println(c2.getRadius());
   }
}

```

 

### 6.1.2 幾種權(quán)限修飾符

如何實(shí)現(xiàn)封裝呢?實(shí)現(xiàn)封裝就是指控制類(lèi)或成員的可見(jiàn)性范圍,這就需要依賴(lài)訪問(wèn)控制修飾符(也稱(chēng)為權(quán)限修飾符)來(lái)控制。

| 修飾符    | 本類(lèi) | 本包 | 其他包子類(lèi) | 其他包非子類(lèi) |
| --------- | ---- | ---- | ---------- | ------------ |
| private   | √    | ×    | ×          | ×            |
| 缺省      | √    | √    | ×          | ×            |
| protected | √    | √    | √          | ×            |
| public    | √    | √    | √          | √            |

外部類(lèi):public和缺省

成員變量、成員方法、構(gòu)造器、成員內(nèi)部類(lèi):public,protected,缺省,private

 

### 6.1.3 成員變量/屬性私有化問(wèn)題

**<span style="color:red">成員變量(field)私有化</span>之后,提供標(biāo)準(zhǔn)的<span style="color:red">get/set</span>方法,我們把這種成員變量也稱(chēng)為<span style="color:red">屬性(property)</span>。**

> 或者可以說(shuō)只要能通過(guò)get/set操作的就是事物的屬性,哪怕它沒(méi)有對(duì)應(yīng)的成員變量。

1、成員變量封裝的目的

* 隱藏類(lèi)的實(shí)現(xiàn)細(xì)節(jié)
* 讓使用者只能通過(guò)事先預(yù)定的方法來(lái)訪問(wèn)數(shù)據(jù),從而可以在該方法里面加入控制邏輯,限制對(duì)成員變量的不合理訪問(wèn)。還可以進(jìn)行數(shù)據(jù)檢查,從而有利于保證對(duì)象信息的完整性。
* 便于修改,提高代碼的可維護(hù)性。主要說(shuō)的是隱藏的部分,在內(nèi)部修改了,如果其對(duì)外可以的訪問(wèn)方式不變的話,外部根本感覺(jué)不到它的修改。例如:Java8->Java9,String從char[]轉(zhuǎn)為byte[]內(nèi)部實(shí)現(xiàn),而對(duì)外的方法不變,我們使用者根本感覺(jué)不到它內(nèi)部的修改。

2、實(shí)現(xiàn)步驟

(1)使用 `private` 修飾成員變量

```java
private 數(shù)據(jù)類(lèi)型 變量名 ;
```

代碼如下:

```java
public class Person {
   private String name;
     private int age;
   private boolean marry;
}
```

(2)提供 `getXxx`方法 / `setXxx` 方法,可以訪問(wèn)成員變量,代碼如下:

靜態(tài)變量的get/set方法也靜態(tài)的,當(dāng)局部變量與靜態(tài)變量重名時(shí),使用“類(lèi)名.靜態(tài)變量”進(jìn)行區(qū)分。

```java
public class Person {
   private String name;
     private int age;
   private boolean marry;

    public void setName(String name) {
        this.name = name;
   }

   public String getName() {
       return name;
    }

   public void setAge(int age) {
       this.age = age;
   }

   public int getAge() {
       return age;
   }
   
   public void setMarry(boolean marry){
       this.marry = marry;
   }
   
   public boolean isMarry(){
       return marry;
   }
}
```

IDEA自動(dòng)生成get/set方法模板

- 大部分鍵盤(pán)模式按Alt + Insert鍵。
- 部分鍵盤(pán)模式需要按Alt + Insert + Fn鍵。
- Mac電腦快捷鍵需要單獨(dú)設(shè)置

![image-20211229171605642](images/image-20211229171605642.png)

![image-20211229171757032](images/image-20211229171757032.png)

(3)測(cè)試

```java
package com.atguigu.encapsulation;

public class TestPerson {
   public static void main(String[] args) {
       Person p = new Person();

       //實(shí)例變量私有化,跨類(lèi)是無(wú)法直接使用的
/*        p.name = "張三";
       p.age = 23;
       p.marry = true;*/

       p.setName("張三");
       System.out.println("p.name = " + p.getName());

       p.setAge(23);
       System.out.println("p.age = " + p.getAge());

       p.setMarry(true);
       System.out.println("p.marry = " + p.isMarry());
   }
}
```

 

### 6.1.4 標(biāo)準(zhǔn)JavaBean

`JavaBean` 是 Java語(yǔ)言編寫(xiě)類(lèi)的一種標(biāo)準(zhǔn)規(guī)范。符合`JavaBean` 的類(lèi),要求:

(1)類(lèi)必須是公共的和具體的(非抽象的,關(guān)于抽象請(qǐng)看6.5小節(jié))

(2)成員變量私有化,并提供用來(lái)操作成員變量的`set` 和`get` 方法

(3)必須有無(wú)參構(gòu)造

(4)提供有參構(gòu)造(可選)

(5)建議重寫(xiě)toString方法、equals和hashCode方法等(關(guān)于重寫(xiě)請(qǐng)看6.3小節(jié))

```java
public class ClassName{
 //成員變量
   
 //構(gòu)造方法
     //無(wú)參構(gòu)造方法【必須】
     //有參構(gòu)造方法【建議】
     
 //getXxx()
 //setXxx()
 //其他成員方法
}
```

編寫(xiě)符合`JavaBean` 規(guī)范的類(lèi),以學(xué)生類(lèi)為例,標(biāo)準(zhǔn)代碼如下:

```java
public class Student {
    // 成員變量
    private String name;
    private int age;

    // 構(gòu)造方法
    public Student() {
    }

    public Student(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // get/set成員方法
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
   
   //其他成員方法列表
   public String getInfo(){
       return "姓名:" + name + ",年齡:" + age;
   }
}
```

測(cè)試類(lèi),代碼如下:

```java
public class TestStudent {
    public static void main(String[] args) {
        // 無(wú)參構(gòu)造使用
        Student s = new Student();
        s.setName("柳巖");
        s.setAge(18);
        System.out.println(s.getName() + "---" + s.getAge());
       System.out.println(s.getInfo());

        // 帶參構(gòu)造使用
        Student s2 = new Student("趙麗穎", 18);
        System.out.println(s2.getName() + "---" + s2.getAge());
       System.out.println(s2.getInfo());
    }
}
```

 

## 6.2  對(duì)象數(shù)組

數(shù)組是用來(lái)存儲(chǔ)一組數(shù)據(jù)的容器,一組基本數(shù)據(jù)類(lèi)型的數(shù)據(jù)可以用數(shù)組裝,那么一組對(duì)象也可以使用數(shù)組來(lái)裝。

即數(shù)組的元素可以是基本數(shù)據(jù)類(lèi)型,也可以是引用數(shù)據(jù)類(lèi)型。當(dāng)元素是引用數(shù)據(jù)類(lèi)型是,我們稱(chēng)為對(duì)象數(shù)組。

> 注意:對(duì)象數(shù)組,首先要?jiǎng)?chuàng)建數(shù)組對(duì)象本身,即確定數(shù)組的長(zhǎng)度,然后再創(chuàng)建每一個(gè)元素對(duì)象,如果不創(chuàng)建,數(shù)組的元素的默認(rèn)值就是null,所以很容易出現(xiàn)空指針異常NullPointerException。

### 6.2.1 對(duì)象數(shù)組的聲明和使用

案例:

(1)定義矩形類(lèi),包含長(zhǎng)、寬屬性,area()求面積方法,perimeter()求周長(zhǎng)方法,String getInfo()返回圓對(duì)象的詳細(xì)信息的方法

(2)在測(cè)試類(lèi)中創(chuàng)建長(zhǎng)度為5的Rectangle[]數(shù)組,用來(lái)裝3個(gè)矩形對(duì)象,并給3個(gè)矩形對(duì)象的長(zhǎng)分別賦值為10,20,30,寬分別賦值為5,15,25,遍歷輸出

```java
package com.atguigu.test08.array;

public class Rectangle {
   double length;
   double width;

   double area(){//面積
       return length * width;
   }

   double perimeter(){//周長(zhǎng)
       return 2 * (length + width);
   }

   String getInfo(){
       return "長(zhǎng):" + length +
               ",寬:" + width +
               ",面積:" + area() + //直接調(diào)用本類(lèi)的另一個(gè)實(shí)例方法
               ",周長(zhǎng):" + perimeter();
   }
}

```

```java
package com.atguigu.test08.array;

public class ObjectArrayTest {
   public static void main(String[] args) {
       //聲明并創(chuàng)建一個(gè)長(zhǎng)度為3的矩形對(duì)象數(shù)組
       Rectangle[] array = new Rectangle[3];

       //創(chuàng)建3個(gè)矩形對(duì)象,并為對(duì)象的實(shí)例變量賦值,
       //3個(gè)矩形對(duì)象的長(zhǎng)分別是10,20,30
       //3個(gè)矩形對(duì)象的寬分別是5,15,25
       //調(diào)用矩形對(duì)象的getInfo()返回對(duì)象信息后輸出
       for (int i = 0; i < array.length; i++) {
           //創(chuàng)建矩形對(duì)象
           array[i] = new Rectangle();

           //為矩形對(duì)象的成員變量賦值
           array[i].length = (i+1) * 10;
           array[i].width = (2*i+1) * 5;

           //獲取并輸出對(duì)象對(duì)象的信息
           System.out.println(array[i].getInfo());
       }
   }
}
```

### 6.2.2 對(duì)象數(shù)組的內(nèi)存圖分析

對(duì)象數(shù)組中數(shù)組元素存儲(chǔ)的是元素對(duì)象的首地址。

![image-20211228153827819](images/image-20211228153827819-17028727226681.png)

 

 

### 6.2.3 增強(qiáng)for循環(huán)

增強(qiáng)for循環(huán)是一種語(yǔ)法糖,即在遍歷數(shù)組中,表面上是一種新語(yǔ)法,但是編譯后仍然是我們學(xué)過(guò)的普通for循環(huán)。

```java
for(元素類(lèi)型 元素名 : 數(shù)組名){
   
}
```

注意:增強(qiáng)for循環(huán)只能用于查看元素,或修改元素屬性值,但不能替換元素。增強(qiáng)for循環(huán)遍歷數(shù)組是沒(méi)有下標(biāo)的。

```java
package com.atguigu.test08.array;

public class TestForeach {
   public static void main(String[] args) {
       //聲明并創(chuàng)建一個(gè)長(zhǎng)度為3的矩形對(duì)象數(shù)組
       Rectangle[] array = new Rectangle[3];
       array[0] = new Rectangle(10,5);
       array[1] = new Rectangle(20,15);
       array[2] = new Rectangle(30,25);
       for(Rectangle r : array){
           System.out.println(r.getInfo());
       }
   }
}
```

普通for循環(huán)與增強(qiáng)for循環(huán)在遍歷數(shù)組時(shí)的區(qū)別:

|                          | 普通for循環(huán)  | 增強(qiáng)for循環(huán) |
| ------------------------ | ------------ | ----------- |
| 是否需要指定下標(biāo)信息     | 是           | 否          |
| 是否可以替換元素         | 是           | 否          |
| 是否可以遍歷數(shù)組部分元素 | 是           | 否          |
| 遍歷查看元素信息時(shí)       | 稍微復(fù)雜一點(diǎn) | 更簡(jiǎn)潔      |

 

## 6.3 繼承

### 6.3.1 繼承的概述

1、生活中的繼承

* 財(cái)產(chǎn):富二代

* 樣貌:如圖所示:

 <img src="images/繼承2.jpg" style="zoom:50%;" />

繼承有延續(xù)(下一代延續(xù)上一代的基因、財(cái)富)、擴(kuò)展(下一代和上一代又有所不同)的意思。

社會(huì)的進(jìn)步就是源于知識(shí)、財(cái)富、經(jīng)驗(yàn)得以繼承,又可以不斷的翻新。

2、Java中的繼承

如圖所示:

<img src="images/貓狗繼承1.jpg" style="zoom:50%;" />

多個(gè)類(lèi)中存在相同屬性和行為時(shí),將這些內(nèi)容抽取到單獨(dú)一個(gè)類(lèi)中,那么多個(gè)類(lèi)中無(wú)需再定義這些屬性和行為,只需要和抽取出來(lái)的類(lèi)構(gòu)成某種關(guān)系。如圖所示:

<img src="images/貓狗繼承2.jpg" style="zoom: 50%;" />

其中,多個(gè)類(lèi)可以稱(chēng)為**子類(lèi)**,也叫**派生類(lèi)**;多個(gè)類(lèi)抽取出來(lái)的這個(gè)類(lèi)稱(chēng)為**父類(lèi)**、**超類(lèi)(superclass)**或者**基類(lèi)**。

繼承描述的是事物之間的所屬關(guān)系,這種關(guān)系是:`is-a` 的關(guān)系。例如,圖中貓屬于動(dòng)物,狗也屬于動(dòng)物。可見(jiàn),父類(lèi)更通用或更一般,子類(lèi)更具體。我們通過(guò)繼承,可以使多種事物之間形成一種關(guān)系體系。

3、繼承的好處

* 提高**代碼的復(fù)用性**。
* 提高**代碼的擴(kuò)展性**。
* 表示類(lèi)與類(lèi)之間的is-a關(guān)系

### 6.3.2 繼承的語(yǔ)法格式

通過(guò) `extends` 關(guān)鍵字,可以聲明一個(gè)子類(lèi)繼承另外一個(gè)父類(lèi),定義格式如下:

```java
【修飾符】 class 父類(lèi) {
    ...
}

【修飾符】 class 子類(lèi) extends 父類(lèi) {
    ...
}

```

1、父類(lèi)

```java
package com.atguigu.inherited.grammar;

/*
* 定義動(dòng)物類(lèi)Animal,做為父類(lèi)
*/
public class Animal {
   // 定義name屬性
   String name;
   // 定義age屬性
   int age;

   // 定義動(dòng)物的吃東西方法
   public void eat() {
       System.out.println(age + "歲的" + name + "在吃東西");
   }
}

```

2、子類(lèi)

```java
package com.atguigu.inherited.grammar;

/*
* 定義貓類(lèi)Cat 繼承 動(dòng)物類(lèi)Animal
*/
public class Cat extends Animal {
   int count;//記錄每只貓抓的老鼠數(shù)量

   // 定義一個(gè)貓抓老鼠的方法catchMouse
   public void catchMouse() {
       count++;
       System.out.println("抓老鼠,已經(jīng)抓了" + count + "只老鼠");
   }
}
```

3、測(cè)試類(lèi)

```java
package com.atguigu.inherited.grammar;

public class TestCat {
   public static void main(String[] args) {
       // 創(chuàng)建一個(gè)貓類(lèi)對(duì)象
       Cat cat = new Cat();
       // 為該貓類(lèi)對(duì)象的name屬性進(jìn)行賦值
       cat.name = "Tom";
       // 為該貓類(lèi)對(duì)象的age屬性進(jìn)行賦值
       cat.age = 2;
       // 調(diào)用該貓繼承來(lái)的eat()方法
       cat.eat();
       // 調(diào)用該貓的catchMouse()方法
       cat.catchMouse();
       cat.catchMouse();
       cat.catchMouse();
       
       //調(diào)用該貓的eat()方法
       cat.eat();
   }
}
```

### 6.3.3 IDEA中如何查看繼承關(guān)系

1、子類(lèi)和父類(lèi)是一種相對(duì)的概念

例如:B類(lèi)對(duì)于A來(lái)說(shuō)是子類(lèi),但是對(duì)于C類(lèi)來(lái)說(shuō)是父類(lèi)

2、查看繼承關(guān)系快捷鍵

例如:選擇A類(lèi)名,按Ctrl + H就會(huì)顯示A類(lèi)的繼承樹(shù)。

![image-20211230090701383](images/image-20211230090701383.png):A類(lèi)的父類(lèi)和子類(lèi)

![image-20211230090719430](images/image-20211230090719430.png):A類(lèi)的父類(lèi)

![image-20211230090732532](images/image-20211230090732532.png):A類(lèi)的所有子類(lèi)

例如:在類(lèi)繼承目錄樹(shù)中選中某個(gè)類(lèi),比如C類(lèi),按Ctrl+ Alt+U就會(huì)用圖形化方式顯示C類(lèi)的繼承祖宗

<img src="images/image-20211229180113255.png" alt="image-20211229180113255" style="zoom: 67%;" />

### 6.3.4 繼承的特點(diǎn)

1、每一個(gè)類(lèi)有一個(gè)默認(rèn)的父類(lèi)java.lang.Object類(lèi),它也是所有類(lèi)的根父類(lèi)

<img src="images/image-20220616110817804.png" alt="image-20220616110817804" style="zoom: 67%;" />

2、子類(lèi)會(huì)繼承父類(lèi)所有的實(shí)例變量和實(shí)例方法

從類(lèi)的定義來(lái)看,類(lèi)是一類(lèi)具有相同特性的事物的抽象描述。父類(lèi)是所有子類(lèi)共同特征的抽象描述。而實(shí)例變量和實(shí)例方法就是事物的特征,那么父類(lèi)中聲明的實(shí)例變量和實(shí)例方法代表子類(lèi)事物也有這個(gè)特征。

- 當(dāng)子類(lèi)對(duì)象被創(chuàng)建時(shí),在堆中給對(duì)象申請(qǐng)內(nèi)存時(shí),就要看子類(lèi)和父類(lèi)都聲明了什么實(shí)例變量,這些實(shí)例變量都要分配內(nèi)存。
- 當(dāng)子類(lèi)對(duì)象調(diào)用方法時(shí),編譯器會(huì)先在子類(lèi)模板中看該類(lèi)是否有這個(gè)方法,如果沒(méi)找到,會(huì)看它的父類(lèi)甚至父類(lèi)的父類(lèi)是否聲明了這個(gè)方法,遵循從下往上找的順序,找到了就停止,一直到根父類(lèi)都沒(méi)有找到,就會(huì)報(bào)編譯錯(cuò)誤。

==所以繼承意味著子類(lèi)的對(duì)象除了看子類(lèi)的類(lèi)模板還要看父類(lèi)的類(lèi)模板。==

![image-20211230090255997](images/image-20211230090255997.png)

3、Java只支持單繼承,不支持多重繼承

```java
public class A{}
class B extends A{}

//一個(gè)類(lèi)只能有一個(gè)父類(lèi),不可以有多個(gè)直接父類(lèi)。
class C extends B{}     //ok
class C extends A,B...    //error
```

4、Java支持多層繼承(繼承體系)

```java
class A{}
class B extends A{}
class C extends B{}
```

> 頂層父類(lèi)是Object類(lèi)。所有的類(lèi)默認(rèn)繼承Object,作為父類(lèi)。

5、一個(gè)父類(lèi)可以同時(shí)擁有多個(gè)子類(lèi)

```java
class A{}
class B extends A{}
class D extends A{}
class E extends A{}
```

### 6.3.5 繼承時(shí)權(quán)限修飾符限制問(wèn)題

權(quán)限修飾符:public,protected,缺省,private

| 修飾符    | 本類(lèi) | 本包(包含子類(lèi)和非子類(lèi))  | 其他包子類(lèi)                                | 其他包非子類(lèi) |
| --------- | ---- | ------------------------- | ----------------------------------------- | ------------ |
| private   | √    | ×                         | ×                                         | ×            |
| 缺省      | √    | √(本包子類(lèi)非子類(lèi)都可見(jiàn)) | ×                                         | ×            |
| protected | √    | √(本包子類(lèi)非子類(lèi)都可見(jiàn)) | √(其他包僅限于子類(lèi)中可見(jiàn),直接使用方式) | ×            |
| public    | √    | √                         | √                                         | √            |

外部類(lèi):public和缺省

成員變量、成員方法等:public,protected,缺省,private

1、外部類(lèi)要跨包使用必須是public,否則僅限于本包使用

(1)外部類(lèi)的權(quán)限修飾符如果缺省,本包使用沒(méi)問(wèn)題

![image-20211230093627763](images/image-20211230093627763.png)

(2)外部類(lèi)的權(quán)限修飾符如果缺省,跨包使用有問(wèn)題

![image-20211230094236974](images/image-20211230094236974.png)

2、父類(lèi)成員變量私有化(private)

子類(lèi)雖會(huì)繼承父類(lèi)私有(private)的成員變量,但子類(lèi)不能對(duì)繼承的私有成員變量直接進(jìn)行訪問(wèn),可通過(guò)繼承的get/set方法進(jìn)行訪問(wèn)。如圖所示:

![](images/繼承私有成員1.jpg)

父類(lèi)代碼:

```java
package com.atguigu.inherited.modifier;

public class Person {
   private String name;
   private int age;

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   public String getInfo(){
       return "姓名:" + name + ",年齡:" + age;
   }
}

```

子類(lèi)代碼:

```java
package com.atguigu.inherited.modifier;

public class Student extends Person {
   private int score;

   public int getScore() {
       return score;
   }

   public void setScore(int score) {
       this.score = score;
   }

   public String getInfo(){
//        return "姓名:" + name + ",年齡:" + age;
       //在子類(lèi)中不能直接使用父類(lèi)私有的name和age
       return "姓名:" + getName() + ",年齡:" + getAge();
   }
}

```

測(cè)試類(lèi)代碼:

```java
package com.atguigu.inherited.modifier;

public class TestStudent {
   public static void main(String[] args) {
       Student student = new Student();

       student.setName("張三");
       student.setAge(23);
       student.setScore(89);

       System.out.println(student.getInfo());
   }
}
```

IDEA在Debug模式下查看學(xué)生對(duì)象信息:

![image-20211230101938382](images/image-20211230101938382.png)

3、成員的權(quán)限修飾符問(wèn)題

(1)本包下使用:成員的權(quán)限修飾符可以是public、protected、缺省

![image-20211230095320646](images/image-20211230095320646.png)

(2)跨包使用時(shí),如果類(lèi)的權(quán)限修飾符缺省,成員權(quán)限修飾符>類(lèi)的權(quán)限修飾符也沒(méi)有意義

![image-20211230100219840](images/image-20211230100219840.png)

(3)跨包下使用:要求嚴(yán)格

![image-20211230095817784](images/image-20211230095817784.png)

注意:跨包時(shí)父類(lèi)中protected修飾的成員,僅限于在子類(lèi)中訪問(wèn),且是子類(lèi)對(duì)象自己訪問(wèn)。

 

### 6.3.6 繼承時(shí)構(gòu)造器問(wèn)題

子類(lèi)繼承父類(lèi)時(shí),不會(huì)繼承父類(lèi)的構(gòu)造器。必須通過(guò)super()或super(實(shí)參列表)的方式調(diào)用父類(lèi)的構(gòu)造器。

- super();:子類(lèi)構(gòu)造器中一定會(huì)調(diào)用父類(lèi)的構(gòu)造器,默認(rèn)調(diào)用父類(lèi)的無(wú)參構(gòu)造,super();可以省略。
- super(實(shí)參列表);:如果父類(lèi)沒(méi)有無(wú)參構(gòu)造或者有無(wú)參構(gòu)造但是子類(lèi)就是想要調(diào)用父類(lèi)的有參構(gòu)造,則必須使用super(實(shí)參列表);的語(yǔ)句。
- super()和super(實(shí)參列表)都只能出現(xiàn)在子類(lèi)構(gòu)造器的首行。

```java
package com.atguigu.constructor;

public class Employee {
   private String name;
   private int age;
   private double salary;

   public Employee() {
       System.out.println("父類(lèi)Employee無(wú)參構(gòu)造");
   }

   public Employee(String name, int age, double salary) {
       this.name = name;
       this.age = age;
       this.salary = salary;
       System.out.println("父類(lèi)Employee有參構(gòu)造");
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   public double getSalary() {
       return salary;
   }

   public void setSalary(double salary) {
       this.salary = salary;
   }

   public String getInfo(){
       return "姓名:" + name + ",年齡:" + age +",薪資:" + salary;
   }
}

```

```java
package com.atguigu.constructor;

public class Manager extends Employee{
   private double bonusRate;

   public Manager() {
       super();//可以省略
   }

   public Manager(String name, int age, double salary, double bonusRate) {
       super(name, age, salary);//調(diào)用父類(lèi)的有參構(gòu)造
       this.bonusRate = bonusRate;
   }

   public double getBonusRate() {
       return bonusRate;
   }

   public void setBonusRate(double bonusRate) {
       this.bonusRate = bonusRate;
   }

   public String getInfo() {
       return "姓名:" + getName() + ",年齡:" + getAge() +",薪資:" + getSalary() +",獎(jiǎng)金比例:" + bonusRate;
   }
}

```

![image-20211231112340813](images/image-20211231112340813.png)

```java
package com.atguigu.constructor;

public class TestEmployee {
   public static void main(String[] args) {
       Manager m1 = new Manager();
       System.out.println(m1.getInfo());

       Manager m2 = new Manager("張三",23,20000,0.1);
       System.out.println(m2.getInfo());
   }
}

```

形式一:

```java
class A{

}
class B extends A{

}

class Test1{
   public static void main(String[] args){
       B b = new B();
       //A類(lèi)和B類(lèi)都是默認(rèn)有一個(gè)無(wú)參構(gòu)造,B類(lèi)的默認(rèn)無(wú)參構(gòu)造中還會(huì)默認(rèn)調(diào)用A類(lèi)的默認(rèn)無(wú)參構(gòu)造
       //但是因?yàn)槎际悄J(rèn)的,沒(méi)有打印語(yǔ)句,看不出來(lái)
   }
}
```

形式二:

```java
class A{
    A(){
        System.out.println("A類(lèi)無(wú)參構(gòu)造器");
    }
}
class B extends A{

}
class Test2{
   public static void main(String[] args){
       B b = new B();
       //A類(lèi)顯示聲明一個(gè)無(wú)參構(gòu)造,
        //B類(lèi)默認(rèn)有一個(gè)無(wú)參構(gòu)造,
        //B類(lèi)的默認(rèn)無(wú)參構(gòu)造中會(huì)默認(rèn)調(diào)用A類(lèi)的無(wú)參構(gòu)造
       //可以看到會(huì)輸出“A類(lèi)無(wú)參構(gòu)造器"
   }
}
```

形式三:

```java
class A{
    A(){
        System.out.println("A類(lèi)無(wú)參構(gòu)造器");
    }
}
class B extends A{
    B(){
        System.out.println("B類(lèi)無(wú)參構(gòu)造器");
    }
}
class Test3{
   public static void main(String[] args){
       B b = new B();
       //A類(lèi)顯示聲明一個(gè)無(wú)參構(gòu)造,
        //B類(lèi)顯示聲明一個(gè)無(wú)參構(gòu)造,        
        //B類(lèi)的無(wú)參構(gòu)造中雖然沒(méi)有寫(xiě)super(),但是仍然會(huì)默認(rèn)調(diào)用A類(lèi)的無(wú)參構(gòu)造
       //可以看到會(huì)輸出“A類(lèi)無(wú)參構(gòu)造器"和"B類(lèi)無(wú)參構(gòu)造器")
   }
}
```

形式四:

```java
class A{
    A(){
        System.out.println("A類(lèi)無(wú)參構(gòu)造器");
    }
}
class B extends A{
    B(){
       super();
        System.out.println("B類(lèi)無(wú)參構(gòu)造器");
    }
}
class Test4{
   public static void main(String[] args){
       B b = new B();
       //A類(lèi)顯示聲明一個(gè)無(wú)參構(gòu)造,
        //B類(lèi)顯示聲明一個(gè)無(wú)參構(gòu)造,        
        //B類(lèi)的無(wú)參構(gòu)造中明確寫(xiě)了super(),表示調(diào)用A類(lèi)的無(wú)參構(gòu)造
       //可以看到會(huì)輸出“A類(lèi)無(wú)參構(gòu)造器"和"B類(lèi)無(wú)參構(gòu)造器")
   }
}
```

形式五:

```java
class A{
    A(int a){
        System.out.println("A類(lèi)有參構(gòu)造器");
    }
}
class B extends A{
    B(){
        System.out.println("B類(lèi)無(wú)參構(gòu)造器");
    }
}
class Test5{
   public static void main(String[] args){
       B b = new B();
       //A類(lèi)顯示聲明一個(gè)有參構(gòu)造,沒(méi)有寫(xiě)無(wú)參構(gòu)造,那么A類(lèi)就沒(méi)有無(wú)參構(gòu)造了
        //B類(lèi)顯示聲明一個(gè)無(wú)參構(gòu)造,        
        //B類(lèi)的無(wú)參構(gòu)造沒(méi)有寫(xiě)super(...),表示默認(rèn)調(diào)用A類(lèi)的無(wú)參構(gòu)造
       //編譯報(bào)錯(cuò),因?yàn)锳類(lèi)沒(méi)有無(wú)參構(gòu)造
   }
}
```

![image-20200227141228450](images/image-20200227141228450.png)

![image-20200227141051954](images/image-20200227141051954.png)

形式六:

```java
class A{
    A(int a){
        System.out.println("A類(lèi)有參構(gòu)造器");
    }
}
class B extends A{
    B(){
        super();
        System.out.println("B類(lèi)無(wú)參構(gòu)造器");
    }
}
class Test6{
   public static void main(String[] args){
       B b = new B();
       //A類(lèi)顯示聲明一個(gè)有參構(gòu)造,沒(méi)有寫(xiě)無(wú)參構(gòu)造,那么A類(lèi)就沒(méi)有無(wú)參構(gòu)造了
        //B類(lèi)顯示聲明一個(gè)無(wú)參構(gòu)造,        
        //B類(lèi)的無(wú)參構(gòu)造明確寫(xiě)super(),表示調(diào)用A類(lèi)的無(wú)參構(gòu)造
       //編譯報(bào)錯(cuò),因?yàn)锳類(lèi)沒(méi)有無(wú)參構(gòu)造
   }
}
```

![image-20200303183542807](images/image-20200303183542807.png)

形式七:

```java
class A{
    A(int a){
        System.out.println("A類(lèi)有參構(gòu)造器");
    }
}
class B extends A{
    B(int a){
        super(a);
        System.out.println("B類(lèi)有參構(gòu)造器");
    }
}
class Test7{
   public static void main(String[] args){
       B b = new B(10);
       //A類(lèi)顯示聲明一個(gè)有參構(gòu)造,沒(méi)有寫(xiě)無(wú)參構(gòu)造,那么A類(lèi)就沒(méi)有無(wú)參構(gòu)造了
        //B類(lèi)顯示聲明一個(gè)有參構(gòu)造,        
        //B類(lèi)的有參構(gòu)造明確寫(xiě)super(a),表示調(diào)用A類(lèi)的有參構(gòu)造
       //會(huì)打印“A類(lèi)有參構(gòu)造器"和"B類(lèi)有參構(gòu)造器"
   }
}
```

形式八:

```java
class A{
   A(){
       System.out.println("A類(lèi)無(wú)參構(gòu)造器");
   }
    A(int a){
        System.out.println("A類(lèi)有參構(gòu)造器");
    }
}
class B extends A{
   B(){
       super();//可以省略,調(diào)用父類(lèi)的無(wú)參構(gòu)造
       System.out.println("B類(lèi)無(wú)參構(gòu)造器");
   }
    B(int a){
        super(a);//調(diào)用父類(lèi)有參構(gòu)造
        System.out.println("B類(lèi)有參構(gòu)造器");
    }
}
class Test8{
   public static void main(String[] args){
       B b1 = new B();
       B b2 = new B(10);
   }
}
```

### 6.3.7 方法重寫(xiě)(Override)

我們說(shuō)父類(lèi)的所有方法子類(lèi)都會(huì)繼承,但是當(dāng)某個(gè)方法被繼承到子類(lèi)之后,子類(lèi)覺(jué)得父類(lèi)原來(lái)的實(shí)現(xiàn)不適合于子類(lèi),該怎么辦呢?我們可以進(jìn)行方法重寫(xiě) (Override)

1、方法重寫(xiě)

```java
package com.atguigu.inherited.override;

public class Person {
   private String name;
   private int age;

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getAge() {
       return age;
   }

   public void setAge(int age) {
       this.age = age;
   }

   public String getInfo(){
       return "姓名:" + name + ",年齡:" + age;
   }
}

```

```java
package com.atguigu.inherited.override;

public class Student extends Person {
   private int score;

   public int getScore() {
       return score;
   }

   public void setScore(int score) {
       this.score = score;
   }

   //方法的重寫(xiě)
   public String getInfo(){
//        return "姓名:" + name + ",年齡:" + age;
       //在子類(lèi)中不能直接使用父類(lèi)私有的name和age
       return "姓名:" + getName() + ",年齡:" + getAge() + ",成績(jī):" + score;
   }
}

```

```java
package com.atguigu.inherited.override;

public class TestStudent {
   public static void main(String[] args) {
       Student student = new Student();

       student.setName("張三");
       student.setAge(23);
       student.setScore(89);

       System.out.println(student.getInfo());
   }
}

```

 

2、在子類(lèi)中如何調(diào)用父類(lèi)被重寫(xiě)的方法

在子類(lèi)中可以通過(guò)super關(guān)鍵字調(diào)用父類(lèi)被重寫(xiě)的方法

```java
super.被重寫(xiě)方法(【實(shí)參列表】)
```

示例代碼:

```java
package com.atguigu.inherited.override;

public class Student extends Person {
   private int score;

   public int getScore() {
       return score;
   }

   public void setScore(int score) {
       this.score = score;
   }

   //方法的重寫(xiě)
   public String getInfo(){
//        return "姓名:" + name + ",年齡:" + age;
       //在子類(lèi)中不能直接使用父類(lèi)私有的name和age
//        return "姓名:" + getName() + ",年齡:" + getAge() + ",成績(jī):" + score;
       return super.getInfo() + ",成績(jī):" + score;
   }
}

```

3、IDEA重寫(xiě)方法快捷鍵:Ctrl + O

![image-20211230104547719](images/image-20211230104547719.png)

```java
package com.atguigu.inherited.override;

public class Student extends Person {
   private int score;

   public int getScore() {
       return score;
   }

   public void setScore(int score) {
       this.score = score;
   }

   @Override
   public String getInfo() {
       return super.getInfo() +",成績(jī):" + score;
   }
}
```

> @Override:寫(xiě)在方法上面,用來(lái)檢測(cè)是不是滿(mǎn)足重寫(xiě)方法的要求。這個(gè)注解就算不寫(xiě),只要滿(mǎn)足要求,也是正確的方法覆蓋重寫(xiě)。建議保留,這樣編譯器可以幫助我們檢查格式,另外也可以讓閱讀源代碼的程序員清晰的知道這是一個(gè)重寫(xiě)的方法。

4、重寫(xiě)方法的要求

(1)必須保證父子類(lèi)之間重寫(xiě)方法的名稱(chēng)相同。

(2)必須保證父子類(lèi)之間重寫(xiě)方法的參數(shù)列表也完全相同。

(3)子類(lèi)方法的返回值類(lèi)型必須【小于等于】父類(lèi)方法的返回值類(lèi)型(小于其實(shí)就是是它的子類(lèi),例如:Student < Person)。

> 注意:如果返回值類(lèi)型是基本數(shù)據(jù)類(lèi)型和void,那么必須是相同

(4)子類(lèi)方法的權(quán)限必須【大于等于】父類(lèi)方法的權(quán)限修飾符。

> 注意:public > protected > 缺省 > private
>
> 父類(lèi)私有方法不能重寫(xiě)
>
> 跨包的父類(lèi)缺省的方法也不能重寫(xiě)

 

5、方法的重載和方法的重寫(xiě)

方法的重載:方法名相同,形參列表不同。不看返回值類(lèi)型。

方法的重寫(xiě):見(jiàn)上面。

(1)同一個(gè)類(lèi)中

```java
package com.atguigu.inherited.method;

public class TestOverload {
   public int max(int a, int b){
       return a > b ? a : b;
   }
   public double max(double a, double b){
       return a > b ? a : b;
   }
   public int max(int a, int b,int c){
       return max(max(a,b),c);
   }
}
```

(2)父子類(lèi)中

```java
package com.atguigu.inherited.method;

public class TestOverloadOverride {
   public static void main(String[] args) {
       Son s = new Son();
       s.method(1);//只有一個(gè)形式的method方法

       Daughter d = new Daughter();
       d.method(1);
       d.method(1,2);//有兩個(gè)形式的method方法
   }
}

class Father{
   public void method(int i){
       System.out.println("Father.method");
   }
}
class Son extends Father{
   public void method(int i){//重寫(xiě)
       System.out.println("Son.method");
   }
}
class Daughter extends Father{
   public void method(int i,int j){//重載
       System.out.println("Daughter.method");
   }
}
```

### 6.3.8 Object根父類(lèi)

#### 1、Object根父類(lèi)

**API(Application Programming Interface)**,應(yīng)用程序編程接口。Java API是一本程序員的`字典` ,是JDK中提供給我們使用的類(lèi)的說(shuō)明文檔。所以我們可以通過(guò)查詢(xún)API的方式,來(lái)學(xué)習(xí)Java提供的類(lèi),并得知如何使用它們。在API文檔中是無(wú)法得知這些類(lèi)具體是如何實(shí)現(xiàn)的,如果要查看具體實(shí)現(xiàn)代碼,那么我們需要查看**src源碼**。

類(lèi) `java.lang.Object`是類(lèi)層次結(jié)構(gòu)的根類(lèi),即所有類(lèi)的父類(lèi)。每個(gè)類(lèi)都使用 `Object` 作為超類(lèi)。所有對(duì)象(包括數(shù)組)都實(shí)現(xiàn)這個(gè)類(lèi)的方法。

* 如果一個(gè)類(lèi)沒(méi)有特別指定父類(lèi),那么默認(rèn)則繼承自O(shè)bject類(lèi)。例如:


```java
public class MyClass /*extends Object*/ {
     // ...
}
```

- 所有對(duì)象(包括數(shù)組)都實(shí)現(xiàn)這個(gè)類(lèi)的方法。而且很多方法子類(lèi)都會(huì)重寫(xiě),通過(guò)子類(lèi)對(duì)象調(diào)用方法后執(zhí)行的是重寫(xiě)后的方法。


#### 2、Object類(lèi)的方法

Object類(lèi)有11個(gè)方法,先介紹如下3個(gè):

(1)public String toString():建議子類(lèi)重寫(xiě)。沒(méi)有重寫(xiě)的話,默認(rèn)返回的是  對(duì)象的運(yùn)行時(shí)類(lèi)型 @ 對(duì)象的哈希值的十六進(jìn)制值。所有對(duì)象的toString方法,在打印對(duì)象時(shí),或者對(duì)象與字符串進(jìn)行拼接時(shí),都會(huì)自動(dòng)調(diào)用。

(2)public boolean equals(Object obj):用于比較兩個(gè)對(duì)象是否“相等”。

- 絕對(duì)相等:同一個(gè)對(duì)象,地址值完全相同。沒(méi)有重寫(xiě),繼承Object里面的equals,默認(rèn)就是比較地址值,等價(jià)于==比較。
- 相對(duì)相等(邏輯相等):比較兩個(gè)對(duì)象的內(nèi)容。這個(gè)時(shí)候需要重寫(xiě)。核心類(lèi)庫(kù)中大部分類(lèi)都重寫(xiě)了equals方法,例如:String類(lèi)。

(3)public int hashCode():返回該對(duì)象的哈希碼值。支持此方法是為了提高哈希表的性能。 暫時(shí)可以認(rèn)為它相當(dāng)于這個(gè)對(duì)象的身份證號(hào)碼。如果設(shè)計(jì)的好,那么對(duì)象的哈希值相同的概率就會(huì)降低。y=f(x)。不同的x,都可能得到相同的y。后面學(xué)習(xí)哈希表時(shí),再來(lái)演示哈希值真正的作用是什么。

```java
package com.atguigu.object;

import java.util.Objects;

public class Student{
   private String name;
   private int score;

   public Student(String name, int score) {
       this.name = name;
       this.score = score;
   }

   public String getName() {
       return name;
   }

   public void setName(String name) {
       this.name = name;
   }

   public int getScore() {
       return score;
   }

   public void setScore(int score) {
       this.score = score;
   }

   //重寫(xiě)hashCode,最快捷的方式 Alt + Insert
   @Override
   public boolean equals(Object o) {
       if (this == o) {//如果當(dāng)前對(duì)象的地址值與o對(duì)象的地址值相同,直接返回true
           return true;
       }
       //(1)非空對(duì)象與null比較
       //(2)getClass() != o.getClass()
       //   this.getClass() != o.getClass() 兩個(gè)對(duì)象的類(lèi)型不同
       if (o == null || getClass() != o.getClass()){
           return false;
       }

       //向下轉(zhuǎn)型
       //這里為什么沒(méi)有instanceof判斷呢?
       //因?yàn)槿绻鹢對(duì)象的運(yùn)行時(shí)類(lèi)型不是Student類(lèi)型,上一個(gè)if就回去了
       //this對(duì)象的類(lèi)型肯定是Student類(lèi)型。
       Student student = (Student) o;

       //為什么要向下轉(zhuǎn)型?
       //如果不向下轉(zhuǎn)型,o的編譯時(shí)類(lèi)型是Object,只能調(diào)用Object里面定義的成員。
       //必須向下轉(zhuǎn)型,才能調(diào)用o對(duì)象的Student類(lèi)聲明的成員

       return score == student.score && Objects.equals(name, student.name);
   }

   @Override
   public int hashCode() {
       return Objects.hash(name, score);
       //根據(jù)某個(gè)規(guī)則,把對(duì)象的所有屬性值 合起來(lái)算出一個(gè)int值
   }
   
   @Override
   public String toString() {
       return "Student{" +
               "name='" + name + '\'' +
               ", score=" + score +
               '}';
   }
}

```

#### 3、native關(guān)鍵字

native:本地的,原生的

native只能修飾方法,表示這個(gè)方法的方法體代碼不是用Java語(yǔ)言實(shí)現(xiàn)的,而是由C/C++語(yǔ)言編寫(xiě)的。但是對(duì)于Java程序員來(lái)說(shuō),可以當(dāng)做Java的方法一樣去正常調(diào)用它,或者子類(lèi)重寫(xiě)它。

![image-20240731152128788](images/image-20240731152128788.png)

### 6.3.9 final關(guān)鍵字

final:最終的,不可更改的

final修飾類(lèi):表示這個(gè)類(lèi)不能被繼承,沒(méi)有子類(lèi)

final修飾方法:表示這個(gè)方法不能被子類(lèi)重寫(xiě)

final修飾某個(gè)變量(成員變量或局部變量),表示它的值就不能被修改,稱(chēng)為常量。

- 其中static final的常量名建議使用大寫(xiě)字母,其余的常量名通常和變量名的命名規(guī)范一樣。

> 如果某個(gè)成員變量用final修飾后,沒(méi)有set方法,并且必須初始化(可以顯式賦值、或在初始化塊賦值、實(shí)例變量還可以在構(gòu)造器中賦值)

```java
final class Eunuch{//太監(jiān)類(lèi)
    
}
class Son extends Eunuch{//錯(cuò)誤
    
}
```

```java
class Father{
    public final void method(){
        System.out.println("father");
    }
}
class Son extends Father{
    public void method(){//錯(cuò)誤
        System.out.println("son");
    }
}
```

```java
package com.atguigu.keyword.finals;

public class TestFinal {
   public static void main(String[] args){
       final int min = 0;
       final int max = 100;

       MyDate m1 = new MyDate();
       System.out.println(m1.getInfo());

       MyDate m2 = new MyDate(2022,2,14);
       System.out.println(m2.getInfo());
       
       System.out.println(Math.PI)
   }
}

class MyDate{
   //沒(méi)有set方法,必須有顯示賦值的代碼
   private final int year;
   private final int month;
   private final int day;

   public MyDate(){
       year = 1970;
       month = 1;
       day = 1;
   }

   public MyDate(int year, int month, int day) {
       this.year = year;
       this.month = month;
       this.day = day;
   }

   public int getYear() {
       return year;
   }

   public int getMonth() {
       return month;
   }

   public int getDay() {
       return day;
   }

   public String getInfo(){
       return year + "年" + month + "月" + day + "日";
   }
}
```

## 6.4 抽象類(lèi)

### 6.4.1 由來(lái)

抽象:即不具體、或無(wú)法具體

例如:當(dāng)我們聲明一個(gè)幾何圖形類(lèi):圓、矩形、三角形類(lèi)等,發(fā)現(xiàn)這些類(lèi)都有共同特征:求面積、求周長(zhǎng)、獲取圖形詳細(xì)信息。那么這些共同特征應(yīng)該抽取到一個(gè)公共父類(lèi)中。但是這些方法在父類(lèi)中又**無(wú)法給出具體的實(shí)現(xiàn)**,而是應(yīng)該交給子類(lèi)各自具體實(shí)現(xiàn)。那么父類(lèi)在聲明這些方法時(shí),**就只有方法簽名,沒(méi)有方法體**,我們把沒(méi)有方法體的方法稱(chēng)為**抽象方法**。Java語(yǔ)法規(guī)定,包含抽象方法的類(lèi)必須是**抽象類(lèi)**。

### 6.4.2 語(yǔ)法格式

* **抽象方法**:被abstract修飾沒(méi)有方法體的方法。
* **抽象類(lèi)**:被abstract修飾的類(lèi)。

抽象類(lèi)的語(yǔ)法格式

```java
【權(quán)限修飾符】 abstract class 類(lèi)名{
   
}
【權(quán)限修飾符】 abstract class 類(lèi)名 extends 父類(lèi){
   
}
```

抽象方法的語(yǔ)法格式

```java
【其他修飾符】 abstract 返回值類(lèi)型 方法名(【形參列表】);
```

> 注意:抽象方法沒(méi)有方法體

代碼舉例:

```java
public abstract class Animal {
   public abstract void eat();
}
```

```java
public class Cat extends Animal {
   public void run (){
         System.out.println("小貓吃魚(yú)和貓糧");      
   }
}
```

```java
public class CatTest {
     public static void main(String[] args) {
       // 創(chuàng)建子類(lèi)對(duì)象
       Cat c = new Cat(); 
      
       // 調(diào)用eat方法
       c.eat();
     }
}
```

此時(shí)的方法重寫(xiě),是子類(lèi)對(duì)父類(lèi)抽象方法的完成實(shí)現(xiàn),我們將這種方法重寫(xiě)的操作,也叫做**實(shí)現(xiàn)方法**。

### 6.4.3 注意事項(xiàng)

關(guān)于抽象類(lèi)的使用,以下為語(yǔ)法上要注意的細(xì)節(jié),雖然條目較多,但若理解了抽象的本質(zhì),無(wú)需死記硬背。

1. 抽象類(lèi)**不能創(chuàng)建對(duì)象**,如果創(chuàng)建,編譯無(wú)法通過(guò)而報(bào)錯(cuò)。只能創(chuàng)建其非抽象子類(lèi)的對(duì)象。

  > 理解:假設(shè)創(chuàng)建了抽象類(lèi)的對(duì)象,調(diào)用抽象的方法,而抽象方法沒(méi)有具體的方法體,沒(méi)有意義。

2. 抽象類(lèi)中,也有構(gòu)造方法,是供子類(lèi)創(chuàng)建對(duì)象時(shí),初始化父類(lèi)成員變量使用的。

  > 理解:子類(lèi)的構(gòu)造方法中,有默認(rèn)的super()或手動(dòng)的super(實(shí)參列表),需要訪問(wèn)父類(lèi)構(gòu)造方法。

3. 抽象類(lèi)中,不一定包含抽象方法,但是有抽象方法的類(lèi)必定是抽象類(lèi)。

  > 理解:未包含抽象方法的抽象類(lèi),目的就是不想讓調(diào)用者創(chuàng)建該類(lèi)對(duì)象,通常用于某些特殊的類(lèi)結(jié)構(gòu)設(shè)計(jì)。

4. 抽象類(lèi)的子類(lèi),必須重寫(xiě)抽象父類(lèi)中**所有的**抽象方法,否則,編譯無(wú)法通過(guò)而報(bào)錯(cuò)。除非該子類(lèi)也是抽象類(lèi)。

  > 理解:假設(shè)不重寫(xiě)所有抽象方法,則類(lèi)中可能包含抽象方法。那么創(chuàng)建對(duì)象后,調(diào)用抽象的方法,沒(méi)有意義。

 

 

## 6.5 接口

### 6.5.1 為什么要使用接口?

多態(tài)的使用前提必須是“繼承”。而類(lèi)繼承有如下問(wèn)題:

(1)類(lèi)繼承有單繼承限制

(2)類(lèi)繼承表示的是事物之間is-a的關(guān)系,但是is-a的關(guān)系要求太嚴(yán)格了。

為了解決這兩個(gè)問(wèn)題,引入了接口,接口支持:

(1)多實(shí)現(xiàn)

(2)實(shí)現(xiàn)類(lèi)和接口是is-like-a關(guān)。只要A類(lèi)想要B接口聲明的行為功能,就可以讓A類(lèi)實(shí)現(xiàn)B接口,不用考慮邏輯關(guān)系。

```java
Bird is a Animal.  鳥(niǎo)是一種動(dòng)物。
Plane is not a Animal.  飛機(jī)不是一種動(dòng)物。
Plane is a Vehicle.      飛機(jī)是一種交通工具。
   
Bird is like a Flyable。        鳥(niǎo)具有飛的能力?;?鳥(niǎo)會(huì)飛。
Plane is like a Flyable。   飛機(jī)具有飛的功能。或飛機(jī)會(huì)飛。
   
is-a解決的是:是不是的問(wèn)題
is-like-a解決的是:要不要的問(wèn)題
```

生活中的USB接口等思想,也是接口的思想。

<img src="images/image-20220616183122869.png" alt="image-20220616183122869" style="zoom:50%;" />

### 6.5.2 定義和使用格式

接口的定義,它與定義類(lèi)方式相似,但是使用 `interface` 關(guān)鍵字。它也會(huì)被編譯成.class文件,但一定要明確它并不是類(lèi),而是另外一種引用數(shù)據(jù)類(lèi)型。

> 引用數(shù)據(jù)類(lèi)型:數(shù)組,類(lèi),枚舉,接口,注解。

1、接口的聲明格式

```java
【修飾符】 interface 接口名{
   //接口的成員列表:
   // 公共的靜態(tài)常量
   // 公共的抽象方法
   // 公共的默認(rèn)方法(JDK1.8以上)
   // 公共的靜態(tài)方法(JDK1.8以上)
   // 私有方法(JDK1.9以上)
}
```

2、接口的成員說(shuō)明

接口定義的是多個(gè)類(lèi)共同的公共行為規(guī)范,這些行為規(guī)范是與外部交流的通道,這就意味著接口里通常是定義一組公共方法。

在JDK8之前,接口中只允許出現(xiàn):

(1)公共的靜態(tài)的常量:其中public static final可以省略

(2)公共的抽象的方法:其中public abstract可以省略

> 理解:接口是從多個(gè)相似類(lèi)中抽象出來(lái)的規(guī)范,不需要提供具體實(shí)現(xiàn)

在JDK1.8時(shí),接口中允許聲明默認(rèn)方法和靜態(tài)方法:

(3)公共的默認(rèn)的方法:其中public 可以省略,建議保留,但是default不能省略

(4)公共的靜態(tài)的方法:其中public 可以省略,建議保留,但是static不能省略

在JDK1.9時(shí),接口又增加了:

(5)私有方法:其中private不可以省略

(6)除此之外,接口中不能有其他成員,沒(méi)有構(gòu)造器,沒(méi)有初始化塊,因?yàn)榻涌谥袥](méi)有成員變量需要?jiǎng)討B(tài)初始化。

3、示例代碼

```java
package com.atguigu.interfacetype;

public interface Flyable {
   long MAX_SPEED = 299792458;//光速:299792458米/秒, 省略public static final
   
   void fly();//省略public abstract
   
   static void start(){//省略public
       System.out.println("start");
   }
   default void end(){//省略public
       System.out.println("end");
   }
   
   private void show(){
       System.out.println("cool!");
   }
}
```

4、其他說(shuō)明

為什么接口中只能聲明公共的靜態(tài)的常量?(面試題)

因?yàn)榻涌谑菢?biāo)準(zhǔn)規(guī)范,那么在規(guī)范中需要聲明一些底線邊界值,當(dāng)實(shí)現(xiàn)者在實(shí)現(xiàn)這些規(guī)范時(shí),不能去隨意修改和觸碰這些底線,否則就有“危險(xiǎn)”。

例如:USB1.0規(guī)范中規(guī)定最大傳輸速率是1.5Mbps,最大輸出電流是5V/500mA

?           USB3.0規(guī)范中規(guī)定最大傳輸速率是5Gbps(500MB/s),最大輸出電流是5V/900mA

例如:尚硅谷學(xué)生行為規(guī)范中規(guī)定學(xué)員,早上8:25之前進(jìn)班,晚上21:30之后離開(kāi)等等。

 

為什么JDK1.8之后要允許接口定義靜態(tài)方法和默認(rèn)方法呢?因?yàn)樗`反了接口作為一個(gè)抽象標(biāo)準(zhǔn)定義的概念。

**靜態(tài)方法**:因?yàn)橹暗臉?biāo)準(zhǔn)類(lèi)庫(kù)設(shè)計(jì)中,有很多Collection/Colletions或者Path/Paths這樣成對(duì)的接口和類(lèi),后面的類(lèi)中都是靜態(tài)方法,而這些靜態(tài)方法都是為前面的接口服務(wù)的,那么這樣設(shè)計(jì)一對(duì)API,不如把靜態(tài)方法直接定義到接口中使用和維護(hù)更方便。

**默認(rèn)方法**:(1)我們要在已有的老版接口中提供新方法時(shí),如果添加抽象方法,就會(huì)涉及到原來(lái)使用這些接口的類(lèi)就會(huì)有問(wèn)題,那么為了保持與舊版本代碼的兼容性,只能允許在接口中定義默認(rèn)方法實(shí)現(xiàn)。比如:Java8中對(duì)Collection、List、Comparator等接口提供了豐富的默認(rèn)方法。(2)當(dāng)我們接口的某個(gè)抽象方法,在很多實(shí)現(xiàn)類(lèi)中的實(shí)現(xiàn)代碼是一樣的,此時(shí)將這個(gè)抽象方法設(shè)計(jì)為默認(rèn)方法更為合適,那么實(shí)現(xiàn)類(lèi)就可以選擇重寫(xiě),也可以選擇不重寫(xiě)。

 

為什么JDK1.9之后要允許接口定義私有的方法呢?

因?yàn)镴DK1.8增加的靜態(tài)方法和默認(rèn)方法都是有方法體的,多個(gè)方法之間就可能存在“重復(fù)的冗余”代碼,這些代碼可以抽取出來(lái)“內(nèi)部共用”。

 

### 6.5.3 接口的使用

#### 1、使用接口的靜態(tài)成員

接口不能直接創(chuàng)建對(duì)象,但是可以通過(guò)接口名直接調(diào)用接口的靜態(tài)方法和靜態(tài)常量。

```java
package com.atguigu.interfacetype;

public class TestFlyable {
   public static void main(String[] args) {
       System.out.println(Flyable.MAX_SPEED);//調(diào)用接口的靜態(tài)常量
       Flyable.start();//調(diào)用接口的靜態(tài)方法
   }
}
```

#### 2、類(lèi)實(shí)現(xiàn)接口(implements)

接口**不能創(chuàng)建對(duì)象**,但是可以被類(lèi)實(shí)現(xiàn)(`implements` ,類(lèi)似于被繼承)。

類(lèi)與接口的關(guān)系為實(shí)現(xiàn)關(guān)系,即**類(lèi)實(shí)現(xiàn)接口**,該類(lèi)可以稱(chēng)為接口的實(shí)現(xiàn)類(lèi),也可以稱(chēng)為接口的子類(lèi)。實(shí)現(xiàn)的動(dòng)作類(lèi)似繼承,格式相仿,只是關(guān)鍵字不同,實(shí)現(xiàn)使用 ` implements`關(guān)鍵字。

```java
【修飾符】 class 實(shí)現(xiàn)類(lèi)  implements 接口{
    // 重寫(xiě)接口中抽象方法【必須】,當(dāng)然如果實(shí)現(xiàn)類(lèi)是抽象類(lèi),那么可以不重寫(xiě)
     // 重寫(xiě)接口中默認(rèn)方法【可選】
}

【修飾符】 class 實(shí)現(xiàn)類(lèi) extends 父類(lèi) implements 接口{
   // 重寫(xiě)接口中抽象方法【必須】,當(dāng)然如果實(shí)現(xiàn)類(lèi)是抽象類(lèi),那么可以不重寫(xiě)
     // 重寫(xiě)接口中默認(rèn)方法【可選】
}
```

注意:

1. 如果接口的實(shí)現(xiàn)類(lèi)是非抽象類(lèi),那么必須==重寫(xiě)接口中所有抽象方法==。

2. 默認(rèn)方法可以選擇保留,也可以重寫(xiě)。

  > 重寫(xiě)時(shí),default單詞就不要再寫(xiě)了,它只用于在接口中表示默認(rèn)方法,到類(lèi)中就沒(méi)有默認(rèn)方法的概念了

3. **接口中的靜態(tài)方法不能被繼承也不能被重寫(xiě)**

示例代碼:

```java
package com.atguigu.interfacetype;

public class Animal {
   public void eat(){
       System.out.println("吃東西");
   }
}

```

```java
package com.atguigu.interfacetype;

public class Bird extends Animal implements Flyable{

   //重寫(xiě)父接口的抽象方法,【必選】
   @Override
   public void fly() {
       System.out.println("我要飛的更高~~~");
   }

   //重寫(xiě)父接口的默認(rèn)方法,【可選】
   @Override
   public void end() {
       System.out.println("輕輕落在樹(shù)枝上~~~");
   }
}
```

#### 3、使用接口的非靜態(tài)方法

* 對(duì)于接口的靜態(tài)方法,直接使用“接口名.”進(jìn)行調(diào)用即可
 * 也只能使用“接口名."進(jìn)行調(diào)用,不能通過(guò)實(shí)現(xiàn)類(lèi)的對(duì)象進(jìn)行調(diào)用
* 對(duì)于接口的抽象方法、默認(rèn)方法,只能通過(guò)實(shí)現(xiàn)類(lèi)對(duì)象才可以調(diào)用
 * 接口不能直接創(chuàng)建對(duì)象,只能創(chuàng)建實(shí)現(xiàn)類(lèi)的對(duì)象

```java
package com.atguigu.interfacetype;

public class TestBirdFlyable {
   public static void main(String[] args) {
       Bird bird = new Bird();
       Flyable.start();//調(diào)用接口的靜態(tài)方法,只能通過(guò)  接口名.
       //必須依賴(lài)于實(shí)現(xiàn)類(lèi)的對(duì)象
       bird.fly();//調(diào)用接口的抽象方法
       bird.end();//調(diào)用接口的默認(rèn)方法
       
       bird.eat();//調(diào)用父類(lèi)繼承的方法
   }

}

```

#### 4、接口的多實(shí)現(xiàn)(implements)

之前學(xué)過(guò),在繼承體系中,一個(gè)類(lèi)只能繼承一個(gè)父類(lèi)。而對(duì)于接口而言,一個(gè)類(lèi)是可以實(shí)現(xiàn)多個(gè)接口的,這叫做接口的**多實(shí)現(xiàn)**。并且,一個(gè)類(lèi)能繼承一個(gè)父類(lèi),同時(shí)實(shí)現(xiàn)多個(gè)接口。

實(shí)現(xiàn)格式:

```java
【修飾符】 class 實(shí)現(xiàn)類(lèi)  implements 接口1,接口2,接口3。。。{
    // 重寫(xiě)接口中所有抽象方法【必須】,當(dāng)然如果實(shí)現(xiàn)類(lèi)是抽象類(lèi),那么可以不重寫(xiě)
     // 重寫(xiě)接口中默認(rèn)方法【可選】
}

【修飾符】 class 實(shí)現(xiàn)類(lèi) extends 父類(lèi) implements 接口1,接口2,接口3。。。{
   // 重寫(xiě)接口中所有抽象方法【必須】,當(dāng)然如果實(shí)現(xiàn)類(lèi)是抽象類(lèi),那么可以不重寫(xiě)
     // 重寫(xiě)接口中默認(rèn)方法【可選】
}
```

> 接口中,有多個(gè)抽象方法時(shí),實(shí)現(xiàn)類(lèi)必須重寫(xiě)所有抽象方法。**如果抽象方法有重名的,只需要重寫(xiě)一次**。

定義多個(gè)接口:

```java
package com.atguigu.interfacetype;

public interface Jumpable {
   void jump();
}
```

```java
package com.atguigu.interfacetype;

public interface Runnable {
   void jump();
   void run();
}
```

定義實(shí)現(xiàn)類(lèi):

```java
package com.atguigu.interfacetype;

public class Bird implements Flyable,Jumpable,Runnable{

   //重寫(xiě)父接口的抽象方法,【必選】
   @Override
   public void fly() {
       System.out.println("我要飛的更高~~~");
   }

   //重寫(xiě)父接口的默認(rèn)方法,【可選】
   @Override
   public void end() {
       System.out.println("輕輕落在樹(shù)枝上~~~");
   }

   @Override
   public void jump() {
       System.out.println("我會(huì)跳跳~~~");
   }

   @Override
   public void run() {
       System.out.println("我會(huì)跑~~");
   }
}
```

測(cè)試類(lèi)

```java
package com.atguigu.interfacetype;

public class TestBird {
   public static void main(String[] args) {
       Bird bird = new Bird();
       bird.fly();//調(diào)用Flyable接口的抽象方法
       bird.jump();//調(diào)用Jumpable接口的抽象方法
       bird.run();//調(diào)用Runnable接口的抽象方法
   }
}
```

#### 5、接口的多繼承  (extends)

一個(gè)接口能繼承另一個(gè)或者多個(gè)接口,接口的繼承也使用 `extends` 關(guān)鍵字,子接口繼承父接口的方法。

定義父接口:

```java
package com.atguigu.interfacetype;

public interface A {
   void a();
}
```

```java
package com.atguigu.interfacetype;

public interface B {
   void b();
}

```

 

定義子接口:

```java
package com.atguigu.interfacetype;

public interface C extends A,B {
   void c();
}

```

定義子接口的實(shí)現(xiàn)類(lèi):

```java
package com.atguigu.interfacetype;

public class D implements C {
   @Override
   public void c() {
       System.out.println("重寫(xiě)C接口的抽象方法c");
   }

   @Override
   public void a() {
       System.out.println("重寫(xiě)C接口的抽象方法a");
   }

   @Override
   public void b() {
       System.out.println("重寫(xiě)C接口的抽象方法b");
   }
}

```

>所有父接口的抽象方法都有重寫(xiě)。
>
>方法簽名相同的抽象方法只需要實(shí)現(xiàn)一次。

 

### 6.5.4 接口的特點(diǎn)總結(jié)

- 接口本身不能創(chuàng)建對(duì)象,只能創(chuàng)建接口的實(shí)現(xiàn)類(lèi)對(duì)象,接口類(lèi)型的變量可以與實(shí)現(xiàn)類(lèi)對(duì)象構(gòu)成多態(tài)引用。
- 聲明接口用interface,接口的成員聲明有限制:(1)公共的靜態(tài)常量(2)公共的抽象方法(3)公共的默認(rèn)方法(4)公共的靜態(tài)方法(5)私有方法(JDK1.9以上)
- 類(lèi)可以實(shí)現(xiàn)接口,關(guān)鍵字是implements,而且支持多實(shí)現(xiàn)。如果實(shí)現(xiàn)類(lèi)不是抽象類(lèi),就必須實(shí)現(xiàn)接口中所有的抽象方法。如果實(shí)現(xiàn)類(lèi)既要繼承父類(lèi)又要實(shí)現(xiàn)父接口,那么繼承(extends)在前,實(shí)現(xiàn)(implements)在后。
- 接口可以繼承接口,關(guān)鍵字是extends,而且支持多繼承。
- 接口的默認(rèn)方法可以選擇重寫(xiě)或不重寫(xiě)。如果有沖突問(wèn)題,另行處理。子類(lèi)重寫(xiě)父接口的默認(rèn)方法,要去掉default,子接口重寫(xiě)父接口的默認(rèn)方法,不要去掉default。
- 接口的靜態(tài)方法不能被繼承,也不能被重寫(xiě)。接口的靜態(tài)方法只能通過(guò)“接口名.靜態(tài)方法名”進(jìn)行調(diào)用。

### 6.5.5 成員沖突問(wèn)題

#### 1、默認(rèn)方法兩種沖突情況

(1)親爹優(yōu)先原則

當(dāng)一個(gè)類(lèi),既繼承一個(gè)父類(lèi),又實(shí)現(xiàn)若干個(gè)接口時(shí),父類(lèi)中的成員方法與接口中的抽象方法重名,子類(lèi)就近選擇執(zhí)行父類(lèi)的成員方法。代碼如下:

定義接口:

```java
package com.atguigu.interfacetype;

public interface Friend {
   default void date(){//約會(huì)
       System.out.println("吃喝玩樂(lè)");
   }
}
```

定義父類(lèi):

```java
package com.atguigu.interfacetype;

public class Father {
   public void date(){//約會(huì)
       System.out.println("爸爸約吃飯");
   }
}
```

定義子類(lèi):

```java
package com.atguigu.interfacetype;

public class Son extends Father implements Friend {
   @Override
   public void date() {
       //(1)不重寫(xiě)默認(rèn)保留父類(lèi)的
       //(2)調(diào)用父類(lèi)被重寫(xiě)的
//        super.date();
       //(3)保留父接口的
//        Friend.super.date();
       //(4)完全重寫(xiě)
       System.out.println("學(xué)Java");
   }
}
```

定義測(cè)試類(lèi):

```java
package com.atguigu.interfacetype;

public class TestSon {
   public static void main(String[] args) {
       Son s = new Son();
       s.date();
   }
}
```

(2)左右為難

- 當(dāng)一個(gè)類(lèi)同時(shí)實(shí)現(xiàn)了多個(gè)父接口,而多個(gè)父接口中包含方法簽名相同的默認(rèn)方法時(shí),怎么辦呢?

![](images/選擇困難.jpg)

無(wú)論你多難抉擇,最終都是要做出選擇的。

聲明接口:

```java
package com.atguigu.interfacetype;

public interface BoyFriend {
   default void date(){//約會(huì)
       System.out.println("神秘約會(huì)");
   }
}
```

選擇保留其中一個(gè),通過(guò)“接口名.super.方法名"的方法選擇保留哪個(gè)接口的默認(rèn)方法。

```java
package com.atguigu.interfacetype;

public class Girl implements Friend,BoyFriend{

   @Override
   public void date() {
       //(1)保留其中一個(gè)父接口的
//        Friend.super.date();
//        BoyFriend.super.date();
       //(2)完全重寫(xiě)
       System.out.println("學(xué)Java");
   }

}
```

測(cè)試類(lèi)

```java
package com.atguigu.interfacetype;

public class TestGirl {
   public static void main(String[] args) {
       Girl girl = new Girl();
       girl.date();
   }
}
```

- 當(dāng)一個(gè)子接口同時(shí)繼承了多個(gè)接口,而多個(gè)父接口中包含方法簽名相同的默認(rèn)方法時(shí),怎么辦呢?

另一個(gè)父接口:

```java
package com.atguigu.interfacetype;

public interface Usb2 {
   //靜態(tài)常量
   long MAX_SPEED = 60*1024*1024;//60MB/s

   //抽象方法
   void in();
   void out();

   //默認(rèn)方法
   public default void start(){
       System.out.println("開(kāi)始");
   }
   public default void stop(){
       System.out.println("結(jié)束");
   }

   //靜態(tài)方法
   public static void show(){
       System.out.println("USB 2.0可以高速地進(jìn)行讀寫(xiě)操作");
   }
}
```

子接口:

```java
package com.atguigu.interfacetype;

public interface Usb extends Usb2,Usb3 {
   @Override
   default void start() {
       System.out.println("Usb.start");
   }

   @Override
   default void stop() {
       System.out.println("Usb.stop");
   }
}

```

> 小貼士:
>
> 子接口重寫(xiě)默認(rèn)方法時(shí),default關(guān)鍵字可以保留。
>
> 子類(lèi)重寫(xiě)默認(rèn)方法時(shí),default關(guān)鍵字不可以保留。

#### 2、變量沖突問(wèn)題

- 當(dāng)子類(lèi)繼承父類(lèi)又實(shí)現(xiàn)父接口,而父類(lèi)中存在與父接口常量同名的成員變量,并且該成員變量名在子類(lèi)中仍然可見(jiàn)。
- 當(dāng)子類(lèi)同時(shí)繼承多個(gè)父接口,而多個(gè)父接口存在相同同名常量。

此時(shí)在子類(lèi)中想要引用父類(lèi)或父接口的同名的常量或成員變量時(shí),就會(huì)有沖突問(wèn)題。

父類(lèi)和父接口:

```java
package com.atguigu.interfacetype;

public class SuperClass {
   int x = 1;
}
```

```java
package com.atguigu.interfacetype;

public interface SuperInterface {
   int x = 2;
   int y = 2;
}
```

```java
package com.atguigu.interfacetype;

public interface MotherInterface {
   int x = 3;
}
```

子類(lèi):

```java
package com.atguigu.interfacetype;

public class SubClass extends SuperClass implements SuperInterface,MotherInterface {
   public void method(){
//        System.out.println("x = " + x);//模糊不清
       System.out.println("super.x = " + super.x);
       System.out.println("SuperInterface.x = " + SuperInterface.x);
       System.out.println("MotherInterface.x = " + MotherInterface.x);
       System.out.println("y = " + y);//沒(méi)有重名問(wèn)題,可以直接訪問(wèn)
   }
}
```