1. 包装类概述
Java中有8中基本数据类型,分别是:
包装类就是这8种数据类型所对应的引用数据类型,分别是:
可能有同学会问:Java为什么要给基本数据类型提供对应的引用数据呢?
第一,Java是面向对象的语言,给它们设计对应的引用类型,非常符合万物皆对象的设计理念
第二,有类,那么就会有属性,会有方法。那么针对这些类型的一些数据操作可以变得更加简单,比如说:
如果要根据字节数计算int的取值范围就有些麻烦,但是它的包装类Integer就提供了Integer.MIN_VALUE
和Integer.MAX_VALUE
记录了范围数据
第三,Java中集合和泛型作为经常使用的对象,它们只支持引用数据类型,比如说,如果需要使用集合存储int类型整数。那么直接声明集合的泛型为int是不可以的,此时就可以使用它对应的包装类Integer
所以结论就是:使用包装类可以方便不同数据类型的相关操作,另外集合也会经常用到包装类!
2. 自动装箱
既然包装类有很好的使用价值,那么自然就是要创建它们的对象去使用。而自动装箱就是一种可以很方便快捷的拿到它们对象的方式,几种包装类的设计非常相似,这里就以Integer举例
Integer num = 23;
这种直接把基本数据类型的数据值赋值给包装类类型的变量,就是自动装箱。底层会自动调用Integer的valueOf(23)
方法,拿到一个Integer对象
所以Integer num=23;
和Interger.valueOf(23);
底层都是一样的,推荐使用自动装箱的方式获取对象,这样的代码更加简单、直接
3. 自动拆箱
和自动装箱相反,自动拆箱指的就是可以将一个包装类对象直接赋值给其对应的基本数据类型变量,例如:
Integer num = 23;
int a = num;
这种情况就是所谓的自动拆箱,其实底层是调用Integer包装类的intValue()
方法,返回了记录的数据值23。以此类推,如果是Double,底层就是调用的doubleValue()
方法获取数据值返回
4. 常见操作
基本数据类型转字符串:
静态方法:toString(基本数据类型的数据值),例如:String str = Integer.toString(23);
推荐使用String的静态方法:valueOf(基本数据类型的数据值),例如:String str = String.valueOf(20);
数值内容的字符串转基本数据类型:
调用parseXXX的方法,例如:int num = Integer.parseInt("66");
注意:如果字符串不是数值内容,而是"a"、"b"、"中"...这样的非数值,就会引发异常:NumberFormatException
5. 面试题
观察以下代码,说结果
Integer a = 127;
Integer b = 127;
System.out.println(a == b);
Integer c = Integer.valueOf(127);
System.out.println(a == c);
Integer d = new Integer(127);
System.out.println(a == d);
Integer x = 128;
Integer y = 128;
System.out.println(x == y);
结果分别是:true,true,false,false
原因:
Integer自动装箱底层会调用valueOf()方法,源代码:
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
可以看到,会拿数据值和Integer的一个静态内部类IntegerCache类的low属性于high属性做范围判断,其中low的值是-128,high的值是127
也就是说,调用valueOf()方法,会判断数据是否在-128~127的范围内。如果在范围内,就从静态内部类IntegerCache的一个cache数组属性中获取一个Integer对象
如果不在这个范围内,就是新new一个Integer对象
IntegerCache这个静态内部类有一个静态代码块:
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
h = Math.max(parseInt(integerCacheHighPropValue), 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(h, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
// Load IntegerCache.archivedCache from archive, if possible
CDS.initializeFromArchive(IntegerCache.class);
int size = (high - low) + 1;
// Use the archived cache if it exists and is large enough
if (archivedCache == null || size > archivedCache.length) {
Integer[] c = new Integer[size];
int j = low;
for(int i = 0; i < c.length; i++) {
c[i] = new Integer(j++);
}
archivedCache = c;
}
cache = archivedCache;
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
一般情况下,这段代码第一个if不会进入,所以high的值就被赋值127,经过计算,size变量的值就是256
第二个if语句的条件通常是可以成立的,所以就创建了一个长度为256的Integer类型数组,通过一个for循环,给这个数组就从-128开始赋值,一致赋值到127结束,刚好是256个
至此,内部类中就出现了一个Integer类型数组,缓冲了256个Integer对象,对应的数据范围正好是:-128~127
那么通过以上分析可知,只要是通过自动装箱或者valueOf()方法去获取对象,只要数据范围在-128~127,不管获取多少次,都会从数组中去拿缓冲的对象,所以拿到的始终是同一个,所以判断的结果就是true
但如果不在这个范围内,就是去new一个新的Integer对象,会开辟一个新的对象空间,地址值肯定不一样,所以结果就是false