網(wǎng)站頂部展出的大幅廣告推廣網(wǎng)站源碼
一、面向對象高級
此筆記參考黑馬教程,僅學習使用,如有侵權,聯(lián)系必刪
文章目錄
- 一、面向對象高級
- 1. static(靜態(tài))
- 1.1 static 修飾成員變量
- 1.1.1 static
- 1.1.2 成員變量的執(zhí)行原理
- 總結
- 1.2 static 修飾成員變量的應用場景
- 類變量的應用場景
- 案例:
- 總結
- 1.3 static 修飾成員方法
- 1.3.1 成員方法的分類
- 1.3.2 成員方法的執(zhí)行原理
- 代碼演示
- 總結
- 補充知識:搞懂 mian 方法
- 1.4 static 修飾成員方法的應用場景
- 1.4.1 類方法的常見應用場景
- 1.4.2 工具類是什么?
- 1.4.3 使用類方法來設計工具類有啥好處?
- 案例
- 代碼實現(xiàn)
- 多學一招
- 總結
- 1.5 static 的注意事項
- 代碼演示
- 1.6 static 的應用知識:代碼塊
- 1.6.1 代碼塊概述
- 代碼演示
- 1.7 static 的應用知識:單例設計模塊
- 1.7.1 什么是設計模式(Design pattern)?
- 1.7.1.2 單例設計模式(餓漢式單例)
- 1.7.1.3 單例模式的應用場景和好處
- 1.7.1.4 單例設計模式的實現(xiàn)方式很多
- 總結
- 1.7.2 懶漢式單例設計模式
- 2. 面向對象三大特征之二:繼承
- 2.1 繼承的快速入門
- 2.1.1 認識繼承、特點
- 2.1.1.1 什么是繼承?
- 2.1.1.2 繼承的特點
- 2.1.1.3 繼承的執(zhí)行原理
- 代碼實現(xiàn)
- 總結
- 2.1.2 繼承的好處、應用場景
- 2.1.2.1 使用繼承有啥好處?
- 總結
- 2.2 繼承相關的注意事項
- 2.2.1 權限修飾符
- 2.2.1.1 什么是權限修飾符?
- 2.2.1.2 權限修飾符有幾種?各自的作用是什么?
- 代碼演示
- 2.2.2 單繼承、Object 類
- 2.2.2.1 單繼承
- 2.2.2.2 為何 Java 中的類不支持多繼承
- 2.2.2.3 Object 類
- 代碼演示
- 總結
- 2.2.3 方法重寫
- 2.2.3.1 認識方法重寫
- 2.2.3.2 方法重寫的應用場景
- 總結
- 2.2.4 子類中訪問其他成員的特點
- 2.2.5 子類構造器的特點
- 2.2.5.1 認識子類構造器的特點
- 代碼演示
- 2.2.5.1 常見的應用場景
- 代碼演示
- 補充知識:this(...) 調用兄弟構造器
- 2.2.6 注意事項小結
- 3. 面向對象的三大特征之三:多態(tài)
- 3.1 認識多態(tài)
- 3.1.1 什么是多態(tài)?
- 3.1.2 多態(tài)的具體代碼實現(xiàn)
- 3.1.3 多態(tài)的前提
- 3.1.4 多態(tài)的一個注意事項
- 代碼演示
- 3.2 使用多態(tài)的好處
- 3.2.1 使用多態(tài)的好處
- 3.2.2 多態(tài)下會產(chǎn)生的一個問題,怎么解決?
- 代碼演示
- 總結
- 3.3 多態(tài)下的類型轉換問題
- 3.3.1 類型轉換
- 3.3.2 強制類型轉換的一個注意事項
- 3.3.3 強轉前,Java 建議:
- 代碼演示
- 總結
1. static(靜態(tài))
1.1 static 修飾成員變量
1.1.1 static
- 叫靜態(tài),可以修飾成員變量、成員方法
成員變量按照有無 static 修飾,分為兩種:
- 類變量:有 static 修飾,屬于類,在計算機里只有一份,會被類的全部對象共享
- 實例變量(對象的變量):無 static 修飾,屬于每個對象的
public class Student {// 類變量static String name;// 實例變量(對象的變量)int age;
}

- 把學生類看成一張學生表,可以創(chuàng)建出學生對象 s1 和 s2,age 沒有 static 修飾,所以是對象的變量,也就是每個學生對象中都有一個自己的 age。
- 而成員變量 name 有 static 修飾,就是類變量,就是說它只屬于當前這個類自己持有,不屬于對象,也就是說無論我們用這個類創(chuàng)建出了多少個學生對象,這些學生對象中都不會持有這個類變量 name
類變量訪問方法
類名.類變量(推薦)
對象.類變量(不推薦)
實例變量訪問方法
對象.實例變量
1.1.2 成員變量的執(zhí)行原理
總結
- static 是什么?
- 叫靜態(tài),可以修飾成員變量、成員方法
- static 修飾的成員變量叫什么?怎么使用?有啥特點?
- 類變量(靜態(tài)成員變量)
類名.類變量(推薦)
對象.類變量(不推薦)
- 屬于類,與類一起加載一次,在內(nèi)存中只有一份,會被類的所有對象共享
- 無 static 修飾的成員變量叫什么?怎么使用?有啥特點?
- 實例變量(對象變量)
對象.實例變量
- 屬于對象,每個對象中都有一份
1.2 static 修飾成員變量的應用場景
類變量的應用場景
- 在開發(fā)中,如果某個數(shù)據(jù)只需要一份,且希望能夠被共享(訪問、修改),則該數(shù)據(jù)可以定義成類變量來記住
案例:
-
系統(tǒng)啟動后,要求用戶類可以記住自己創(chuàng)建了多少個用戶對象了
-
User.java
package Advanced.a_static.d1_staticdemo;public class User {// 類變量public static int number; // public是讓它對外完全公開和暴露的public User() {
// User.number++;// 注意:在同一個類中,訪問自己類的類變量,才可以省略類名不寫number++;}
}
- Test2.java
package Advanced.a_static.d1_staticdemo;public class Test2 {public static void main(String[] args) {// 目標:通過案例理解類變量的應用場景User u1 = new User();User u2 = new User();User u3 = new User();User u4 = new User();System.out.println(User.number); // 4}
}
總結
- 成員變量有幾種?各自在什么情況下定義?
- 類變量:數(shù)據(jù)只需要一份,且需要被共享時(訪問,修改)
- 實例變量:每個對象都要有一份,數(shù)據(jù)各不同(如:name、score、age)
- 訪問自己類中的類變量,是否可以省略類名不寫?
- 可以的
- 注意:在某個類中訪問其他類里的類變量,必須帶類名訪問
1.3 static 修飾成員方法
1.3.1 成員方法的分類
- 類方法:有 static 修飾的成員方法,屬于類
public static void printHelloWorld() {System.out.println("Hello World!");System.out.println("Hello World!");
}
類名.類方法(推薦)
對象名.類方法(不推薦)
- 實例方法:無 static 修飾的成員方法,屬于對象
public void printPass() {...
}
對象.實例方法
1.3.2 成員方法的執(zhí)行原理

代碼演示
- Student.java
package Advanced.a_static.d2_static_method;public class Student {double score;// 類方法public static void printHelloWorld() {System.out.println("Hello World!");System.out.println("Hello World!");}// 實例方法(對象的方法)public void printPass() {System.out.println("成績:" + (score >= 60 ? "及格" : "不及格"));}
}
- Test.java
package Advanced.a_static.d2_static_method;public class Test {public static void main(String[] args) {// 目標:掌握有無static修飾方法的用法// 1. 類方法的用法// 類名.類方法(推薦)Student.printHelloWorld();// 對象.類方法(不推薦)Student s = new Student();s.printHelloWorld();// 2. 實例方法的用法// 對象.實例方法s.printPass();}
}
總結
- static 修飾的成員方法叫什么?如何使用?
- 類方法(靜態(tài)方法)
- 屬于類,可以直接用類名訪問,也可以用對象訪問
類名.類方法(推薦)
對象名.類方法(不推薦)
- 無 static 修飾的成員方法叫什么?如何使用?
- 實例方法(對象的方法)
- 屬于對象,只能用對象訪問
對象.實例方法
補充知識:搞懂 mian 方法
public class Test {public static void main(String[] args) {...}
}
- main 方法是啥方法?
- 類方法
- 因為有 static
- main 方法咋就能直接跑起來?
- 我們用 Java 命令執(zhí)行這個 Test 程序的時候,虛擬機其實會用這個 Test 類直接去點這個 main 方法來觸發(fā)這個 main 方法的執(zhí)行
- 為什么可以用類名直接去點這個 main 方法執(zhí)行呢?因為 main 方法也是類方法
- 參數(shù)
String[] args
是什么?
- 當我們來執(zhí)行這個 main 方法的時候實際上我們可以送一些數(shù)據(jù)給這個參數(shù)接收的,這個參數(shù)它是一個 String 類型的數(shù)組,它可以接一些數(shù)據(jù),然后讓你這個 main 方法里面的程序來使用這些數(shù)據(jù)
- 怎么在執(zhí)行 main 方法的時候把數(shù)據(jù)傳給這個參數(shù)(String[] args)呢?
- 在調用 java 命令執(zhí)行時,直接跟在文件名后面
1.4 static 修飾成員方法的應用場景
1.4.1 類方法的常見應用場景
- 類方法最常見的應用場景是做工具類
1.4.2 工具類是什么?
- 工具類中的方法都是一些類方法,每個方法都是用來完成一個功能的,工具類是給開發(fā)人員共同使用的
1.4.3 使用類方法來設計工具類有啥好處?
- 提高了代碼復用;調用方便,提高了開發(fā)效率
public class XxxxUtil {public static void xxx() {...}public static boolean xxxx(String email) {...}public static String xxxxx(int n) {...}...
}
案例
需求:
- 某系統(tǒng)的登錄界面需要開發(fā)并展示四位隨機的驗證碼,系統(tǒng)的注冊界面需要開發(fā)并展示六位的隨機驗證碼
這兩套程序的代碼幾乎一模一樣,存在大量重復代碼,這時我們就可以使用類方法設計一個工具類讓這兩個程序來調用,這樣就優(yōu)化好這個結構了
代碼實現(xiàn)
- MyUtil.java
package Advanced.a_static.d3_util;import java.util.Random;public class MyUtil {private MyUtil() {}public static String createCode(int n) {// 1. 定義2個變量是,一個是記住最終產(chǎn)生的隨機驗證碼,一個是記住可能用到的全部字符String code = "";String data = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";Random r = new Random();// 2. 開始定義一個循環(huán)產(chǎn)生每位隨機數(shù)字符for (int i = 0; i < 4; i++) {// 3. 隨機一個字符范圍內(nèi)的索引int index = r.nextInt(data.length());// 4. 根據(jù)索引去全部字符中提取該字符code += data.charAt(index); // code = code + 字符}return code;}
}
-
為什么工具類中的方法要用類方法,而不是實例方法?
- 實例方法需要創(chuàng)建對象來調用,此時對象只是為了調用方法,對象占內(nèi)存,這樣會浪費內(nèi)存
多學一招
- 工具類沒有創(chuàng)建對象的需求,建議將工具類的構造器進行私有
總結
- 類方法有啥應用場景?
- 可以用來設計工具類
- 工具類是什么,有什么好處?
- 工具類中的方法都是類方法,每個類方法都是用來完成一個功能的
- 提高了代碼的復用性;調用方便,提高了開發(fā)效率
- 為什么工具類要用類方法,而不是實例方法?
- 實例方法需要創(chuàng)建對象來調用,會浪費內(nèi)存
- 工具類定義時有什么要求?
- 工具類不需要創(chuàng)建對象,建議將工具類的構造器私有化
1.5 static 的注意事項
- 類方法中可以直接訪問類的成員,不可以直接訪問實例成員
- 實例方法既可以直接訪問類成員,也可以直接訪問實例成員
- 實例方法中可以出現(xiàn) this 關鍵字,類方法中不可以出現(xiàn) this 關鍵字的
代碼演示
package Advanced.a_static.d4_static_attention;public class Student {static String schoolName; // 類變量double score; // 實例變量// 1. 類方法中可以直接訪問類的成員,不可以直接訪問實例成員public static void printHelloWorld() {// 注意:同一個類中,訪問類成員可以省略類名不寫schoolName = "Feng";Student.printHelloWorld2();// System.out.println(score); // 會報錯
// printPass(); // 會報錯// System.out.println(this); // 會報錯}// 類方法public static void printHelloWorld2() {}// 2. 實例方法中既可以直接訪問類成員,也可以直接訪問實例成員// 實例方法// 3. 實例方法中可以出現(xiàn)this關鍵字,類方法中不可以出現(xiàn)this關鍵字public void printPass() {schoolName = "Feng2";printHelloWorld2();System.out.println(score);printPass2();System.out.println(this); // 哪個對象調用它的實例方法 this就會指向哪個對象}// 實例方法public void printPass2() {}
}
1.6 static 的應用知識:代碼塊
1.6.1 代碼塊概述
- 代碼塊是類的的5大成分之一(成員變量、構造器、方法、代碼塊、內(nèi)部類)
代碼塊分為兩種:
-
靜態(tài)代碼塊:
- 格式:
static {}
- 特點:類加載時自動執(zhí)行,由于類只會加載一次,所以靜態(tài)代碼塊也只會執(zhí)行一次
- 作用:完成類的初識化,例如:對類變量的初始化賦值
- 格式:
-
實例代碼塊:
- 格式:
{}
- 特點:每次創(chuàng)建對象時,執(zhí)行實例代碼塊,并在構造器前執(zhí)行
- 作用:和構造器一樣,都是用來完成對象的初始化的,例如:對實例對象變量進行初始化賦值
- 格式:
代碼演示
- Student.java
package Advanced.a_static.d5_block;public class Student {static int number = 80;static String schoolName;// 靜態(tài)代碼塊static {System.out.println("靜態(tài)代碼塊執(zhí)行了");schoolName = "Feng";}// 實例代碼塊{System.out.println("實例代碼塊執(zhí)行了~~~");System.out.println("有人創(chuàng)建了對象" + this);}public Student() {System.out.println("無參數(shù)構造器執(zhí)行了~~~");}public Student(String name) {System.out.println("有參數(shù)構造器執(zhí)行了~~~");}
}
- Test.java
package Advanced.a_static.d5_block;public class Test {public static void main(String[] args) {// 目標:認識兩種代碼塊,了解他們的特點和基本作用System.out.println(Student.number); // 靜態(tài)代碼塊執(zhí)行了 80System.out.println(Student.number); // 80System.out.println(Student.number); // 80System.out.println(Student.schoolName); // FengSystem.out.println("-----------------------");Student s1 = new Student(); // 實例代碼塊執(zhí)行了~~~ 有人創(chuàng)建了對象Advanced.a_static.d5_block.Student@b4c966a 無參數(shù)構造器執(zhí)行了~~~Student s2 = new Student("張三"); // 實例代碼塊執(zhí)行了~~~ 有人創(chuàng)建了對象Advanced.a_static.d5_block.Student@1d81eb93 有參數(shù)構造器執(zhí)行了~~~}
}
1.7 static 的應用知識:單例設計模塊
1.7.1 什么是設計模式(Design pattern)?
- 一個問題通常有 n 種解法,其中肯定有一種解法是最優(yōu)的,這個最優(yōu)的解法被人總結出來了,稱之為設計模式
- 設計模式有20多種,對應20多種軟件開發(fā)中會遇到的問題
1.7.1.2 單例設計模式(餓漢式單例)
- 確保一個類只有一個對象
寫法
- 把類的構造器私有
- 定義一個類變量記住類的一個對象
- 定義一個類方法,返回對象
代碼實現(xiàn)
- A.java
package Advanced.a_static.d6_singleInstance;public class A {// 2.定義一個類變量記住類的對象private static A a = new A();// 1. 必須私有類的構造器// 使得外面不能創(chuàng)建對象private A() {}// 3. 定義一個類方法返回類的對象public static A getObject() {return a;}
}
- Test1.java
package Advanced.a_static.d6_singleInstance;public class Test1 {public static void main(String[] args) {// 目標:掌握單例設計模式的寫法A a1 = A.getObject();A a2 = A.getObject();System.out.println(a1); // Advanced.a_static.d6_singleInstance.A@b4c966aSystem.out.println(a2); // Advanced.a_static.d6_singleInstance.A@b4c966a}
}
1.7.1.3 單例模式的應用場景和好處
- Runtime 類
- 程序的運行環(huán)境,Java 程序執(zhí)行的時候只有一套運行環(huán)境,因此 Runtime 它也只需要一個對象就可以代表你那個唯一的運行環(huán)境
- 任務管理器
- 無論啟動多少次任務管理器,其窗口對象始終只有一個
1.7.1.4 單例設計模式的實現(xiàn)方式很多
總結
- 什么是設計模式,設計模式主要學什么?單例模式解決了上面問題
- 設計模式就是具體問題的最優(yōu)解決方案
- 解決了什么問題?怎么寫?
- 確保一個類只有一個對象
- 單例怎么寫?餓漢式單例的特點是什么?
- 把類的構造器私有;定義一個類變量存儲類的一個對象;提供一個類方法返回對象
- 在獲取類對象時,對象已經(jīng)創(chuàng)建好了
- 單例有啥應用場景,有啥好處?
- 任務管理器對象、獲取運行時對象
- 在這些業(yè)務場景下,使用單例模式,可以避免浪費內(nèi)存
1.7.2 懶漢式單例設計模式
- 拿對象時,才開始創(chuàng)建對象
寫法
- 把類的構造器私有
- 定義一個類變量用于存儲對象
- 提供一個類方法,保證返回的是同一個對象
代碼實現(xiàn)
- B.java
package Advanced.a_static.d6_singleInstance;public class B {// 2. 定義一個類變量,用于存儲這個類的一個對象private static B b;// 1. 把類的構造器私有private B() {}// 3. 定義一個類方法,這個方法要保證第一次調用時才創(chuàng)建一個對象,后面調用時都會用這同一個對象返回public static B getInstance() {if (b == null) {System.out.println("第一次創(chuàng)建對象~~~");b = new B();}return b;}
}
- Test2.java
package Advanced.a_static.d6_singleInstance;/*** 目標:掌握懶漢式單例的寫法*/public class Test2 {public static void main(String[] args) {B b1 = B.getInstance(); // 第一次拿對象B b2 = B.getInstance();System.out.println(b1 == b2); // true}
}
2. 面向對象三大特征之二:繼承
2.1 繼承的快速入門
2.1.1 認識繼承、特點
2.1.1.1 什么是繼承?
- Java 中提供了一個關鍵字
extends
,用這個關鍵字,可以讓一個類和另一個類建立起父子關系
public class B extends A {}
- A 類稱之為父類(基類或超類)
- B 類稱之為子類(派生類)
2.1.1.2 繼承的特點
- 子類能繼承父類的非私有成員(成員變量、成員方法)
繼承后對象的創(chuàng)建
- 子類的對象是由子類、父類共同完成的
2.1.1.3 繼承的執(zhí)行原理
- 子類對象實際上是由子父類這兩張設計圖共同創(chuàng)建出來的
代碼實現(xiàn)
- A.java
package Advanced.b_extends.d7_extends;// 父類
public class A {// 公開成員public int i;public void print1() {System.out.println("===print1===");}// 私有成員private int j;private void print2() {System.out.println("===print2===");}
}
- B.java
package Advanced.b_extends.d7_extends;// 子類
public class B extends A {// 子類是可以繼承父類的非私有成員public void print3() {System.out.println(i);print1();// System.out.println(j); // 報錯
// print2(); // 報錯}
}
- Test.java
package Advanced.b_extends.d7_extends;public class Test {public static void main(String[] args) {// 目標:認識繼承、掌握繼承的特點B b = new B();System.out.println(b.i); // 0
// System.out.println(b.j); // 報錯b.print1();
// b.print2(); // 報錯b.print3();}
}
總結
- 什么是繼承?繼承后有啥特點?
- 繼承就是用
extends
關鍵字,讓一個類和另一個類建立起一種父子關系 - 子類可以繼承父類非私有的成員
- 帶繼承關系的類,Java 會怎么創(chuàng)建它的對象?對象創(chuàng)建出來后,可以直接訪問哪些成員?
- 帶繼承關系的類,Java 會用類和其父類,這多張設計圖來一起創(chuàng)建類的對象
- 對象能直接訪問什么成員,是由子父類這多張設計圖共同決定的,這多張設計圖對外暴露了什么成員,對象就可以訪問什么成員
2.1.2 繼承的好處、應用場景
2.1.2.1 使用繼承有啥好處?
- 減少重復代碼的編寫
需求:
Feng 的員工管理系統(tǒng)種
需要處理講師、咨詢師的數(shù)據(jù)
講師的數(shù)據(jù)有:姓名、具備的技能
咨詢師的數(shù)據(jù)有:姓名、解答問題的總人數(shù)
代碼實現(xiàn)
- People.java
package Advanced.b_extends.d8_extends_application;public class People {private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}
}
- Teacher.java
package Advanced.b_extends.d8_extends_application;public class Teacher extends People {private String skill;public String getSkill() {return skill;}public void setSkill(String skill) {this.skill = skill;}public void printInfo() {System.out.println(getName() + "具備的技能" + skill);}
}
- Test.java
package Advanced.b_extends.d8_extends_application;public class Test {public static void main(String[] args) {// 目標:搞清楚繼承的好處Teacher t = new Teacher();t.setName("張三");t.setSkill("Java、Spring");System.out.println(t.getName()); // 張三System.out.println(t.getSkill()); // Java、Springt.printInfo(); // 張三具備的技能Java、Spring}
}
總結
- 使用繼承有啥好處?
- 減少了重復代碼的編寫,提高了代碼的復用性
2.2 繼承相關的注意事項
2.2.1 權限修飾符
2.2.1.1 什么是權限修飾符?
- 就是用來限制類中的成員(成員變量、成員方法、構造器、代碼塊…)能夠被訪問的范圍
2.2.1.2 權限修飾符有幾種?各自的作用是什么?
修飾符 | 在本類中 | 同一個包下的其他類里 | 任意包下的子類里 | 任意包下的任意類里 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
private < 缺省 < protected < public
代碼演示
- d9_modifer.Fu.java
package Advanced.b_extends.d9_modifer;public class Fu {// 1. 私有:只能在本類中訪問private void privateMethod(){System.out.println("===private===");}// 2. 缺省:本類,同一個包下的類void method(){System.out.println("===缺省===");}// 3. protected:本類,同一個包下的類,任意包下的子類protected void protectedMethod(){System.out.println("===protected===");}// 4. public:本類,同一個包下的類,任意包下的子類,任意包下的任意類public void publicMethod(){System.out.println("===public===");}public void test(){privateMethod();method();privateMethod();publicMethod();}
}
- d9_modifer.Demo.java
package Advanced.b_extends.d9_modifer;public class Demo {public static void main(String[] args) {// 目標:掌握不同權限修飾符的作用Fu f = new Fu();
// f.privateMethod(); // 報錯f.method();f.protectedMethod();f.publicMethod();}
}
- d10_modifer.Zi.java
package Advanced.b_extends.d10_modifer;import Advanced.b_extends.d9_modifer.Fu;public class Zi extends Fu {public void test() {
// privateMethod(); // 報錯
// method(); // 報錯protectedMethod();publicMethod();}
}
- d10_modifer.Demo2.java
package Advanced.b_extends.d10_modifer;import Advanced.b_extends.d9_modifer.Fu;public class Demo2 {public static void main(String[] args) {Fu f = new Fu();
// f.privateMethod(); // 報錯
// f.method(); // 報錯
// f.protectedMethod(); // 報錯f.publicMethod();Zi zi = new Zi();
// zi.protectedMethod(); // 報錯}
}
2.2.2 單繼承、Object 類
2.2.2.1 單繼承
Java 是單繼承的,Java 中的類不支持多繼承,但是支持多層繼承
單繼承:指的是一個類只能繼承一個直接父類
2.2.2.2 為何 Java 中的類不支持多繼承
反證法:
- 假設 Java 中的類支持多繼承,現(xiàn)在有兩個類,一個 A 中有一個 method 方法里面打印 “aaa”,一個 B 中有一個 method 方法里面打印 “bbb”
- 再定義一個 C 類同時繼承 A 和 B,那么 C 調用 method 方法時就不知道調用誰的方法了
2.2.2.3 Object 類
- Object 類是 Java 所有類的祖宗類。我們寫的任意一個類,其實都是 Object 的子類或者子孫類
代碼演示
package Advanced.b_extends.d11_extends_feature;public class Test {public static void main(String[] args) {// 目標:掌握繼承的兩個注意事項// 1. Java是單繼承的:一個類只能繼承一個直接父類;Java中的類不支持多繼承,但支持多層繼承// 2. Object類是Java中所有類的祖宗A a = new A();B b = new B();}
}class A {
} // extends Object{}class B extends A {
}//class C extends B, A{} // 報錯class D extends B {
}
總結
- 繼承相關的兩個注意事項?
-
Java 是單繼承的:一個類只能繼承一個直接父類;Java 中的類不支持多繼承,但是支持多層繼承
-
Object 類是 Java 中所有類的祖宗
2.2.3 方法重寫
2.2.3.1 認識方法重寫
什么是方法重寫?
- 當子類覺得父類中的某個方法不好用,或者無法滿足自己的需求時,子類可以重寫一個方法名稱、參數(shù)列表一樣的方法,去覆蓋父類的這個方法,這就是方法重寫
- 注意:重寫后,方法的訪問,Java 會遵循就近原則
方法重寫的其他注意事項
- 重寫小技巧:使用 Override 注解,他可以指定 Java 編譯器,檢查我們方法重寫的格式是否正確,代碼可讀性也會更好
- 子類重寫父類方法時,訪問權限必須大于或者等于父類該方法的權限(public > protected > 缺省)
- 重寫的方法返回值類型,必須與被重寫方法的返回值類型一樣,或者范圍更小
- 私有方法、靜態(tài)方法不能被重寫,如果重寫會報錯的
代碼演示
- A.java
package Advanced.b_extends.d12_extends_override;public class A {public void print1() {System.out.println("111");}public void print2(int a, int b) {System.out.println("111111");}
}
- B.java
package Advanced.b_extends.d12_extends_override;public class B extends A {// 方法重寫@Override // 安全,可讀性好public void print1() {System.out.println("666");}// 方法重寫@Overridepublic void print2(int a, int b) {System.out.println("666666");}
}
- Test.java
package Advanced.b_extends.d12_extends_override;public class Test {public static void main(String[] args) {// 目標:認識方法重寫,掌握方法重寫的常見應用場景B b = new B();b.print1(); // 666b.print2(2, 3); // 666666}
}
2.2.3.2 方法重寫的應用場景
- 子類重寫 Object 類的
toString()
方法,以便返回對象的內(nèi)容
代碼演示
- Student.java
package Advanced.b_extends.d12_extends_override;public class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = 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;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
- Test.java
package Advanced.b_extends.d12_extends_override;import java.util.ArrayList;public class Test {public static void main(String[] args) {// 目標:認識方法重寫,掌握方法重寫的常見應用場景B b = new B();b.print1(); // 666b.print2(2, 3); // 666666System.out.println("----------------------------");Student s = new Student("張三", 19);System.out.println(s.toString()); // Advanced.b_extends.d12_extends_override.Student@4e50df2e Student{name=張三,age=19}System.out.println(s); // Advanced.b_extends.d12_extends_override.Student@4e50df2e Student{name=張三,age=19}ArrayList list = new ArrayList();list.add("java");System.out.println(list); // [java] 說明ArrayList類中把Object類里面的toString方法進行了重寫}
}
總結
- 方法重寫是什么?
- 子類寫了一個方法名稱、形參列表與父類某個方法一樣的方法去覆蓋父類的該方法
- 重寫方法有哪些注意事項?
- 建議加上:
@Override
注解,可以校驗重寫是否正確,同時可讀性好 - 子類重寫父類方法時,訪問權限必須大于或者等于父類被重寫的方法的權限
- 重寫的方法返回值類型,必須與被重寫方法的返回值類型一樣,或者范圍更小
- 私有方法、靜態(tài)方法不能被重寫
- 方法重寫有啥應用場景?
- 當子類覺得父類的方法不好用,或者不滿足自己的需求時,就可以用方法重寫
2.2.4 子類中訪問其他成員的特點
- 在子類方法中訪問其他成員(成員變量、成員方法),是依照就近原則的
- 先子類局部范圍找
- 然后子類成員范圍找
- 然后父類成員范圍找,如果父類范圍還沒有找到則報錯
- 如果父類中,出現(xiàn)了重名的成員,會優(yōu)先使用子類的,如果此時一定要在子類中使用父類的怎么辦?
- 可以通過
super
關鍵字,指定訪問父類的成員:super.父類成員變量/父類成員方法
代碼演示
- F.java
package Advanced.b_extends.d13_extends_visit;public class F {String name = "父類名字";public void print1() {System.out.println("===父類的print1方法執(zhí)行===");}
}
- Z.java
package Advanced.b_extends.d13_extends_visit;public class Z extends F {String name = "子類名稱";public void showName() {String name = "局部名稱";System.out.println(name);System.out.println(this.name);System.out.println(super.name);}@Overridepublic void print1() {System.out.println("===子類的print1方法執(zhí)行了===");}public void showMethod() {print1(); // 子類的super.print1(); // 父類的}
}
- Test.java
package Advanced.b_extends.d13_extends_visit;public class Test {public static void main(String[] args) {// 目標:掌握子類中訪問其他成員的特點:就近原則Z z = new Z();z.showName(); // 局部名稱 子類名稱 父類名字z.showMethod(); // ===子類的print1方法執(zhí)行了=== ===父類的print1方法執(zhí)行===}
}
2.2.5 子類構造器的特點
2.2.5.1 認識子類構造器的特點
子類構造器的特點:
- 子類的全部構造器,都會先調用父類的構造器,再執(zhí)行自己
子類構造器是如何實現(xiàn)調用父類構造器的:
- 默認情況下,子類全部構造器的第一行代碼都是
super();
(寫不寫都有),它會調用父類的無參數(shù)構造器 - 如果父類沒有無參數(shù)構造器,則我們必須在子類構造器的第一行手寫
super(...);
,指定去調用父類的有參數(shù)構造器
代碼演示
package Advanced.b_extends.d14_extends_constructor;class F {
// public F() {
// System.out.println("===父類F的無參數(shù)構造器執(zhí)行了===");
// }public F(String name, int age) {}
}class Z extends F {public Z() {
// super(); // 默認存在的super("張三",17);System.out.println("===子類Z的無參數(shù)構造器執(zhí)行了===");}public Z(String name) {
// super(); // 默認存在的super("張三",17);System.out.println("===子類Z的有參數(shù)構造器執(zhí)行了===");}
}public class Test {public static void main(String[] args) {// 目標:先認識子類構造器的特點,再掌握這個特點的常見應用場景Z z = new Z(); // ===父類F的無參數(shù)構造器執(zhí)行了=== ===子類Z的無參數(shù)構造器執(zhí)行了===Z z2 = new Z("張三"); // ===父類F的無參數(shù)構造器執(zhí)行了=== ===子類Z的有參數(shù)構造器執(zhí)行了===}
}
2.2.5.1 常見的應用場景
搞清楚子類構造器為什么要調用父類的構造器,有啥應用場景?
- 在繼承情況下,由于處理對象數(shù)據(jù)的構造器拆到了多個類里面去了,所以對象要通過調用這多個構造器才能把對象的數(shù)據(jù)處理完整
運行過程
- 子類構造器可以通過父類構造器,把對象中包含父類這部分的數(shù)據(jù)先初始化賦值,再回來把對象里包含子類這部分的數(shù)據(jù)也進行初始化賦值
代碼演示
package Advanced.b_extends.d14_extends_constructor;public class Test2 {public static void main(String[] args) {// 目標:搞清楚子類構造器為什么要調用父類的構造器,有啥應用場景Teacher t = new Teacher("李四", 36, "Java");System.out.println(t.getName()); // 李四System.out.println(t.getAge()); // 36System.out.println(t.getSkill()); // Java}
}class Teacher extends People {private String skill;public Teacher(String name, int age, String skill) {super(name, age);this.skill = skill;}public String getSkill() {return skill;}public void setSkill(String skill) {this.skill = skill;}
}class People {private String name;private int age;public People() {}public People(String name, int age) {this.name = name;this.age = 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;}
}
補充知識:this(…) 調用兄弟構造器
- 任意類的構造器中,是可以通過
this(...)
去調用該類的其他構造器的
this(…) 和 super(…) 使用時的注意事項:
- this(…)、super(…) 都只能放在構造器的第一行,因此,有了 this(…) 就不能寫 super(…) 了,反之亦然
代碼演示
package Advanced.b_extends.d14_extends_constructor;public class Test3 {public static void main(String[] args) {// 目標:掌握在類的構造器中,通過this(...)調用兄弟構造器的作用Student s1 = new Student("李四", 26, "家里蹲大學");// 需求:如果學生沒有填寫學校,那么學校默認就是FengStudent s2 = new Student("張三", 28);System.out.println(s2.getName()); // 張三System.out.println(s2.getAge()); // 28System.out.println(s2.getSchoolName()); // Feng}
}class Student {private String name;private int age;private String schoolName;public Student() {}public Student(String name, int age) {this(name, age, "Feng");}public Student(String name, int age, String schoolName) {this.name = name;this.age = age;this.schoolName = schoolName;}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 getSchoolName() {return schoolName;}public void setSchoolName(String schoolName) {this.schoolName = schoolName;}
}
2.2.6 注意事項小結
- 子類構造器有啥特點?
- 子類中的全部構造器,都必須先調用父類的構造器,再執(zhí)行自己
- super(…) 調用父類有參數(shù)構造器的常見場景是什么?
- 為對象中包含父類這部分的成員變量進行賦值
- this(…) 的作用是什么?
- 在構造器中調用本類的其他構造器
- this(…) 和 super(…) 的使用需要注意什么?
- 都必須放在構造器的第一行
3. 面向對象的三大特征之三:多態(tài)
3.1 認識多態(tài)
3.1.1 什么是多態(tài)?
- 多態(tài)是在繼承/實現(xiàn)情況下的一種現(xiàn)象,表現(xiàn)為:對象多態(tài)、行為多態(tài)
3.1.2 多態(tài)的具體代碼實現(xiàn)
People p1 = new Student();
p1.run();People p2 = new Teacher();
p2.run();
3.1.3 多態(tài)的前提
- 有繼承/實現(xiàn)關系;存在父類引用子類對象;存在方法重寫
3.1.4 多態(tài)的一個注意事項
- 多態(tài)是對象、行為的多態(tài),Java 中的屬性(成員變量)不談多態(tài)
代碼演示
- People.java
package Advanced.c_polymorphism.d1_polymorphism;// 父類
public class People {public String name = "父類People的名稱";public void run() {System.out.println("人可以跑~~~");}
}
- Student.java
package Advanced.c_polymorphism.d1_polymorphism;public class Student extends People {public String name = "子類Student的名稱";@Overridepublic void run() {System.out.println("學生跑得賊快~~~");}
}
- Teacher.java
package Advanced.c_polymorphism.d1_polymorphism;public class Teacher extends People {public String name = "子類Teacher的名稱";@Overridepublic void run() {System.out.println("老師跑的氣喘吁吁~~~");}
}
- Test.java
package Advanced.c_polymorphism.d1_polymorphism;public class Test {public static void main(String[] args) {// 目標:認識多態(tài):對象多態(tài)、行為多態(tài)// 1. 對象多態(tài)People p1 = new Teacher();// 行為多態(tài):識別技巧:編譯看左邊,運行看右邊p1.run(); // 老師跑的氣喘吁吁~~~// 注意:對于變量:編譯看左邊,運行看左邊System.out.println(p1.name); // 父類People的名稱People p2 = new Student();// 行為多態(tài):識別技巧:編譯看左邊,運行看右邊p2.run(); // 學生跑得賊快~~~System.out.println(p2.name); // 父類People的名稱}
}
3.2 使用多態(tài)的好處
3.2.1 使用多態(tài)的好處
-
在多態(tài)形勢下,右邊對象是解耦合的,更便于擴展和維護
- 解耦合:比如說我們在開發(fā)系統(tǒng)時,希望把這個系統(tǒng)中的每個模塊拆分成一個一個的服務,可以隨時的對接,這樣更容易擴展和維護,這就是解耦合
-
定義方法時,使用父類類型的形參,可以接收一切子類對象,擴展性更強、更便利
3.2.2 多態(tài)下會產(chǎn)生的一個問題,怎么解決?
- 多態(tài)下不能使用子類的獨有功能
代碼演示
- People.java
package Advanced.c_polymorphism.d2_polymorphism;// 父類
public class People {public String name = "父類People的名稱";public void run() {System.out.println("人可以跑~~~");}
}
- Student.java
package Advanced.c_polymorphism.d2_polymorphism;public class Student extends People {public String name = "子類Student的名稱";@Overridepublic void run() {System.out.println("學生跑得賊快~~~");}public void test() {System.out.println("學生需要考試~~~");}
}
- Teacher.java
package Advanced.c_polymorphism.d2_polymorphism;public class Teacher extends People {public String name = "子類Teacher的名稱";@Overridepublic void run() {System.out.println("老師跑的氣喘吁吁~~~");}public void test() {System.out.println("老師需要教知識~~~");}
}
- Test.java
package Advanced.c_polymorphism.d2_polymorphism;public class Test {public static void main(String[] args) {// 目標:理解多態(tài)的好處// 好處1:可以實現(xiàn)解耦合,右邊對象可以隨時切換,后續(xù)業(yè)務隨之改變People p1 = new Student();p1.run();
// p1.test(); // 多態(tài)下存在的問題,無法直接調用子類的獨有功能Student s = new Student();go(s);Teacher t = new Teacher();go(s);}// 好處2:可以使用父類類型的變量作為形參,可以接收一切子類對象public static void go(People p) {}
}
總結
- 使用多態(tài)有什么好處?存在什么問題?
- 可以解耦合,擴展性更強;使用父類類型的變量作為方法的形參時,可以接收一切子類對象
- 多態(tài)下不能直接調用子類的獨有方法
3.3 多態(tài)下的類型轉換問題
3.3.1 類型轉換
-
自動類型轉換:
父類 變量名 = new 子類();
- eg:
People p = new Teacher();
- eg:
-
強制類型轉換:
子類 變量名 = (子類) 父類變量
- eg:
Teacher t = (Teacher) p;
- eg:
3.3.2 強制類型轉換的一個注意事項
- 存在繼承/實現(xiàn)關系就可以在編譯階段進行強制類型轉換,編譯階段不會報錯
- 運行時,如果發(fā)現(xiàn)對象的真實類型與強轉后的類型不同,就會報類型轉換異常(ClassCastException)的錯誤出來
People p = new Teahcer();Student s = (Student) p; // java.lang.ClassCastException
3.3.3 強轉前,Java 建議:
- 使用
instanceof
關鍵字,判斷當前對象的真實類型,再進行強轉
p instanceof Student
代碼演示
- People.java
package Advanced.c_polymorphism.d2_polymorphism;// 父類
public class People {public String name = "父類People的名稱";public void run() {System.out.println("人可以跑~~~");}
}
- Student.java
package Advanced.c_polymorphism.d2_polymorphism;public class Student extends People {public String name = "子類Student的名稱";@Overridepublic void run() {System.out.println("學生跑得賊快~~~");}public void test() {System.out.println("學生需要考試~~~");}
}
- Teacher.java
package Advanced.c_polymorphism.d2_polymorphism;public class Teacher extends People {public String name = "子類Teacher的名稱";@Overridepublic void run() {System.out.println("老師跑的氣喘吁吁~~~");}public void teach() {System.out.println("老師需要教知識~~~");}
}
- Test.java
package Advanced.c_polymorphism.d2_polymorphism;public class Test {public static void main(String[] args) {// 目標:理解多態(tài)的好處// 好處1:可以實現(xiàn)解耦合,右邊對象可以隨時切換,后續(xù)業(yè)務隨之改變People p1 = new Student();p1.run();
// p1.test(); // 多態(tài)下存在的問題,無法直接調用子類的獨有功能// 強制類型轉換Student s1 = (Student) p1;s1.test(); // 學生需要考試~~~// 強制類型轉換可能存在的問題,編譯階段有繼續(xù)或者實現(xiàn)關系就可以強制轉換,但是運行時可能出現(xiàn)類型轉換異常
// Teacher t1 = (Teacher) p1; // 運行時出現(xiàn)了:ClassCastExceptionif (p1 instanceof Student) {Student s2 = (Student) p1;s2.test(); // 學生需要考試~~~} else {Teacher t2 = (Teacher) p1;t2.teach();}System.out.println("------------------------------");// 好處2:可以使用父類類型的變量作為形參,可以接收一切子類對象Student s = new Student();go(s); // 學生跑得賊快~~~ 學生需要考試~~~Teacher t = new Teacher();go(t); // 老師跑的氣喘吁吁~~~ 老師需要教知識~~~}// 好處2:可以使用父類類型的變量作為形參,可以接收一切子類對象public static void go(People p) {p.run();if (p instanceof Student) {Student s2 = (Student) p;s2.test();} else if (p instanceof Teacher) {Teacher t2 = (Teacher) p;t2.teach();}}
}
總結
- 類型轉換有幾種形式?能解決什么問題?
- 自動類型轉換、強制類型轉換
- 可以把對象轉換成其真正的類型,從而解決了多態(tài)下不能調用子類獨有方法的問題
- 強制類型轉換需要注意什么?
- 存在繼承/實現(xiàn)時,就可以進行強制類型轉換,編譯階段不會報錯
- 但是,運行時,如果發(fā)現(xiàn)對象的真是類型與強轉后的類型不同會報錯(ClassCastException)
- 強制類型轉換前,Java 建議我們做什么事情?
- 使用
instanceof
判斷當前對象的真是類型:對象 instanceof 類型