對象的創(chuàng)建和銷毀
第一條 用靜態(tài)工廠方法來代替構造器
類除了可以通過構造器來實例化之外,還可以通過靜態(tài)的工廠方法(newInstance)
優(yōu)勢
1. 他們有名稱
比如一個Apple類摩幔,你想獲得一個紅蘋果和綠蘋果的實例榄檬,Apple.newRedInstance()
和Apple.newGreenInstance()
比Apple(red)
和Apple(green)
看上去要更能突出他們之間的區(qū)別
2. 不必在每次調(diào)用的時候都創(chuàng)建一個新對象
class Apple{
private Apple apple;
private Apple();
newInstance(){
return apple ==null ?new Apple : apple;
}
}
單例模式的簡單寫法,單例模式的有各種好處,比如不用重復創(chuàng)建對象,可以用==來代替equals方法等等之類的我就不多說神馬了。
3. 可以返回原返回類型的任何子類型對象
這種時候我們在返回對象的類時就有了更大的靈活性寝姿,甚至于我們在編寫該靜態(tài)方法的時候這個類是可以不存在的。書上管這個叫服務提供者框架(Service Provicer Framework)划滋。 例如JDBC 的API就是一個很好的例子饵筑。Connection是他的服務接口,DriverManager.registerDriver 是提供者的API处坪,DriverManager.getConnection是服務訪問的API根资,Driver就是服務提供者接口。適配器模式也是這種框架的一種變體同窘。
// 服務提供者框架簡易Demo
// 服務類的接口
public interface Service{
// 具體需要實現(xiàn)的服務方法
}
// 服務提供者的接口
public interface Provider{
Service newService();
}
// Service 注冊和調(diào)用的類
public class Services{
private Services(); // 防止實例化
// Service 名字跟服務的Map
private static final Map<String,Provider> providers = new ConcurrentHashMap<String , Provider>( );
public static final String DEFAULT_PROVIDER_NAME = "<dev>";
// 服務提供者注冊的API
public static void registerDefaultProvider(Provider p){
registerProvider(DEFAULT_PROVIDER_NAME ,p);
}
public static void registerDefaultProvider(String name ,Provider p){
providers.put(name ,p);
}
// 獲取服務接口
public static Service newInstance(){
return newInstance(DEFAULT_PROVIDER_NAME);
}
publicstatic Service newInstance(String name ){
Provider p = providers.get(name);
if(p== null)
throw new IllegalArgumentException("這個服務者沒有注冊");
return p.newService();
}
適配器模式的簡單例子玄帕。
4. 實例化參數(shù)類型時更簡單
比如
Map<String,List<String>> m = new HashMap<String,List<String>>();
隨著參數(shù)越來越長,越來越復雜想邦,代碼就越來越丑了裤纹。
這個時候如果有這個方法
public static <K,V> HashMap<K,V> newInstance(){
return new HashMap<K,V>();
}
那我們只要這樣寫就好了Map<String,List<String>> m = HshMap.newInstance();
,代碼就簡潔了好多。目前(jdk 1.8.0_73)官方并沒有實現(xiàn)這樣的方法丧没,可以放在自己的工具類中體現(xiàn)自己的逼格鹰椒。
缺點
- 如果類不含public 或者protect 的構造方法則不能被實例化
是缺點也是優(yōu)點,關鍵看你腫么用吧呕童。漆际。。 - 不能在java doc中有什么體現(xiàn)拉庵。
java doc認識構造方法灿椅,會重點標識出來套蒂,但是靜態(tài)方法不會钞支,所以用起來找api會麻煩。
第二條 當構造方法有多個可選參數(shù)的時候考慮用構造器
當實例化一個類有很多可選參數(shù)的時候操刀,有如下的幾種方法
創(chuàng)建多個構造器
優(yōu)點: 能用
缺點:寫起來容易錯烁挟,參數(shù)的順序啊神馬的記起來容易錯
用JavaBean 的getter 和setter 模式
優(yōu)點:寫的時候不容易出錯
缺點:線程不安全
使用構造器模式
優(yōu)點:使代碼簡單好些,而且看上去很有逼格
缺點:增加了一些開銷
拿一個蘋果類舉例
class Apple{
private String color;
private String size;
private String weight;
private String price;
public static class Builder{
private String color;
private String size;
private String weight;
public Builder color(String color){
this.color = color;
return this;
}
public Builder size(String size){
this.size = size;
return this;
}
public Builder weight(String weight){
this.weight = weight;
return this;
}
public Apple bulid(){
return new Apple(this);
}
}
private Apple(Builder bulider){
this.color = bulider.color;
this.size = bulider.size;
this.weight = bulider.weight;
};
}
然后就可以通過下面的方式來實例化一個蘋果
Apple apple = new Apple.Builder().color("red").size("100").weight("100").bulid();
這樣逼格滿滿的代碼就出來了骨坑。在可選參數(shù)很多的時候選擇這種寫法是種很不錯的選擇撼嗓。
第三條 單例的三種寫法和注意事項
原翻譯 使用枚舉增強類的單例屬性
單例模式的各種好處就不說柬采,這條講了單例的三種寫法和注意事項
寫法一
class Apple{
public static Apple INSTANCE = new Apple();
// someThing
private Apple(){
};
}
寫法二
class Apple{
private static Apple INSTANCE = new Apple();
// someThing
private Apple(){
};
public static Apple newInstance(){
return INSTANCE;
}
}
*** 注意事項 ***
AccessibleObject.setAccessible
可以通過反射調(diào)用私有的構造方法。在構造器加上第二次實例化拋異常的邏輯可以防范這種攻擊且警。如果單例的類為了可序列化而繼承了
Serializable
接口粉捻,那么為了防止類在反序列化的時候被偷偷的實例化,可以在類中加入如下代碼
private Object readResolve() {
return INSTANCE;
}
寫法三 使用枚舉實現(xiàn)單例
enum Apple{
INSTANCE;
}
- 優(yōu)點
簡單明了斑芜,不僅能防止因為反序列而重新創(chuàng)建對象肩刃,又是線程安全的,簡直逼格滿滿 - 缺點
這是JDK1.5之后的新特性杏头,這么寫可能有些人會看不懂(怪我咯j╮(╯_╰)╭)盈包。
第4條 通過私有化構造器使單例類更加單例
有些工具類我們可能不希望它被實例化,可以使用如下代碼
public class AppleUtils{
private AppleUtils(){
throw new AssertionError();
}
}
這樣一來醇王,AppleUtils(和他的子類)就不能被實例化了呢燥。
第5條 避免創(chuàng)建不必要的對象
對象么能少創(chuàng)建就少創(chuàng)建,這條主要講了如何更少的創(chuàng)建對象
- 比如不要寫
String s = new String("Don't do this");
這樣的代碼(因為這里創(chuàng)建了兩個String 對象) - 對于有靜態(tài)工廠方法和構造器的不可變類寓娩,盡量使用靜態(tài)工廠方法
- 對象能重用的就盡量重用
明顯能重用那肯定是要重用的叛氨,有些不明顯的比如像Map
接口的KeySet
方法每次返回的Set
實例這種也是考慮用一個對象減少實例化的 - 使用基本數(shù)據(jù)類型時防止程序自從裝箱
例如如下代碼
Long sum =0L;
for (int i = 0; i < Integer.MAX_VALUE; i++) {
sum +=i;
}
System.out.println(sum);
這段代碼在我機器上的運行時間為
但是如果把Long sum 換成long sum
可以看到時間從6886ms變成了781ms,效率大大的優(yōu)化了。
第六條 避免內(nèi)存泄露的幾點建議
原翻譯:消除過期的對象引用
- 警惕自己管理內(nèi)存的類
- 警惕讓不用的對象留在緩存中
wakHashMap 和LinkedHashMap 可以一定程度解決這個問題 - 警惕監(jiān)聽器和其他回調(diào)
最好只保存他們的若引用根暑,例如只將他們保存成WeakHashMap中的鍵
第七條 避免使用終結(finalizer)方法
- finalizer方法并不能保證被及時的執(zhí)行
從對象不可到達到它的finalizer方法被執(zhí)行力试,其中的這段時間是不好估計的。 - finalizer方法并不能保證被正確的執(zhí)行
不同的JVM實現(xiàn)中finalizer的效果是不同的排嫌。 - finalizer方法并不能保證被執(zhí)行
當一個程序被終止的時候畸裳,某些已經(jīng)無法訪問的對象上的終結方法沒有被執(zhí)行也是有可能的。 - finalizer 有一個非常嚴重的性能損失
Effective Java的作者說他試了下淳地,真的有很嚴重的性能損失怖糊。
所以最好有顯示的終止方法,向InputStream
颇象、OutputStream``的
close方法伍伤,
java.util.Timer中的
cancel```方法之類的。
總結
這一章主要講了對象的創(chuàng)建和銷毀時候需要注意的一些點遣钳。有些代碼真的是使人豁然開朗扰魂,瞬間逼格滿滿。期待第二章的學習蕴茴,F(xiàn)ighting~