目录
[8 种基本数据类型](#8 种基本数据类型)
[int 和 long 互相转换](#int 和 long 互相转换)
[用 bigDecimal 而不是 double](#用 bigDecimal 而不是 double)
[128 陷阱](#128 陷阱)
[Integer 缓存](#Integer 缓存)
数据类型
8 种基本数据类型

浮点数默认为 double(float 型数值在末尾加上 f 或 F),整数默认为 int(long 型在末尾加上 l 或 L)。
int 和 long 互相转换
long 的范围比 int 大,int 转为 long 是安全的,但 long 转为 int 有可能导致溢出。
- int 转 long 安全;
java
int intValue = 10;
long longValue = intValue; // 自动转换,安全
- long 转 int 有可能溢出。
java
long longValue = 100L;
int intValue = (int) longValue; // 强制类型转换,可能会有数据丢失或溢出
将 long 转为 int 时,如果 longValue 的值超过了 int 类型的范围,结果将会是截断后的低位部分。
用 bigDecimal 而不是 double
计算机中主要采用二进制的科学计数法来存储和表示浮点数,遵循 IEEE 754。将实数表示为

这种格式(符号位、尾数、阶码),不仅能用统一形式表示极大或极小的数,而且还能节省内存空间并提高计算效率。
下图用 float 和 double 举例,仅仅 double 的阶位就远超过 long 类型所能表示的范围。

double 会出现精度丢失的问题,double 执行的是二进制浮点运算,二进制有些情况下不能准确地表示一个小数,就像十进制不能准确的表示1/3 (1/3 = 0.3333...),也就是说二进制表示小数的时候只能表示"若干 1/(2^n) 的组合"。由于若干个 1/(2^n) 无法表示 0.1,所以 double 无法表示 0.1 等浮点数。
而 Decimal 是精确计算, 所以一般牵扯到金钱的计算 , 都使用 Decimal。
java
public class BigDecimalExample {
public static void main(String[] args) {
BigDecimal num1 = new BigDecimal("0.1");
BigDecimal num2 = new BigDecimal("0.2");
BigDecimal sum = num1.add(num2);
BigDecimal product = num1.multiply(num2);
System.out.println("Sum: " + sum); // 0.3
System.out.println("Product: " + product); // 0.02
}
}
使用 BigDecimal 可以确保精确的十进制数值计算,避免了使用 double 可能出现的舍入误差。需要注意的是,在创建 BigDecimal 对象时,应该使用字符串作为参数,而不是直接使用浮点数值,以避免浮点数精度丢失。
装箱与拆箱
Java 是面向对象的,但基本数据类型不是对象,包装类使得基本数据类型具备对象的特性。
装箱 :基本数据类型转换为包装类。自动装箱有两种情况,包括赋值时 和方法调用 时。Integer.valueOf(数值)。
- 赋值时自动装箱:
java
Integer i = 10; // 自动装箱
- 方法调用时自动装箱:
java
public static Integer show(Integer iParam) {
System.out.println("i : " + iParam);
return iParam;
}
show(3); // 自动装箱
拆箱 :包装类转化为基本数据类型。对象.intValue()。
经典题
java
public static void main(String[] args) {
int a = 10;
int b = 10;
Integer a1 = 10;
Integer b1 = 10;
Integer a2 = new Integer(10);
Integer b2 = new Integer(10);
System.out.println(a == b);
System.out.println(a1 == b1);
System.out.println(a2 == b2);
System.out.println(a1 == a);
System.out.println(a1.equals(a));
System.out.println(a1 == a2);
System.out.println(a == a2);
}
| 代码表达式 | 运行结果 | 核心物理逻辑 |
|---|---|---|
a == b |
true | 基本类型值比较 :int 存储在栈帧的局部变量表中,比较的是字面量数值。 |
a1 == b1 |
true | IntegerCache 机制 :自动装箱调用 Integer.valueOf(10)。由于 10 在 [-128, 127] 缓存范围内,a1 与 b1 指向堆中同一个缓存对象。 |
a2 == b2 |
false | 显式堆分配 :new 关键字绕过缓存,在堆内存中强制开辟两个独立的空间,其引用地址不同。 |
a1 == a |
true | 自动拆箱 :当包装类与基本类型进行 == 运算时,包装类 a1 会自动调用 intValue(),转化为基本类型后进行值比较。 |
a1.equals(a) |
true | 比较数值内容 :equals 被重写,比较的是对象内部包裹的 int 数值。 |
a1 == a2 |
false | 引用比较 :a1 指向常量池缓存对象,a2 指向 new 出来的堆对象,地址不一致。 |
a == a2 |
true | 自动拆箱 :同 a1 == a,a2 拆箱为基本类型 10,与 a 进行数值比较。 |
128 陷阱
由于 Integer 缓存范围为 -128 到 127,当使用 == 比较该范围外的对象(如 128)时,会因创建新对象 导致地址不一致返回 false,而范围内则为 true。
解决方案
比较 Integer 对象时,应使用重写后的 .equals() 方法而不是 == 操作符。
Integer 缓存
源码分析
java
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer[] cache;
static Integer[] archivedCache;
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
CDS.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
@IntrinsicCandidate
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
Java 的 Integer 类内部实现了一个静态缓存池 ,用于存储特定范围内的整数值对应的 Integer 对象,只在装箱时生效,new 时不生效。
默认情况下,这个范围是 -128 至 127。当通过 Integer.valueOf(int) 方法创建一个在这个范围内的整数对象时,并不会每次都生成新的对象实例,而是复用缓存中的现有对象。

可见 c 数组中存储的是指向数值的指针,而不是数值本身。