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. 八种基本数据类型对应包装类

基本数据类型包装类型
bytejava.lang.Byte(父类Number)
shortjava.lang.Short(父类Number)
intjava.lang.Integer(父类Number)
longjava.lang.Long(父类Number)
floatjava.lang.Float(父类Number)
doublejava.lang.Double(父类Number)
booleanjava.lang.Boolean(父类Object)
charjava.lang.Character(父类Object)

3. 装箱与拆箱

3.1 装箱

装箱其实在上面的例题中演示过了,就是将基本数据类型包装成引用数据类型

//基本数据类型10经过构造方法的包装,实现了从基本数据类型向引用数据类型的转换
//基本数据类型 --> 引用数据类型(装箱)
Integer integer = new Integer(10);

3.2 拆箱

既然装箱是将基本数据类型转换成引用数据类型,那么拆箱可想而知就是将引用数据类型转换成基本数据类型。

那么,如何来实现呢?

查看包装类底层代码可知,8种包装类中有6个的父类都是Number,故可以先研究一下Number类。

Number类是一个抽象类,有如下方法:

修饰语和类型方法描述
bytebyteValue()返回指定号码作为值 byte ,这可能涉及舍入或截断。
abstract doubledoubleValue()返回指定数字的值为 double ,可能涉及四舍五入。
abstract floatfloatValue()返回指定数字的值为 float ,可能涉及四舍五入。
abstract intintValue()返回指定号码作为值 int ,这可能涉及舍入或截断。
abstract longlongValue()返回指定数字的值为 long ,可能涉及四舍五入或截断。
shortshortValue()返回指定号码作为值 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TSCCG

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值