如何正確的創建和銷毀 Java 對象?

来源:https://www.cnblogs.com/javastack/archive/2022/11/24/16921983.html
-Advertisement-
Play Games

一、介紹 Java由Sun Microsystems發明併在1995年發佈,是世界上使用最廣泛的編程語言之一。Java是一個通用編程語言。由於它擁有功能強大的庫、運行時、簡單的語法、平臺無關(Write Once, Run Anywhere - WORA)以及令人敬畏的社區從而吸引了很多的開發者。 ...


一、介紹

Java由Sun Microsystems發明併在1995年發佈,是世界上使用最廣泛的編程語言之一。Java是一個通用編程語言。由於它擁有功能強大的庫、運行時、簡單的語法、平臺無關(Write Once, Run Anywhere - WORA)以及令人敬畏的社區從而吸引了很多的開發者。

本系列文章我們我們將會覆蓋一些高級的Java概念,我們假設你對Java語言已經有一些基礎知識。本系列文章並不是一個完整的參考,而是一個將您的Java技能提升到下一個級別的詳細指南。

本系列文章中將會看到一些代碼片段,在這些代碼片段裡面將會使用java 7的語法以及java 8的語法。

二、實例構造(Instance Construction)

Java是面向對象的編程語言,所以新實例(objects)的創建可能是它最重要的概念之一。在新的類實例中構造器(Constructors)扮演了非常核心的角色,Java對於構造器(Constructors)的定義提供了很多方案。

2.1 隱式(implicitly)構造器

Java允許定義無任何構造器的類,但是這並不意味著此類沒有構造器。比如說,讓我們看一下下麵這個類。

package com.javacodegeeks.advanced.construction;
public class NoConstructor {
}

此類沒有構造器,但是Java編譯器會隱式地(implicitly)生成一個構造器並且在使用new關鍵字創建新的類實例時會被調用。

final NoConstructor noConstructorInstance = new NoConstructor();

2.2 無參構造器(Constructors without Arguments)

無參構造器是顯式執行Java編譯器工作的最簡單的方法。

package com.javacodegeeks.advanced.construction;
public class NoArgConstructor {
    public NoArgConstructor() {
        // Constructor body here
    }
}

在使用new關鍵字創建此類的新實例時會此構造器將會被調用。

final NoArgConstructor noArgConstructor = new NoArgConstructor();

2.3 有參構造器(Constructors with Arguments)

有參構造器是參數化創建類實例的一個非常有意思和有用的方法。下麵這個類定義了一個具有兩個參數的構造器。

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);
}

2.4 初始化塊(Initialization Blocks)

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() {
    }
}

2.5 構造保障(Construction guarantee)

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

2.6 可見性(Visibility)

構造器受Java可見性規則約束並且可以擁有訪問控制修飾符來決定是否其他類可以調用特定的構造函數。

2.7 垃圾回收(Garbage collection)

Java(特別是JVM)使用自動垃圾回收機制。簡而言之,當新對象被創建,JVM就會自動為這些新創建的對象分配記憶體。於是,當這些對象沒有任何引用的時候,他們就會被銷毀並且他們所占用的記憶體就會被回收。

Java垃圾回收是分代的,基於這種假設(分代假設)大多數的對象在很年輕的時候就已經不可達(在他們被創建之後的很短的時間內就沒有任何引用並且被安全銷毀)。大多數開發者曾經相信在Java中創建對象是很慢的並且應該儘可能地避免新對象的實例化。

實際上,這並不成立:在Java中創建對象的開銷非常的小並且很快。雖然如此,但是沒有必要創建生命周期比較長的對象,因為創建過多的長壽命對象最終可能會填滿老年代空間從而引發stop-the-world的垃圾回收,這樣的話開銷就會比較大。

2.8 終結器(Finalizers)

到目前為止,我們已經談到了構造函數和對象初始化,但實際上並沒有提到任何關於對象銷毀的內容。這是因為Java使用垃圾收集器去管理對象的生命周期,並且垃圾收集器的責任就是去銷毀無用對象並回收這些對象占用的記憶體。

然而,在Java中有一個被稱為終結器(Finalizers)的特殊特性,它有點類似於析構函數,但是在執行資源清理時它所解決的是不同的意圖。終結器(Finalizers)是被考慮用來解決一些危險的特征(比如會導致無數的副作用和性能問題的問題)。

一般來說,他們是沒有必要的,應該避免(除了非常罕見的情況下,主要是有關本地對象)。Java 7語言引入了一種名為try-with-resources的更好的替代方法和AutoCloseable介面,它允許像如下的方式這樣乾凈的寫代碼:

try ( final InputStream in = Files.newInputStream( path ) ) {
    // code here
}

3、靜態初始化(Static initialization)

到目前為止,,我們已經談到了構造函數和對象初始化。但是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運行時保證線上程安全的前提下僅僅被執行一次。

4、構造模式(Construction Patterns)

過去這幾年很多易於理解和廣泛應用的構造模式在Java社區出現。我們將會介紹幾個比較常用的:單例模式(singleton)、幫助器(helpers)、工廠模式(factory)、依賴註入(dependency injection )——大家熟知的控制反轉(inversion of control)。

4.1 單例模式(Singleton)

單例模式是軟體開發者社區中最老也是最具爭議性的模式之一。基本來說,它的主要思想就是確保在任何時候類僅僅只有一個實例被創建。思想就是如此簡單,然而單例模式引發了很多關於如何使之正確的討論,特別是線程安全的討論。下麵是單例模式原生版本的例子:

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;
    }
}

如今,在大多數的案例中單例模式並不被考慮作為一個很好的選擇,主要是因為單例模式將會導致代碼很難測試。依賴註入模式讓單例模式變得沒有必要。

4.2 Utility/Helper類

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類經常所扮演的是一個容器的角色,這個容器中放了很多在其他地方找不到但是其他類需要相互共用和使用的互相不相關的方法。這種設計決定了在很多情況下要避免使用:總能找到另一種重用所需功能的方式,保持代碼的簡潔和清晰。

4.3 工廠模式(Factory)

工廠模式被證明是軟體開發人員手中非常有用的技術。因此,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的通用方式。

4.4 依賴註入(Dependency Injection)

依賴註入(一說控制反轉)被類設計者認為是一個很好的做法:如果某些類的實例依賴其他類的實例,被依賴的實例應該通過構造(比如通過設置器——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和為類寫測試用例。

在本系列文章的這一部分中,我們一直在研究類和類的實例構造以及初始化技術,涵蓋了幾種廣泛使用的模式。在下一部分中,我們將分析Object類以及其熟知方法的用法:equals,hashCode,toString和clone。

原文鏈接:https://blog.csdn.net/zyhlwzy/article/details/78937421

版權聲明:本文為CSDN博主「RonTech」的原創文章,遵循CC 4.0 BY-SA版權協議,轉載請附上原文出處鏈接及本聲明。

近期熱文推薦:

1.1,000+ 道 Java面試題及答案整理(2022最新版)

2.勁爆!Java 協程要來了。。。

3.Spring Boot 2.x 教程,太全了!

4.別再寫滿屏的爆爆爆炸類了,試試裝飾器模式,這才是優雅的方式!!

5.《Java開發手冊(嵩山版)》最新發佈,速速下載!

覺得不錯,別忘了隨手點贊+轉發哦!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 個人名片: 對人間的熱愛與歌頌,可抵歲月冗長:sun_with_face: Github👨🏻‍💻:念舒_C.ying CSDN主頁✏️:念舒_C.ying 個人博客:earth_asia: :念舒_C.ying 預覽圖 直接進入我的網站吧 >> Z_C戀愛日記 下載源碼 鏈接:https:// ...
  • 在vue2中,提供了provide和inject配置,可以讓開發者在高層組件中註入數據,然後在後代組件中使用 除了相容vue2的配置式註入,vue3在composition api 中添加了provide和inject方法,可以在setup函數中註入 和使用數據 基本使用 provide('key' ...
  • 自定義 封裝單列模式! global state 由於vue3的響應式系統本身可以脫離組件而存在,因此可以充分利用這一點,輕鬆製造多個全局響應式數據, 並且通過和vuex一樣 通過某個模塊指定方法修改數據,不能直接修改數據,並且讓數據成為全局響應式 並且在代碼體積上絕對的輕量級!比市面上的任何第三方 ...
  • 一.小結 1.程式模塊化和可重用性是軟體工程的中心目標之一。java提供了很多有助於完成這一目標的有效結構。方法就是一個這樣的結構。 2.方法指定方法的修飾符,返回值類型,方法名和參數。比如靜態修飾符static。 3.方法可以返回一個值。返回值類型returnValueType是方法要返回的值數據 ...
  • 伺服器端渲染技術01 為什麼需要jsp? 在之前的開發過程中,我們可以發現servlet做界面非常不方便: 引出jsp技術=> jsp=html+java代碼+標簽+javascript+css 1.JSP基本介紹 JSP全稱是Java Server Pages,Java的伺服器頁面,就是伺服器端渲 ...
  • 目錄 一.OpenGL 褐色 1.IOS Object-C 版本 1.Windows OpenGL ES 版本 2.Windows OpenGL 版本 二.OpenGL 褐色 GLSL Shader 三.猜你喜歡 零基礎 OpenGL ES 學習路線推薦 : OpenGL ES 學習目錄 >> Op ...
  • 多線程理解 繼承Thread類 子類繼承Thread類具備多線程能力 啟動線程:子類對象.start() 不建議使用:避免oop單繼承局限性 實現Runnable介面 實現介面Runnable具有多線程能力 啟動線程:傳入目標對象+Thread對象.start() 推薦使用:避免單繼承局限性,可能一 ...
  • 之前我們已經知道什麼是 數組(一維數組)java 基礎——數組,數組的存取 這裡補充一點: 數組本身是引用數據類型 ,數組的元素 可以是 基本數據類型 跟 引用數據類型 那麼?什麼是二維數組 ? 官方定義:以一維數組作為一維數組元素的數組 要是有點繞,不好理解,沒關係,簡單來說,就是一維數組裡面存一 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...