首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 核心基礎概念深度解析:類初始化、字符串機制與記憶體模型

     編輯:Java 相關知識

前言

Java 語言雖然語法簡潔,但其底層機制卻蘊含許多值得深究的細節。本文將系統梳理類初始化順序、String 常量池機制、繼承體系中的方法調用、參數傳遞本質、final/finally/finalize 差異、基本類型特性以及日期時間處理等核心知識點,幫助開發者建立扎實的 Java 核心認知體系。

一、類初始化順序詳解

當實例化一個子類對象時,父子類的靜態成員、實例成員、構造器執行順序遵循嚴格規則。通過一個完整的代碼示例來驗證:

class Parent {
    public static StaticVarible staticVarible = new StaticVarible("父類-靜態變量1");
    public StaticVarible instVarible = new StaticVarible("父類-成員變量1");

    static { System.out.println("父類-靜態塊"); }

    { System.out.println("父類-初始化塊"); }

    public static StaticVarible staticVarible2 = new StaticVarible("父類-靜態變量2");
    public StaticVarible instVarible2 = new StaticVarible("父類-成員變量2");

    public Parent() { System.out.println("父類-構造函數"); }
}

class Child extends Parent {
    public static StaticVarible staticVarible = new StaticVarible("子類-靜態變量1");
    public StaticVarible instVarible = new StaticVarible("子類-成員變量1");

    static { System.out.println("子類-靜態塊"); }

    public Child() { System.out.println("子類-構造函數"); }

    { System.out.println("子類-初始化塊"); }

    public static StaticVarible staticVarible2 = new StaticVarible("子類-靜態變量2");
    public StaticVarible instVarible2 = new StaticVarible("子類-成員變量2");
}

class StaticVarible {
    public StaticVarible(String info) { System.out.println(info); }
}

// 測試
Child child = new Child();

執行結果與結論

輸出順序為:

  1. 父類靜態變量1 → 父類靜態塊 → 父類靜態變量2
  2. 子類靜態變量1 → 子類靜態塊 → 子類靜態變量2
  3. 父類成員變量1 → 父類初始化塊 → 父類成員變量2 → 父類構造函數
  4. 子類成員變量1 → 子類初始化塊 → 子類成員變量2 → 子類構造函數

核心規律:父類靜態成員/塊 → 子類靜態成員/塊 → 父類實例成員/塊 → 父類構造器 → 子類實例成員/塊 → 子類構造器。靜態初始化在類加載階段完成,實例初始化在對象創建時依序執行。

二、String 的存儲結構與常量池機制

1. 記憶體模型基礎:棧與堆

  • 棧:存儲基本類型(char/byte/short/int/long/float/double/boolean)和對象引用
  • 堆:存儲對象實例,棧中保存指向堆對象的引用

String 作為特殊引用類型,兼具基本類型特性(不可變、常量池優化)與引用類型特徵。

2. String 存儲結構

以 String a = "abc"; 為例:

  1. 棧中創建引用變量 a
  2. 堆中創建 String 對象,內部包含 char 數組 {'a','b','c'}

3. 字符串常量池與 intern() 機制

JVM 為節省內存維護字符串常量池,編譯期可確定的字面量會被緩存。關鍵區別在於編譯期常量與運行期常量:

public class StringTest {
    public static final String constValue = "ab";      // 編譯期常量
    public static final String staticValue;            // 運行期常量
    static { staticValue = "ab"; }

    public static void main(String[] args) {
        String v1 = "ab";
        String v2 = "ab";                    // true - 常量池緩存
        String v3 = new String("ab");        // false - new 創建新對象
        String v4 = "abcd";
        String v5 = "ab" + "cd";             // true - 編譯期優化
        String v6 = v1 + "cd";               // false - 運行期拼接
        String v7 = constValue + "cd";       // true - 編譯期常量拼接
        String v8 = staticValue + "cd";      // false - 運行期常量拼接
        String v9 = v4.intern();             // true - 手動入池
        String v10 = new String(new char[]{'a','b','c','d'});
        String v11 = v10.intern();           // true - 手動入池
    }
}

核心結論

  1. JVM 僅緩存編譯期可確定的常量(final 且賦值為字面量或常量表達式)
  2. new String() 總創建新對象,需調用 intern() 手動入池
  3. String 設計體現享元模式思想,但嚴格意義上屬於常量池優化而非標準享元模式實現

三、繼承體系中的方法與字段調用

1. 靜態綁定 vs 動態綁定

class ParentDef {
    public static final String staticValue = "父類靜態變量";
    public String value = "父類實例變量";
}
class ChildDef extends ParentDef {
    public static final String staticValue = "子類靜態變量";
    public String value = "子類實例變量";
}

// 測試
ParentDef v1 = new ParentDef();
ParentDef v2 = new ChildDef();
ChildDef v3 = new ChildDef();

結果:

  • v1: 父類靜態/實例變量
  • v2: 父類靜態/實例變量(靜態字段按聲明類型綁定,實例字段同理)
  • v3: 子類靜態/實例變量

結論:字段訪問(含靜態/實例)遵循靜態綁定,僅取決於引用聲明類型;唯有實例方法遵循動態綁定(多態)。

四、參數傳遞:值傳遞的本質

Java 唯一支持值傳遞。基本類型傳值副本,引用類型傳引用副本(指向同一堆對象)。

public void change(int value) { value = 10; }           // 基本類型:原值不變
public void change(Value value) { value = new Value(); } // 引用類型:重新指向新對象,原對象不變
public void add(Value value) { value.value += 10; }      // 修改對象內部狀態:生效

關鍵點:方法內對參數引用的重新賦值不影響外部,但通過引用修改對象內部狀態則生效。

五、final / finally / finalize 三者辨析

關鍵字用途關鍵規則
final 修飾類、方法、變量、參數 類不可繼承、方法不可重寫、變量只能賦值一次(聲明時/初始化塊/構造器)
finally 異常處理塊 配合 try-catch,無論是否拋出異常/return/break/continue 均執行,用於資源釋放
finalize Object 方法 GC 回收前調用,用於釋放非內存資源,不保證執行時機,Java 9+ 已棄用

finally 執行細節

  • catch 中 return 前執行 finally
  • 循環中 break/continue 同樣觸發 finally
  • try 中直接 return,finally 仍執行並可修改返回值(慎用)

六、基本類型完整規格

類型位數包裝類最小值最大值
byte8Byte-128127
short16Short-32,76832,767
int32Integer-2,147,483,6482,147,483,647
long64Long-9.22e189.22e18
float32Float1.4E-453.4028235E38
double64Double4.9E-3241.7976931348623157E308
char16Character065,535
boolean1 (JVM實現相關)Boolean--

類型轉換規則

  • 無損擴寬:byte→short→int→long→float→double、char→int
  • 可能損失精度:int→float、long→float/double
  • 其他轉換需強制轉型,char 不能直接轉 byte/short

字面量後綴

  • 整數默認 int,長整數加 L
  • 浮點數默認 double,單精度加 F

七、日期時間處理(舊 API)

注意:Java 8+ 推薦使用 java.time 包,以下為舊 API 要點:

  • java.util.Date:大多 getXxx() 方法已棄用,僅作時間戳容器
  • java.util.Calendar:獲取日期細節的主力,月份 0-based(0=1月)
  • java.text.SimpleDateFormat:格式化/解析,非線程安全
Calendar c = Calendar.getInstance();
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONTH); // 0-11
int day = c.get(Calendar.DATE);

總結

掌握類初始化順序、String 常量池、靜態/動態綁定、值傳遞本質、final 三重奏、基本類型規格與日期處理,構成 Java 核心基礎的「七大支柱」。這些知識不僅是面試高頻考點,更是編寫高效、無 Bug 代碼的基石。建議結合 JVM 規範與源碼深入理解,並在實戰中驗證每個結論。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved