【java基础2】自动装箱和拆箱

本文详细解析了Java中的自动装箱和拆箱机制,包括Integer的valueOf方法优化,以及自动拆箱在不同场景的应用。通过例子展示了装箱、拆箱的过程,以及它们在性能和内存使用上的考量。同时,提到了不同包装类valueOf方法的实现差异,并解释了自动装箱时编译valueOf方法,自动拆箱时编译intValue方法的原因。
摘要由CSDN通过智能技术生成

先有个大概了解,自动装箱和自动拆箱是java的一个语法糖(让我想到了C#中get和set的语法糖),是只针对基本数据类型和它对应的包装器类型的。

一、自动装箱

一般情况下,创建一个类的对象时,需要通过new关键字来实例化,比如:

Object obj = new Object();

但是对于包装器类型,除了用new创建外,还可以这样:

Integer a = 128;
Character c = '嗯';

这种与众不同的实例化方式就是【自动装箱】。简单地说,装箱就是将基本数据类型转换为包装器类型。128和‘嗯’都是基本数据类型,因为赋值给了包装器类型的变量,就被自动装箱变成了对应的包装器类型的对象。

其实,在编译上述两条代码时,系统实际执行的是:

Integer a = Integer.valueOf(128);
Character c = Character.valueOf('嗯');

valueOf( )方法是每个包装器类型里都有的,用来根据基本数据类型来创建对应的包装类对象。以Integer为例看一下valueOf( )的内部代码:

@Override
public static Integer valueOf(int i) {
	return  i >= 128 || i < -128 ? new Integer(i) : SMALL_VALUES[i + 128];
 }

判断完 i 的大小后,会进行这两个操作之一:

1.调用Integer的构造器,返回创建的Integer对象
2.返回SMALL_VALUES的第[i + 128]个元素

先看一下Integer的构造函数:

private final int value;
//初值为int类型的构造器
public Integer(int value) {
    this.value = value;
}
// 初值为String类型的构造器
public Integer(String string) throws NumberFormatException {
    this(parseInt(string));
}

再看 SMALL_VALUES[i + 128] 是什么:

private static final Integer[] SMALL_VALUES = new Integer[256]; 
// 为SMALL_VALUES数组赋初值
static {
    for (int i = -128; i < 128; i++) {
        SMALL_VALUES[i + 128] = new Integer(i);
    }
}

可以看到,SMALL_VALUES本身是一个静态的长度为256的Integer数组,那SMALL_VALUES[i + 128]就是该数组的第[i+128]号元素,也是个Integer对象。

为什么valueOf( )要这么写呢,不直接对每个int数值都用构造器返回一个Integer对象?其实是为了优化性能,Integer把 [-128, 127],共256个使用频率较高的小数字缓存了下来,这就出现了这样的现象:

Integer a = 100, b = 100;
System.out.println(a == b); //true
// 这是因为a得到的是SMALL_VALUES[228]的地址,b也是那个地址
// 而==比较的就是栈内存的内容,既然二者都存的是同一个地址,自然就相等了

Integer c = 1000,d = 1000;
System.out.println(c == d); //false
// c是新分配的地址,d也是新分配的地址,二者不一样,所以是false

需要注意的是:

· Integer、Short、Byte、Character、Long这四种包装类的valueOf方法的实现是类似的,都用到缓存。
· Double、Float的valueOf方法的实现是类似的,都只调用构造器。
· Boolea独树一帜,返回TRUE或者FALSE(可不是true和false啊)。

上面说到的TRUE和FALSE是什么呢,看一下定义:

public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
// TRUE和FALSE都是已经提前创建好的、静态的、不可赋值的Boolen对象

二、自动拆箱

拆箱是什么就好理解多了,拆箱就是将包装器类型转换成基本数据类型

下面是几种触发拆箱的情况:

// 1.将包装类赋值给基本数据类型
Integer i = 10; // 装箱
int n = i; // 拆箱

// 2.将包装类和基本数据类型进行==比较
Integer num1 = 400;  
int num2 = 400;  
System.out.println(num1 == num2); //true

// 3.包装类和基本数据类型进行算术运算
Integer num1 = 100;  
int num2 = 100;  
Long num3 = 200l;  
System.out.println(num1 + num2);  //200
System.out.println(num3 == (num1 + num2));  //true
System.out.println(num3.equals(num1 + num2));  //false

倒数第二行为啥是true很好理解,==和算术运算符两边的包装类都会进行拆箱。
最后一行中num3.equals(num1 + num2)为啥是false呢,就需要看一下equal的源码了:

// 不同包装类的equal方法都被重写了,以Long类型为例
@Override
public boolean equals(Object o) {
return (o instanceof Long) && (((Long) o).value == value);
}

可以看到,必须满足两个条件才会返回true值:

1.类型相同
2.值相等

num1 + num2执行时需要拆箱,得到的是值为200的基本数据类型,这就导致和num3值相等但类型不相同,所以返回false。

最后说一下,自动装箱时实际编译了包装类的valueOf方法,那对应的,自动拆箱时编译的是包装类的(以Integer为例)intValue方法:

@Override
public int intValue() {
     return value;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值