面向物件-2
11.許可權修飾符
許可權修飾符是用來控制一個成員能夠被訪問的範圍的
可以修飾成員變數,方法,構造器,內部類,不同許可權修飾符修飾的成員能夠被訪問的範圍將受到限制
四種許可權修飾符作用範圍從小到大(private > 預設 > protected > public)
修飾符 | 同一個類中 | 同一個包中其他類 | 不同包下的子類 | 不同包下的無關類 |
---|---|---|---|---|
private | ✔ | |||
預設 | ✔ | ✔ | ||
protected | ✔ | ✔ | ✔ | |
public | ✔ | ✔ | ✔ | ✔ |
12.final關鍵字
final的作用
- final關鍵字是最終的意思可修飾(方法,變數,類)
- 修飾方法:表明該方法是最終方法,不能被重寫。
- 修飾變數:表示該變數第一次賦值後,不能再次被賦值(有且僅能被賦值一次)
- 修飾類:表明該類是最終類,不能被繼承
final修飾變數的注意點
- final修飾的變數是基本型別,那麼變數儲存的資料值不能發生改變
- final修飾的變數是引用型別,那麼變數儲存的地址值不能發生改變,但是地址指向的物件的內容是可以發生變化的
13.列舉
列舉的概述
- 列舉是java中的一種特殊型別
- 列舉的作用:是為了做資訊的標誌和資訊的分類
定義列舉的格式
修飾符 enum 列舉名稱{
第一行都是羅列列舉類例項的名稱,建議全部大寫
}
列舉的特徵
- 列舉類都是繼承了列舉型別:java.lang.Enum
- 列舉都是最終類,不可以被繼承
- 列舉類的構造器都是私有的,列舉類對外不能建立物件
選擇常量做資訊標誌和分類雖然可以實現可讀性,但是入參值不受約束,程式碼相對不夠嚴謹;列舉做資訊標誌和分類,程式碼可讀性好,入參約束嚴謹,程式碼優雅,是最好的資訊分類技術,建議使用
public enum Season {
SPRING,SUMMER,AUTUMN,WINTER;
}
14.抽象類
抽象類概述
- 某個父類知道其所有子類要完成某功能,但是每個子類完成的情況都不一樣,父類就只定義該功能的基本要求,具體實現由子類完成,這個類就可以是一個抽象類,抽象類其實就是一種不完全的設計圖
抽象類必須使用abstract修飾
- 格式:修飾符 abstract class 類名{}
抽象方法
- 抽象類中定義的子類必須完成的功能的基本要求
- 沒有方法體,只有方法簽名,必須abstract修飾
- 格式:修飾符 abstract 返回值型別 方法名稱(形參列表);
抽象類的使用總結與注意事項
- 抽象類是用來被繼承的,抽象方法是交給子類重寫實現的
- 一個類如果繼承了抽象類,那麼這個類必須重寫完抽象類的全部抽象方法,否則這個類也必須定義成抽象類
抽象類特徵
- 有得有失:得到了抽象方法,失去了建立物件的能力
- 類有的成員(成員變數、方法、構造器)抽象類都具備
- 抽象類中不一定有抽象方法,有抽象方法的類一定是抽象類
- 一個類繼承了抽象類必須重寫完抽象類的全部抽象方法,否則這個類也必須定義成抽象類
- 不能用abstract修飾變數、程式碼塊、構造器
- final和abstract是互斥關係
14.1模板方法模式
使用場景:當系統中出現同一個功能多處在開發,而該功能中大部分程式碼是一樣的,只有其中部分可能不同的時候
模板方法模式實現步驟
- 把功能定義成一個所謂的模板方法,放在抽象類中,模板方法中只定義通用且能確定的程式碼
- 模板方法中不能決定的功能定義成抽象方法讓具體子類去實現
- 模板方法建議使用final關鍵字修飾,因為模板方法是讓子類直接使用,不是讓子類重寫的,一旦重寫,模板方法就無效了
模板方法的好處
- 模板方法極大提高了程式碼的複用性
- 模板方法已經定義了通用結構,模板不能確定的定義成抽象方法
- 使用者只需關心自己需要實現的功能
15.介面
介面概述
- 介面就是體現規範的,其中用抽象方法定義的一組行為規範,介面時更加徹底的抽象
- 體現了現實世界中"如果你是這類事物…則必須完成某些行為…"的思想
格式
介面用關鍵字interface來定義
public interface 介面名{
//常量
//抽象方法
}
jdk8之前介面中只能是抽象方法和常量,沒有其他成分,jdk8之後介面中可以定義方法,可以定義預設方法和靜態方法,預設方法必須要用default關鍵字修飾,且只有在介面被實現之後才能呼叫,靜態方法要用static關鍵字修飾,只能用介面名.方法名的形式來呼叫。
介面不能例項化(建立物件)
介面中的成員變數都是public修飾的,寫不寫都是,因為規範的目的是為了公開化
/**
* 定義了預設方法和靜態方法的介面
*/
public interface InterNew {
default void defaultMethod(){
System.out.println("介面預設方法被執行~~");
}
static void staticMethod(){
System.out.println("介面靜態方法被執行~~");
}
}
/**
* 介面的實現類
*/
public class InterNewImpl implements InterNew {
}
public class Demo {
public static void main(String[] args) {
InterNewImpl interNew = new InterNewImpl();
//呼叫介面的預設方法
interNew.defaultMethod();
//呼叫介面的靜態方法
InterNew.staticMethod();
}
}
執行結果
介面的用法
- 介面是用來被類實現(implements)的,實現介面的類被稱為實現類,實現類可以理解成所謂的子類
格式
修飾符 class 實現類 implements 介面1,介面2,介面3……{
}
介面可以被類單實現,也可以被類多實現
一個類實現介面,必須重寫完全部介面的全部抽象方法,否則這個類需要定義成抽象類。
介面和介面之間的關係
- 類和類之間的關係是單繼承(一個類只能繼承一個父類),類和介面之間的關係是多實現(一個類可以實現多個介面)。
- 介面和介面之間的關係是多繼承(一個介面可以同時繼承多個介面)。
介面多繼承的作用:整合多個介面為同一個介面,便於子類實現。
介面的注意事項
- 介面不能建立物件
- 一個類實現多個介面,多個介面中有同樣的靜態方法不衝突
- 一個類繼承了父類,同時又實現了介面,父類和介面中有同名方法,預設呼叫父類的
- 一個類實現了多個介面,多個介面中存在同名的預設方法,不衝突,這個類重寫該方法即可
16.多型
同類型的物件,執行同一個行為,會表現出不同的行為特徵
多型的常見形式
- 父類型別 物件名稱 = new 子類構造器;
- 介面 物件名稱 = new 實現類構造器;
多型成員訪問特點
- 方法呼叫:編譯看左邊,執行看右邊
- 變數呼叫:編譯看左邊,執行也看左邊(多型側重行為多型)
多型的實現前提
- 有繼承/實現關係;有父類引用指向子類物件;有方法重寫
優勢
- 在多型形式下,右邊物件可以實現解耦合,便於擴充套件和維護
- 定義方法的時候,使用父類作為引數,該方法就可以接收這父類的一切子類物件,體現出多型的擴充套件性與便利
public abstract class Shape {
public String name = "形狀";
public abstract void area();
}
public class Circle extends Shape{
public String name = "圓形";
@Override
public void area() {
System.out.println("面積為Πr^2");
}
}
public class Rectangle extends Shape{
public String name = "矩形";
@Override
public void area() {
System.out.println("面積為長乘寬");
}
}
public class Test {
public static void main(String[] args) {
//多型的形式 父類型別 物件名稱 = new 子類構造器
Shape s1 = new Circle();
System.out.println(s1);//變數:編譯看左邊,執行看右邊
s1.area();//方法:編譯看左邊,執行也看左邊
Shape s2 = new Rectangle();
System.out.println(s2);
s2.area();
}
}
執行結果
public class Test2 {
public static void main(String[] args) {
Circle c = new Circle();
calc(c);
Rectangle r = new Rectangle();
calc(r);
}
public static void calc(Shape s){
s.area();
}
}
執行結果
弊端
- 但是多型情況下不能使用子類的獨有功能(可以使用型別轉換)
自動型別轉換(從子到父):子類物件賦值給父類型別的變數指向
強制型別轉換(從父到子):
- 子類 物件變數 = (子類)父類型別的變數
- 作用:可以解決多型下的劣勢,可以實現呼叫子類獨有的功能
- 如果轉型後的型別和物件真實型別不是同一種類型,那麼執行的時候會報異常
- 轉換前使用instanceof判斷當前物件的真實型別,再進行強制轉換
- 變數名 instanceof 類名;
- 判斷關鍵字左邊的變數指向的物件的真實型別,是否為右邊的型別或其子型別,是則返回true
17.內部類
內部類就是定義在一個類裡面的類,裡面的類可以理解成寄生,外部類可以理解成宿主
public class People {
//內部類
public class heart {}
}
內部類的使用場景、作用
- 當一個事物的內部,還有一個部分需要完整的結構進行描述,而這個內部的完整結構又只為外部事物提供服務,那麼整個內部的完整結構可以選擇內部類來設計
- 內部類通常可以方便訪問外部成員,包括私有的成員
- 內部類提供了更好的封裝性,內部類本身就可以用private protected等修飾,封裝性可以做到更多控制
內部類分類
- 靜態內部類
- 成員內部類
- 區域性內部類
- 匿名內部類
17.1靜態內部類
有static修飾,屬於外部類本身
它的特點和使用與普通類是完全一樣的,類有的成分都有,只是在別的類裡面
public class Outer {
//靜態成員內部類
public static class inner{
}
}
靜態內部類建立物件的格式
public class Test {
public static void main(String[] args) {
Outer.Inner in = new Outer.Inner();
}
}
靜態內部類可以直接訪問外部類的靜態成員,不能直接訪問外部類的例項成員,必須用外部類的物件訪問
17.2成員內部類
無static修飾,屬於外部類的物件
public class Outer{
//成員內部類
public class Inner{
}
}
成員內部類建立物件格式
格式:外部類名.內部類名 = new 外部類構造器.() new 內部類構造器();
範例:Outer.Inner in = new Outer().new Inner();
成員內部類中可以直接訪問外部類的靜態成員,例項方法中可以直接訪問外部類的例項成員
在成員內部類中訪問所在外部類物件 外部類名.this
17.3區域性內部類
區域性內部類放在方法、程式碼塊、構造器等執行體中
17.4匿名內部類
本質上是一個沒有名字的區域性內部類,定義在方法中、程式碼塊中
方便建立子類物件,最終目的是為了簡化程式碼編寫
new 類|抽象類名|介面名(){
重寫方法
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal() {
@Override
public void run() {
System.out.println("動物在跑");
}
};
a. run();
}
}
abstract class Animal{
public abstract void run();
}
執行結果
- 匿名內部類是一個沒有名字的內部類
- 匿名內部類寫出來就會產生一個匿名內部類的物件
- 匿名內部類的物件型別相當於是當前new的那個型別的子型別