Java中的数据类型

Java 数据类型详解

目录


数据类型分类

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数据类型

posted @ 2026-02-02 12:08  雀啼春  阅读(7)  评论(0)    收藏  举报