Java編程思想 第七章

來源:互聯網
上載者:User

標籤:並且   cas   表達   opp   cleanup   樣本   何事   assign   額外   

7.1 組合文法

1)組合即 將對象引用置於新類中

2)每一個非基本類型的對象都有一個toString()方法,而且當編譯器需要一個String而你卻只有一個對象時,該方法便會被調用。

3)初始化一個類中的對象引用有如下四種方式:

1.在定義對象的地方初始化,,意味著總能在調用構造器之前被初始化

2.在類的構造器中

3.就在正要使用這些對象之前,這種叫惰性初始化,這種可以減少額外的負擔

4.使用執行個體初始化

class Soap {  private String s;  Soap() {    print("Soap()");  //2.在類的構造器中初始化    s = "Constructed";  }  public String toString() { return s; }} public class Bath {  private String // 1.在定義對象的地方初始化:    s1 = "Happy",    s2 = "Happy",    s3, s4;  private Soap castille;  private int i;  private float toy;  public Bath() {    print("Inside Bath()");  //2.在類的構造器中初始化:    s3 = "Joy";    toy = 3.14f;    castille = new Soap();  }   // 4.執行個體初始化:  { i = 47; }  public String toString() {    if(s4 == null) // 3.惰性初始化(Delayed initialization):      s4 = "Joy";    return      "s1 = " + s1 + "\n" +      "s2 = " + s2 + "\n" +      "s3 = " + s3 + "\n" +      "s4 = " + s4 + "\n" +      "i = " + i + "\n" +      "toy = " + toy + "\n" +      "castille = " + castille;  }   public static void main(String[] args) {    Bath b = new Bath();    print(b);  }} /* Output:Inside Bath()Soap()s1 = Happys2 = Happys3 = Joys4 = Joyi = 47toy = 3.14castille = Constructed

 

7.2繼承文法

1)當建立一個類時,總是在繼承,因此,除非已明確指出要從其他類中繼承,否則就是在隱式地從Java的標準根類Object進行繼承。

2)繼承關鍵字extends:繼承會自動得到基類中所有的域(什麼是域)和方法。

3)調用另外一個類的main函數的方式與調用另外一個類的普通靜態函數相同,即類名.main(args);,args可以是主調用類從命令列獲得的參數,也可以是其他任意的String數組。

4)可以為每個類都建立一個main方法。這種在每個類中都設定一個main方法的技術可使每個類的單元測試都變得簡單易行。而且在完成單元測試之後,也無需刪除main(),可以留待下次測試。

5)即使一個類只具有包存取權限,其public main()仍然是可以訪問的。(還沒證實?)

6)為了繼承,一般的規則是將所有的資料成員都指定為private,將所有的方法指定為public(protected方法也可以藉助匯出類來訪問,後面說到)。

7)Java用 super 關鍵字表示超類(父類)。運算式super.fun();可以調用父類中的函數(此處是調用函數fun())。

7.2.1 初始化基類 注意:基類=父類;匯出類=子類。
1)當建立了一個匯出類的對象時,該對象包含了一個基類的子物件,該子物件被封裝在匯出類對象內部。
2)基類子物件的初始化:在構造器中調用基類構造器來執行初始化。在執行基類構造器之前,定義處初始化、執行個體初始化等均會被執行。                                       ※ Java會自動在匯出類的構造器中插入對基類構造器的調用

範例程式碼:
class Art {  private String art = " test art.\n";  private String artS;  {    artS = " ART";  }  Art() { print("Art constructor"+art+artS); }}class Drawing extends Art {  private String draw = " test drawing.\n";  private String drawS;  {    drawS = " DRAW";  }  Drawing() { print("Drawing constructor"+draw+drawS); }}public class Cartoon extends Drawing {  public Cartoon() { print("Cartoon constructor"); }  public static void main(String[] args) {    Cartoon x = new Cartoon();  }} /* Output:Art constructor test art. ARTDrawing constructor test drawing. DRAWCartoon constructor

可以看出,構建過程是從基類“向外”擴散的,所以基類在匯出類構造器可以訪問它之前,就已經完成初始化了。當然,預設構造器也會逐層調度基類的構造器。

7.2.2 帶參數的構造器

編譯器可以自動調用預設的建構函式,是因為它們沒有任何參數。

但是如果沒有預設的基類建構函式,或者想調用一個帶參數的基類建構函式,必須使用關鍵字super顯示地編寫調用基類建構函式的語句,並且配以適當的參數列表。

如果基類沒有預設構造器(無參構造器),匯出類不顯式的調用基類的帶參構造器,則編譯器會報錯。

7.3 代理

代理是第三種複用代碼的關係,Java並沒有提供對它的直接支援。它是繼承和組合之間的中庸之道:

  • 首先,我們需要將一個成員對象置於所要構造的類中(就像組合);
  • 其次,我們需要在新類中暴露該成員對象的所有方法(就像繼承)或該成員對象的所有方法的某個子集。

範例程式碼:

public class SpaceShipControls {  void up(int velocity) {}  void down(int velocity) {}  void left(int velocity) {}  void right(int velocity) {}  void forward(int velocity) {}  void back(int velocity) {}  void turboBoost() {}}public class SpaceShipDelegation {  private String name;  private SpaceShipControls controls =    new SpaceShipControls();  public SpaceShipDelegation(String name) {    this.name = name;  }  // Delegated methods:  public void back(int velocity) {    controls.back(velocity);  }  public void down(int velocity) {    controls.down(velocity);  }  public void forward(int velocity) {    controls.forward(velocity);  }  public void left(int velocity) {    controls.left(velocity);  }  public void right(int velocity) {    controls.right(velocity);  }  public void turboBoost() {    controls.turboBoost();  }  public void up(int velocity) {    controls.up(velocity);  }  public static void main(String[] args) {    SpaceShipDelegation protector =      new SpaceShipDelegation("NSEA Protector");    protector.forward(100);  }}

反例代碼:

public class SpaceShip extends SpaceShipControls{    private String name;    public SpaceShip(String name)    {        this.name = name;    }    @Override    public String toString()    {        return name;    }    public static void main(String[] args)    {        SpaceShip ship = new SpaceShip("NSEA Protector");        ship.foward(100);    }}

SpaceShip並非真正的SpaceSbipControls類型,即便你可以“告訴”SpaceShip向前運動(forward())。更準確地講,SpaceShip包含SpaceShipControls,與此同時,SpaceShipControls的所有方法在SpaceShip中都暴露了出來(不懂哪裡暴露了)。上面的例子就可以解決。

7.4 結合使用組合和繼承

同時使用組合和繼承,並配以必要的構造器初始化,可以建立更加複雜的類。

7.4.1 確保正確清理
try{  //......}finally{  x.cleanup();}

上述代碼中的finally子句表示的是“無論發生什麼事,一定要為x調用cleanup()。”
在清理方法(dispose())中,必須注意對基類清理方法和成員對象清理方法的調用順序,以防某個子物件依賴於另外一個子物件的情形發生。

假若一個子物件要以另一個為基礎。通常,應採取與C++編譯器對它的“破壞器”採取的同樣的形式:首先完成與類有關的所有特殊工作(可能要求基礎類元素仍然可見),然後調用基礎類清除方法。
  • 一般,採用與C++編譯器在其解構函式上所施加的形式:首先,執行類的所有特定的清理工作,其順序同產生順序相反(通常這就要求基類元素仍舊存活);然後調用基類的清理方法。
  • 注意:除了記憶體以外,不能依賴記憶體回收行程去做任何事。如果需要進行清理,最好編寫自己的清理方法,但是不要使用finalize()。
7.4.2 名稱屏蔽
  • 如果Java的基類擁有某個已被多次重載的方法名稱,那麼在匯出類中重新定義該方法名稱並不會屏蔽器在基類中的任何一個版本(這一點與C++不同)。因此,無論是在該層或者它的基類中對方法進行定義,重載機制都可以正常工作。

  • 如果你只是想覆寫某個方法,但是害怕不留心重載了該方法(而並非覆寫了該方法)時,可以選擇添加@Override註解(Java SE5新增)。

  • 在一個方法前添加了@Override註解,該方法便只能是覆寫父類的某個方法,若是不留心寫成了重載,編譯器便會報錯。這樣@Override註解便可以防止你在不想重載時而意外地進行了重載。

7.8 final 關鍵字7.8.1  資料

聲明資料為常量,可以是編譯時間常量,也可以是在運行時被初始化後不能被改變的常量。

  • 對於基本類型,final 使數值不變;
  • 對於參考型別(包含數組),final 使引用不變,也就不能引用其它對象,但是被引用的對象本身是可以修改的。
final int x = 1;x = 2;  // cannot assign value to final variable ‘x‘final A y = new A();y.a = 1;

空白final:是指被聲明為final但又未給定初值的域。但空白final必須在構造器中用運算式賦值。例子如下:

class Poppet {  private int i;  Poppet(int ii) { i = ii; }}public class BlankFinal {  private final int i = 0; // Initialized final  private final int j; // Blank final  private final Poppet p; // Blank final reference  // Blank finals MUST be initialized in the constructor:  public BlankFinal() {    j = 1; // Initialize blank final    p = new Poppet(1); // Initialize blank final reference  }  public BlankFinal(int x) {    j = x; // Initialize blank final    p = new Poppet(x); // Initialize blank final reference  }  public static void main(String[] args) {    new BlankFinal();    new BlankFinal(47);  }}

總之,必須在域的定義外或每個構造器中用運算式對fianl進行賦值。

7.8.2 方法 

使用final方法的原因有兩個:

  • 把方法鎖定,以防任何繼承類修改它的含義。這是出於設計的考慮:確保在繼承類中使方法行為保持不變,並且不會被覆蓋。
  • 效率:類似於C++的inline機制,早期的虛擬機器需要,現在不需要了,故現在不需要使用final方法進行最佳化了。
    綜上,僅當你想顯式地阻止覆蓋該方法時,才使該方法成為final的。

聲明方法不能被子類覆蓋。

private 方法隱式地被指定為 final,如果在子類中定義的方法和基類中的一個 private 方法簽名相同,此時子類的方法不是覆蓋基類方法,而是重載了。

final方法和private方法類似,區別在於,private只能在類內訪問,類外訪問不到,final方法可以在類外被訪問,但不可以重寫,可以使用該方法的功能但是不可以改變其功能

7.8.3  類

final置於類的定義之前表示該類不允許被繼承。這樣做的原因如下:

  • 出於某種考慮,你對該類的設計永不需要做任何變動;
  • 出於安全的考慮,你不希望它有子類。

Java編程思想 第七章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.