如何正確的創(chuàng)建和銷毀 Java 對象?
點擊關(guān)注公眾號,Java干貨及時送達
一、介紹
Java由Sun Microsystems發(fā)明并在1995年發(fā)布,是世界上使用最廣泛的編程語言之一。Java是一個通用編程語言。由于它擁有功能強大的庫、運行時、簡單的語法、平臺無關(guān)(Write Once, Run Anywhere - WORA)以及令人敬畏的社區(qū)從而吸引了很多的開發(fā)者。
本系列文章我們我們將會覆蓋一些高級的Java概念,我們假設(shè)你對Java語言已經(jīng)有一些基礎(chǔ)知識。本系列文章并不是一個完整的參考,而是一個將您的Java技能提升到下一個級別的詳細指南。
本系列文章中將會看到一些代碼片段,在這些代碼片段里面將會使用java 7的語法以及java 8的語法。另外,Java 8+?系列教程全部整理好了,微信搜索Java技術(shù)棧在菜單欄閱讀。
二、實例構(gòu)造(Instance Construction)
Java是面向?qū)ο蟮木幊陶Z言,所以新實例(objects)的創(chuàng)建可能是它最重要的概念之一。在新的類實例中構(gòu)造器(Constructors)扮演了非常核心的角色,Java對于構(gòu)造器(Constructors)的定義提供了很多方案。
2.1 隱式(implicitly)構(gòu)造器
Java允許定義無任何構(gòu)造器的類,但是這并不意味著此類沒有構(gòu)造器。比如說,讓我們看一下下面這個類。
package?com.javacodegeeks.advanced.construction;
public?class?NoConstructor?{
}
此類沒有構(gòu)造器,但是Java編譯器會隱式地(implicitly)生成一個構(gòu)造器并且在使用new關(guān)鍵字創(chuàng)建新的類實例時會被調(diào)用。
final?NoConstructor?noConstructorInstance?=?new?NoConstructor();
2.2 無參構(gòu)造器(Constructors without Arguments)
無參構(gòu)造器是顯式執(zhí)行Java編譯器工作的最簡單的方法。
package?com.javacodegeeks.advanced.construction;
public?class?NoArgConstructor?{
????public?NoArgConstructor()?{
????????//?Constructor?body?here
????}
}
在使用new關(guān)鍵字創(chuàng)建此類的新實例時會此構(gòu)造器將會被調(diào)用。
final?NoArgConstructor?noArgConstructor?=?new?NoArgConstructor();
2.3 有參構(gòu)造器(Constructors with Arguments)
有參構(gòu)造器是參數(shù)化創(chuàng)建類實例的一個非常有意思和有用的方法。下面這個類定義了一個具有兩個參數(shù)的構(gòu)造器。
package?com.javacodegeeks.advanced.construction;
public?class?ConstructorWithArguments?{
????public?ConstructorWithArguments(final?String?arg1,final?String?arg2)?{
????????//?Constructor?body?here
????}
}
在這種情況下,當使用new關(guān)鍵字創(chuàng)建類實例時,兩個構(gòu)造參數(shù)都必須提供。
final?ConstructorWithArguments?constructorWithArguments?=?new?ConstructorWithArguments(?"arg1",?"arg2"?);
非常有意思的是,使用this關(guān)鍵字,構(gòu)造器之間可以相互調(diào)用。這種連接構(gòu)造函數(shù)的方式在作為減少代碼重復(fù)方面是一個非常好的實踐,并且從跟本上說這樣做可以讓一個類只有一個初始化入口點。接上例,我們添加一個只有一個參數(shù)的構(gòu)造器。
public?ConstructorWithArguments(final?String?arg1)?{
this(arg1,?null);
}
2.4 初始化塊(Initialization Blocks)
Java也提供了另外一種使用初始化塊的方式實現(xiàn)初始化邏輯。這個特性很少使用但是非常有必要了解一下它的存在。
package?com.javacodegeeks.advanced.construction;
????public?class?InitializationBlock?{
????{
????????//?initialization?code?here
????}
}
在某些情況下,初始化塊可以彌補匿名無參構(gòu)造器的缺陷。有一些特殊的類可能會有很多個初始化塊并且他們會依次按照他們在代碼中定義的順序被調(diào)用,比如:
package?com.javacodegeeks.advanced.construction;
public?class?InitializationBlocks?{
????{
????????//?initialization?code?here
????}?{
????????//?initialization?code?here
????}
}
初始化塊并不是替代構(gòu)造器并且他們可以獨立于構(gòu)造器而存在。但是需要提及的最重要的一點就是初始化塊會在任何構(gòu)造器被調(diào)用之前被執(zhí)行。
最新面試題整理好了,點擊Java面試庫小程序在線刷題。
package?com.javacodegeeks.advanced.construction;
public?class?InitializationBlockAndConstructor?{
????{
????????//?initialization?code?here
????}
????public?InitializationBlockAndConstructor()?{
????}
}
2.5 構(gòu)造保障(Construction guarantee)
Java提供了一些開發(fā)者所依賴的初始化保障,未初始化的實例和類參數(shù)會自動初始化為它們的默認值。

學(xué) Java 推薦這個實戰(zhàn)倉庫:https://github.com/javastacks/javastack
package?com.javacodegeeks.advanced.construction;
public?class?InitializationWithDefaults?{
????private?boolean?booleanMember;
????private?byte?byteMember;
????private?short?shortMember;
????private?int?intMember;
????private?long?longMember;
????private?char?charMember;
????private?float?floatMember;
????private?double?doubleMember;
????private?Object?referenceMember;
????public?InitializationWithDefaults()?{
????????System.out.println(?"booleanMember?=?"?+?booleanMember?);
????????System.out.println(?"byteMember?=?"?+?byteMember?);
????????System.out.println(?"shortMember?=?"?+?shortMember?);
????????System.out.println(?"intMember?=?"?+?intMember?);
????????System.out.println(?"longMember?=?"?+?longMember?);
????????System.out.println(?"charMember?=?"?+
????????Character.codePointAt(?new?char[]?{?charMember?},?0?)?);
????????System.out.println(?"floatMember?=?"?+?floatMember?);
????????System.out.println(?"doubleMember?=?"?+?doubleMember?);
????????System.out.println(?"referenceMember?=?"?+?referenceMember?);
????}
}
inal?InitializationWithDefaults?initializationWithDefaults?=?new?InitializationWithDefaults();
booleanMember?=?false
byteMember?=?0
shortMember?=?0
intMember?=?0
longMember?=?0
charMember?=?0
floatMember?=?0.0
doubleMember?=?0.0
referenceMember?=?null
2.6 可見性(Visibility)
構(gòu)造器受Java可見性規(guī)則約束并且可以擁有訪問控制修飾符來決定是否其他類可以調(diào)用特定的構(gòu)造函數(shù)。

2.7 垃圾回收(Garbage collection)
Java(特別是JVM)使用自動垃圾回收機制。簡而言之,當新對象被創(chuàng)建,JVM就會自動為這些新創(chuàng)建的對象分配內(nèi)存。于是,當這些對象沒有任何引用的時候,他們就會被銷毀并且他們所占用的內(nèi)存就會被回收。
Java垃圾回收是分代的,基于這種假設(shè)(分代假設(shè))大多數(shù)的對象在很年輕的時候就已經(jīng)不可達(在他們被創(chuàng)建之后的很短的時間內(nèi)就沒有任何引用并且被安全銷毀)。大多數(shù)開發(fā)者曾經(jīng)相信在Java中創(chuàng)建對象是很慢的并且應(yīng)該盡可能地避免新對象的實例化。
實際上,這并不成立:在Java中創(chuàng)建對象的開銷非常的小并且很快。雖然如此,但是沒有必要創(chuàng)建生命周期比較長的對象,因為創(chuàng)建過多的長壽命對象最終可能會填滿老年代空間從而引發(fā)stop-the-world的垃圾回收,這樣的話開銷就會比較大。
點擊關(guān)注公眾號,Java干貨及時送達
2.8 終結(jié)器(Finalizers)
到目前為止,我們已經(jīng)談到了構(gòu)造函數(shù)和對象初始化,但實際上并沒有提到任何關(guān)于對象銷毀的內(nèi)容。這是因為Java使用垃圾收集器去管理對象的生命周期,并且垃圾收集器的責任就是去銷毀無用對象并回收這些對象占用的內(nèi)存。
然而,在Java中有一個被稱為終結(jié)器(Finalizers)的特殊特性,它有點類似于析構(gòu)函數(shù),但是在執(zhí)行資源清理時它所解決的是不同的意圖。終結(jié)器(Finalizers)是被考慮用來解決一些危險的特征(比如會導(dǎo)致無數(shù)的副作用和性能問題的問題)。
一般來說,他們是沒有必要的,應(yīng)該避免(除了非常罕見的情況下,主要是有關(guān)本地對象)。Java 7語言引入了一種名為try-with-resources的更好的替代方法和AutoCloseable接口,它允許像如下的方式這樣干凈的寫代碼:
try?(?final?InputStream?in?=?Files.newInputStream(?path?)?)?{
????//?code?here
}
3、靜態(tài)初始化(Static initialization)
到目前為止,,我們已經(jīng)談到了構(gòu)造函數(shù)和對象初始化。但是Java也支持類級別的初始化構(gòu)造,我們稱之為靜態(tài)初始化(Static initialization)。
靜態(tài)初始化(Static initialization)有點類似于初始化塊,除了需要添加static關(guān)鍵字之外。注意靜態(tài)初始化在每次類加載的時候它只執(zhí)行一次。比如:
package?com.javacodegeeks.advanced.construction;
public?class?StaticInitializationBlock?{
????static?{
????????//?static?initialization?code?here
????}
}
和初始化塊類似,在類定義時你可以包含任意數(shù)量的初始化塊,它們會根據(jù)在類代碼中出現(xiàn)的順序依次執(zhí)行,比如:
package?com.javacodegeeks.advanced.construction;
public?class?StaticInitializationBlocks?{
????static?{
????????//?static?initialization?code?here
????}
????static?{
????????//?static?initialization?code?here
????}
}
因為靜態(tài)初始化(Static initialization)塊可以從多個并行線程中觸發(fā)(第一次類加載發(fā)生),Java運行時保證在線程安全的前提下僅僅被執(zhí)行一次。另外,多線程系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺發(fā)送:面試,可以在線閱讀。
4、構(gòu)造模式(Construction Patterns)
過去這幾年很多易于理解和廣泛應(yīng)用的構(gòu)造模式在Java社區(qū)出現(xiàn)。我們將會介紹幾個比較常用的:單例模式(singleton)、幫助器(helpers)、工廠模式(factory)、依賴注入(dependency injection )——大家熟知的控制反轉(zhuǎn)(inversion of control)。
4.1 單例模式(Singleton)
單例模式是軟件開發(fā)者社區(qū)中最老也是最具爭議性的模式之一?;緛碚f,它的主要思想就是確保在任何時候類僅僅只有一個實例被創(chuàng)建。思想就是如此簡單,然而單例模式引發(fā)了很多關(guān)于如何使之正確的討論,特別是線程安全的討論。下面是單例模式原生版本的例子:
package?com.javacodegeeks.advanced.construction.patterns;
public?class?NaiveSingleton?{
????private?static?NaiveSingleton?instance;
????private?NaiveSingleton()?{
????}
????public?static?NaiveSingleton?getInstance()?{
????????if(?instance?==?null?)?{
????????????instance?=?new?NaiveSingleton();
????????}
????????return?instance;
????}
}
這段代碼至少有一個問題就是如果多個線程同時調(diào)用,那么此類就能夠創(chuàng)建多個實例。設(shè)計合適的單例模式的方法之一是使用類的 static final屬性。
final?property?of?the?class.
package?com.javacodegeeks.advanced.construction.patterns;
public?class?EagerSingleton?{
????private?static?final?EagerSingleton?instance?=?new?EagerSingleton();
????private?EagerSingleton()?{
????}
????public?static?EagerSingleton?getInstance()?{
????????return?instance;
????}
}
如果你不想浪費資源并且希望在單例對象真正需要的時候才被延遲創(chuàng)建的話,這就要求顯示同步了(explicit synchronization),這就有可能導(dǎo)致多線程環(huán)境中的并發(fā)性降低(關(guān)于并發(fā)的詳細內(nèi)容我們將會在后續(xù)的文章中討論)。
package?com.javacodegeeks.advanced.construction.patterns;
public?class?LazySingleton?{
private?static?LazySingleton?instance;
????private?LazySingleton()?{
????}
????public?static?synchronized?LazySingleton?getInstance()?{
????????if(?instance?==?null?)?{
????????????instance?=?new?LazySingleton();
????????}
????????return?instance;
????}
}
如今,在大多數(shù)的案例中單例模式并不被考慮作為一個很好的選擇,主要是因為單例模式將會導(dǎo)致代碼很難測試。依賴注入模式讓單例模式變得沒有必要。
4.2 Utility/Helper類
utility或者helper類是被許多開發(fā)者所使用的相當流行的一種模式?;緛碚f,它所代表的是無實例( non-instantiable)類(構(gòu)造器被定義成private),僅僅可以選擇將方法定義成final(后續(xù)會介紹如何定義類)或者static。比如;
package?com.javacodegeeks.advanced.construction.patterns;
public?final?class?HelperClass?{
????private?HelperClass()?{
????}
????public?static?void?helperMethod1()?{
????????//?Method?body?here
????}
????public?static?void?helperMethod2()?{
????????//?Method?body?here
????}
}
站在開發(fā)者的角度,helpers類經(jīng)常所扮演的是一個容器的角色,這個容器中放了很多在其他地方找不到但是其他類需要相互共享和使用的互相不相關(guān)的方法。這種設(shè)計決定了在很多情況下要避免使用:總能找到另一種重用所需功能的方式,保持代碼的簡潔和清晰。
4.3 工廠模式(Factory)
工廠模式被證明是軟件開發(fā)人員手中非常有用的技術(shù)。因此,Java有幾種風格工廠模式,從工廠方法到抽象工廠。工廠模式最簡單的例子是返回特定類的新實例的靜態(tài)方法(工廠方法)。
另外,Java 設(shè)計模式系列面試題和答案全部整理好了,微信搜索Java技術(shù)棧,在后臺發(fā)送:面試,可以在線閱讀。
例如:
package?com.javacodegeeks.advanced.construction.patterns;
public?class?Book?{
????private?Book(?final?String?title)?{
????}
????public?static?Book?newBook(?final?String?title?)?{
????????return?new?Book(?title?);
????}
}
有人可能會爭辯說,介紹newBook工廠方法并沒有什么意義,但是使用這種模式通常會使代碼更具可讀性。工廠模式的另一個變化涉及接口或抽象類(抽象工廠)。例如,讓我們定義一個工廠接口:
public?interface?BookFactory?{
????Book?newBook();
}
依賴庫類型,完成幾種不同的實現(xiàn):
public?class?Library?implements?BookFactory?{
????@Override
????public?Book?newBook()?{
????????return?new?PaperBook();
????}
}
public?class?KindleLibrary?implements?BookFactory?{
@Override
????public?Book?newBook()?{
????????return?new?KindleBook();
????}
}
現(xiàn)在,Book的特定類被隱藏在BookFactory接口實現(xiàn)之后,BookFactory仍然提供創(chuàng)建book的通用方式。
4.4 依賴注入(Dependency Injection)
package?com.javacodegeeks.advanced.construction.patterns;
import?java.text.DateFormat;
import?java.util.Date;
public?class?Dependant?{
????private?final?DateFormat?format?=?DateFormat.getDateInstance();
????public?String?format(?final?Date?date?)?{
????????return?format.format(?date?);
????}
}
package?com.javacodegeeks.advanced.construction.patterns;
import?java.text.DateFormat;
import?java.util.Date;
public?class?Dependant?{
????private?final?DateFormat?format;
????public?Dependant(?final?DateFormat?format?)?{
????????this.format?=?format;
????}
????public?String?format(?final?Date?date?)?{
????????return?format.format(?date?);
????}
}
按這種方案實現(xiàn)的話,類的所有依賴都是通過外部提供,這樣就很容易的修改date format和為類寫測試用例。
在本系列文章的這一部分中,我們一直在研究類和類的實例構(gòu)造以及初始化技術(shù),涵蓋了幾種廣泛使用的模式。另外,關(guān)注公眾號Java技術(shù)棧,在后臺回復(fù):面試,可以獲取我整理的 Java 系列面試題和答案,非常齊全。
原文鏈接:https://blog.csdn.net/zyhlwzy/article/details/78937421
版權(quán)聲明:本文為CSDN博主「RonTech」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

關(guān)注Java技術(shù)??锤喔韶?/strong>


