面向物件進階(一)
一、繼承
1. 繼承概述
1.1什麼是繼承?
- 繼承是類與類之間的一種關係。
- 多個類繼承單獨的某個類,多個類就可以使用單獨的這個類的屬性和行為。
- 多個類稱為子類(派生類),單獨的這個類稱為父類(基類或超類)。
1.2 為什麼用繼承?
- 使用繼承的好處:提高程式碼的複用。減少程式碼冗餘、增強類的功能拓展性。
1.3 繼承後子類的特點:
- 子類繼承父類,子類可以得到父類的屬性和行為,子類可以使用。
- Java中子類更為強大。
2.繼承的設計規範、記憶體原理
繼承設計規範:
- 子類們相同特徵(共同屬性,共性方法)放在父類中定義,子類獨有的屬性和行為應該定義在子類自己裡面。
為什麼?
- 如果子類的獨有屬性、行為定義在父類中,會導致其他子類也會得到這些屬性和行為,這不符合面向物件邏輯。
3.繼承的特點
- 子類可以繼承父類的屬性和行為,但是子類不能繼承父類的構造器。
- Java是單繼承模式:一個類智慧繼承一個直接父類
- Java不支援多繼承,但是支援多層繼承
- Java中所有的類都是Object類的子類
4.繼承後:成員變數、成員方法的訪問特點
在子類方法中訪問成員(成員變數、成員方法)滿足:就近原則
- 先子類區域性範圍找
- 然後子類成員範圍找
- 然後父類成員範圍找,如果父類範圍還沒有找到則報錯
**如果子父類中出現了重名的成員,會優先使用子類的,此時如果一定要在子類中使用父類的怎麼辦?
- 可以通過super關鍵字,指定訪問父類成員。
- 格式:super.父類成員變數/父類成員方法
5. 繼承後:方法重寫
什麼是方法重寫
- 在繼承體系中,子類出現了和父類中一模一樣的方法宣告,我們就稱子類的這個方法是重寫的方法。
方法重寫的應用場景
- 當子類需要父類的功能,但父類的該功能不完全滿足自己的需求時。
- 子類可以重寫父類中的方法。
@Override重寫註解
- @Override是放在重寫後的方法上,作為重寫是否正確的校驗註解。
- 加上該註解後如果重寫錯誤,編譯階段會出現錯誤提示。
- 建議重寫方法都加@Override註解,程式碼安全,優雅!
方法重寫注意事項和要求
- 重寫方法的名稱、形參列表必須與被重寫方法的名稱和引數列表一致。
- 私有方法不能被重寫
- 子類重寫父類方法時,訪問許可權必須大於或者等於父類
- 子類不能重寫父類的靜態方法,如果重寫會報錯。
6.繼承後:子類構造器的特點
子類繼承父類後構造器的特點
- 子類中所有的構造器預設都會先訪問父類中的無參構造器,再執行自己。
為什麼
- 子類在初始化的時候,有可能會使用到父類中的資料,如果父類沒有完成初始化,子類將無法使用父類的資料。
- 子類初始化之前,一定要呼叫父類構造器先完成父類資料空間的初始化。
怎麼呼叫父類的無參構造器的?
- 子類構造器的第一行預設語句都是:super(),不寫也存在
7.繼承後:子類構造器訪問父類有參構造器
super呼叫父類的有參構造的作用:
- 初始化繼承父類的資料。
如果父類中沒有無參構造器,只有有參構造器,會出現什麼現象?
- 會報錯。因為子類預設呼叫父類無參構造器的、
如何解決?
- 子類構造器中可以通過書寫super(...),手動呼叫父類的有參構造器
8.this和super的總結
this和super詳解
- this代表本類資料的引用;super是表示父類儲存空間的標識。
this(...)和super(...)使用注意點:
- 子類通過this(...)去呼叫本類的其他構造器,本類其他構造器會通過super去手動呼叫父類的無參構造器,最終還是會呼叫父類的構造器的。
- 注意:this(...) super(...)都只能放在構造器的第一行,所以二者不能存在於同一個構造器中。
二、包
什麼是包
- 包是用來分門別類的管理各種不同類的,類似於資料夾、建包利於程式的管理和維護。
- 建包的語法格式:package 公司域名倒寫.技術名稱。包名建議全部小寫,且具備意義
- 建包語句必須放在第一行,一般IDEA工具會幫助建立
導包
- 相同包下的類可以直接訪問,不同包下的類必須導包,才可以使用!導包格式:import 包名.類名;
- 假如一個類中需要用到不同類,而這兩個類的名稱是一樣的,那麼預設只能匯入一個類,另一個類需要帶包名訪問。
三、許可權修飾符
什麼是許可權修飾符
- 許可權修飾符:是用來控制一個成員能夠被訪問的範圍的。
- 可以修飾成員變數,方法,構造器,內部類,不同許可權修飾符修飾的成員能夠被訪問的範圍將受到限制
許可權修飾符的分類和具體作用範圍:
- 許可權修飾符:有四種作用範圍由小到大(private --> 預設(不寫) -->protected -->public)
四、final
final的作用
- final關鍵字是最終的意思,可以修飾(方法,類,變數)
- 修飾方法:表面該方法是最終方法,不能被重寫
- 修飾變數:表示該變數第一次賦值後,不能被再次賦值(有且僅能被賦值一次)
- 修飾類:表明該類是最終類,不能被繼承。
final修飾變數的注意
-
final修飾的變數是基本型別:那麼變數儲存的資料值是不能發生改變的。
-
final修飾的變數是引用型別:那麼變數儲存的地址值不能發生改變,但是地址指向的物件內容是可以發生改變的。
final int[] arr = {1,2,3,4}; //int[] arr = {1,3};會報錯,不可以二次賦值 //修改內容 arr[1] = 22;//可以的,物件的地址不會變
五、常量
常量概述和基本作用
常量
-
常量是使用了public static final修飾的成員變數,必須有初始值,而且執行的過程中其值不能被改變,
-
常量的作用和好處:可以用於做系統的配置資訊,方便程式的維護,同時也能提高可讀性。
-
常量命名規範:英文單詞全部大寫,多個單詞用下劃線連線起來
常量的執行原理
- 在編譯階段會進行"巨集替換",把使用常量的地方全部替換成真是的字面量。
- 這樣做的好處是讓使用常量的程式的執行效能與直接使用字面量是一樣的。
常量做資訊標誌和分類
- 程式碼的可讀性好,實現了軟編碼形式。
六、列舉
6.1 列舉的概述
- 列舉是java中的一種特殊型別
- 列舉的作用:"是為了做資訊的標誌和資訊的分類"。
6.2 定義列舉類的格式:
/*
修飾符 enum 列舉名稱{
第一行都是羅列列舉類例項的名稱。
}
*/
enum Season{
SPRING,SUMMER,AUTUMN,WINTER;
}
6.3 反編譯後觀察列舉的特徵
public final class Season extends java.lang.Enum<Season> {
public static final Season SPRING;
public static final Season SUMMER;
public static final Season AUTUMN;
public static final Season WINTER;
public static Season[] values();
public static Season valueOf(java.lang.String);
static {};
}
列舉的特徵
- 列舉類都是繼承了列舉型別:java.lang.Eumn
- 列舉類都是最終類,不可以被繼承
- 列舉類的構造器都是私有的,列舉對外不能建立物件
- 列舉類的第一行預設都是羅列列舉物件的名稱的。
- 列舉類相當於是多例模式。
選擇常量做資訊標誌和分類:
- 雖然可以實現可讀性,但是入參值不受約束,程式碼相對不夠嚴謹。
列舉做資訊標誌和分類:
- 程式碼可讀性好,入參約束嚴謹,程式碼優雅,是最好的資訊分類技術!建議使用!
七、抽象類
- 某個父類知道其所有子類要完成某功能,但是每個子類完成情況都不一樣,父類就只定義該功能的基本要求,具體實現由子類完成,這個類就可以是一個抽象類,抽象類其實就是一種不完全的設計圖。
- 抽象類必須要用abstract修飾;
修飾符 abstract class 類名{}
抽象方法
- 就是抽象類中定義的子類必須完成的功能的基本要求
- 沒有方法體,只有方法簽名,必須用abstract修飾。
修飾符 abstract 返回值型別 方法名稱(形參列表);
抽象類的使用總結與注意事項
- 抽象類用來被繼承的,抽象方法是交給子類重寫的。
- 一個類如果繼承的抽象類,那麼這個類必須重寫完抽象類的全部抽象方法,否則這個類也必須定義為抽象類。
7.1 抽象類特徵、注意事項小結
特徵
- 有得有失:得到了抽象方法,失去了建立物件的能力。
- 抽象類為什麼不可以建立物件?
- 類有的成員(成員物件,方法、構造器)抽象類都具備
- 抽象類中不一定有抽象方法,有抽象方法的類一定是抽象類
- 一個類繼承了抽象類,必須重寫完抽象類的全部抽象方法,否則這個類也必須定義成抽象類
- 不能用abstract修飾變數、程式碼塊、構造器。
final和abstract是什麼關係?
- 互斥關係
- abstract定義的抽象類作為模板讓子類繼承,final定義的類不能被繼承。
- 抽象方法定義通用功能讓子類來重寫,final定義的方法子類不能重寫。
7.2 抽象類的應用知識:模板方法模式
什麼時候使用模板方法
- 使用場景說明:當系統中出現同一個功能多處在開發,而該功能中大部分程式碼是一樣的,只有其中部分可能不同的時候。
模板方法模式實現步驟
- 把功能定義成一個所謂的模板方法,放在抽象類中,模板方法中只定義通用且能確定的程式碼
- 模板方法中不能決定的功能定義成抽象方法讓具體子類去實現
模板方法建議使用final修飾,這樣更專業,為什麼呢?
答:模板方法是給子類直接使用的,不是讓子類重寫的,一旦子類重寫了,模板方法就失效了。
模板方法解決了什麼問題
- 極大的提高了程式碼的複用性
- 模板方法已經定義了通用結構,模板不能確定的定義成抽象方法。
八、介面
8.1 什麼是介面?
- 介面就是體現規範的,其中用抽象方法定義的一組行為規範,介面是更加徹底的抽象
- 體現了現實世界中,“如果你是這類事物...則必須完成某些行為...”的思想。
8.2 介面的定義與特點
- 介面的定義格式如下:
介面用關鍵字interface來定義
public interface 介面名{
//常量
//抽象方法
}
- JDK8之前介面中只能是抽象方法和常量,沒有其他成分了
- 介面不能例項化。
- 介面中的成員都是public修飾的,寫不寫都是,因為規範就是為了公開化。
8.3 介面的基本使用:被實現
介面的用法:
- 介面是用來被類實現(implements)的,實現介面的類稱為實現類。實現類可以理解成所謂的子類。
修飾符 class 實現類 implements 介面1,介面2,介面3,...{
}
實現的關鍵字:implements
- 從上面可以看出,介面可以被類單實現,也可以被類多實現。
介面實現的注意事項:
- 一個類實現介面,必須重寫完全部介面的全部抽象方法,否則這個類需要定義成抽象類。
8.4 介面與介面的關係:多繼承
基本小結
- 類和類的關係:單繼承
- 類和介面的關係:多實現
- 介面和介面的關係:多繼承,一個介面可以同事繼承多個介面。
介面多繼承的作用
- 規範合併,整合多個介面為同一個介面,便於子類實現。
8.5 JDK8 開始介面新增方法:
JDK8版本開始後,Java只對介面的成員方法進行了新增。
第一種:預設方法
- 類似之前寫的普通例項方法:必須用default修飾
- 預設是用public修飾,需要用介面的實現類的物件來呼叫
public interface SportManInter{
/**
1.JDK8開始:預設方法(例項方法)
必須default修飾,預設用public修飾
預設方法,介面不能建立物件,這個方法只能過繼給實現類,由實現類物件呼叫
*/
default void run(){
System.out.println("開始跑。。。");
}
}
class PingPongMan implements SportManInter{
}
class Test{
public static void main(String[] arsg){
PingPongMan = new PingPongMan();
p.run();
}
}
第二種:靜態方法
-
預設會public修飾,必須要用static修飾
-
注意:介面的靜態方法的必須用本身的介面名來呼叫
/** 2.靜態方法 必須使用static修飾,預設用public修飾 介面的靜態方法,必須介面名自己呼叫 */ static void inAddr(){ System.out.println("加油學習!"); } class Test{ public static void main(String[] arsg){ SportManInter.inAdrr(); } }
第三種:私有方法
- 就是私有的例項方法,必須用private修飾,從JDK 1.9才開始有的。
- 只能在本類中被其他預設方法或私有方法訪問。
/**
3.私有方法(例項方法)
---JDK1.9才開始支援的
--必須在介面內部才能被訪問
*/
default void run{
go();
}
private void go(){
System.out.println("開始");
}
8.6 使用介面的注意事項
-
介面不能建立物件
-
一個類實現多個介面,多個介面中有同樣的靜態方法不衝突
-
一個類實現了多個介面,多個介面中存在同名的預設方法,不衝突,這個類重寫該方法即可。
-
一個類繼承了父類,同時又實現了介面,父類和介面中有同名方法,預設用父類的。
-
一個介面繼承多個介面,是沒有問題的,如果多個介面中存在規則衝突則不能多繼承。