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

posted @ 2021-06-02 21:47  TSCCG  阅读(64)  评论(0编辑  收藏  举报