java物件導向(上)

期待發表於2019-01-19


  1. 子類繼承父類就可以繼承到父類的成員變數和方法。如果訪問控制允許,子類例項可以直接呼叫父類裡定義的方法。
  2. 如果多個過載的構造器裡包含了相同的初始化程式碼,則可以把這些初始化程式碼放置在普通程式碼塊裡完成,初始化塊總在構造器執行之前被呼叫。
  3. 靜態初始化塊用於初始化類,在類初始化階段被執行。
  4. 如果繼承樹裡的某一個類需要被初始化時,系統將會同時初始化該類的所有父類。

類和物件

  1. Static修飾的成員表明它屬於這個類本身,而不屬於該類的單個例項,因為通常把static修飾的成員變數和方法也稱為類變數,類方法。
  2. 通常把不使用static修飾的成員變數和方法也稱為例項變數和例項方法。
  3. 靜態成員不能直接訪問非靜態成員。
  4. Static真正的作用就用於區分成員變數,方法,內部類,初始化塊這四種成員到底屬於類本身還是屬於例項。
  5. 產生的例項變數是在記憶體中儲存的。
  6. 引用變數裡存放的僅僅是一個引用,他指向實際的物件。
  7. This關鍵字總是指向呼叫該方法的物件。
  8. 根據this出現的位置,this作為物件的預設引用有兩種情形:

    - 構造器中引用該構造器正在初始化的物件。
    - 在方法中引用呼叫該方法的物件。
  9. This可以代表任何物件,當this出現在某個方法體中時,他所代表的物件是不確定的,但它的型別是確定的:他所代表的只能是當前類的例項,只有當這個方法被呼叫時,他所代表的物件才被確定下來,誰在呼叫這個方法,this就是誰。
  10. Static修飾的方法中不能使用this引用。
  11. 靜態成員不能訪問非靜態成員。
  12. Java程式設計中不要使用物件去呼叫static修飾的成員變數,方法,而是應該使用類去呼叫static修飾的成員變數,方法。
  13. 如果確實需要在靜態方法中訪問另一個普通方法,只能建立物件。
  14. 如果方法中有個區域性變數和成員變數同名,但程式又需要再該方法裡訪問這個被覆蓋的成員變數,則必須使用this字首。
  15. This在構造器中代表該構造器正在初始化的物件。
  16. 例項方法必須使用物件呼叫。

       public class A {
           int a  =  123;
           public A() {
               System.out.println("構造A");
               System.out.println(this.a);
           }
       }
       public class B extends A {
           int a = 456789;
           public B() {
               System.out.println("構造b");
               System.out.println(this.a+"BBB");
           }
           public static void main(String[] args) {
               new B();
           }
       }
       
       構造A
       123
       構造b
       456789BBB
       
    

方法詳解

  1. 同一個類的一個方法呼叫另一個方法時,如果被調方法是普通方法,則預設使用this作為呼叫者。如果被調方法是靜態方法,則預設使用類作為呼叫者。
  2. Java裡方法的傳遞方式只有一種:值傳遞。所謂值傳遞就是將實際引數值的副本傳入方法內,而且引數本身不會收到任何影響。
  3. Java中的基本型別的引數傳遞,和引用型別的傳遞都是值傳遞,對於引用型別的傳遞也是將原來的物件的地址複製一份給引數,這時候就是原來物件和引數的地址是一樣的,都指向了同一個堆記憶體中的物件,對原來物件或者引數任何一方賦值null都不會影響另一方。
  4. 陣列形式的形參可以處理形參列表的任意位置,但和數可變的形參只能處於形參列表的最後。
  5. 遞迴一定要向已知方向遞迴。
  6. 一個類中包含了兩個或者兩個以上的方法名相同而形參不同則被稱為方法過載。
  7. 如果需要呼叫test(String …
    books)方法,又只想傳入一個字串引數,則可採用傳入字串陣列的形式:object.test(new
    String[]{“TEST”});
  8. 方法過載的要求:同一個類中方法名相同,形參列表不同。
  9. Java中不能使用方法返回值型別作為區分方法過載的依據。

成員變數和區域性變數

成員變數指的是在類裡定義的變數,也就是field,區域性變數就是在方法裡定義的變數.

所有變數
成員變數
例項變數,不以static修飾
類變數,static修飾的
區域性變數
程式碼塊區域性變數,在程式碼塊中定義的
方法區域性變數,在方法內定義
形參,方法簽名中定義的變數
  1. 類變數從該類的準備階段起開始存在,直到系統完全銷燬這個類,類變數的作用域與這個類的生存範圍相同;而例項變數則從該類的例項被建立起開始存在,知道系統完全銷燬這個例項,例項變數的作用域與對應例項的生存範圍相同。
  2. 成員變數無需顯示初始化,只要為了一個類定義了類變數或例項變數,系統就會在這個類的準備階段或者建立該類的例項時進行預設初始化,成員變數預設初始化時的賦值規則與陣列動態初始化時陣列元素的賦值規則完全相同。
  3. 與成員變數不同的是,區域性變數除形參之外,都必須顯示初始化。必須先給方法區域性變數和程式碼塊區域性變數指定初始值,否則不可以訪問他們。
  4. 當系統載入類或者建立該類的例項時,系統自動為成員變數分配記憶體空間,並在分配記憶體空間後,自動為成員變數指定初始值。
  5. 在程式中使用區域性變數,也應該儘可能的所有區域性變數的作用範圍,作用範圍越小,他在記憶體中停留的時間也就越短,程式執行效能就越好。
  6. 什麼情況下考慮使用成員變數??

    • 如果需要定義的變數是用於描述某個類或某個物件的固有資訊,例如人的身高,因為每個人都必須具有這些資訊,這時候就可以定義成成員變數,如果此變數對於這個類的所有例項都是相同的,如眼睛的數量,那麼他就可以考慮定義成為類變數。
    • 如果某個類中需要以一個變數來儲存該類或者例項執行時的狀態資訊,例如五子棋棋盤的陣列,這種用於儲存某個類或某個例項狀態資訊的變數通常應該使用成員變數。
    • 如果某個資訊需要在某個類的多個方法之間進行共享,則這個資訊應該使用成員變數來儲存。

隱藏和封裝

  1. 為了實現良好的封裝,需要考慮兩方面

    • 將物件的成員變數和實現細節隱藏起來,不允許外部直接訪問。
    • 把方法暴露出來,讓方法來控制對這些成員變數進行安全的訪問和操作。
  2. 訪問控制修飾符

    • private:當前類訪問許可權
    • default:包訪問許可權
    • protected:子類訪問許可權
    • public:公共訪問許可權
  3. 訪問控制修飾符的使用原則

    • 類裡的絕大部分成員變數都應該使用private來修飾,只有一些static修飾的,類似全域性變數的成員變數,才可能考慮使用public修飾,除此之外,有些方法只用於輔助實現該類的其他方法,這些工具方法也應該使用private修飾。
    • 如果某個型別主要用作其他類的父類,該類裡包含的大部分方法可能僅希望被其子類重寫,而不相被外界直接呼叫,則應該使用protected修飾。
    • 希望暴露出來給其他類自由呼叫的方法應該使用public修飾,因此,類的構造器通過使用public修飾,從而允許在其他地方建立該類的例項。
    • JDK 1.5增加了靜態匯入import static * ,用於匯入制定類的某個靜態成員變數,方法或全部的靜態成員變數,方法。
    • 使用import可以省略寫包名,import static 則連類名都可以省略。

深入構造器

  1. 當系統開始執行構造器的執行體之前,系統已經建立了一個物件,只是這個物件還不能被外部程式訪問到,只能在該構造器中通過this來引用。當構造器的執行體執行結束後,這個物件作為構造器的返回值被返回,通常還會賦給另一個引用型別的變數,從而讓外部程式可以訪問該物件。
  2. 如果兩個構造器中有相同的程式碼,可以提取相同的程式碼到一個構造器,另一個構造器中用this來引用相同程式碼的構造器。

類的繼承

  1. 方法的重寫要遵循“兩同兩小一大”規則,兩同即方法名相同,引數列表相同,兩小即子類方法返回值型別應比父類方法返回值型別更小或相等,子類方法宣告丟擲的異常型別比父類方法丟擲的異常更小或相等,一大是子類方法的訪問許可權應該比父類方法的訪問許可權更大或相等。
  2. 在子類中呼叫父類中被覆蓋的方法,則可以使用super或者父類類名作為呼叫者來呼叫父類中被覆蓋的方法。
  3. 過載主要發生在同一個類的多個同名方法之間,重寫發生在子類和父類的同名方法之間。
  4. 如果需要在子類方法中呼叫父類被覆蓋的例項方法,則可使用super限定來呼叫父類被覆蓋的例項方法。
  5. Super用於限定該物件呼叫它從父類得到的例項變數和方法。This和super 都不可以出現在類方法中。
  6. 如果在某個方法中訪問名為a的成員變數,但是沒有顯示指定呼叫者,那麼系統的查詢順序為:

    • 查詢該方法中是否有名為a 的區域性變數
    • 查詢當前類中是否有名為a 的成員變數
    • 查詢a的直接父類中是否包含a的成員變數,依次上溯a的所有父類,未找到則編譯出錯。
  7. 當程式建立一個子類物件時,系統不僅會為該類中定義的例項變數分配記憶體,也會為它從父類繼承得到的所有例項變數分配記憶體,即使子類定義了與父類同名的例項變數。
  8. 子類呼叫父類構造器的情況

    • 子類構造器顯示使用super來指定執行的父類構造器。
    • 子類構造器中使用this來呼叫其它構造器,程式會執行另一個構造器之前呼叫父類構造器。
    • 沒有super和this顯式呼叫,會預設呼叫父類的無參構造。
  9. 建立任何物件總是從該類所在繼承樹最頂層類的構造器開始執行,然後依次向下執行,最後才執行本類的構造器。
  10. Java引用變數有兩種型別:一種是編譯時型別,一個是執行時型別。編譯時型別由宣告該變數時使用的型別決定。執行時型別由實際賦給該變數的物件決定,這就是所謂的多型。
  11. 物件的例項方法具有多型性,物件的例項變數則不具備多型性。
  12. 引用變數在編譯階段只能呼叫其編譯時型別所具有的方法,但執行時則執行他執行時型別所具有的方法。
  13. 通過引用變數來訪問其包含的例項變數時,系統總是試圖訪問他編譯時型別所定義的成員變數,而不是它執行時型別所定義的成員變數。
  14. 強制型別轉換:基本型別之間的轉換隻能在數值型別之間進行,引用型別之間的轉換隻能在具有繼承關係的兩個型別之間轉換。
  15. Instanceof用於判斷前面的物件是否是後面的類。Instanceof運算子前面運算元的編譯時型別要麼與後面的類相同,要麼與後面的類具有父子繼承關係。A是否是B的子類。

繼承和組合

  1. 繼承帶來的最大壞處就是破壞封裝。
  2. 設計父類遵循的規則

    • 儘量隱藏父類的內部資料,儘量所有的成員變數都用private修飾,不會被子類隨意改變。
    • 不要讓子類可以隨意訪問,修改父類的方法。父類中那些僅為輔助其他的工具方法,應該使用private修飾,如果希望被外部類呼叫,那麼就使用public修飾,如果不希望子類重寫該方法,加final,如果允許子類重寫,那麼protected。
    • 儘量不要再父類構造器中呼叫將要被子類重寫的方法。
  3. 何時需要從父類派生新的子類問題??

    • 子類需要額外增加屬性,而不僅僅是屬性值的改變。例如person 派生出student。
    • 子類需要增加自己獨有的行為方式,包括增加新的方法或重寫父類的方法。
  4. 組合就是用一個個的類來描述一個整體的各個部分,然後用零散的類來組成一個完整的整體,對這個類進行更詳盡的描述。
  5. 用繼承還是組合??

    • 對於Cat和Animal,繼承顯然更加合適,因為用一個動物組成一個Cat毫無意義,繼承是is-a關係,組合是has-a關係。

初始化塊

  1. 初始化塊的修飾符只能是static,使用static修飾的程式碼塊叫做靜態初始化塊,裡面可以包含任何可執行語句。
  2. 當java建立一個物件時,系統先為該物件的所有例項變數分配記憶體,前提是該類已經被載入過了,接著程式開始對這些例項變數執行初始化,其初始化順序是,先執行初始化塊或者宣告例項變數時制定的初始值,在執行構造器裡制定的初始值。
  3. 初始化塊總是在構造器之前執行。
  4. 實際上初始化塊是一個假象,使用javac命令編譯過後。該java類中的初始化塊會消失,初始化塊中程式碼會被還原到每個構造器中,且位於構造器所有程式碼的前面。
  5. 系統將會在類初始化階段執行靜態初始化塊,而不是在建立物件的時候才執行,因此靜態初始化塊總是比普通初始化塊先執行。
  6. Java系統載入並初始化某個類時,總是保證該類的所有父類全部載入並初始化。
  7. 當JVM第一主動使用某一個類時,系統會在類準備階段為該類的所有靜態成員變數分配記憶體,在初始化階段則負責初始化這些靜態成員變數,初始化靜態成員變數就是執行類初始化程式碼或者宣告類成員變數時制定的初始值,他們的執行順序與原始碼中的排列順序相同。

相關文章