在了解包装类之前,我们先回顾一下Java的变量类型:
Java中有两种变量类型,一是基本类型,二是引用类型,两者区别如下:
基本类型就是值类型,总共有八种:整型:byte short int long
浮点型:float double
字符型:char
布尔型:boolean
引用类型:
非基本类型的都是引用类型,如String和ArrayList等以类名作为变量类型。在Java中,非基本类型都可以理解为对象,因为都是对引用地址进行操作。
为什么会出现包装类?
在集合中,都是以对象作为元素,数组也同样可以添加元素,但功能很少,且类型必须一致。在没包装类之前,我们是无法直接往集合中添加值类型的元素的,需要自己写一个类,该类的成员变量为值类型。还有集合中常用的get(Object obj)的方法,需要的是一个Object类型,那么值类型是不能作为参数的。所以后来就出现了包装类。包装类的作用:
使基本类型具备对象的特性,让我们以对象的方式去操作值类型。增加了许多操作值类型的方法,例如Integer类很方便的获取int类型的最大值和最小值,Character类可以对字符进行判断操作。
基本类型和包装类的对应关系:
自动装箱和拆箱机制机制:
- 自动装箱:基本类型自动转为包装类,相当于调用 valueOf()方法。
- 自动拆箱:包装类自动转为基本类型,相当于调用 xxxValue()方法。
- 该机制的作用就是简化了基本数据类型和相对应包装类对象的转化步骤。
演示自动装箱和自动拆箱:
Integer a = Integer.valueOf(123); //手动转换
Integer a1 = 123; //自动装箱 ,相当于调用valueOf()
Integer in = new Integer(“123”); //可以放入字符串
int a2 = a1; //自动拆箱,相当于调用a1.intValue()
System.out.println(a1 == a2); //true
public static int test(Object obj) {
return (int) obj;
}
public static int test(Integer in) {
return in;
}
传入的是Integer类型参数时,return in语句会将包装类自动拆箱,所以无需强制转换。而传入的是Object类型时,两者类型不符合,会提示我们需要强制转换。包装类中的常用方法:
把值类型或者字符串转成对应的包装类:- 使用构造器:new Integer() 、new Byte()、new Short()、new Character()...
- 使用valueOf() :Integer.valueOf() 、Byte.valueOf()、Float.valueOf()...
- Integer.parseInt()
- Boolean.parseBoolean()
- Short.parseShort()
Character类需要注意的地方:
Character包装类没有parseXxx()方法,无法把字符串变成字符,因为字符串就是一堆字符组成的,但是调用该类方法来获取指定字符。它的valueOf()方法无法传入字符串,只能将字符转成包装类。整型包装类的缓存机制:
在整型包装类中都有一个静态内部类来作为缓存对象。因为字符和整型可以相互转换,所以也实现了缓存机制,如下所示:IntegerCache用于缓存Integer对象
ByteCache 用于缓存 Byte 对象
ShortCache 用于缓存 Short 对象
LongCache 用于缓存 Long 对象
CharacterCache 用于缓存 Character 对象
整型类型包装类的缓存范围在[-128~127]之间,Character类的缓存范围在[0~127]之间。实现缓存机制的包装类只有Integer类可以改变缓存范围,别的都是固定的。
以Integer类为例,测试缓存机制的作用:
Integer i = 127;
Integer j = 127;
Integer k = 128;
Integer k1 = 128;
System.out.println(i == j); //true
System.out.println("i的原始哈希值:" + System.identityHashCode(i));
System.out.println("j的原始哈希值:" + System.identityHashCode(j));
System.out.println(k == k1);
System.out.println("k的原始哈希值:" +System.identityHashCode(k));
System.out.println("k1的原始哈希值:" +System.identityHashCode(k1));
//true
//i的原始哈希值:366712642
//j的原始哈希值:366712642
//false
//k的原始哈希值:1829164700
//k1的原始哈希值:2018699554
上面代码中的输出结果可以看出,当两个Integer类的数值在缓存范围内,则会使用同一个Integer对象,通过原始哈希值可以验证。
当超出缓存范围,则是不同的Integer对象,通过==操作符来比较则得到false,因为内存地址不同。通过原始哈希值可以验证。
//自动拆箱成值类型再对比
int q1 = k;
int q2 = k1;
System.out.println(q1 == q2);//true
所以当使用的Integer对象值超过缓存范围时,不要使用==操作符,而应该使用equals()方法来比较内容。因为整型包装类都重写了equals()方法。
贴上Integer类重写的equals()方法,看它如何对比的:
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
该方法最终是以它们对应的值类型来进行对比的,想了解更详细的细节,可以自己去查看Integer源码。