Java 数据类型详解
目录
- 数据类型分类
- 基本数据类型
- 引用类型
- 基本类型 vs 引用类型
- 包装类
- String 详解
- 字符串常量池
- 内存布局
- 对象生命周期
- 常见问题
数据类型分类
- Java 数据类型
- ├── 基本类型(8种)
- │ ├── 整数类型:byte, short, int, long
- │ ├── 浮点类型:float, double
- │ ├── 字符类型:char
- │ └── 布尔类型:boolean
- └── 引用类型
- ├── 类(如 String)
- ├── 接口
- └── 数组
复制代码 基本数据类型
整数类型
类型位数范围默认值示例byte8位-128 ~ 1270byte b = 100;short16位-32,768 ~ 32,7670short s = 10000;int32位-21亿 ~ 21亿0int i = 100000;long64位极大0Llong 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 范围
复制代码 浮点类型
类型位数精度默认值示例float32位单精度(7位有效数字)0.0ffloat f = 3.14f;double64位双精度(15位有效数字)0.0double d = 3.14;- double a = 3.14; // 默认是 double
- // float b = 3.14; // ❌ 编译错误
- float c = 3.14f; // ✅
复制代码 字符类型
类型位数说明默认值示例char16位单个 Unicode 字符\u0000char 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 只能存单个字符
复制代码 布尔类型
类型取值默认值示例booleantrue, falsefalseboolean flag = true;- boolean a = true;
- boolean b = false;
复制代码 引用类型
什么是引用类型
引用类型存储的是对象的地址(引用),对象本身存储在堆中。
常见引用类型
类型说明示例String字符串类String s = "hello";数组数组int[] arr = {1, 2, 3};接口接口类型List list;自定义类用户定义的类Person p = new Person();基本类型 vs 引用类型
对比维度基本类型引用类型(如 String)存储位置栈中,直接存值栈存引用,堆存对象大小固定(如 int 4字节)不固定默认值0/false/\u0000null比较== 比较值== 比较地址,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(内容相同)
复制代码 包装类
每个基本类型都有对应的包装类:
基本类型包装类说明byteByteshortShortintIntegerlongLongfloatFloatdoubleDoublecharCharacterbooleanBoolean- 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 对象一旦创建,内容不能改finalString 类内部用 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";
复制代码 流程:
- JVM 检查常量池中是否有 "hello"
- 有 → 直接引用(复用)
- 无 → 在常量池创建 "hello",然后引用
- 常量池:
- ┌─────────┐
- │ "hello" │ ← a ─┐
- └─────────┘ │
- │ 两者指向同一对象
- ├─ b
复制代码- System.out.println(a == b); // true(同一对象)
复制代码 new(显式赋值)
- String c = new String("hello");
- String d = new String("hello");
复制代码 流程:
- 先在常量池检查/创建 "hello"
- 然后在堆内存强制创建新对象
- 变量引用堆上的新对象(不是常量池)
- 常量池: 堆内存:
- ┌─────────┐ ┌─────────┐
- │ "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"StringnextInt()27intnextDouble()27.0doubleQ4: 常量池在堆上吗?
是的,现代 Java(7+)中,字符串常量池在堆上。
但常量池是堆中特殊管理的区域,与普通堆对象有区别:
对比维度常量池对象普通堆对象位置堆(特殊区域)堆(普通区域)创建方式字面量、intern()new String()去重✅ 自动去重❌ 不去重生命周期一般长期存在无引用后 GC 回收总结速查表
基本类型速查
类型位数范围默认值byte8-128 ~ 1270short16±3.2万0int32±21亿0long64极大0Lfloat32单精度0.0fdouble64双精度0.0char16单字符\u0000boolean1true/falsefalseString 对比速查
操作结果String a = "hello";
String b = "hello";a == b → trueString c = new String("hello");
String d = new String("hello");c == d → falsea == cfalse(常量池 vs 堆)a.equals(c)true(值相同)记忆口诀
- 基础类型 引用类型
- ↓ ↓
- 栈 栈 + 堆
- 直接值 引用+对象
- 字面量 scanner输入
- ↓ ↓
- 常量池 堆新对象
- 可复用 不复用
- String → 引用同,值必同(不可变)
- StringBuilder → 引用同,值随变(可变)
复制代码 注:同步发布于金蝶开发者社区:Java数据类型
来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作! |