一、介紹
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開發手冊(嵩山版)》最新發布,速速下載!
覺得不錯,別忘了隨手點贊+轉發哦!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/538301.html
標籤:Java
上一篇:RabbitMQ個人實踐
下一篇:java基礎——二維陣列基本概念
