Practical Java(重點版)之對象(完)

 1. 運用interfaces 支持多重繼承(multiple inheritance)。

Java的接口是支持多繼承的。Java是不支持實現上的多繼承。

2. 避免interfaces 中的函數發生衝突。

如果兩個接口具有相同的方法名字,但是其返回類型不一樣,那麼其實現類不能夠同時實現這兩個接口,編譯會報錯。

如果兩個接口的具有相同的方法名字,並且返回類型也一致,那麼其實現類可以同時實現這兩個接口,編譯不會報錯。

如果兩個接口聲明的變量是一模一樣的話,那麼在實現類中調用變量的時候,需要指定是哪個接口的變量,或者編譯不會通過。

3. 如需提供部分實現(partial implementation),請使用abstract classes(抽象類)。

所謂abstract class 是一種本身不能被具現化(instantiated)的class,但允許爲其內聲明的函數提供實現代碼。不同與interface(接口),接口是不能夠有實現。

4.  區分interface、abstract class和concrete class。

 

Interface

Abstract class

Concrete class

描述

一種契約(contract)表示法,不

帶實現

一種契約(contract)表示法,帶有部分實現

一種具象(具體)實現,經常是某個interface 或者某個abstract class 的實現

使用時機

當你想要支持單一或多個interface 繼承,或爲了確定某一marker interface(標識接口)

當你打算提供帶有部分實現的class

當你打算提供一份完

整的具象實現

內容限制

Public 函數以及public static final 常量

無任何限制

無任何限制

實現

不允許實現

允許部分實現

全部實現

 

interface、abstract classes 和concrete classes 的總結

 

支持多重繼承

支持抽象函數

允許實現

允許創建實體

允許部分實現

Interface

Y

Y

N

N

N

 

abstract classes

N

Y

Y

N

Y

 

concrete classes

N

N

Y

Y

N

 

interface 的所有函數都暗自爲abstract。

5.  審慎地定義和實現immutable classes (不可變類)。

不可變對象是一種一點構建好就不再變化的object,在其生存期間不可被改變內容,不可變類通常用來表示字符串、顏色和數字。由於不可變對象永遠不會改變本身數據,所以沒有必要對他們同步控制。

Class的定義和實現多方面合作纔可能造就不變性,以下幾個步驟是必須的:

a.  將class中的所有數據聲明爲private;(如果聲明爲其他修飾符的話,實例類則可以修改數據信息)

b.  只提供取值方法(getter),不允許存在設置方法(setter);(如果提供了setter方法,那麼其數據信息肯定會被修改掉)

c.  將class聲明爲final(即使提供了getter方法,但是在子類中也覆蓋父類方法的話,仍然有可能是修改數據,因此需要將類聲明爲不能夠被繼承);

d.  從獲取器返回reference to mutable object之前,先克隆(cloning)那些mutable objects;

e.  將[傳遞給構造函數之reference to mutable object]先克隆(clone);

f.  在構造函數中設定class 內含的所有數據。(由於數據信息不能夠被修改,所以其賦值只能夠是在構造函數)

6.  欲傳遞或接收mutable objects(可變對象)之object references時,請實施clone()。

實現immutable class(不可變類)時,必須對傳入或傳出之mutable objects(可變對象)進行克隆(clone)動作。如果一個object及其指涉(引用)所有objects都不會被改變,該object便是爲恆常不變的(immutable)。如果不實施克隆動作,你的對象不變性(immutability)就得不到保證。這是因爲他處可能保留了這個immutable object 內的某個object 的reference,並對其進行了修改,從而衝破了不變性的[界限]。

就是說,如果一個對象A中還引用了其他對象的引用B,那麼引用B中的數據是可以被修改掉。如下代碼:

public class User {

   private String name;

   private int age;

 

   public String getName() {

      return name;

   }

 

   public void setName(String name) {

      this.name = name;

   }

 

   public int getAge() {

      return age;

   }

 

   public void setAge(int age) {

      this.age = age;

   }

}

 

public final class DiskInfo {

   private String disk;

   private User user;

 

   public DiskInfo(String disk, User user) {

      this.disk = disk;

      this.user = user;

   }

 

   public User getUser() {

      return user;

   }

 

   public String disk() {

      return disk;

   }

 

   public static void main(String[] args) {

      User u = new User();

      u.setAge(1);

      u.setName("test1");

      DiskInfo d = new DiskInfo("test", u);

      User u1 = d.getUser();

      System.out.println("-----" + u1.getName());

      u1.setName("test2");

     

      User u2 = d.getUser();

      System.out.println("-----" + u2.getName());

   }

 

}

其輸入結果:

-----test1

-----test2

可以看到上述的結論是正確的。因此爲了保持數據的不變性,需要將mutable對象進行克隆。

克隆:Shallow Cloning(淺層克隆)。此技術對對象進行逐位拷貝(bitwise copy)。如果cloned object 包含了一些object references,那麼新的object 將內含與[cloned object 所含之object reference]完全一致的副本。所以new object 和cloned object 依然共享數據。使用了克隆的代碼如下:

package com.study6;

 

public class User implements Cloneable{

   private String name;

   private int age;

 

   public String getName() {

      return name;

   }

 

   public void setName(String name) {

      this.name = name;

   }

 

   public int getAge() {

      return age;

   }

 

   public void setAge(int age) {

      this.age = age;

   }

   @Override

   protected Object clone(){

      try {

        return super.clone();

      } catch (CloneNotSupportedException e) {

        throw new InternalError();

      }

   }

}

 

package com.study6;

 

public final class DiskInfo {

   private String disk;

   private User user;

 

   public DiskInfo(String disk, User user) {

      this.disk = disk;

      this.user = (User)user.clone();

   }

 

   public User getUser() {

      return (User)user.clone();

   }

 

   public String disk() {

      return disk;

   }

 

   public static void main(String[] args) {

      User u = new User();

      u.setAge(1);

      u.setName("test1");

      DiskInfo d = new DiskInfo("test", u);

      User u1 = d.getUser();

      System.out.println("-----" + u1.getName());

     

      u1.setName("test2");

      User u2 = d.getUser();

      System.out.println("-----" + u2.getName());

   }

 

}

輸出結果:

-----test1

-----test1

 

Deep Cloning(深層克隆):缺省情況下,Vector clone()執行的是淺層克隆(shallow clone)。Vector的值域driveShare其實是個object reference,因此當代碼對Vector進行克隆時,雖然建立了一個新副本,其內容(references to User object)卻沒有被克隆。因此需要深層克隆。

在實現—個immutable class(不可變類)時,請遵循下列規則:

a.聲明這個class 爲final。

b.聲明所有數據爲private。

c.只提供取值函數(getter),不提供設值函數(setter)。

d.在構造函數中設置所有instance 數據。

e. 如果函數返回reference to mutable objects,請克隆那些mutable objects。

f.如果函數接受reference to mutable objects,請克隆那些mutable objects。

g.如果缺省之淺層克隆(shallow clone)不能符合immutable object 的正常行爲請實現出深層克隆(deep clone)。

7.     使用繼承(inheritance)或委託(delegation)來定義immutable classes(不可變類)。

另外三個你可以用來定義immutable classes 的技術:

a.immutable interface(不可變接口)。

b.公共接口或基類(common interface or base class)。

c.immutable delegation class (不可變的委託類)。

技術

優點

缺點

Immutable inference 不可變(恆常)接口

簡單易行。無需付出性能上的代價

有破綻,可被破壞。(轉型會破壞不變性)

Common interface or base calss 公共接口或基類

沒有破綻。清晰地將

mutable object 和immutable

objects 分離

需實現更多的classes,完成更深的classes 繼承系(一個接口必須有多個實現類)

Immutable delegation class不可變(恆常)委託類

沒有破綻。當你無法修改既

有的mutable object 源碼

時,此法可用

需付出性能上的代價(不可變類中含有一個可變類的引用,造成性能下降)

8.實現clone()時記得調用super.clone()。

clone ()中首先必須調用super.clone(),可確保java.long.Object 中的clone會被調用,因而使得克隆件得以被正確建構出來。java.1ang.Object 的clone()函數會創建一個正確型別的新對象,並執行淺層克隆(shallow clone,即從[被克隆物]複製所有位域(fields)到[新對象]身上)。即使需要深層克隆(deep clone),仍然齋要調用java.1ang.Object 的clone()函數,才能創建對象的正確型別。

任何classes 如果支持克隆操作,就必須實現Cloneable interface——這是一個標識接口(marker interface),也就是說它並沒有實現任何函數,任何classes 如果實現Cloneable,就是宣稱它們支持克隆操作。

9. 別隻依賴finalize()清理non—memory(內存以外)的資源

在垃圾問收器(garbage collector)回收某個class object 之前,JVM 會調用這個class 的finalize()。這個函數有時被宣揚爲[在回收某個對象的內存之前確保釋放non-memory 資源]的有效方法。由於垃圾回收器僅僅釋放對象內存,finalize()因此提供了一種釋放其他資源的辦法。

對象的finalize()函數只有在垃圾回收器釋放對象佔用的空間之前纔會被調用。

當垃圾回收器執行時,可能並非所有符合回收條件的對象都被回收(實際情況取決於垃圾回收機制所採用之算法),此外亦無法保證finalize()在可預期的時間上執行。這是由於[終結(finalization)動作[與垃圾回收]之間的異步(asynchronous)本性造成的。由此推斷,finalize()未必在程序結束前被執行。這意味即使你正確撰寫了finalize()來釋放non-memory 資源,你的程序結束前仍有可能耗盡這些資源。

避免這個問題的可能辦法之一是使用System.runFinalization()。這個函數要求JVM 在垃圾回收器下次運行之前,對所有標記爲[可終結(finalizable)]對象調用其finalize()。是否能夠做到這一點,取決於垃圾回收器和這個函數所採用的算法。但不能保證調用該函數就一定會執行起finalize()。

早先的另一個可用函數是System.runFinalization(),現已作廢。這個函數僅僅保證所有對象的finalize()將在JVM 退出之前被執行。這意味等到finalize()執行時,你可能已經耗盡了資源。通常你會希望finalize()在你程序運行期間被執行,而不是等到最後JVM退出時刻。這個函數被認爲是不安全的,因此在Java2 中被廢除了。

因此不能把希望單單寄託於[finalize()被調用]這件事情上,需要自己編寫清理no-memory資源的函數,並且結合finalize方法一起使用。可以自己編寫一個清理函數,並且該函數是public的,同時要被finalize函數所調用(確定JVM調用finalize時可以調用到清理函數)。將清理函數聲明爲public,是爲了可以再任何時刻都可以清理資源。將清理函數被聲明爲synchronized,這可以保證多個線程(threads)不會針對相同的對象同時進入這個函數。

所有finalize()都應當調用super.finalize()以確保任superclass 的finalize()被調用。由於無法保證finalize()是否被調用、何時被調用,所以你應當提供一個Public 專員函數,用以執行non-momory 資源清理工作;該函數應當由class 的finalize()調用之。

10. 在構造函數內調用non—final函數時要當心

當新建一個對象的時候,構造方法能夠被調用。

package com.study6;

 

public class NoFinalTest {

 

   /**

    * @param args

    */

   public static void main(String[] args) {

      Sub s = new Sub();

      System.out.println(s.getNum());

   }

 

}

 

class Base {

   private int value;

 

   public Base() {

      System.out.println("base");

      value = lookUp();

   }

 

   public int lookUp() {

      System.out.println("base lookUp");

      return 5;

   }

 

   public int getNum() {

      return value;

   }

}

 

class Sub extends Base {

   private int num = 10;

 

   public int lookUp() {

      System.out.println("Sub lookUp " + num);

      return num;

   }

}輸出結果爲:

base

Sub lookUp 0

0

.這不是我們想要的結果,不過從上可以看出來num並還沒有賦值。

分析:該函數返回instance變量num,它在instance 變量初始化時被賦值爲10.事實真象是,當Sub的1ookup()開始執行時,其instance變量初始化工作還未來得及進行呢。

當構造函數調用non-final 函數時,就有可能發生這種錯誤。如果該函數被一個derived class 覆寫,而是該函數返回一個[instance 變量初始化期間]被初始化的值,就會產生問題。

發佈了42 篇原創文章 · 獲贊 0 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章