Javase学习15-自动装箱与自动拆箱
Javase学习15-自动装箱与自动拆箱
1. 问题引入
java中8种基本数据类型又对应了8种包装类,8种包装类属于引用数据类型,父类是Object。
那么问题来了,SUN公司为什么要再提供8种包装类呢?
我们试想这样一种情况,一个方法需要传入一个数字,但是参数类型是Object类型的,而数字属于基本数据类型,显然该方法无法接收基本数据类型的数字。这说明8种基本数据类型不够用。
那么这时该如何处理?
public class IntegerDemo01 {
//程序入口
public static void main(String[] args) {
}
//需要传入数字的方法
public static void getNum(Object obj) {
System.out.println(obj);
}
}
我们可以传入一个数字对应的包装类进去:
/**
* @Author TSCCG
* @Date 2021/6/2 18:16
*/
public class IntegerDemo01 {
public static void main(String[] args) {
//将100这个数字通过构造方法包装成一个对象
YourInt yourInt = new YourInt(100);
//getNum()方法虽然不能直接传入100,但是可以传入100对应的包装类型
getNum(yourInt);
}
public static void getNum(Object obj) {
System.out.println(obj);
}
}
//int类型的包装类,继承Object
class YourInt {
int value;
//new对象的时候通过构造方法将数字传入对象
public YourInt(int value) {
this.value = value;
}
//重写toString,使其输出的是数字
@Override
public String toString() {
return String.valueOf(value);
}
}
输出结果:
100
如此便解决了这种问题。
在实际开发中,我们不需要自己写包装类,8种基本数据类型对应的8种包装类,SUN公司已经为我们写好了,我们直接使用即可。
2. 八种基本数据类型对应包装类
基本数据类型 | 包装类型 |
---|---|
byte | java.lang.Byte(父类Number) |
short | java.lang.Short(父类Number) |
int | java.lang.Integer(父类Number) |
long | java.lang.Long(父类Number) |
float | java.lang.Float(父类Number) |
double | java.lang.Double(父类Number) |
boolean | java.lang.Boolean(父类Object) |
char | java.lang.Character(父类Object) |
3. 装箱与拆箱
3.1 装箱
装箱其实在上面的例题中演示过了,就是将基本数据类型包装成引用数据类型
//基本数据类型10经过构造方法的包装,实现了从基本数据类型向引用数据类型的转换
//基本数据类型 --> 引用数据类型(装箱)
Integer integer = new Integer(10);
3.2 拆箱
既然装箱是将基本数据类型转换成引用数据类型,那么拆箱可想而知就是将引用数据类型转换成基本数据类型。
那么,如何来实现呢?
查看包装类底层代码可知,8种包装类中有6个的父类都是Number,故可以先研究一下Number类。
Number类是一个抽象类,有如下方法:
修饰语和类型 | 方法 | 描述 |
---|---|---|
byte | byteValue() | 返回指定号码作为值 byte ,这可能涉及舍入或截断。 |
abstract double | doubleValue() | 返回指定数字的值为 double ,可能涉及四舍五入。 |
abstract float | floatValue() | 返回指定数字的值为 float ,可能涉及四舍五入。 |
abstract int | intValue() | 返回指定号码作为值 int ,这可能涉及舍入或截断。 |
abstract long | longValue() | 返回指定数字的值为 long ,可能涉及四舍五入或截断。 |
short | shortValue() | 返回指定号码作为值 short ,这可能涉及舍入或截断。 |
以上方法在所有数字包装类子类中都有,是专门负责拆箱的,我们可以用引用数据类型调用上面的方法拆箱成各种基本数据类型
//基本数据类型10经过构造方法的包装,实现了从基本数据类型向引用数据类型的转换
//基本数据类型 --> 引用数据类型(装箱)
Integer integer = new Integer(10);
//引用数据类型 --> 基本数据类型(拆箱)
int i = integer.intValue();
System.out.println(i);//10
//引用数据类型通过调用自身的floatValue返回一个float类型的数字
float f = integer.floatValue();
System.out.println(f);//10.0
4. 自动装箱与自动拆箱
4.1 JDK1.5之后,支持自动装箱和自动拆箱了
//自动装箱
//int基本数据类型-->自动转换-->Integer类型
Integer a = 200;
//自动拆箱
//Integer类型-->自动转换-->int基本数据类型
int b = a;
4.2 进行加减乘除运算时自动拆箱
判断以下语句:
//自动装箱
Integer a = 200;
System.out.println(a + 1);
a经过自动装箱后已经转换成引用数据类型了,那么将a与基本数据类型相加,程序是否报错?
答案是不会的。
"+" 两边要求的是基本数据类型的数字,而a是包装类,不属于基本数据类型,这里会进行自动拆箱,将a转换成基本数据类型,然后再进行相加。
运行结果:
201
判断以下语句
System.out.println("-----new Integer & int------");
Integer e = new Integer(128);
int f = 128;
System.out.println(e == f);
System.out.println("-----Integer & int------");
Integer j = 127;
int h = 127;
System.out.println(j == h);
结果:
-----new Integer & int------
true
-----Integer & int------
true
e是Integer类型的,f是int类型的,在进行e == f比较时,e会自动拆箱成int类型。
同理,j在与h比较时也会自动拆箱。
注意:
- JDK1.5之前是不能将包装类直接与基本数据类型相加减的。
- 在包装类与基本数据类型进行数学运算时都会自动拆箱
4.3 整数型常量池
基本数据类型自动装箱后转换成引用数据类型,存储的是一个对象的地址
Integer c = 128;// Integer c = new Integer(128);
Integer d = 128;
System.out.println(c == d);//false
但为什么下列程序的结果为true呢?
Integer e = 127;
Integer f = 127;
System.out.println(e == f);//true
java中为了提高程序的执行效率,将[-128~127]之间的所有包装对象都提前创建好,放入一个方法区的"整数型常量池"中。
目的是,只要在这个区间的数据,都不需要new对象了,直接从整数型常量池中取出来即可。
而上面的程序两个数是同一个数且处于-128~127中,故e和f变量保存的对象的内存地址是一致的。
4.4 基本数据类型作为参数传入集合中时自动装箱
ArrayList<Object> list = new ArrayList<>();
//向ArrayList集合中添加两个元素
list.add(10);
list.add(10);
我们知道,集合中只能存放对象的内存地址,在往集合中添加整数时,会装箱成引用数据类型,存储值为10的Integer对象地址。
我想知道,当把整数作为参数直接传入集合中时,是如何进行装箱的?是自动的还是手动的?是执行了Integer i= 10; 还是Integer i= new Integer(10); ?
如果把整数作为参数直接传入集合中时执行的是Integer i = new Integer(10); 那么当我们比较两个对象地址时结果会是false,如果执行的是Integer i = 10; 结果会是true.
比较两个对象的内存地址:
ArrayList<Object> list = new ArrayList<>();
list.add(10);
list.add(10);
//比较内存地址
System.out.println(list.get(0) == list.get(1));
结果:
true
故可得出结论:
当把整数作为参数直接传入集合中时,会自动装箱,执行Integer i = 10;
此外我们可以将集合中的两个对象地址打印出来:
但是使用什么方法打印呢?
Object的hashCode()默认是返回内存地址的,但是hashCode()可以重写,所以hashCode()不能代表内存地址的不同
System.identityHashCode(Object)方法可以返回对象的内存地址,不管该对象的类是否重写了hashCode()方法。
所以选择System.identityHashCode(Object)方法将两个对象的内存地址打印出来
ArrayList<Object> list = new ArrayList<>();
list.add(10);
list.add(10);
//打印内存地址
System.out.println(System.identityHashCode(list.get(0)));
System.out.println(System.identityHashCode(list.get(1)));
结果:
685325104
685325104
参考博客:https://blog.csdn.net/wusejiege6/article/details/114311746