博客 / 詳情

返回

Java中的數據類型

Java 數據類型詳解

目錄

  • 數據類型分類
  • 基本數據類型
  • 引用類型
  • 基本類型 vs 引用類型
  • 包裝類
  • String 詳解
  • 字符串常量池
  • 內存佈局
  • 對象生命週期
  • 常見問題

數據類型分類

Java 數據類型
├── 基本類型(8種)
│   ├── 整數類型:byte, short, int, long
│   ├── 浮點類型:float, double
│   ├── 字符類型:char
│   └── 布爾類型:boolean
└── 引用類型
    ├── 類(如 String)
    ├── 接口
    └── 數組

基本數據類型

整數類型

類型 位數 範圍 默認值 示例
byte 8位 -128 ~ 127 0 byte b = 100;
short 16位 -32,768 ~ 32,767 0 short s = 10000;
int 32位 -21億 ~ 21億 0 int i = 100000;
long 64位 極大 0L long l = 100000L;
byte  a = 127;     // ✅ 最大值
// byte b = 128;  // ❌ 超出範圍

int   c = 100;
long  d = 100000L; // long 需加 L 或 l
long  e = 10000000000L; // ✅ 超出int範圍必須加L
// long  f = 10000000000; // ❌ 編譯錯誤,超出 int 範圍

浮點類型

類型 位數 精度 默認值 示例
float 32位 單精度(7位有效數字) 0.0f float f = 3.14f;
double 64位 雙精度(15位有效數字) 0.0 double d = 3.14;
double a = 3.14;     // 默認是 double
// float b = 3.14;  // ❌ 編譯錯誤
float  c = 3.14f;    // ✅

字符類型

類型 位數 説明 默認值 示例
char 16位 單個 Unicode 字符 \u0000 char c = 'A';
char a = 'A';       // 字符
char b = '中';      // 中文
char c = 65;        // 數字對應字符 'A'
char d = '\u0041';  // Unicode 轉義,'A'
char e = '\u0000';  // null 字符,char 的默認值

// char f = 'AB';  // ❌ char 只能存單個字符

布爾類型

類型 取值 默認值 示例
boolean true, false false boolean flag = true;
boolean a = true;
boolean b = false;

引用類型

什麼是引用類型

引用類型存儲的是對象的地址(引用),對象本身存儲在堆中。

常見引用類型

類型 説明 示例
String 字符串類 String s = "hello";
數組 數組 int[] arr = {1, 2, 3};
接口 接口類型 List<String> list;
自定義類 用户定義的類 Person p = new Person();

基本類型 vs 引用類型

對比維度 基本類型 引用類型(如 String)
存儲位置 棧中,直接存值 棧存引用,堆存對象
大小 固定(如 int 4字節) 不固定
默認值 0/false/\u0000 null
比較 == 比較值 == 比較地址,equals() 比較內容

內存圖示

基本類型:
棧
┌─────────┐
│ int a   │ = 10
└─────────┘
  直接存值

引用類型:
棧                    堆
┌─────────┐          ┌─────────┐
│String s │ ────────→│ "hello" │
└─────────┘          └─────────┘
 引用變量            實際對象

比較示例

// 基本類型
int a = 10;
int b = 10;
System.out.println(a == b);  // true(比較值)

// 引用類型
String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2);         // false(不同地址)
System.out.println(s1.equals(s2));    // true(內容相同)

包裝類

每個基本類型都有對應的包裝類:

基本類型 包裝類 説明
byte Byte
short Short
int Integer
long Long
float Float
double Double
char Character
boolean Boolean
int a = 10;              // 基本類型
Integer b = 10;          // 包裝類
Integer c = Integer.valueOf(10); // 顯式創建

// 自動裝箱/拆箱
Integer d = a;           // 自動裝箱
int e = d;               // 自動拆箱

String 詳解

String 是什麼?

String 是類,不是基本類型,是不可變的引用類型。

String 的不可變性

String a = "hello";
String b = a;              // a 和 b 指向同一對象

a = "world";               // a 指向新對象

System.out.println(b);     // 輸出: hello(b 不變)
System.out.println(a == b); // false(已不是同一對象)
內存變化:

修改前:
a ──→ ["hello"]
      ↑
      b

修改後:
a ──→ ["world"]  ← 新對象

b ──→ ["hello"]  ← 原對象不變

為什麼 String 不可變?

// String 簡化版源碼
public final class String {
    private final char value[];  // final,不能修改

    // 所有修改操作都返回新 String
    public String substring(int beginIndex) {
        return new String(value, beginIndex, count);
    }
}
原因 説明
不可變 String 對象一旦創建,內容不能改
final String 類內部用 final 修飾字符數組
安全 多線程環境下,無需加鎖就能安全共享

String vs 可變對象

// String - 不可變
String a = "hello";
String b = a;
a = "world";
System.out.println(b);  // hello(不變)

// StringBuilder - 可變
StringBuilder sb1 = new StringBuilder("hello");
StringBuilder sb2 = sb1;
sb1.append(" world");
System.out.println(sb2);  // hello world(變了!)

字符串常量池

什麼是字符串常量池

┌─────────────────────────────────────────────────────┐
│                   字符串常量池                       │
│  (JVM 堆內存中的一塊特殊區域,專門存儲字符串字面量)  │
│                                                     │
│   "hello"                                           │
│   "world"                                           │
│   "java"                                            │
└─────────────────────────────────────────────────────┘

字面量 vs new

方式 寫法 存儲位置 是否複用
隱式(字面量) String s = "hello"; 字符串常量池 ✅ 複用
顯式(new) String s = new String("hello"); 堆內存(新對象) ❌ 新對象
動態創建 scanner.nextLine() 堆內存(新對象) ❌ 新對象

字面量(隱式賦值)

String a = "hello";
String b = "hello";

流程:

  1. JVM 檢查常量池中是否有 "hello"
  2. 有 → 直接引用(複用)
  3. 無 → 在常量池創建 "hello",然後引用
常量池:
┌─────────┐
│ "hello" │ ← a ─┐
└─────────┘       │
                  │ 兩者指向同一對象
                  ├─ b
System.out.println(a == b);  // true(同一對象)

new(顯式賦值)

String c = new String("hello");
String d = new String("hello");

流程:

  1. 先在常量池檢查/創建 "hello"
  2. 然後在堆內存強制創建新對象
  3. 變量引用堆上的新對象(不是常量池)
常量池:            堆內存:
┌─────────┐        ┌─────────┐
│ "hello" │ ───→   │ "hello" │ ← c
└─────────┘        └─────────┘
                   ┌─────────┐
                   │ "hello" │ ← d
                   └─────────┘
System.out.println(c == d);   // false(不同對象)
System.out.println(c == a);   // false(c 在堆,a 在常量池)
System.out.println(c.equals(a)); // true(值相同)

scanner 輸入

String c = scanner.nextLine(); // 輸入: hello
String d = scanner.nextLine(); // 輸入: hello
System.out.println(c == d);    // false(每次新對象)

內存佈局

Java 版本差異

版本 常量池位置
Java 6 及之前 方法區(永久代 PermGen)
Java 7+
Java 8+ (方法區改為元空間 Metaspace)

現代 Java(7+):字符串常量池在堆上。

完整內存佈局

String s1 = "first";
String s2 = new String("second");

棧:
┌─────────┐      ┌─────────┐
│   s1    │      │   s2    │
└─────────┘      └─────────┘
  ↓ 引用            ↓ 引用

堆(都在堆上):
┌───────────────────────────────────────────────────┐
│                                                   │
│  常量池區域:         普通堆區域:                  │
│  ┌─────────┐        ┌─────────────────────┐       │
│  │ "first" │        │ String對象           │       │
│  │"second" │        │ value → "second"    │       │
│  └─────────┘        └─────────────────────┘       │
│                             ↓                      │
│                     ┌─────────┐                   │
│                     │"second" │ ← 在常量池         │
│                     └─────────┘                   │
│                                                   │
└───────────────────────────────────────────────────┘

new String("xxx") 的步驟

1. 先處理 "xxx" 字面量
   → 檢查常量池有沒有 "xxx"
   → 沒有 → 在常量池創建 "xxx"
   → 有 → 複用

2. 再執行 new String()
   → 在普通堆創建新對象
   → 對象內部引用常量池的 "xxx"
String s = new String("second");

// 等價於:
String literal = "second";      // 先處理字面量,進常量池
String s = new String(literal); // 再創建堆對象

對象生命週期

對象的一生

┌─────────────────────────────────────────────────────────┐
│                   對象一生                               │
├─────────────────────────────────────────────────────────┤
│                                                         │
│  1. 創建     →  new String() / scanner.nextLine()      │
│     ↓                                                  │
│  2. 使用     →  被變量引用,可以訪問                    │
│     ↓                                                  │
│  3. 失去引用 →  變量指向其他對象或離開作用域            │
│     ↓                                                  │
│  4. 可回收    →  等待 GC 清理                          │
│     ↓                                                  │
│  5. 回收     →  GC 自動回收內存                        │
│                                                         │
└─────────────────────────────────────────────────────────┘

示例:引用丟失

String s1 = scanner.nextLine();  // 創建對象
s1 = scanner.nextLine();         // s1 指向新對象,原對象可被回收

垃圾回收(GC)

GC = Garbage Collection,Java 自動內存管理機制。

GC 工作原理:
1. 定期掃描堆內存
2. 找出沒有被引用的對象
3. 回收內存

不同區域的回收策略

類型 生命週期 回收方式
字面量(常量池) 程序運行期間 一般不回收
動態對象(堆) 到無引用時 GC 回收
基礎類型(棧) 作用域內 自動銷燬
String a = "hello";  // 常量池,程序運行期間存在
String b = scanner.nextLine();  // 堆對象
b = null;  // 失去引用,等待 GC

intern() 方法

將字符串內容放入常量池。

String s = new String("aaa");
s.intern();  // 將 "aaa" 內容放入常量池(如果還沒)
String s1 = new String("aaa");
String s2 = new String("aaa");
String s3 = "aaa";  // 字面量,常量池
String s4 = s1.intern();  // 返回常量池中的引用

System.out.println(s1 == s2);   // false(堆對象,不同)
System.out.println(s1 == s3);   // false(堆 vs 常量池)
System.out.println(s3 == s4);   // true(都是常量池)

常見問題

Q1: \u0000 是什麼?

\u0000 是 Unicode 字符的轉義表示,表示 Unicode 值為 0 的字符。

方面 説明
Unicode 值 0
名稱 null 字符 / 空字符
可見性 不可見(控制字符)
Java 中 char 的默認值
char c = '\u0000';          // null 字符
char d = 0;                 // 等價寫法
System.out.println(c == d); // true
System.out.println((int)c); // 輸出: 0

Q2: String 引用相同但值不同?

對於 String,不可能。

String a = "hello";
String b = a;
a = "world";
System.out.println(b);  // hello(b 不變)

核心原因: String 是不可變的,引用相同則值必相同。

Q3: scanner.nextLine() 返回什麼?

永遠返回 String,無論輸入什麼。

Scanner scanner = new Scanner(System.in);
String input = scanner.nextLine();  // 輸入 27 → "27"(字符串)

// 如果要轉整數
int num = Integer.parseInt(input);  // "27" → 27
方法 輸入 27 返回類型
nextLine() "27" String
nextInt() 27 int
nextDouble() 27.0 double

Q4: 常量池在堆上嗎?

是的,現代 Java(7+)中,字符串常量池在堆上。

但常量池是堆中特殊管理的區域,與普通堆對象有區別:

對比維度 常量池對象 普通堆對象
位置 堆(特殊區域) 堆(普通區域)
創建方式 字面量、intern() new String()
去重 ✅ 自動去重 ❌ 不去重
生命週期 一般長期存在 無引用後 GC 回收

總結速查表

基本類型速查

類型 位數 範圍 默認值
byte 8 -128 ~ 127 0
short 16 ±3.2萬 0
int 32 ±21億 0
long 64 極大 0L
float 32 單精度 0.0f
double 64 雙精度 0.0
char 16 單字符 \u0000
boolean 1 true/false false

String 對比速查

操作 結果
String a = "hello";
String b = "hello";
a == btrue
String c = new String("hello");
String d = new String("hello");
c == dfalse
a == c false(常量池 vs 堆)
a.equals(c) true(值相同)

記憶口訣

基礎類型     引用類型
    ↓            ↓
   棧          棧 + 堆
 直接值       引用+對象

字面量       scanner輸入
    ↓            ↓
 常量池        堆新對象
 可複用        不復用

String  → 引用同,值必同(不可變)
StringBuilder → 引用同,值隨變(可變)

注:同步發佈於金蝶開發者社區:Java數據類型

user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.