java學習總結之面向對象

前言

  • 上一篇文章java基礎
    本篇文章繼續Java知識點的歸納,梳理一下關於面向對象的知識點,涉及到封裝、繼承、多態,還有接口,類之間的關係。

接口和抽象類

1、抽象類

抽象類和抽象方法都用abstract關鍵字進行聲明,抽象類不能被實例化,不能直接創建,抽象方法必須放在抽象類中,類中如果有抽象方法,這個類繼續聲明爲抽象類,如下:

public abstract class Hero{
    public abstract void fight();
}

//子類實現抽象方法,完成實際操作
public class Warrior extends Hero{
    @Override
    public void fight(){}
}

//子類繼續聲明爲抽象類
public abstract class LongRange extends Hero{}

2、接口

接口被認爲是一種特殊的抽象類,同樣不能使用new實例化,接口的字段和方法都默認爲public,且不能聲明爲private或protected,接口只能包含常量(static final)和待實現的方法,java8以後接口中可以有方法的實現,如下:

interface Eat{
    //...
    default public void eating(){
        System.out.println("eating");
    }
}

在接口中,也可以定義內部類,但是隻能是public static修飾的內部類,所以接口中只能定義靜態內部類,但是在接口中定義內部類的用處不大,很少使用。

3、接口和抽象類的比較

變量 成員方法 構造方法 使用場合
抽象類 無限制 無限制 可以有 強的“is a”關係(是一種)
接口 所有變量必須是public static final 所有方法必須是public abstract 弱的“is a”關係(is kind of,是一類)

在很多情況下,接口優先於抽象類。因爲接口沒有抽象類嚴格的類層次結構要求,可以靈活地爲一個類添加行爲。並且從 Java 8 開始,接口也可以有默認的方法實現,使得修改接口的成本也變的很低,而且接口可以實現多繼承。

外部類和內部類

把一個類放在另外一個類的內部,處於類內部的類就叫做內部類,包含內部類的類就叫做外部類,一個外部類不一定有內部類,但是一個內部類就一定有一個”依附“的外部類,內部類比外部類多使用3個修飾符:private、protected、static,外部類不能使用這3個修飾符,雖然外部類和內部類整體是一個類文件,但是編譯之後,它會分別生成外部類的class文件和內部類的class文件,這說明在運行時外部類和內部類其實是兩個獨立的類,內部類可以分爲:非靜態內部類靜態內部類局部內部類匿名內部類

1、非靜態內部類

public class OuterClass {
    
    private String outer = "外部類私有變量";
    private void outerMethod(){
        System.out.println("外部類私有方法" );
    }

    public class InnerClass{
        
        public void innerMethod(){
            //非靜態內部類調用外部類的私有方法
            outerMethod();
            //非靜態內部類訪問外部類的私有變量
            System.out.println(outer);
        }
        
    }
}

沒有使用static修飾的就是非靜態內部類,它依賴於外部類實例,要創建非靜態內部類實例,必須先創建外部類實例,如下:

public static void main(String[] args) {
    OuterClass outerClass = new OuterClass();
    //通過 外部類實例 調用非靜態內部類的構造器
    OuterClass.InnerClass innerClass = outerClass.new InnerClass();
    innerClass.innerMethod();
}

總的來說,非靜態內部類有以下幾個特點:

  • 1、非靜態內部類屬於外部類實例,必須依賴外部類實例才能夠創建;
  • 2、非靜態內部類編譯之後,會保留一個外部類對象的引用(在構造方法中傳入);
  • 3、非靜態內部類不能含有靜態成員、靜態方法和static語句塊;
  • 4、非靜態內部類中可以直接訪問外部類的所有成員和方法(包括private的),但是在外部類中不能直接訪問內部類的任何成員和方法,必須先創建實例後才能訪問(包括private的)。

如果外部類和內部類的成員變量重名,可以通過this外部類.this區分。

2、靜態內部類

public class OuterClass {

    private static String outer = "外部類私有變量";
    private static void outerMethod(){
        System.out.println("外部類私有方法" );

    }

    public static class InnerClass{

        public void innerMethod(){
            //靜態內部類調用外部類的私有靜態方法
            outerMethod();
            //靜態內部類訪問外部類的私有靜態變量
            System.out.println(outer);
        }
        
    }
}

使用static修飾的就是靜態內部類,它依賴於外部類,創建靜態內部類實例時,不需要先創建外部類實例,因爲靜態內部類依賴的是類而不是類實例,如下:

public static void main(String[] args) {
    //通過 外部類 調用靜態內部類的構造器
    OuterClass.InnerClass innerClass = new OuterClass.InnerClass();
    innerClass.innerMethod();
}

總的來說,靜態內部類有以下幾個特點:

  • 1、靜態內部類屬於類,而不屬於類實例;
  • 2、靜態內部類編譯之後,不會含有外部類對象的引用;
  • 3、靜態內部類可以包含靜態成員、靜態方法和static語句塊,也能包含非靜態的;
  • 4、靜態內部類不能直接訪問外部類的實例成員或方法,只能訪問靜態成員或方法(包括private的),外部類可以直接通過類名訪問靜態內部類的靜態成員或方法(包括private的),如果要訪問實例成員或方法,必須先創建實例後才能訪問(包括private的)。

外部類和內部類之間要訪問對方的private/protected成員時,編譯器會自動生成合適的“access method”靜態方法來提供合適的可訪問性,這樣就繞開了原本的成員的可訪問性不足的問題。

3、局部內部類

public class OuterClass {

    public static void main(String[] args) {
        //局部內部類
       class InnerClass{
            private String inner = "內部類私有變量";
            private void innerMethod(){
                System.out.println("內部類私有方法");
            }
       }

       InnerClass innerClass = new InnerClass();
       //訪問局部內部類的私有變量
       System.out.println(innerClass.inner);
       //調用局部內部類的私有方法
        innerClass.innerMethod();
    }
}

把一個類在方法裏定義,這個類就是局部內部類,局部內部類只能在方法裏面使用,它的作用域在方法之內,由於局部內部類不能在方法之外使用,所以它不能使用static和任何訪問修飾符修飾,局部內部類在開發中很少用到,因爲它的作用域太小了,不能被其他方法複用。

4、匿名內部類

public class OuterClass {

    private void outerMethod(){
        //創建匿名內部類
        InnerClass innerClass = new InnerClass(){
            @Override
            public void innerMethod() {
                System.out.println("內部類私有方法");
            }
        };
        //調用匿名內部類的方法
        innerClass.innerMethod();
    }

    public abstract class InnerClass{
        int num = 1;
        abstract void innerMethod();
    }
}

匿名內部類就是沒有名字的內部類,它沒有使用class關鍵字來定義類,而是在使用時直接創建接口或抽象父類的實現類來使用,匿名內部類一般在只使用一次的場景下使用,總的來說,匿名內部類有以下幾個特點:

  • 1、匿名內部類不能繼續聲明爲抽象類,它必須實現接口或抽象父類的所有抽象方法;
  • 2、匿名內部類不能定義構造器,因爲它沒有名字,但是它可以使用實例語句塊來完成初始化;
  • 3、匿名內部類必須實現一個接口或抽象父類,但最多隻能實現一個接口或抽象父類;
  • 4、匿名內部類編譯之後,會保留一個外部類對象的引用。

在java8之前,局部內部類或匿名內部類訪問方法的局部變量時,這個局部變量必須使用final修飾,在java8之後,被局部內部類或匿名內部類訪問的局部變量編譯器會自動加上final,無需顯式聲明,但是如果局部變量在內部類中被修改,那麼它還是要顯式聲明final,總之,在局部內部類或匿名內部類中使用局部變量時,必須按照有final修飾的方式來用(一次賦值,以後不能重複賦值)。

面向對象三大特性

面向對象是一種程序設計方法,它的基本思想是封裝、繼承、多態,根據現實世界中各種事物的本質特點,把它們抽象成類,作爲系統的基本構成單元,然後這些類可以生成系統中的多個對象,而這些對象則是映射成客觀世界的各種事物的實例。

1、封裝

封裝就是儘可能地隱藏對象內部的實現細節,只保留一些對外接口使之與外部發生聯繫。用戶無需知道對象內部的細節,但可以通過對象對外提供的接口來訪問該對象。

2、繼承

繼承是面向對象實現複用的手段,繼承是一種“is a”關係,父類和子類之間必須存在“is a”關係,通過繼承,子類可以獲得父類的所有非 private 屬性和方法,父類的私有屬性在子類中不能直接訪問,例如Cat 和 Animal 就是一種 “is a” 關係,因此 Cat 可以繼承自 Animal,從而獲得 Animal 非 private 的屬性和方法。

2.1、父類構造和子類構造

  • 構造方法不可繼承,使用super關鍵字調用父類構造
  • 默認會先調用父類構造,再調用子類構造

2.2、子類調用父類信息

  • 使用super關鍵字
  • 可以調用父類的公有屬性和方法
  • 可以調用父類的protected屬性和方法

下面一張表給出java中訪問權限修飾符的訪問範圍:

修飾符 在同一類中可訪問 在同一包內可訪問 在子類內可訪問 在不同包可訪問
public 可以 可以 可以 可以
protected 可以 可以 可以
default 可以 可以
private 可以

2.3、方法重寫

在子類中提供一個對方法的新的實現。

  • 方法重寫發生在通過繼承而相關的不同類中
  • 方法重寫具有相同的方法簽名和返回值
  • 子類重寫方法時子類方法訪問權限大於父類的
  • 子類重寫方法時子類拋出的異常類型是父類拋出異常的子類。
  • @Overiide稱爲重寫標註,用來保證重寫的方法和原方法的簽名和返回值一致

方法重載:方法重載是指在於同一個類中,一個方法與已經存在的方法名稱上相同,但是參數類型、個數、順序至少有一個不同。應該注意的是,返回值不同,其它都相同不算是重載。

3、多態

多態是指子類對象可以直接賦值給父類變量,但是運行時依然表現出子類的行爲特徵,這表示同一個類型的對象在運行時可以具有多種形態,多態分爲以下兩種:

  • 編譯時多態:主要指方法的重載
  • 運行時多態:指程序中定義的對象引用所指向的具體類型在運行期間才確定(運行時多態有三個條件:繼承,方法重寫,向上轉型)

例如下面的代碼中,子類Warrior繼承父類Hero,它重寫了父類的fight方法,並且在main函數中父類Hero引用子類Warrior(父類引用指向子類對象稱爲向上轉型),在Hero引用調用fight方法時,會執行實際對象所在類的fight方法,即Warrior類的fight方法,而不是Hero的fight方法。

public class Hero{
    public void fight(){
        System.out.println("hero");
    }
}

public class Warrior extends Hero{
    @Override
    public void fight(){
        System.out.println("Warrior");
    }
}

public static void main(String[] args) {
    Hero warrior = new Warrior();
    warrior.fight();
}

輸出:Warrior

類圖

瞭解下面6種關係有助於看懂UML圖。

1、泛化關係(Generalization)

泛化關係用一條帶空心箭頭的直線表示,在類圖中表示爲類的繼承關係(“is a”關係),在java中用extends關鍵字表示,最終代碼中,泛化關係表現爲繼承非抽象類。例如下面ASUS繼承自Laptop,ASUS是一臺筆記本,ASUS與Laptop之間是泛化關係。

generalization

2、實現關係(Realization)

實現關係用一條帶空心箭頭的虛線表示,在類圖中表示實現了一個接口(在java中用implements 關鍵字表示),或繼承抽象類,實現了抽象類中的方法。例如下面Laptop實現了IO接口,同時它繼承Computer這個抽象類,Laptop是它們的具體實現。

realization

3、聚合關係(Aggregation)

聚合關係用一條帶空心菱形箭頭的直線表示,表示整體是由部分組成的,但是整體和部分之間並不是強依賴的,整體不存在了,部分還是會存在。例如下面表示Staff聚合到Department,或者說部門是由員工組成的,部門不存在了,員工還是會存在的。

aggregation

4、組合關係 ( Composition )

組合關係是用一條帶實心菱形箭頭的直線表示,和聚合關係不同,組合關係中整體和部分是強依賴的,即整體不存在了部分也不存在,組合關係是一種強依賴的特殊聚合關係。例如下面表示Department組合到Company中,或者說Company是由Department組成的,但是公司不存在了,部門也將不存在。

composition

5、關聯關係(Association)

關聯關係用一條直線表示,表示不同類對象之間有關聯,這是一種靜態關係,與運行過程的狀態無關,在最開始就可以確定。因此也可以用 1 對 1、多對 1、多對多這種關聯關係來表示.。例如下面學生和學校就是一種關聯關係,一個學校可以有很多學生,但是一個學生只屬於一個學校,因此這是一種多對一的關係,在運行開始之前就可以確定。

association

關聯關係默認不強調方向,表示對象之間相互知道,如果要特別強調方向,如下圖,表示A知道B,但是B不知道A,這又叫DirectedAssociation。

directedassociation

ps: 在最終代碼中,關聯對象通常以成員變量的形式存在。

6、依賴關係(Dependency)

依賴關係用一條帶箭頭的虛線表示,與關聯關係不同的是,他描述一個對象在運行期間會用到另一個對象的關係,是一種動態關係,並且隨着運行時的變化, 依賴關係也可能發生變化。依賴也有方向,但是我們總是應該保持單向依賴,避免雙向依賴的產生。例如下面表示A依賴於B,A的一個方法中使用到了B作爲參數。

dependency

ps: 在最終代碼中,依賴關係主要表現爲:

1、A 類是 B 類方法的局部變量;

2、A 類是 B 類方法或構造的傳入參數;

3、A 類向 B 類發送消息,從而影響 B 類發生變化;

箭頭的指向爲調用關係。

結語

本文都是關於面向對象的一些知識,雖然簡單,但是也挺繁瑣的,積少成多,希望大家閱讀過後有所收穫。

參考資料:

看懂UML類圖和時序圖
爲什麼內部類的private變量可被外部類直接訪問?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章