《Effective Java讀書筆記》--類和接口

使類和成員的可訪問能力最小化

應該儘量使每個類或成員不被外界訪問。
若是一個包級私有的頂層類或者接口只是在某一個類的內部被調用到,應該考慮使用嵌套類實現。
確保public staitc final所引用的對象是不可變的。
public static final的數組幾乎老是錯誤的:
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。 安全

讓一個非可變類不能被繼承有三種方法: 函數

  • 將類聲明成final
  • 將類的每個方法聲明成final
  • 將構造函數聲明成private,另提供一個靜態工廠方法。(推薦)

爲何非可變類要設計成不能繼承了,能夠看一下這個討論: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);
		}
	}
}
相關文章
相關標籤/搜索