(1)Java語言支持四種類型:接口、類、數組、基本類型(primitive),前三種爲引用類型,而基本類型的值不是對象。java
(2)方法的簽名包括它的名稱和全部參數的類型,簽名不包括它的返回類型。sql
(1)靜態工廠有名稱數據庫
(2)沒必要再每次調用都建立一個新的對象數組
public static Boolean valueOf(boolean b){ return b? Boolean.TRUE : Boolean.FALSE; }
(3)能夠返回原返回類型的任何子類對象緩存
適用於返回類型爲接口/抽象類安全
public interface Service { // Service-specific methods go here } public interface Provider { Service newService(); } public class Services { private Services() { } // Prevents instantiation (Item 4) // Maps service names to services private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>(); public static final String DEFAULT_PROVIDER_NAME = "<def>"; // Provider registration API public static void registerDefaultProvider(Provider p) { registerProvider(DEFAULT_PROVIDER_NAME, p); } public static void registerProvider(String name, Provider p){ providers.put(name, p); } // Service access API public static Service newInstance() { return newInstance(DEFAULT_PROVIDER_NAME); } public static Service newInstance(String name) { Provider p = providers.get(name); if (p == null) throw new IllegalArgumentException( "No provider registered with name: " + name); return p.newService(); } }
客戶端
ide
public class Test { public static void main(String[] args) { // Providers would execute these lines Services.registerDefaultProvider(DEFAULT_PROVIDER); Services.registerProvider("comp", COMP_PROVIDER); Services.registerProvider("armed", ARMED_PROVIDER); // Clients would execute these lines Service s1 = Services.newInstance(); Service s2 = Services.newInstance("comp"); Service s3 = Services.newInstance("armed"); System.out.printf("%s, %s, %s%n", s1, s2, s3); } private static Provider DEFAULT_PROVIDER = new Provider() { public Service newService() { return new Service() { @Override public String toString() { return "Default service"; } }; } }; private static Provider COMP_PROVIDER = new Provider() { public Service newService() { return new Service() { @Override public String toString() { return "Complementary service"; } }; } }; private static Provider ARMED_PROVIDER = new Provider() { public Service newService() { return new Service() { @Override public String toString() { return "Armed service"; } }; } }; }
(4)建立參數化實例更簡潔性能
public static <K,V> HashMap<K,V> newInstance(){ return new HashMap<K,V)(); }
(5)靜態工廠慣用名稱ui
valueOf,of,getInstance,newInstance,getType,newType
構造器的參數太多的話,調用方容易混淆,特別是針對相鄰的同類型的參數,若是不當心將順序顛倒,則編譯時難以發現,運行時出問題。this
(1) 重疊構造器版本,好處是安全性,壞處是難以閱讀
public class NutritionFacts { private final int servingSize; // (mL) required private final int servings; // (per container) required private final int calories; // optional private final int fat; // (g) optional private final int sodium; // (mg) optional private final int carbohydrate; // (g) optional public NutritionFacts(int servingSize, int servings) { this(servingSize, servings, 0); } public NutritionFacts(int servingSize, int servings, int calories) { this(servingSize, servings, calories, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat) { this(servingSize, servings, calories, fat, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) { this(servingSize, servings, calories, fat, sodium, 0); } public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) { this.servingSize = servingSize; this.servings = servings; this.calories = calories; this.fat = fat; this.sodium = sodium; this.carbohydrate = carbohydrate; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 0, 35, 27); } }
(2)JavaBean版本,好處是可讀性,壞處是本身要保證線程安全
public class NutritionFacts { // Parameters initialized to default values (if any) private int servingSize = -1; // Required; no default value private int servings = -1; // " " " " private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public NutritionFacts() { } // Setters public void setServingSize(int val) { servingSize = val; } public void setServings(int val) { servings = val; } public void setCalories(int val) { calories = val; } public void setFat(int val) { fat = val; } public void setSodium(int val) { sodium = val; } public void setCarbohydrate(int val) { carbohydrate = val; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts(); cocaCola.setServingSize(240); cocaCola.setServings(8); cocaCola.setCalories(100); cocaCola.setSodium(35); cocaCola.setCarbohydrate(27); } }
(3)Builder版本,兼容兩者好處
public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public static class Builder { // Required parameters private final int servingSize; private final int servings; // Optional parameters - initialized to default values private int calories = 0; private int fat = 0; private int carbohydrate = 0; private int sodium = 0; public Builder(int servingSize, int servings) { this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories = val; return this; } public Builder fat(int val) { fat = val; return this; } public Builder carbohydrate(int val) { carbohydrate = val; return this; } public Builder sodium(int val) { sodium = val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } private NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } public static void main(String[] args) { NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8). calories(100).sodium(35).carbohydrate(27).build(); } }
newInstance方法可充當build方法的一部分,可是它老是企圖去調用類的無參構造器,若是類沒有無參構造器,編譯時是不會暴露出來的,只能到客戶端調用運行時才暴露。
(4)實踐版
通常好比sql構造之類的,參數太多,能夠用builder模式,普通bean的賦值,採用builder反而搞得太過負責,工業上的最佳實踐,通常是採用JavaBean + Design by Contract 的模式,要求開發者根據約定傳參數,service層再進行一層校驗,來確保必填參數是不爲null的。
(1)私有構造器可以防止經過反射調用去構造實例
(2)爲確保反序列化以後仍是單例,須要重寫readResolve方法,return INSTANCE,或者直接使用枚舉單例,它內置了反序列化單例的功能。
(1)達到儘可能重用對象的目的,好比字符串字面常量,不可變的對象。
反例
public class Person { private final Date birthDate; public Person(Date birthDate) { // Defensive copy - see Item 39 this.birthDate = new Date(birthDate.getTime()); } // Other fields, methods omitted // DON'T DO THIS! public boolean isBabyBoomer() { // Unnecessary allocation of expensive object Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); Date boomStart = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); Date boomEnd = gmtCal.getTime(); return birthDate.compareTo(boomStart) >= 0 && birthDate.compareTo(boomEnd) < 0; } }
正解:使用靜態初始化器,初始化起止日期
class Person { private final Date birthDate; public Person(Date birthDate) { // Defensive copy - see Item 39 this.birthDate = new Date(birthDate.getTime()); } // Other fields, methods /** * The starting and ending dates of the baby boom. */ private static final Date BOOM_START; private static final Date BOOM_END; static { Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0); BOOM_START = gmtCal.getTime(); gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0); BOOM_END = gmtCal.getTime(); } public boolean isBabyBoomer() { return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0; } }
(2)優先使用基本類型,而不是封裝類型,小心無心識的自動裝箱額外產生的對象,下面的sum聲明爲long,便可減小2的31次方個多餘的Long實例。
public class Sum { // Hideously slow program! Can you spot the object creation? public static void main(String[] args) { Long sum = 0L; for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i; } System.out.println(sum); } }
(3)對象建立的誤解
誤解:對象建立的代價很是昂貴,應該避免建立對象。
正解:小對象的建立,構造器只作不多量的顯式工做,其建立和回收都是很是廉價的,於是對小對象不必搞什麼對象池之類的;只有重量級的對象,好比數據庫鏈接池等,鏈接數據庫代價是昂貴的,採用對象池正好。
public class Stack { private Object[] elements; private int size = 0; private static final int DEFAULT_INITIAL_CAPACITY = 16; public Stack() { elements = new Object[DEFAULT_INITIAL_CAPACITY]; } public void push(Object e) { ensureCapacity(); elements[size++] = e; } public Object pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; } /** * Ensure space for at least one more element, roughly * doubling the capacity each time the array needs to grow. */ private void ensureCapacity() { if (elements.length == size) elements = Arrays.copyOf(elements, 2 * size + 1); } }
出棧的時候沒有清空引用
public Object pop() { if (size == 0) throw new EmptyStackException(); Object result = elements[--size]; elements[size] = null; return result; }
(1)清空對象引用應該是一種例外,而不是一種行爲規範,沒必要每次使用變量的時候,都緊張兮兮的,考慮要不要在使用後賦值爲null
(2)內存泄露的常見來源
A、本身管理內存的容器
B、緩存
C、監聽器和回調方法(註冊監聽,不須要的時候沒有取消監聽)
(1)JVM不保證finalize方法會被及時執行,並且根本不保證它們會被執行
(2)不要依賴finalize方法去關閉重要資源,好比關閉文件、關閉數據庫鏈接,通常採用finally語句便可
(3)使用finalize可能會形成額外的性能損失