java物件導向(中)

期待發表於2019-01-19


  1. Java提供了final關鍵字來修飾變數,方法和類,系統不允許為final變數重新賦值,子類不允許覆蓋父類的final方法,final類不能派生子類。
  2. Abstract 和 interface兩個關鍵字分別用於定義抽象類和介面,抽象類和介面都是從多個子類中抽象出來的共同特徵,但抽象類主要作為多個類的模板,而介面則定義了多類應該遵守的規則。
基本資料型別 包裝類
byte Byte
short Short
int Integer
long Long
char Character
double Double
float Float
boolean Boolean

Java8 增強的包裝類

  1. 自動裝箱,就是把一個基本型別變數直接賦值給對應的包裝類變數,或者OBJECT。
  2. 自動拆箱,允許直接把包裝類物件直接賦值給一個對應的基本型別變數。
  3. 包裝型別的變數是引用資料型別,但包裝類的例項可以與數值型別的值作比較,這種比較是直接取出包裝類例項所包裝的數值來進行比較的。
  4. 兩個包裝類例項比較,只有兩個包裝類引用指向同一個物件才會返回true。
  5. 對於Integer包裝型別的數值超過了(-128 — 127)比較為false的解釋如下:

    • 系統會把一個-128到127之間的整數自動裝箱成Integer例項,並放入了一個名為cache的陣列中快取起來,如果以後把一個-128到127之間的整數自動裝箱成一個Integer例項的時候,實際上是直接指向對應的陣列元素,因此-128到127之間的同一個整數自動裝箱成Integer例項時,永遠都是引用此cache陣列的同一個陣列元素,所以他們全部相等,但每次把一個不在-128到127範圍內的整數自動裝箱成Integer例項時,系統總是重新建立一個Integer例項,所以比較為false。

      public static void main(String[] args) {

          Integer valueOf = 112;
          Integer valueOf1 = 112;
          //dsds :  true
          System.out.println("dsds :  "+(valueOf == valueOf1));
          Integer valueOf2 = 128;
          Integer valueOf3 = 128;
          //dsds :  false
          System.out.println("dsds :  "+(valueOf2 == valueOf3));

      }

  6. Boolean值的比較:true > false

處理物件

  1. 當使用==來判斷兩個變數是否相等時,如果兩個變數是基本型別,且都是數值型別,則只要兩個變數的值相等就會返回true。
  2. 對於兩個引用變數必須引用一個物件的時候==才會返回true,==不可用於比較型別上沒有父子關係的兩個物件。
  3. 常量池:constant pool,專門用於管理在編譯時被確定並且被儲存在以編譯的.class檔案中的一些資料。包括了關於類,方法,介面中的常量,還包括字串常量。
  4. JVM常量池保證相同的字串直接量只有一個,不會產生多餘副本。
  5. 使用new String()建立的字串物件是執行時建立出來的,它被儲存在執行時記憶體區,即堆記憶體,不會放入常量池中。

類成員

  1. 建立的物件例項中根本不會擁有對應類的類變數。
  2. Null物件可以訪問他所屬類的類成員。
  3. 類成員不能訪問例項成員,包括成員變數,方法,初始化塊,內部類和列舉類。因為類成員是屬於類的,類成員的作用域比例項成員的作用域更大,完全可能出現類成員已將初始化完成,但例項成員還不曾初始化的情況,如果允許類成員訪問例項成員將會引起大量錯誤。
  4. 如果一個類始終只能建立一個例項,那就被成為單例類。
  5. 為了避免其他類自由建立該類的例項,應該把構造器private修飾,從而把該類的所有構造器隱藏起來。
  6. 一旦把該類的構造器隱藏起來,就需要提供一個public方法作為該類的訪問點,用於建立該類的物件,且該方法必須使用static修飾。除此之外,該類還必須快取已經建立的物件,否則該類無法知道是否曾經建立過物件。

    • 程式碼例項
    public class Singleton {
             //單例類
            public static Singleton singleton;//用於儲存建立好的物件。
            private Singleton() {}//private修飾,對外隱藏構造器
            
            public static Singleton getInstance() {
                if (singleton == null) {//如果為空那麼就是未曾建立物件
                    singleton = new Singleton();//建立物件
                }
                return singleton;//返回
            }
            public static void main(String[] args) {
                Singleton instance = Singleton.getInstance();
                Singleton instance1 = Singleton.getInstance();
                System.out.println(instance == instance1);//true
            }
        }

Final修飾符

  1. 當類初始化時,系統會為該類的類變數分配記憶體,並分配預設值,當建立物件時,系統會為該物件的例項變數分配記憶體,並分配預設值,也就是說:當執行靜態初始化快時可以對類變數附初始值,當執行初始化程式碼塊,構造器時可以對例項變數附初始值。
  2. Final修飾的成員變數而言,一旦有了初始值,就不能二次賦值,如果既沒有初始值,也沒有在構造器,初始化塊中指定初始值,那麼就為系統預設的初始值,也就失去了意義,因此final修飾的成員變數必須由程式設計師顯示的指定初始值。
  3. Final修飾的類變數,例項變數能指定初始值的地方

    • 類變數:必須在靜態初始化塊中指定初始值或宣告該類變數時指定初始值。
    • 例項變數:非靜態初始化塊,宣告變數時和構造器中。
  4. 例項變數不能在靜態初始化塊中指定初始值,因為靜態初始化塊是靜態成員,不可訪問例項變數-非靜態成員。類變數不能在普通初始化塊中指定初始值,因為類變數在類初始化階段已經被初始化了。
  5. Final成員變數在顯示初始化之前不能直接訪問,但可以通過方法來訪問,基本可以算是java的一個設計缺陷。
  6. 系統不會對區域性變數初始化,區域性變數必須由程式設計師顯示初始化,因此使用final修飾區域性變數時。既可以在定義時指定預設值,也可以不指定預設值。
  7. 當使用final修飾基本型別變數時,不能對基本型別變數重新賦值,因此基本型別變數不能被改變。但對於引用變數而言,他僅僅儲存的是一個引用,final只保證這個引用型別變數所引用的地址不會改變,即一直引用同一個物件,但是在堆記憶體中的這個物件完全可以發生改變。
  8. 巨集變數的確定條件

    • 使用final修飾。
    • 在定義該final變數時就指定了初始值。
    • 該初始值可以在編譯時被確定下來。
    • 巨集變數一旦確定下來,編譯器會把程式中所有用到該變數的地方直接替換成該變數的值。
  9. 巨集變數與非巨集變數不相等
  10. 不希望子類衝重寫父類的某個方法,則可以使用final修飾該方法。
  11. 為了保證某個類不被繼承,則可以使用final修飾這個類。
  12. 不可變類:建立例項後,例項的例項變數是不可改變的。如Integer in = new Integer(8);
  13. 建立自定義的不可變類規則如下

    • 使用private和final修飾符來修飾該類中的成員。
    • 提供帶引數的構造器,用於傳入引數以便初始化成員變數。
    • 僅為該類的成員變數提供getter方法,不要為該類提供setter方法,因為普通方法無法修改final修飾的成員變數。
    • 如果有必要重寫Object中的equals 和 hashCode方法
  14. 與可變類相比,不可變類的例項在整個生命週期中要永遠處於初始化狀態,他的例項變數不可以改變,因此對不可變類的例項的控制更加簡單。
public class NoChangeClass {
    private final ClassNames names;
    public NoChangeClass() {
        this.names = new ClassNames("1","2");//預設值
    }
    public NoChangeClass(ClassNames names) {
        //如果利用有參構造,那麼直接new一個新例項,這樣避免了舊例項的更改造成當前類的變化。
        this.names = new ClassNames(names.getName(),names.getTime());
    }
    public ClassNames getNames() {
        return new ClassNames(names.getName(),names.getTime());
    } 
    public static void main(String[] args) {
        ClassNames classNames = new ClassNames("?","??");
        NoChangeClass noChangeClass = new NoChangeClass(classNames);
        System.out.println(classNames.getName());
        classNames.setName("21e1231");
        System.out.println(classNames.getName());
        System.out.println(noChangeClass.names.getName());
    }
}
Output:
?
21e1231
?

抽象類

  1. 某個父類只是知道其子類應該包含怎麼樣的方法,但是無法準確的知道這些子類是如何實現這些方法的。
  2. 抽象方法只有方法簽名,沒有方法實現的方法。
  3. 有抽象方法的類只能被定義為抽象類,抽象類可以沒有抽象方法。
  4. 抽象方法和抽象類的規則

    • 抽象類抽象方法必須使用abstract修飾符修飾,抽象方法不能有方法體。
    • 抽象類不能被例項化,無法使用new呼叫抽象類的構造器建立抽象類的例項,即使抽象類裡不包含抽象方法。
    • 抽象類可以包含成員變數,方法,構造器,初始化塊,內部類。抽象類的構造器不能用於建立例項,主要是用於被其子類呼叫。
    • 含有抽象方法的類,包括直接定義了一個抽象方法;或者整合了一個抽象父類,但沒有完全實現父類包含的抽象方法;或實現了一個藉口,但沒有完全實現介面包含的抽象方法,只能被定義為抽象類.
  5. 在建立其子類例項的時候呼叫抽象父類的構造器,初始化塊。
  6. 抽象類不能用於建立例項,只能當做父類被其他子類繼承。
  7. Abstract修飾類,表示只能被繼承,修飾方法,表明必須由子類實現重寫。而final修飾的類不能被繼承,final修飾的方法不能被重寫,所以final和abstract不能同時修飾類和方法。
  8. Static與abstract不能同時修飾某個方法,即沒有所謂的類抽象方法。但是可以同時修飾內部類。
  9. Abstract關鍵字修飾的方法必須被其子類重寫才有意義,否則就永遠不會擁有方法體,所以abstract方法不能是private修飾。Private與abstract不能同時修飾方法。
  10. 抽象類是從多個具體類中抽象出來的父類,它具有更高層次的抽象。
  11. 抽象類别範本模式的簡單規則:

    • 抽象父類可以只定義需要使用的某些方法,把不能實現的部分抽象成抽象方法,留給其子類去實現。
    • 父類中可能包含需要呼叫其他系列方法的方法,這些被呼叫方法即可以由父類實現,也可以由其子類實現。父類裡提供的方法只是定義了一個通用演算法,其實現也許並不完全由自身實現,而必須依賴於其子類的輔助。

Java 9 改進的介面

  1. Java9對介面進行了改進,允許在介面中定義預設方法和類方法。
  2. 介面定義的是多個類共同的公共行為規範,這些行為是與外部交流的通道,這就意味著介面裡通常是定義一組公共方法。
  3. 介面定義的基本語法說明:

    • 修飾符可以是public或者省略,如果省略了public,則預設採用包許可權訪問控制符。
    • 一個介面可以有多個直接父介面,但介面只能繼承介面,不能繼承類。
  4. 由於介面定義的是一種規範,因此介面裡不能包含構造器和初始化塊定義,可以包含常量(靜態常量),方法(只能是抽象例項方法,類方法,預設和私有方法),內部類定義。
  5. 介面中的私有方法可以擁有方法體,但私有方法不能使用default修飾。私有方法可以使用static修飾,所以就是說私有方法可以是類方法也可以是例項方法。
  6. 介面中的靜態常量是介面相關的。因此預設增加static和final修飾符。所以在介面中的成員變數總是使用public static final修飾,並且只能在定義時指定初始值。
  7. 介面中如果不是定義預設方法類方法或私有方法,系統自動為普通方法增加abstract修飾符。介面中的普通方法總是使用public abstract修飾。
  8. 介面中定義的內部類,內部介面,內部列舉預設都採用public static兩個修飾符,不管定義時是否指定,系統會為其自動修飾。
  9. 預設方法都是採用default修飾,不能使用static修飾,並且總是public修飾。需要使用介面的實現類例項來呼叫這些方法。
  10. 類方法總是public修飾,可以直接使用介面名來呼叫。
  11. 介面中的預設方法:當兩個預設方法中包含一段相同的實現邏輯時,就有必要把這段程式碼抽取成工具方法,而且工具方法一般是被隱藏的,所以這就是java9中增加私有方法的原因。
  12. 一個介面可以由多個直接父介面,和類繼承相似,子介面擴充套件某個父介面時,將會獲得父介面裡定義的所有抽象方法,常量。
  13. 介面不能用於建立例項,但是可以用於宣告引用型變數。
  14. 介面的主要用途就是被實現類實現:

    • 定義變數,也可以用於強制型別轉換。
    • 呼叫介面中定義的常量、
    • 被其他類實現。
  15. 實現介面與繼承父類相似,一樣可以獲得所實現介面裡定義的常量,方法,
  16. 一個類實現了一個或者多個介面之後,必須完全實現介面中定義的全部抽象方法,否則該類就將保留從父介面整合得到的抽象方法,該類也必須定義成抽象類。
  17. 一個類實現某個介面時,該類將會獲得介面中定義的常量,方法等。因此可以把實現介面理解為一種特殊的繼承。相當於實現類繼承了一個徹底抽象的類。
  18. 為什麼實現類實現介面裡的方法只能使用public?

    • 因為介面裡的方法都是public的,而子類重寫父類方法時訪問許可權只能更高或者相等。
  19. 抽象類即只對事物本身的抽象,介面則是對事物本身擁有的行為的抽象。
  20. 介面與抽象類的在用法上的差別。:

    • 介面裡只能包含抽象方法,靜態方法,預設方法和私有方法。不能為普通方法提供實現,抽象類完全可以。
    • 介面裡只能定義靜態常量,不能定義普通成員變數,抽象類都可以。
    • 介面裡不包含構造器;抽象類裡可以包含構造器,抽象類裡的構造器並不是用於建立物件。而是讓其子類呼叫這些構造器來完成屬於抽象類的初始化操作。
    • 介面裡不能包含初始化塊,但抽象類則完全可以包含。
    • 一個類最多隻能有一個直接父類,包括抽象類。但一個類可以實現多個介面,通過實現多個介面可以彌補java單繼承的不足。

相關文章