字节与字节码的底层原理
一个字节由8位二进制组成,是计算机存储的基本单位。Java字节码则是JVM执行的中间指令集,二者本质不同:
- 十进制与二进制转换:
int i = 10对应二进制00001010 - int的内存结构:占用4字节(32位),最高位为符号位
- 数值范围限制:最大值为31个1组成的二进制数(约21亿)
ASCII码与进制声明
字符在内存中以ASCII码存储,例如:
char c = 'a'对应二进制01100001(十进制97)- 支持多种进制声明:
int hex = 0x1F; // 十六进制(31) int oct = 037; // 八进制(31)
Java数据类型体系
1. 原生数据类型(8种)
| 类型 | 位数 | 范围 | 特性 |
|---|---|---|---|
| byte | 8 | -128 ~ 127 | 溢出会循环(127+1=-128) |
| int | 32 | -2^31 ~ 2^31-1 | 最大值约21亿 |
| long | 64 | -2^63 ~ 2^63-1 | 需加L后缀:100L |
| float | 32 | IEEE 754标准 | 需加F后缀:3.14F |
| double | 64 | IEEE 754标准 | 默认小数类型 |
| char | 16 | \u0000~\uFFFF | 使用Unicode编码 |
| boolean | 1 | true/false | JVM实现依赖 |
2. 引用数据类型
所有class定义的类及数组类型:
String s = "abc"; // 引用类型
int[] arr = new int[5]; // 数组类型
- 数组本质:JVM自动生成的类,包含
length属性和连续存储空间 - 不可扩容:创建后长度固定
浮点数精度陷阱
由于二进制表示限制,浮点数是近似值:
System.out.println(0.1 + 0.2); // 输出0.30000000000000004
正确比较方式:
double d = 0.1 + 0.2;
if (Math.abs(d - 0.3) < 1e-6) { // 误差阈值
System.out.println("相等");
}
类型转换与运算陷阱
1. 隐式类型提升
低精度→高精度自动补位:
byte b = 100;
int i = b; // 安全转换
2. 强制类型转换
高精度→低精度需显式转换,可能丢失数据:
int i = 200;
byte b = (byte)i; // b = -56(二进制截断)
3. 除法陷阱
整数除法直接截断小数部分:
int a = 5, b = 2;
System.out.println(a/b); // 输出2
解决方案:提升为浮点数
double result = 1.0 * a / b; // 输出2.5
装箱类型与拆箱陷阱
为什么需要包装类?
- 容器类只能存储对象:
List<Integer> - 提供工具方法:
Integer.parseInt() - 支持null值:
Boolean canBeNull = null
自动拆箱的致命陷阱
Integer obj = null;
int i = obj; // 抛出NullPointerException!
- 编译后实质:
int i = obj.intValue()
== 与 equals 的终极区别
| 场景 | == 行为 | equals 行为 |
|---|---|---|
| 原生类型 | 比较值 | 不可用 |
| 引用类型 | 比较内存地址 | 比较对象内容 |
| 包装类(-128~127) | 缓存内对象地址相同 | 比较数值 |
Integer a = 127, b = 127;
System.out.println(a == b); // true(缓存对象)
Integer c = 128, d = 128;
System.out.println(c == d); // false(新对象)
关键结论:
- 包装类比较值必须用
equals() Integer.valueOf()使用缓存优化- 包装类不可变,运算产生新对象