一、手动装箱和手动拆箱
- 手动装箱(Manual Boxing):指的是程序员通过调用包装类的构造函数或静态工厂方法,将基本数据类型转换为对应的包装类对象的过程。例如,通过调用
Integer
类的构造函数或valueOf()
方法实现整数类型的装箱。// 手动装箱示例 int intValue = 10; Integer integerValue = new Integer(intValue); // 使用构造函数进行装箱 // 或者 Integer integerValue = Integer.valueOf(intValue); // 使用静态工厂方法进行装箱
- 手动拆箱(Manual Unboxing):指的是程序员通过调用包装类的方法,将包装类对象转换为对应的基本数据类型的过程。例如,通过调用
xxxValue()
方法实现包装类对象到基本数据类型的拆箱。// 手动拆箱示例 Integer integerValue = 10; int intValue = integerValue.intValue(); // 使用intValue()方法进行拆箱
手动装箱和手动拆箱与自动装箱和自动拆箱相对应,区别在于手动操作需要程序员显式地调用相应的方法来进行类型转换,而自动操作则是由编译器自动完成的。通常情况下,推荐使用自动装箱和自动拆箱,因为它们可以减少代码的冗余和提高代码的可读性。
二、自动装箱和自动拆箱
- 自动装箱(Autoboxing):将基本数据类型自动转换为对应的包装类对象。例如,将int转换为Integer、double转换为Double等。在源码中,当我们将基本数据类型赋值给对应的包装类对象时,编译器会自动调用valueOf()方法进行装箱操作。
// 自动拆箱示例 Integer integerValue = 10; int intValue = integerValue; // 自动拆箱
在源码中,自动拆箱的实现主要通过调用包装类的xxxValue()方法来完成,例如Integer类的intValue()方法:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
- 自动拆箱(Unboxing):将包装类对象自动转换为对应的基本数据类型。例如,将Integer转换为int、Double转换为double等。在源码中,当我们将包装类对象赋值给基本数据类型时,编译器会自动调用xxxValue()方法进行拆箱操作。
// 自动拆箱示例 Integer integerValue = 10; int intValue = integerValue; // 自动拆箱
在源码中,自动拆箱的实现主要通过调用包装类的xxxValue()方法来完成,例如Integer类的intValue()方法:
public int intValue() { return value; }
总的来说,自动装箱和自动拆箱使得在基本数据类型和包装类之间的转换更加便捷,编码更加简洁。这些特性的实现是通过编译器在编译期间自动插入相应的装箱和拆箱代码来实现的。
三、128陷阱
128陷阱 是指在 Java 中,当比较 Integer 类型的数据时,有时候会发现两个明明相同的值,最后比较的结果为 false 。这是因为在 Java 中, Integer 是数据类型 int 的封装类。Java 设计者认为大家对数的使用大多在100以内,因此规定在-128至127之间的 Integer 类型的变量,直接指向常量池中的缓存地址,不会 new 开辟出新的空间。
下面提供一个示例,展示 128陷阱 :
public class IntegerComparisonExample {
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
Integer cc = 1000;
Integer dd = 1000;
System.out.println((a == b));
System.out.println((c == d));
System.out.println((cc == dd));
System.out.println("----------------");
System.out.println((a.equals(b)));
System.out.println((c.equals(d)));
System.out.println((cc.equals(dd)));
}
}
在上述代码中,定义了六个 Integer 类型的对象,分别是 a 、 b 、 c 、 d 、 cc 和 dd 。通过 == 运算符比较它们的值,可以发现 a 和 b 的值相等,返回 true 。而 c 和 d 的值相等,返回 false 。这是因为 c 和 d 的值为128,自动装箱时超出了常量池的范围,创建了新的对象,因此它们的地址不同。使用 equals() 方法比较它们的值,结果都是 true ,因为 equals() 方法比较的是对象的值,而不是对象的地址。
这是因为,在Integer的valueOf()方中,当数值在-128-127之间时,数值都存储在有一个catch数组当中,该数组相当于一个缓存,当我们在-128-127之间进行自动装箱的时候,我们就直接返回该值在内存当中的地址,所以在-128-127之间的数值用==进行比较是相等的。
如何避免128陷阱
为了避免 Integer 128陷阱 ,推荐使用 equals 方法进行比较,因为 equals 方法比较的是对象的内容,而不是引用。下面提供一个具体示例:
public class IntegerComparisonExample {
public static void main(String[] args) {
Integer a = 127;
Integer b = 127;
Integer c = 128;
Integer d = 128;
System.out.println((a == b));
System.out.println((c == d));
System.out.println("----------------");
System.out.println((a.equals(b)));
System.out.println((c.equals(d)));
}
}
在上述代码中,定义了四个 Integer 类型的对象,分别是 a 、 b 、 c 和 d 。通过 == 运算符比较它们的值,可以发现 a 和 b 的值相等,返回 true 。而 c 和 d 的值相等,返回 false 。这是因为 c 和 d 的值为128,自动装箱时超出了常量池的范围,创建了新的对象,因此它们的地址不同。使用 equals() 方法比较它们的值,结果都是 true ,因为 equals() 方法比较的是对象的值,而不是对象的地址。