Upload
kane-shih
View
89
Download
1
Embed Size (px)
Citation preview
2015/03/10 讀書會分享
Effective Java摘選條目分享 1
- 物件、複合、可變性、Leak
Kane
關於 Effective Java 一書
Joshua Bloch - Google的首席Java架構師
學習語言:語法、詞彙、用法
進階讀物
條列式
報告大綱
物件 - 創建及銷毀 (ch.2)
複合 - 是否真的需要繼承? (p.34 & no.16)
可變性 (no.15)
Member Class 注意事項 (no.22)
You should already know ...
class
instance
reference
gc
You should already know ...
class
instance
reference
gc
藍圖
蓋出來的房子
門牌號碼、地址
拆房子、垃圾回收
創建及銷毀 instance1. Static Factory
2. Builder
3. Singleton
4. 不必要的創建
5. 過期的引用
Static Factory
1. 名稱例:Bitmap.createBitmap(...), Toast.makeText(...)
2. 管理例:Flyweight pattern (不用每次都創新的 instance)
3. 彈性可以返回 sub-class instance
Builder
管理建構參數
new AlertDialog.Builder(context) .setTitle("Exit") .setMessage("Are you sure?") .setPositiveButton("Yes", onUserExitListener) .setNegativeButton("No", null) .show();
Singleton
三種方法:
1. Field - public static final
2. Static Factory - getInstance()
3. Enum ← 推薦
不必要的創建 1/2class Person { Date birthDate; boolean isBabyBoomer() { 創建 Calendar instance 設定為 1946/1/1,再取得 Date instance 設定為 1965/1/1,再取得 Date instance 比較 birthDate 是否落於上面兩者之間
}} 1. 每次被調用時,都要重複做,而且一模一樣
2. 創建 Calendar instance 的代價
不必要的創建 2/2Long sum = 0L;for (long i = 0; i < Integer.MAX_VALUE; i++) { sum += i;} 1. autoboxing:創建 2^31個 Long instance!
2. 基本類型優先於裝箱基本類型
Where’s leak?class Stack { Object[] elements; int size = 0; .... push(Object e) { ensureCapacity(); elements[size++] = e; } pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; }}
過期的引用
class Stack { Object[] elements; int size = 0; .... push(Object e) { ensureCapacity(); elements[size++] = e; } pop() { if (size == 0) throw new EmptyStackException(); return elements[--size]; }}
reference 仍然指著 instance,
gc 時不會被清除,
但意義上,pop 後應該脫離 stack
無意識的對象保持
1. 自己管理的物件
結束使用時、考慮被別人取用的情況
2. 別人管理的物件
該 instance 的生命週期之決定權?
3. Callback 物件
非同步調用
Android開發中常見
● Static field
● Activity(Context)
● Handler
● Callback
● 生命週期概念混淆 (onCreate/onDestroy vs new/gc)
複合優先於繼承
用 has-A 取代 is-A
更彈性、減少束縛
考慮以下需求
想要監控物件放入 set 的次數,怎麼做?
繼承 set,監控所有 addclass InstrumentedHashSet<E> extends HashSet<E> {
add(E e) { } addAll(Collection<? extends E> c) { }}
繼承 set,監控所有 addclass InstrumentedHashSet<E> extends HashSet<E> { addCount = 0; // 計算 add 的次數
add(E e) { addCount++; } addAll(Collection<? extends E> c) { addCount += c.size(); }}
繼承 set,監控所有 addclass InstrumentedHashSet<E> extends HashSet<E> { addCount = 0; // 計算 add 的次數
add(E e) { addCount++; return super.add(e); } addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); }}
實際跑看看
class InstrumentedHashSet<E> extends HashSet<E> { addCount = 0; // 計算 add 的次數 add(E e) { addCount++; return super.add(e); } addAll(Collection<? extends E> c) { addCount += c.size(); return super.addAll(c); }}
Set<String> set = new InstrumentedHashSet<String>();
List<String> list = Arrays.asList(“a”, “b”, “c”);
set.addAll(list);
int count = ((InstrumentedHashSet) list).getAddCount();
出乎預料
執行結果為 6 !
繼承一個類別,便會產生依賴
父類別有可能改版,子類別變得不穩定!
計數功能:本質上只是一個實現細節,不是HashSet的一部分
真的需要繼承嗎?
HashSet addAll() 呼叫了自己的 add()
以複合(has-a)取代繼承(is-a)class ForwardingSet<E> { addCount = 0; // 計算 add 的次數
Set<E> set; // 實際存放的容器
add(E e) { addCount++; return set.add(e); } addAll(Collection<? extends E> c) { addCount += c.size(); return set.addAll(c); }}
不論 set 裡面怎麼實作,
都不影響 addCount
可變性最小化 1/2不可變 immutable
例子:String, BigInteger等
優點:自由共享、不易誤用(繼承、thread-safe等)
缺點:不同的值(狀態)就需要不同的instance
配套類(companing class):例 StringBuilder, BitSet 等
可變性最小化 2/2作法:
1. 不提供 mutator2. final class3. final field4. private field5. 確保可變組件無法被使用(access)
a. 必要時須用 defensive copy
兩種 Member class
A a = new A();B b = a.new B(); // 需要 a
C c = new C();
A 是 enclosing classclass A { class B { ... } static class C { ... } ...}
b 需要 a 的狀態
c 不需要 a 的狀態
隱含的 referenceclass A { class B { ... }}
class Client { void main() { A a = new A(); B b = a.new B(); a = null; ... System.gc(); ... }}
原本 a 指向的 instance 不會被回收!
B 為 nonstatic,b 隱含 a 的 reference。
優先考慮 Static Member Class
除非需要 enclosing class 的 instance 的狀態
Q & A