java教學(xué)筆記之對象的創(chuàng)建與銷毀
本課程的目標(biāo)是幫你更有效的使用Java。其中討論了一些高級主題,包括對象的創(chuàng)建、并發(fā)、序列化、反射以及其他高級特性。本課程將為你的精通Java的旅程提供指導(dǎo)。
1. 引言
在TIOBE 編程語言排名中,Sun 公司于1995年開發(fā)的Java語言是世界上使用最廣泛的編程語言之一。作為一種通用編程語言,因為強大的工具包和運行時環(huán)境、簡單的語法、豐富的平臺支持(一次編寫,到處運行)以及的異?;钴S的社區(qū)支持,Java語言對軟件開發(fā)工程師極具吸引力。
在這一系列的文章中,涵蓋了Java相關(guān)的高級內(nèi)容,因此假設(shè)讀者已具有基本語言知識。這并不是一個完整的參考手冊,而是讓你的技能更上一層樓的詳盡指南。
本課程中包含了大量的代碼片段,在有些對方為了做對比,會同時提供Java 7和Java 8的示例。
2. 實例構(gòu)造
作為一種面向?qū)ο笳Z言,對象的創(chuàng)建也許就是Java語言中最重要的概念之一。構(gòu)造方法是在對象實例初始化過程中具有舉足輕重的地位,并且Java提供了多種方式來定義構(gòu)造方法。
2.1 隱式(產(chǎn)生的)構(gòu)造方法
Java允許在定義類時不聲明任何的構(gòu)造方法,并這并不代表類沒有構(gòu)造方法。我們看下面類的定義:
package com.javacodegeeks.advanced.construction; public class NoConstructor { }
這個類未定義構(gòu)造方法,但是Java編譯器會為其隱式生成一個,從而使我們可以使用new關(guān)鍵字來創(chuàng)建新的對象實例。
final NoConstructor noConstructorInstance = new NoConstructor();
2.2 無參構(gòu)造方法
無參構(gòu)造方法是最簡單的通過顯式聲明來替代Java編譯生成構(gòu)造方法的方式。
package com.javacodegeeks.advanced.construction; public class NoArgConstructor { public NoArgConstructor() { // Constructor body here } }
在使用new關(guān)鍵字創(chuàng)建新的對象實例時,上面的構(gòu)造方法就會被調(diào)用。
2.3 有參構(gòu)造方法
有參構(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 } }
這種場景中,當(dāng)使用new關(guān)鍵字來創(chuàng)建實例時,需要同時提供構(gòu)造方法上定義的兩個參數(shù)。
final ConstructorWithArguments constructorWithArguments = new ConstructorWithArguments( "arg1", "arg2" );
有趣的是構(gòu)造方法之間可以通過this關(guān)鍵字互相調(diào)用。在實踐中,推薦通過使用this把多個構(gòu)造方法鏈起來以減少代碼重復(fù),并從基礎(chǔ)上使對象具有單一的初始化入口。作為示例,下面的代碼中定義了只有一個參數(shù)的構(gòu)造方法。
public ConstructorWithArguments(final String arg1) { this(arg1, null); }
2.4 初始化代碼塊
除了構(gòu)造方法,Java還提供了通過初始化代碼塊進行初始化的邏輯。這種用法雖然少見,但多了解一些也沒害處。
package com.javacodegeeks.advanced.construction; public class InitializationBlock { { // initialization code here } }
另一方面,初始化代碼塊也可被看作是無參的隱式構(gòu)造方法。在一個具體的類中可以定義多個初始化代碼塊,在執(zhí)行的時候按照他們在代碼中的位置順序被調(diào)用,如下面的代碼所示:
package com.javacodegeeks.advanced.construction; public class InitializationBlocks { { // initialization code here } { // initialization code here } }
實始化代碼塊并不是為了取代構(gòu)造方法,相反它們可以同時出現(xiàn)。但是要記住,初始化代碼快會在構(gòu)造方法調(diào)用之前被執(zhí)行。
package com.javacodegeeks.advanced.construction; public class InitializationBlockAndConstructor { { // initialization code here } public InitializationBlockAndConstructor() { } }
2.5 保證構(gòu)造默認(rèn)值
Java提供了確定的初始化保證,程序員可以直接使用初始化結(jié)果。未初始化的實例以及類變量(static)會自動初始化為相應(yīng)的默認(rèn)值。
類型 默認(rèn)值
boolean False
byte 0
short 0
int 0
long 0L
char \u0000
float 0.0f
double 0.0d
對象引用 null
表 1
我們通過下面的例子來驗證上表中的默認(rèn)值:
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 ); } }
當(dāng)使用new關(guān)鍵字實例化對象之后:
final InitializationWithDefaults initializationWithDefaults = new InitializationWithDefaults(),
可從控制臺中看到輸出結(jié)果如下:
booleanMember = false byteMember = 0 shortMember = 0 intMember = 0 longMember = 0 charMember = 0 floatMember = 0.0 doubleMember = 0.0 referenceMember = null
2.6 可見性
構(gòu)造方法遵從Java的可見性規(guī)則,并且可以通過訪問控制修飾符決定在其他類中是否能調(diào)用該構(gòu)造方法。
修飾符 包可見性 子類可見性 公開可見性
public 可見 可見 可見
protected 可見 可見 不可見
<無修飾符> 可見 不可見 不可見
private 不可見 不可見 不可見
表2
2.7 垃圾回收
Java(準(zhǔn)確的說是JVM)擁有自動的垃圾回收機制。簡單來講,當(dāng)有新對象創(chuàng)建時,會自動為其分配內(nèi)在;然后當(dāng)對象不再被引用后,他們會被自動銷毀,相應(yīng)的內(nèi)存也會被回收。
Java垃圾回收采用分代回收的機制,并基于"大多數(shù)對象生命短暫"的假設(shè)(即在對象創(chuàng)建之后很快就不會被再引用,所以可以被安全的銷毀)。大多程序員習(xí)慣性的認(rèn)為Java中對象創(chuàng)建的效率很低所以要盡可能避免新對象的創(chuàng)建。事實上,這種認(rèn)識是不對的。在Java中創(chuàng)建對象的開銷是相當(dāng)?shù)偷?,并且速度很快。真正代來巨大開銷的是不必要的長期存活的對象,因此他們最終會被遷移到老年代,并導(dǎo)致stop-the-world發(fā)生。
2.8 對象終結(jié)器(Finalizers)
前面我們講述的都是構(gòu)造方法和對象初始化相關(guān)的主題,但還未提及他們的反面:對象銷毀。主要是因為Java使用垃圾回收機制來管理對象的生命周期,所以銷毀不必要的對象并釋放所需內(nèi)存就成了垃圾回收的職責(zé)了。
不過,Java還是提供了另外一種類似于析構(gòu)函數(shù)的終結(jié)器(finalizer)的特性,擔(dān)任多種資源清理的責(zé)任。Finalizer一般被看作是危險的事情(因為它會帶來多種副作用和性能問題)。通常并不需要finalizer因此要盡量避免使用它(除了極少見的包含大量本地對象(native objects)的場景)。Java 7中引入的try-with-resources語法和AutoCloseable接口可當(dāng)作finalizer的替代選擇,并可寫出如下簡潔的代碼:
try ( final InputStream in = Files.newInputStream( path ) ) { // code here }
3. 靜態(tài)初始化
上面我們學(xué)習(xí)了類實例的構(gòu)造與初始化,除此之外,Java還支持類級別的初始化構(gòu)造,稱作靜態(tài)初始化。靜態(tài)初始化與上面介紹的初始化代碼塊類似,只是多了額外的static關(guān)鍵字修飾。需要注意的是靜態(tài)初始化只會在類加載時執(zhí)行一次。示例如下:
與初始化代碼塊類似,可以在類中定義多個靜態(tài)初始化塊,它們在類中的位置決定在初始化時執(zhí)行的順序。示例如下;
package com.javacodegeeks.advanced.construction; public class StaticInitializationBlocks { static { // static initialization code here } static { // static initialization code here } }
因為靜態(tài)初始化塊可以被多個并行執(zhí)行的線程觸發(fā)(當(dāng)類被初始加載時),JVM運行時保證初始化的代碼以線程安全的方式只被執(zhí)行一次。
4. 構(gòu)造器模式
這些年多種容易理解的構(gòu)造器(創(chuàng)建者)模式被引入到Java社區(qū)。下面我們會學(xué)習(xí)其中比較流行的幾個:單例模式、輔助類模式、工廠模式以及依賴注入(也稱為控制反轉(zhuǎn))。
4.1 單例模式
單例是一種歷史悠久卻在軟件開發(fā)社區(qū)中飽受爭議的模式。單例模式的核心理念是保證在任何時候給定的類只有一個對象被創(chuàng)建。雖然聽起來很簡單,但人們對如何以正確且線程安全的方式創(chuàng)建對象進行了大量的討論。下面的代碼中展示了簡單版本的單例模式實現(xià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; } }
上面的代碼至少有一個問題:在多線程并發(fā)場景中可能會創(chuàng)建出多個對象。一種合理的實現(xiàn)方式(但不能延遲加載)是使用類的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)建,那么就要使用顯式的同步方式,不可這種方法可能會降低多線程環(huán)境下的并發(fā)性(更多關(guān)于Java并發(fā)的細(xì)節(jié)將會在Java進階9-并發(fā)最佳實踐中詳細(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; } }
現(xiàn)在,在很多場景下單例模式不再被認(rèn)為是一種好的選擇,因為他們會使代碼不易于測試。另外依賴注入模式的產(chǎn)生也使單例模式變得不再必要。
4.2 工具類/輔助類
工具類/輔助類模式在Java開發(fā)者當(dāng)中相當(dāng)流行。它的核心理念就是使用不可實例化的類(通過聲明private構(gòu)造方法)、可選的final(更多關(guān)于聲明final類的細(xì)節(jié)將會在Java進階3-類和接口的設(shè)計中詳細(xì)介紹)關(guān)鍵字以及靜態(tài)方法。示例如下:
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 } }
很多經(jīng)驗豐富的開發(fā)者認(rèn)為這種模式會讓工具類成為各種不相關(guān)方法的容器。因為有些方法沒有合適的放置位置卻需要被其他類使用,就會被誤放入工具類中。在大多數(shù)場景中也應(yīng)該避免這種設(shè)計:總會有更好的功能復(fù)用的方式,保持代碼清晰簡潔。
4.3 工廠模式
工廠模式被證明是開發(fā)者的極其強大的利器,在Java中有多種實現(xiàn)方式:工廠方法和抽象工廠。最簡單的例子就是使用static方法返回特定類的實例(工廠方法),如下:
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 ); } }
雖然使用這種方法能提高代碼的可讀性,但經(jīng)常爭議的一點是難以給newBook工廠方法賦予更豐富的場景。另外一種實現(xiàn)工廠模式的方法是采用接口或抽象類(抽象工廠)。如下,我們定義一個工廠接口:
public interface BookFactory { Book newBook(); }
根據(jù)圖片館的不同,我們可以有多種不同的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)在,BookFactory的不同實現(xiàn)屏蔽掉了具體Book的不同,卻提供了通用的newBook的方法。
4.4 依賴注入
依賴注入(也稱為控制反轉(zhuǎn))被類設(shè)計者認(rèn)為是一種良好的設(shè)計實踐:如果一些類實例依賴其他類的實例,那些被依賴的實例應(yīng)該通過構(gòu)造方法(或者setter方法、策略等方式)提供(注入),而不應(yīng)該是由實例自己去創(chuàng)建。先看一下下面的代碼:
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 ); } }
Dependant類需要一個DateFormat類的實例并通過在實例化對象時通過DateFormat.getDateInstance()的方式獲得。更好的方式應(yīng)該通過構(gòu)造方法的參數(shù)來完成同樣的事情:
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 ); } }
在上面的例子中,類實例的所有依賴都由外部提供,這樣就很容易調(diào)整DateFormat,并易于編寫測試代碼。
相關(guān)文章
java中的JsonSerializer用法,前后端單位轉(zhuǎn)換必備
這篇文章主要介紹了java中的JsonSerializer用法,前后端單位轉(zhuǎn)換必備!具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-10-10java高并發(fā)下CopyOnWriteArrayList替代ArrayList
這篇文章主要為大家介紹了java高并發(fā)下CopyOnWriteArrayList替代ArrayList的使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-12-12一文詳解Spring任務(wù)執(zhí)行和調(diào)度(小結(jié))
這篇文章主要介紹了一文詳解Spring任務(wù)執(zhí)行和調(diào)度(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-08-08Java利用SpEL表達(dá)式實現(xiàn)權(quán)限校驗
這篇文章主要為大家詳細(xì)介紹了Java如何利用SpEL表達(dá)式實現(xiàn)權(quán)限校驗功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2024-01-01Java transient關(guān)鍵字與序列化操作實例詳解
這篇文章主要介紹了Java transient關(guān)鍵字與序列化操作,結(jié)合實例形式詳細(xì)分析了java序列化操作相關(guān)實現(xiàn)方法與操作注意事項,需要的朋友可以參考下2019-09-09MybatisPlus多數(shù)據(jù)源及事務(wù)解決思路
這篇文章主要介紹了MybatisPlus多數(shù)據(jù)源及事務(wù)解決思路,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-01-01