java包装类 —— 自动装箱与拆箱、常量池内存复用(底层原理)

一、什么是包装类

java提供了八种基本数据类型,但是有些需求,靠基本数据类型无法满足我们的需求,或不方便。例如做一些进制转换,获取int数据类型的取值范围等等。
java中的数据类型int,double等不是对象,无法通过向上转型获取到Object提供的方法,而像String却可以,只因为String是一个对象而不是一个类型。基本数据类型由于这样的特性,导致无法参与转型,泛型,反射等过程。
因此为了弥补这个缺陷,Java在JDK1.0的时候就设计了基本数据类型的包装类。

包装类基本数据类型
Bytebyte
Shortshort
Integerint
Longlong
Floatfloat
Doubledouble
Characterchar
Booleanboolean

包装类对象为引用类型。
.
基本类型和引用类型的区别有以下几点:

  • 基本类型不是对象,而引用类型是对象.
  • 声明方式:基本数据类型直接声明,引用数据类型需要用new关键字创建.
  • 存储的位置:基本数据类型存储在堆栈中,引用类型则通过引用指向实例,具体的实例保存在堆中.
  • 基本数据类型的初始值依据其类型而定,引用数据类型初始值为null.

二、包装类的作用

包装类的作用是为了方便对基本数据类型进行操作。

Boolean和Character为对象型(Object
的直接子类)包装类,Byte、Short、Integer、Long、Float、Double为数值型(继承了Number类)包装类。

以Integer为例,部分源码如下:

public final class Integer extends Number implements Comparable<Integer>, Constable, ConstantDesc {
	//int类型的最小值为, -2^31
	@Native public static final int   MIN_VALUE = 0x80000000;
	//int类型的最大值为, 2^31-1
	@Native public static final int   MAX_VALUE = 0x7fffffff;

	//int的toString方法
	public static String toString(int i, int radix) {
	...
	}
	//int转16进制方法
	public static String toHexString(int i) {
        return toUnsignedString0(i, 4);
    }
	//int转8进制方法
	public static String toOctalString(int i) {
        return toUnsignedString0(i, 3);
    }
    //int转2进制方法
    public static String toBinaryString(int i) {
        return toUnsignedString0(i, 1);
    }
    ...
}

定义了int类型的属性和操作int类型的方法。方便对基本数据类型进行操作。

三、自动装箱与拆箱

JDK5新特性:包装类的自动装箱和自动拆箱

1. 装箱

自动装箱:基本数据类型自动会转换为包装数据类型。

(1)JDK1.5前,实现手动装箱写法:

//jdk1.5前,手动装箱
Integer i = new Integer(5);

(2)JDK1.5后,自动装箱写法:

//jdk1.5后,自动装箱
Integer i = 5;

包装类中的valueOf方法,用于装箱:

//返回Integer类型的对象
public static Integer valueOf(int i) {
	if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
2. 拆箱

自动拆箱:包装数据类型转换为基本数据类型。

(1)JDK1.5前,手动拆箱写法:

Integer i = new Integer(5);

//jdk1.5前,手动拆箱
int i = (int)i;

(2)JDK1.5后,自动拆箱写法:

Integer i = new Integer(5);

//jdk1.5前,自动拆箱
int i = i;

Number中声明的intValue()等方法返回基本数据类型用于拆箱:
在这里插入图片描述

四. 自动装箱和拆箱的实现原理

1. 自动装箱与拆箱
//自动装箱
Integer i = 5;
//自动拆箱
int i = i;

通过javap 反编译看一下class:

//装箱
Integer i = Integer.valueOf(5);
//拆箱
int i = intValue(5);

当你使用基本类型数据在做运算或者将其放入泛型仓库的时候,jvm识别到需要转型,就会触发装箱,装箱其实不是自动调用valueOf函数,在编译器编译的时候,如果发现了这样的程序,jvm就会在class指令中加入装箱程序,改变编译的结果。自动拆箱也是如此。

五、包装类的内存复用(常量池)

在程序运行时 -128 ~ 127的int值就被jvm装入常量池中。自动装箱时,对于Integer var = 5,如果var指向的对象在-128 至 127范围内的赋值时,生成的Integer实例化对象是由IntegerCache.cache()方法产生,它会复用已有对象。和String的常量池是一个道理,cache()方法会将位于-128~127范围内产生的Integer对象入池,下次使用的时候,从池中拿去,就不会在创建了。

所以,在这个数值区间内的 Integer对象的栈指向(属性名) 可以直接使用==进行判断,因为值相同,指向的就是同一片区域。但是这个区间之外的所有数据,自动装箱都会在堆上产生实例化,并不再复用已有对象,尤其要注意,为了避免这个问题,推荐使用equals 方法进行Integer值的比较判断。当然判断值和地址是否都相同,还是得用 ==。

除了两个包装类Long和Double 没有实现这个缓存技术,其它的包装类均实现了它。这是因为Long和Double类型的对象占用内存较大,不宜应用于常量池中,长时间占用内存。

包装类都实现了equals方法,如Integer中:

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

内存复用实例:

int:
在这里插入图片描述
Integer:

此时,a和b地址相同,a和b复用常量池的内存。

再看valueOf方法:

//返回Integer类型的对象
public static Integer valueOf(int i) {
	if (i >= IntegerCache.low && i <= IntegerCache.high)		//判断是否已在常量池中
        return IntegerCache.cache[i + (-IntegerCache.low)];		//溢出计算,得到的对应索引,返回引用
    return new Integer(i);		 //不在-128~127的,都是创建的新对象
}

默认IntegerCache.low 是-128,IntegerCache.high是127,当自动装箱调用valueOf方法时,执行常量池的内存复用。

不复用的情况:
在这里插入图片描述
用new创建,其实例对象存储于不同的堆空间地址。
此时,a和b地址不同。

六、包装类的应用

  • 所有的相同类型的包装类对象之间值的比较,全部使用equals()方法。

  • 所有的POJO(简单Java类,只包含基本属性,有参构造,get/set)类属性推荐使用包装类数据类型,类属性即static属性。

  • RPC(远程方法调用)方法返回值和参数必须使用包装数据类型。

  • 推荐所有的局部变量使用基本数据类型。

包装类转换要点:

字符串转数值类型时字符串只能包含数字,否则会抛出 NumberFormatException 异常,这是一个非受查异常。

但是字符串转Boolean是个特例,parseBoolean()方法会将”true”转为true,而将非”true”的字符串转为false。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Whitemeen太白

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

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

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

打赏作者

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

抵扣说明:

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

余额充值