您好,登錄后才能下訂單哦!
這篇文章主要介紹了Java怎么創建和銷毀對象的相關知識,內容詳細易懂,操作簡單快捷,具有一定借鑒價值,相信大家閱讀完這篇Java怎么創建和銷毀對象文章都會有所收獲,下面我們一起來看看吧。
Java由Sun Microsystems發明并在1995年發布,是世界上使用最廣泛的編程語言之一。Java是一個通用編程語言。由于它擁有功能強大的庫、運行時、簡單的語法、平臺無關(Write Once, Run Anywhere - WORA)以及令人敬畏的社區從而吸引了很多的開發者。
Java是面向對象的編程語言,所以新實例(objects)的創建可能是它最重要的概念之一。在新的類實例中構造器(Constructors)扮演了非常核心的角色,Java對于構造器(Constructors)的定義提供了很多方案。
Java允許定義無任何構造器的類,但是這并不意味著此類沒有構造器。比如說,讓我們看一下下面這個類。
package com.javacodegeeks.advanced.construction; public class NoConstructor { }
此類沒有構造器,但是Java編譯器會隱式地(implicitly)生成一個構造器并且在使用new關鍵字創建新的類實例時會被調用。
final NoConstructor noConstructorInstance = new NoConstructor();
無參構造器是顯式執行Java編譯器工作的最簡單的方法。
package com.javacodegeeks.advanced.construction; public class NoArgConstructor { public NoArgConstructor() { // Constructor body here } }
在使用new關鍵字創建此類的新實例時會此構造器將會被調用。
final NoArgConstructor noArgConstructor = new NoArgConstructor();
有參構造器是參數化創建類實例的一個非常有意思和有用的方法。下面這個類定義了一個具有兩個參數的構造器。
package com.javacodegeeks.advanced.construction; public class ConstructorWithArguments { public ConstructorWithArguments(final String arg1,final String arg2) { // Constructor body here } }
在這種情況下,當使用new關鍵字創建類實例時,兩個構造參數都必須提供。
final ConstructorWithArguments constructorWithArguments = new ConstructorWithArguments( "arg1", "arg2" );
非常有意思的是,使用this關鍵字,構造器之間可以相互調用。這種連接構造函數的方式在作為減少代碼重復方面是一個非常好的實踐,并且從跟本上說這樣做可以讓一個類只有一個初始化入口點。接上例,我們添加一個只有一個參數的構造器。
public ConstructorWithArguments(final String arg1) { this(arg1, null); }
Java也提供了另外一種使用初始化塊的方式實現初始化邏輯。這個特性很少使用但是非常有必要了解一下它的存在。
package com.javacodegeeks.advanced.construction; public class InitializationBlock { { // initialization code here } }
在某些情況下,初始化塊可以彌補匿名無參構造器的缺陷。有一些特殊的類可能會有很多個初始化塊并且他們會依次按照他們在代碼中定義的順序被調用,比如:
package com.javacodegeeks.advanced.construction; public class InitializationBlocks { { // initialization code here } { // initialization code here } }
初始化塊并不是替代構造器并且他們可以獨立于構造器而存在。但是需要提及的最重要的一點就是初始化塊會在任何構造器被調用之前被執行。
package com.javacodegeeks.advanced.construction; public class InitializationBlockAndConstructor { { // initialization code here } public InitializationBlockAndConstructor() { } }
Java提供了一些開發者所依賴的初始化保障,未初始化的實例和類參數會自動初始化為它們的默認值。
讓我們使用下面的例子來確認一下這些默認值。
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 ); } }
一旦使用new關鍵字實例化:
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
構造器受Java可見性規則約束并且可以擁有訪問控制修飾符來決定是否其他類可以調用特定的構造函數。
Java(特別是JVM)使用自動垃圾回收機制。簡而言之,當新對象被創建,JVM就會自動為這些新創建的對象分配內存。于是,當這些對象沒有任何引用的時候,他們就會被銷毀并且他們所占用的內存就會被回收。
Java垃圾回收是分代的,基于這種假設(分代假設)大多數的對象在很年輕的時候就已經不可達(在他們被創建之后的很短的時間內就沒有任何引用并且被安全銷毀)。大多數開發者曾經相信在Java中創建對象是很慢的并且應該盡可能地避免新對象的實例化。
實際上,這并不成立:在Java中創建對象的開銷非常的小并且很快。雖然如此,但是沒有必要創建生命周期比較長的對象,因為創建過多的長壽命對象最終可能會填滿老年代空間從而引發stop-the-world的垃圾回收,這樣的話開銷就會比較大。
到目前為止,我們已經談到了構造函數和對象初始化,但實際上并沒有提到任何關于對象銷毀的內容。這是因為Java使用垃圾收集器去管理對象的生命周期,并且垃圾收集器的責任就是去銷毀無用對象并回收這些對象占用的內存。
然而,在Java中有一個被稱為終結器(Finalizers)的特殊特性,它有點類似于析構函數,但是在執行資源清理時它所解決的是不同的意圖。終結器(Finalizers)是被考慮用來解決一些危險的特征(比如會導致無數的副作用和性能問題的問題)。
一般來說,他們是沒有必要的,應該避免(除了非常罕見的情況下,主要是有關本地對象)。Java 7語言引入了一種名為try-with-resources的更好的替代方法和AutoCloseable接口,它允許像如下的方式這樣干凈的寫代碼:
try ( final InputStream in = Files.newInputStream( path ) ) { // code here }
到目前為止,,我們已經談到了構造函數和對象初始化。但是Java也支持類級別的初始化構造,我們稱之為靜態初始化(Static initialization)。
靜態初始化(Static initialization)有點類似于初始化塊,除了需要添加static關鍵字之外。注意靜態初始化在每次類加載的時候它只執行一次。比如:
package com.javacodegeeks.advanced.construction; public class StaticInitializationBlock { static { // static initialization code here } }
和初始化塊類似,在類定義時你可以包含任意數量的初始化塊,它們會根據在類代碼中出現的順序依次執行,比如:
package com.javacodegeeks.advanced.construction; public class StaticInitializationBlocks { static { // static initialization code here } static { // static initialization code here } }
因為靜態初始化(Static initialization)塊可以從多個并行線程中觸發(第一次類加載發生),Java運行時保證在線程安全的前提下僅僅被執行一次。
過去這幾年很多易于理解和廣泛應用的構造模式在Java社區出現。我們將會介紹幾個比較常用的:單例模式(singleton)、幫助器(helpers)、工廠模式(factory)、依賴注入(dependency injection )——大家熟知的控制反轉(inversion of control)。
單例模式是軟件開發者社區中最老也是最具爭議性的模式之一。基本來說,它的主要思想就是確保在任何時候類僅僅只有一個實例被創建。思想就是如此簡單,然而單例模式引發了很多關于如何使之正確的討論,特別是線程安全的討論。下面是單例模式原生版本的例子:
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; } }
這段代碼至少有一個問題就是如果多個線程同時調用,那么此類就能夠創建多個實例。設計合適的單例模式的方法之一是使用類的 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; } }
如果你不想浪費資源并且希望在單例對象真正需要的時候才被延遲創建的話,這就要求顯示同步了(explicit synchronization),這就有可能導致多線程環境中的并發性降低(關于并發的詳細內容我們將會在后續的文章中討論)。
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; } }
如今,在大多數的案例中單例模式并不被考慮作為一個很好的選擇,主要是因為單例模式將會導致代碼很難測試。依賴注入模式讓單例模式變得沒有必要。
utility或者helper類是被許多開發者所使用的相當流行的一種模式。基本來說,它所代表的是無實例( non-instantiable)類(構造器被定義成private),僅僅可以選擇將方法定義成final(后續會介紹如何定義類)或者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 } }
站在開發者的角度,helpers類經常所扮演的是一個容器的角色,這個容器中放了很多在其他地方找不到但是其他類需要相互共享和使用的互相不相關的方法。這種設計決定了在很多情況下要避免使用:總能找到另一種重用所需功能的方式,保持代碼的簡潔和清晰。
工廠模式被證明是軟件開發人員手中非常有用的技術。因此,Java有幾種風格工廠模式,從工廠方法到抽象工廠。工廠模式最簡單的例子是返回特定類的新實例的靜態方法(工廠方法)。例如:
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(); }
依賴庫類型,完成幾種不同的實現:
public class Library implements BookFactory { @Override public Book newBook() { return new PaperBook(); } } public class KindleLibrary implements BookFactory { @Override public Book newBook() { return new KindleBook(); } }
現在,Book的特定類被隱藏在BookFactory接口實現之后,BookFactory仍然提供創建book的通用方式。
依賴注入(一說控制反轉)被類設計者認為是一個很好的做法:如果某些類的實例依賴其他類的實例,被依賴的實例應該通過構造(比如通過設置器——setters,或者策略——strategies模式等)的思想提供給依賴的實例,而不是依賴的實例自行創建。看一下下面這種情況:
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() 創建。最好的設計方案應該是通過構造器參數的形式去完成相同的事情。
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 ); } }
按這種方案實現的話,類的所有依賴都是通過外部提供,這樣就很容易的修改date format和為類寫測試用例。
關于“Java怎么創建和銷毀對象”這篇文章的內容就介紹到這里,感謝各位的閱讀!相信大家對“Java怎么創建和銷毀對象”知識都有一定的了解,大家如果還想學習更多知識,歡迎關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。