6.6.2 示例
需求
- 定義一個标準的學生類Student, 屬性: 姓名和年齡, 行為: 學習, 吃飯.
- 在測試類中建立學生類的對象, 然後通路類中的成員.
參考代碼
//學生類
public class Student {
//屬性(成員變量), 全部用private修飾.
private String name; //姓名
private int age; //年齡
//構造方法, 一般提供兩個(無參, 全參)
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
//getXxx()和setXxx()方法
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 void study() {
System.out.println("鍵盤敲爛, 月薪過萬!");
}
public void eat() {
System.out.println("學習餓了要吃飯");
}
}
//學生類的測試類
public class StudentTest {
public static void main(String[] args) {
//1. 建立學生對象.
Student s = new Student();
//2. 設定成員變量值.
s.setName("張三");
s.setAge(23);
//3. 列印成員變量值.
System.out.println(s.getName() + "..." + s.getAge());
//4. 調用成員方法.
s.study();
s.eat();
}
}
複制
7. 繼承
7.1 概述
多個類中存在相同屬性和行為時, 将這些内容抽取到單獨的一個類中, 那麼這多個類就無需再定義這些屬性和行為了, 隻要繼承那個類即可. 這個關系, 就叫繼承.
注意:
有了繼承以後, 我們在定義一個類的時候, 可以在一個已經存在的類的基礎上, 還可以定義自己的新成員.
7.2 格式
在Java中, 可以通過extends關鍵字來實作類與類的繼承, 具體格式如下:
public class 類A extends 類B { //子承父業
}
解釋:
- 類A: 叫子類, 或者派生類.
- 類B: 叫父類, 基類, 或者超類.
我們一般會念做: 子類和父類.
7.3 繼承的好處和弊端
7.3.1 示例
需求
- 按照标準格式定義一個人類(Person類), 屬性為姓名和年齡.
- 定義老師類(Teacher), 繼承自人類, 并在老師類中定義teach()方法.
- 定義學生類(Student), 繼承自人類, 并在學生類中定義study()方法.
- 在PersonTest測試類的main方法中, 分别建立老師類和學生類的對象, 并調用各自類中的成員.
參考代碼
略
7.3.2 好處
- 提高了代碼的複用性.
- 提高了代碼的可維護性.
- 讓類與類之間産生關系, 是多态的前提.
7.3.3 弊端
讓類與類之間産生了關系, 也就讓類的耦合性增強了.
解釋:
開發原則: 高内聚, 低耦合.
- 内聚: 指的是類自己獨立完成某些事情的能力.
- 耦合: 指的是類與類之間的關系.
7.4 Java中繼承的特點
7.4.1 示例
- 定義GrandFather類, 該類有一個grandFatherSay()方法, 該方法列印一句話爺爺都是從孫子過來的.
- 定義Father類, 該類有一個fatherSay()方法, 該方法列印一句話爸爸都是從兒子過來的.
- 定義Son類, 分别先繼承自GrandFather類和Father類, 此時發現, 上述的兩個方法隻能同時調用一個.
- 如果想讓Son類的對象, 同時能調用上述的兩個方法, 則可以這樣做:
- Son類繼承自Father類, Father類繼承自GrandFather類.
7.4.2 參考代碼
//爺爺類,
//一個類如果沒有顯式的寫父類, 預設就繼承自Object類.
//Object類, 是所有類的父類.
public class GrandFather {
public void grandFather() {
System.out.println("grandFather....");
}
}
//父親類
public class Father extends GrandFather{
public void father() {
System.out.println("father....");
}
}
//兒子類
public class Son extends Father {
}
//測試類
public class SonTest {
public static void main(String[] args) {
//建立子類對象.
Son s = new Son();
//問: Son的對象, 可以調用哪些方法?
s.father(); //從Father類繼承過來的.
s.grandFather(); //從Father類繼承過來的, 而Father類又從GrandFather類繼承過來了.
}
}
複制
7.4.3 總結
- Java中類與類之間隻能單繼承, 不能多繼承.
-
public class 類A extends 類B,類C { //這種寫法會報錯.
}
- Java中類與類之間, 可以多層繼承.
-
public class A { }
public class B extends A{ }
public class C extends B{ } //這種寫法可以.
7.5 繼承中成員變量的特點
7.5.1 示例
- 定義Father類, 在該類的成員位置定義變量: int age = 30;
- 定義Son類, 讓它繼承Father類, 并在該類的成員位置定義變量: int age = 20;
- 在測試類FatherTest的main方法中, 定義變量: int age = 10;
- 通過輸出語句, 直接列印age變量的值, 并檢視程式的運作結果.
7.5.2 參考代碼
public class Father {
int age = 30;
}
public class Son extends Father {
int age = 20;
}
public class FatherTest {
public static void main(String[] args) {
int age = 10;
System.out.println(age); //這裡列印結果會是多少呢?
}
}
複制
7.5.3 總結
Java中使用變量遵循就近原則, 局部位置有就使用, 沒有就去本類的成員位置找. 有就使用, 沒有就去父類的成員位置找, 有就使用, 沒有就報錯.
注意: 不考慮父類的父類這種情況, 因為會一直往上找, 直到把所有的父類都找完, 還找不到, 就報錯了.
7.6 super關鍵字
7.6.1 概述
super的用法和this很像:
- this: 代表本類對象的引用.
- super: 代表目前對象的父類的記憶體空間辨別.
- 解釋: 可以了解為父類對象引用.
7.6.2 用法
功能 | 本類 | 父類 |
---|---|---|
通路成員變量 | this.成員變量名 | super.成員變量名 |
通路構造方法 | this(...) | super(...) |
通路成員方法 | this.成員方法名(參數值...) | super.成員方法名(參數值...) |
7.7 繼承中構造方法的特點
7.7.1 示例
- 定義父類Father, 并在空參構造中列印一句話: Father空參構造.
- 定義子類Son, 并在空參構造中列印一句話: Son空參構造.
- 在FatherTest測試類的main方法中, 建立Son類的對象, 并檢視程式的執行結果.
- 分别修改Father類和Son類的代碼, 添加帶參構造, 并觀察程式的執行結果.
7.7.2 參考代碼
略.
7.7.3 總結
- 子類中所有的構造方法預設都會通路父類的空參構造.
- 問: 為什麼這樣設計呢?
- 答: 用于子類對象通路父類資料前, 對父類資料進行初始化.
- 即: 每一個構造方法的第一條語句預設都是: super()
- 如果父類沒有空參構造, 我們可以通過super(參數)的形式通路父類的帶參構造.
- 解釋: 但是這樣做比較繁瑣, 是以建議我們定義類時, 永遠手動給出空參構造.
7.8 繼承中成員方法的特點
7.8.1 示例
- 定義Father類, 并在類中定義method()方法和show()方法.
- 定義Son類, 并在類中定義method()方法和show()方法.
- 在FatherTest測試類中, 建立Son類的對象, 并調用類中的成員方法.
- 注釋子類中的method()方法或者show()方法, 并觀察程式的執行結果.
7.8.2 參考代碼
略.
7.8.3 總結
調用成員方法時, 也遵循就近原則, 本類中有, 就直接調用, 本類中沒有, 就去父類中查找, 有就使用, 沒有就報錯.
7.9 方法重寫
7.9.1 概述
子類中出現和父類一模一樣的方法時, 稱為方法重寫. 方法重寫要求傳回值的資料類型也必須一樣.
7.9.2 應用場景
當子類需要使用父類的功能, 而功能主體又有自己獨有需求的時候, 就可以考慮重寫父類中的方法了, 這樣, 即沿襲了父類的功能, 又定義了子類特有的内容.
7.9.3 示例
需求
- 定義Phone類, 并在類中定義call(String name)方法.
- 定義NewPhone類, 繼承Phone類, 然後重寫call(String name)方法.
- 在PhoneTest測試類中, 分别建立兩個類的對象, 然後調用call()方法, 觀察程式執行結果.
參考代碼
略.
7.9.4 注意事項
- 子類重寫父類方法時, 方法聲明上要用@Override注解來修飾.
- 父類中私有的方法不能被重寫.
- 子類重寫父類方法時, 通路權限不能更低.
7.10 老師和學生案例(繼承版)
7.10.1 需求
- 已知老師類Teacher, 屬性有姓名和年齡, 行為有teach().
- 已知學生類Student, 屬性有姓名和年齡, 行為有study().
- 分析上述的需求, 并通過代碼實作.
- 在PersonTest類的main方法中, 分别建立老師類和學生類的對象, 然後通路其成員.
7.10.2 參考代碼
略.
注意: 子類繼承父類時, 一般構造方法也要手動生成, 目的是為了幫助我們快速建立對象并指派.
8. 多态
8.1 概述
多态指的是同一個事物(或者對象)在不同時刻表現出來的不同狀态.
例如: 一杯水.
- 常溫下是液體.
- 高溫下是氣體.
- 低溫下是固體.
但是水還是那杯水, 隻不過在不同的環境下, 表現出來的狀态不同.
8.2 前提條件
- 要有繼承關系.
- 要有方法重寫.
- 要有父類引用指向子類對象.
8.3 示例: 多态入門
需求
- 定義動物類Animal, 并在類中定義一個成員方法: eat()
- 定義貓類Cat, 繼承Animal類, 并重寫eat()方法.
- 在AnimalTest測試類的main方法中, 通過多态的方式建立貓類對象.
- 通過貓類對象, 調用eat()方法.
參考代碼
略.
8.4 多态中的成員通路特點
- 成員變量: 編譯看左邊, 運作看左邊.
- 成員方法: 編譯看左邊, 運作看右邊.
需求
- 定義一個人類Person. 屬性為姓名和年齡, 行為是: eat()方法.
- 定義Student類, 繼承自Person類, 定義age屬性及重寫eat()方法.
- 在PersonTest測試類的main方法中, 建立Student類的對象, 并列印其成員.
參考代碼
略.
8.5 好處和弊端.
8.5.1 好處
提高了程式的擴充性.
8.5.2 弊端
父類引用不能通路子類的特有功能.
問: 那如何解決這個問題呢?
答: 通過向下轉型來解決這個問題.
8.5.3 示例
需求
- 定義動物類Animal, 該類有一個eat()方法.
- 定義貓類Cat, 繼承自Animal類, 該類有一個自己獨有的方法: catchMouse().
- 在AnimalTest測試類中, 通過多态的方式建立Cat類的對象, 并嘗試調用catchMouse()方法.
參考代碼
略.
8.5.4 向上轉型和向下轉型
向上轉型
//格式
父類型 對象名 = new 子類型();
//例如
Animal an = new Cat();
向下轉型
//格式
子類型 對象名 = (子類型)父類引用;
//例如
Cat c = (Cat)an;
小Bug
//下述代碼會報: ClassCastException(類型轉換異常)
Animal an = new Cat();
Cat c = (Cat)an; //這樣寫不報錯.
Dog d = (Dog)an; //這樣寫會報錯.
8.6 示例: 貓狗案例
需求
- 已知貓狗都有姓名和年齡, 都要吃飯.
- 貓類獨有自己的catchMouse()方法, 狗類獨有自己的lookHome()方法.
- 在AnimalTest測試類的main方法中, 通過多态分别建立貓類, 狗類的對象.
- 分别通過貓類對象和狗類對象, 通路對象的成員.
參考代碼
略.
9. 兩個關鍵字
9.1 final關鍵字
9.1.1 概述
final是一個關鍵字, 表示最終的意思, 可以修飾類, 成員變量, 成員方法.
- 修飾的類: 不能被繼承, 但是可以繼承其他的類.
- 修飾的變量: 是一個常量, 隻能被指派一次.
- 修飾的方法: 不能被子類重寫.
9.1.2 示例
需求
- 定義Father類, 并定義它的子類Son.
- 先用final修飾Father類, 看Son類是否還能繼承Father類.
- 在Son類中定義成員變量age, 并用final修飾, 然後嘗試給其重新指派, 并觀察結果.
- 在Father類中定義show()方法, 然後用final修飾, 看Son類是否能重寫該方法.
參考代碼
略
9.2 static關鍵字
9.2.1 概述
static是一個關鍵字, 表示靜态的意思, 可以修飾成員變量, 成員方法.
9.2.2 特點
- 随着類的加載而加載.
- 優先于對象存在.
- 被static修飾的内容, 能被該類下所有的對象共享.
- 解釋: 這也是我們判斷是否使用靜态關鍵字的條件.
- 可以通過類名.的形式調用.
9.2.3 示例
需求
- 定義學生類, 屬性為姓名, 年齡, 畢業院校(graduateFrom).
- 在學生類中定義show()方法, 用來列印上述的各個屬性資訊.
- 在測試類的main方法中, 建立學生對象, 并調用學生類的各個成員.
參考代碼
//學生類
public class Student {
//屬性
String name;
int age;
static String graduateFrom;
//行為
public void show() {
System.out.println(name + ".." + age + ".." + graduateFrom);
}
}
//測試類
public class StudentTest {
public static void main(String[] args) {
//1. 設定學生的畢業院校.
Student.graduateFrom = "傳智學院";
//2. 建立學生對象s1.
Student s1 = new Student();
s1.name = "劉亦菲";
s1.age = 33;
//3. 建立學生對象s2.
Student s2 = new Student();
s2.name = "趙麗穎";
s2.age = 31;
//4. 列印屬性值.
s1.show();
s2.show();
}
}
複制
9.2.4 靜态方法的通路特點及注意事項
- 通路特點
- 靜态方法隻能通路靜态的成員變量和靜态的成員方法.
- 簡單記憶: 靜态隻能通路靜态.
- 注意事項
- 在靜态方法中, 是沒有this, super關鍵字的.
- 因為靜态的内容是随着類的加載而加載, 而this和super是随着對象的建立而存在.
- 即: 先進記憶體的, 不能通路後進記憶體的.
需求
- 定義學生類, 屬性為姓名和年齡(靜态修飾), 非靜态方法show1(),show2(), 靜态方法show3(), show4().
- 嘗試在show1()方法中, 調用: 姓名, 年齡, show2(), show4().
- 結論: 非靜态方法可以通路所有成員(非靜态變量和方法, 靜态變量和方法)
- 嘗試在show3()方法中, 調用: 姓名, 年齡, show2(), show4().
- 結論: 靜态方法隻能通路靜态成員.
參考代碼
略.
10. 抽象類
10.1 概述
回想前面我們的貓狗案例, 提取出了一個動物類, 這個時候我們可以通過Animal an = new Animal();來建立動物對象, 其實這是不對的, 因為, 我說動物, 你知道我說的是什麼動物嗎? 隻有看到了具體的動物, 你才知道, 這是什麼動物. 是以說, 動物本身并不是一個具體的事物, 而是一個抽象的事物. 隻有真正的貓, 狗才是具體的動物. 同理, 我們也可以推想, 不同的動物吃的東西應該是不一樣的, 是以, 我們不應該在動物類中給出具體的展現, 而是應該給出一個聲明即可. 在Java中, 一個沒有方法體的方法應該定義為抽象方法, 而類中如果有抽象方法, 該類必須定義為抽象類.
10.2 入門案例
需求
- 建立抽象類Animal.
- 在該類中定義抽象方法eat()
參考代碼
//抽象的動物類.
public abstract class Animal {
//抽象方法, 吃.
public abstract void eat();
}
複制
10.3 抽象類的特點
- 抽象類和抽象方法必須用abstract關鍵字修飾.
-
//抽象類
public abstract class 類名{ }
//抽象方法
public abstract void eat();
- 抽象類中不一定有抽象方法, 有抽象方法的類一定是抽象類.
- 抽象類不能執行個體化.
- 那抽象類如何執行個體化呢?
- 可以通過多态的方式, 建立其子類對象, 來完成抽象類的執行個體化. 這也叫: 抽象類多态.
- 抽象類的子類:
- 如果是普通類, 則必須重寫父抽象類中所有的抽象方法.
- 如果是抽象類, 則可以不用重寫父抽象類中的抽象方法.
需求
- 定義抽象類Animal , 類中有一個抽象方法eat(), 還有一個非抽象方法sleep().
- 嘗試在測試類中, 建立Animal類的對象, 并觀察結果.
- 建立普通類Cat, 繼承Animal類, 觀察是否需要重寫Animal#eat()方法.
- 建立抽象類Dog, 繼承Animal類, 觀察是否需要重寫Animal#eat()方法.
參考代碼
略
10.4 抽象類的成員特點
抽象類中可以有變量, 常量, 構造方法, 抽象方法和非抽象方法.
思考: 既然抽象類不能執行個體化, 那要構造方法有什麼用?
答: 用于子類對象通路父類資料前, 對父類資料進行初始化.
需求
- 定義抽象類Person, 在類中定義變量age, 常量country, 空參, 全參構造.
- 在Person類中定義非抽象方法show(), 抽象方法eat().
- 在測試類的main方法中, 建立Person類的對象, 并調用類中的成員.
參考代碼
略
10.5 案例: 老師類
10.5.1 需求
- 傳智播客公司有基礎班老師(BasicTeacher)和就業班老師(WorkTeacher), 他們都有姓名和年齡, 都要講課.
- 不同的是, 基礎班老師講JavaSE, 就業班老師講解JavaEE.
- 請用所學, 模拟該知識點.
10.5.2 分析
- 定義父類Teacher, 屬性: 姓名和年齡, 行為: 講課(因為不同老師講課内容不同, 是以該方法是抽象的).
- 定義BasicTeacher(基礎班老師), 繼承Teacher, 重寫所有的抽象方法.
- 定義WorkTeacher(就業班老師), 繼承Teacher, 重寫所有的抽象方法.
- 定義TeacherTest測試類, 分别測試基礎班老師和就業班老師的成員.
10.5.3 參考代碼
略.
11. 接口
11.1 概述
繼續回到我們的貓狗案例,我們想想狗一般就是看門,貓一般就是作為寵物了。但是,現在有很多的馴養員或者是馴獸師,可以訓練出:貓鑽火圈,狗跳高,狗做計算等。而這些額外的動作,并不是所有貓或者狗一開始就具備的,這應該屬于經過特殊的教育訓練訓練出來的。是以,這些額外的動作定義到動物類中就不合适,也不适合直接定義到貓或者狗中,因為隻有部分貓狗具備這些功能。
是以,為了展現事物功能的擴充性,Java中就提供了接口來定義這些額外功能,并不給出具體實作,将來哪些貓狗需要被訓練,隻需要這部分貓狗把這些額外功能實作即可。
11.2 特點
- 接口用interface關鍵字修飾.
- 類和接口之間是實作關系, 用implements關鍵字表示.
- 接口不能執行個體化.
- 那接口如何執行個體化呢?
- 可以通過多态的方式, 建立其子類對象, 來完成接口的執行個體化. 這也叫: 接口多态.
- 接口的子類:
- 如果是普通類, 則必須重寫父接口中所有的抽象方法.
- 如果是抽象類, 則可以不用重寫父接口中的抽象方法.
需求
- 定義Jumpping接口, 接口中有一個抽象方法jump().
- 定義Cat類, 實作Jumpping接口, 重寫jump()方法.
- 在測試類的main方法中, 建立Jumpping接口對象, 并調用其jump()方法
參考代碼
略.
11.3 成員特點
接口中有且隻能有常量或者抽象方法, 原因是因為:
- 成員變量有預設修飾符: public static final
- 成員方法有預設修飾符: public abstract
注意: 因為接口主要是擴充功能的, 而沒有具體存在, 所有接口中是沒有構造方法的.
記憶: JDK1.8的時候, 接口中加入了兩個新的成員: 靜态方法, 預設方法(必須用default修飾).
需求
定義接口Inter, 測試接口中的成員特點.
參考代碼
略.
11.4 類與接口之間的關系
- 類與類之間: 繼承關系, 隻能單繼承, 不能多繼承, 但是可以多層繼承.
- 類與接口之間: 實作關系, 可以單實作, 也可以多實作. 還可以在繼承一個類的同時實作多個接口.
- 接口與接口之間: 繼承關系, 可以單繼承, 也可以多繼承.
11.5 抽象類和接口之間的差別
- 成員特點差別.
11.6.2 參考代碼
略.
12. 運動員和教練案例
12.1 需求
- 已知有乒乓球運動員(PingPangPlayer)和籃球運動員(BasketballPlayer), 乒乓球教練(PingPangCoach)和籃球教練(BasketballCoach).
- 他們都有姓名和年齡, 都要吃飯, 但是吃的東西不同.
- 乒乓球教練教如何發球, 籃球教練教如何運球和投籃.
- 乒乓球運動員學習如何發球, 籃球運動員學習如何運球和投籃.
- 為了出國交流, 跟乒乓球相關的人員都需要學習英語.
- 請用所學, 模拟該知識.
12.2 參考代碼
略
13. 包
13.1 簡述層
包(package)就是檔案夾, 用來對類進行分類管理的. 例如:
- 學生的增加, 删除, 修改, 查詢.
- 老師的增加, 删除, 修改, 查詢.
- 其他類的增删改查...
- 基本的劃分: 按照子產品和功能劃分.
- 進階的劃分: 就業班做項目的時候你就能看到了.
13.2 格式
package 包名1.包名2.包名3; //多級包之間用.隔開
注意:
- package語句必須是程式的第一條可執行的代碼.
- package語句在一個.java檔案中隻能有一個.
13.3 常見分類
- 按照功能分
- com.itheima.add
- AddStudent.java
- AddTeacher.java
- com.itheima.delete
- DeleteStudent.java
- DeleteTeacher.java
- com.itheima.add
- 按照子產品分
- com.itheima.student
- AddStudent
- DeleteStudent
- com.itheima.teacher
- AddTeacher
- DeleteTeacher
- com.itheima.student
13.4 導包
13.4.1 概述
不同包下的類之間的通路,我們發現,每次使用不同包下的類的時候,都需要加包的全路徑。比較麻煩。這個時候,java就提供了導包的功能。
13.4.2 格式
import 包名;
- import java.util.*; 意思是導入java.util包下所有類, 這樣做效率較低, 不推薦使用.
- import java.util.Scanner; 意思是導入java.util.Scanner類, 推薦使用. 用誰就導入誰.
13.4.3 示例
需求
- 在cn.itcast包下定義Teacher類, 該類下有一個method()方法.
- 在com.itheima包下定義Student類, 該類有一個show()方法.
- 在com.itheima包下定義Test測試類, 嘗試通路Student#show(), Teacher#method().
參考代碼
略.
14. 權限修飾符
14.1 概述
權限修飾符是用來修飾類, 成員變量, 構造方法, 成員方法的, 不同的權限修飾符對應的功能不同. 具體如下:
public | protected | 預設 | private | |
---|---|---|---|---|
同一個類中 | √ | √ | √ | √ |
同一個包中的子類或者其他類 | √ | √ | √ | |
不同包中的子類 | √ | √ | ||
不同包中的其他類(無關類) | √ |
14.2 示例
需求
- 在com.itheima包下定義Father類, 該類中有四個方法, 分别是show1(), show2(), show3(), show4().
- 上述的四個方法分别用權限修飾符private, 預設, protected, public修飾.
- 在Father類中, 建立main方法, 然後建立Father類的對象, 并調用上述的4個方法, 并觀察結果.
- 在com.itheima包下建立Test類及main方法, 然後建立Father類的對象, 并調用上述的4個方法, 并觀察結果.
- 在com.itheima包下建立Father類的子類Son, 添加main方法.
- 在main方法中, 建立Son類的對象, 并調用上述的4個方法, 并觀察結果.
- 在cn.itcast包下建立Father類的子類Son, 然後在該類中添加main方法.
- 在main方法中, 建立Son類的對象, 并調用上述的4個方法, 并觀察結果.
- 在cn.itcast包下建立Test類及main方法, 然後建立Father類的對象, 并調用上述的4個方法, 并觀察結果.
參考代碼
略
14.3 總結
- 通路權限修飾符的權限從大到小分别是: public > protected > 預設 > private
- 在實際開發中, 如果沒有特殊需求, 則成員變量都用private修飾, 其它都用public修飾.
- 大白話總結四個通路權限修飾符的作用:
- private: 強調的是給自己使用.
- 預設: 強調的是給同包下的類使用.
- protected: 強調的是給子類使用.
- public: 強調的是給大家使用.