import java.util.Arrays; import java.util.Collections; import java.util.List; public class Test { public static void main(String[] args) { for (int i = 0; i < foo.VALUES.length; i++) { foo.VALUES[i] = new Integer(0); } for (int i = 0; i < foo.VALUES.length; i++) { System.out.println(foo.VALUES[i]); } } } class foo { public static final Integer[] VALUES = { new Integer(0), new Integer(1), new Integer(2), new Integer(3) }; }
雖然VALUES被定義成final,可是用戶在外部仍是能修改數組中的值,這是很不安全的。 java
以下代碼是推薦作法,公有數組應該被替換爲一個私有數組,以及一個公有的非可變列表。 程序員
private static final Integer[] PRIVATE_VALUES = {new Integer(0),new Integer(1), new Integer(2),new Integer(3)}; public static final List VALUES = Collections.unmodifiableList(Arrays.asList(PRIVATE_VALUES));
也能夠把公有數組替換成一個公有方法,它返回私有數組的一份拷貝。 數組
public static final Integer[] VALUES() { return (Integer[])PRIVATE_VALUES.clone(); }
非可變類建立和使用都比較簡單,它也是線程安全的。但它有個缺點須要注意:對於每個不一樣的值都要求一個單獨的對象。這點很重要,關乎程序的執行效率。好比經過大量的String去生成另外一個String是不太划算的,由於除了最後的結果,中間的其餘對象都會被丟棄,因此應該考慮用字符串變量StringBuffer。 安全
讓一個非可變類不能被繼承有三種方法: 函數
爲何非可變類要設計成不能繼承了,能夠看一下這個討論:http://stackoverflow.com/questions/2068804/why-is-string-final-in-java 工具
個人理解是,若是非可變類A支持繼承,那麼子類可能實現爲可變類B,會增長使用上的複雜性。 this
好比有個拷貝函數形參是非可變類A,但我不知道傳入的是A仍是B,咱們還要經過判斷才能決定採用淺拷貝仍是深拷貝。 spa
想一想以下的程序輸出是6,而不是3,爲何? 線程
import java.util.Arrays; import java.util.Collection; import java.util.HashSet; public class Test { public static void main(String[] args) { MyHashSet mhs = new MyHashSet(); mhs.addAll(Arrays.asList(new String[] { "a", "b", "c" })); System.out.println(mhs.getSum()); } } class MyHashSet extends HashSet { private int sum = 0; public boolean add(Object arg0) { sum++; return super.add(arg0); } public boolean addAll(Collection arg0) { sum += arg0.size(); return super.addAll(arg0); } public int getSum() { return sum; } }
由於HashSet的addAll會調用add實現,因此在addAll中,sum加了3,而且有調用了add三次。 設計
從這個例子能夠看出,繼承時,子類的行爲還要受超類的實現的影響。當一個程序員同時維護超類和子類時,這不會有什麼問題,可是實現的子類依賴第三方發佈包的時候呢?
實現超類時,構造函數中若是有調用其餘方法,那麼這些方法建議聲明成final,防止子類改寫。
接口優於抽象類,接口只是被用於定義類型。
常量接口模式是對接口的不良使用,接口應該只是被用來定義類型的,它們不該該用來導出常量。若是要導出常量時,應該用一個不可實例化的工具類:
public class Constants { private Constants() { } public static final int A = 1; public static final int B = 2; public static final int C = 3; public static final int D = 4; }
嵌套類有四種:靜態成員類、非靜態成員類、匿名類和局部類。
以下代碼展現了靜態成員類和非靜態成員類的使用。
若是你聲明的成員類不要求訪問外圍實例,那麼請記住把static修飾符放到成員類聲明中。不然,每一個實例都將包含一個額外的指向外圍對象的引用,維護這份引用一消耗時間和空間,但又沒有相應的好處。
public class Test { public static void main(String args[]) { Human mike = new Human("mike"); Human.Cup mikeCup = mike.new Cup(); // 非static的成員類,實例化時要依賴於外部類的實例。 System.out.println(mikeCup.whoseCup()); Human.Car joyCar = new Human.Car("joy"); // static的成員類,實例化時不依賴於外部類的實例。 System.out.println(joyCar.whoseCar()); } } class Human { private String name; public Human(String name) { this.setName(name); } public String getName() { return name; } public void setName(String name) { this.name = name; } public class Cup { String whoseCup() { return getName(); // ok } } public static class Car { private String ownerName; Car(String name) { this.ownerName = name; } String whoseCar() { // return getName(); //error...staitc的靜態類不能訪問外部類的非static成員。 return this.ownerName; } } }
而局部類和匿名類是針對方法而言的,若是你只須要在一個地方建立它的實例,而且已經有了一個預先存在的類型能夠說明這個類的特徵,則把它作成匿名類,不然就作成局部類。
以下代碼展現了匿名類的兩個經常使用用法:
import java.util.Arrays; import java.util.Comparator; public class Test { public static void main(String[] args) { new Thread(new Runnable() { public void run() { int i = 0; while (true) { i++; System.out.println("thread i = " + i); if (100 == i) { break; } } } }).start(); Integer[] lst = { new Integer(5), new Integer(3), new Integer(2), new Integer(1) }; Arrays.sort(lst, new Comparator() { public int compare(Object x, Object y) { return ((Integer) x).intValue() - ((Integer) y).intValue(); } }); for (Integer i : lst) { System.out.println(i); } } }