昨天改 bug遇到一个问题,用integer定义的id类型用==判断相等的时候,如果id的值大于127的时候,比较出来的结果是不准确的。于是打算了解下包装类这方面的知识。
众所周知,java有8种基本的数据类型,根据所占空间大小排序如下:
byte,char,short,int long,float,double 还有boolean
而每种基本类型都有其相应的包装类型。
1.包装类型和基本数据类型的区别:
- 从数据类型上来讲,包装类型属于对象,而基本数据类型不属于对象(仅不属于对象类型)
- 从jvm存储来讲,包装类型属于对象,存储于队中,通过存储于栈中的对象的引用来引用实现。而基本数据类型本身直接存储于栈中,不会占用堆内的内存
- 从创建方式来讲,因为包装类型属于对象,故需要通过new关键在堆中创建,而基本数据类型不需要(更正:integer并不一定非要需要new关键字创建,后面装箱拆箱会讲到)
2.那我们为什么要用包装类型呢
- 通过上面的区别我们得知,基本数据类型不是对象类型,所以说没有继承父类Object,也并不具有对象类型的一些属性和特性。而在实际 的开发中,我们是需要用到对象类型的属性的,比如ArrayList和HashMap,都是需要存储object类型的对象值的,所以说包装类型是为了应对某些需要对象类型的数据而基本类型并不满足的情况
3.关于装箱和拆箱
- 装箱指的是基本数据类型转换为包装类型
- 拆箱指的是包装类型转化为基本数据类型
下面我们以integer为例详解自动装箱和自动拆箱
对于Integer数据类型,我们有一个名为IntegerCatch的内部静态类
private static class IntegerCache {
static final int low = -128;
static final int high;
static final Integer cache[];
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++)
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
对于这种类,查阅了相关文档,发现解释如下:
- 在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。
- 这种 Integer 缓存策略仅在自动装箱(autoboxing)的时候有用,使用构造器创建的 Integer 对象不能被缓存
这种缓存机制的要求是最低值是-128,最高值为可设置(jvm启动参数-XX:AutoBoxCacheMax=size),默认是127.在调用valueOf方法的时候如果值在这个范围,会采取利用缓存机制存储对象,否则会在堆内简历响应对象,代码如下:
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
所以,我们再次查阅相关资料来弄清楚,到底装箱是什么?
java中的自动装箱大概可以分为以下三类:
1.Integer a=3;
2.Integer a=Integer.valueof(3);
3.Integer a=new Integer(3)
而第一种方式其实内在也是通过调用Integer.valueof()方法实现的,所以说装箱都是通过valueof方法实现的。接下来针对装箱进行下测试:
public static void main(String[] args) {
Integer a1=3;
Integer a2 = Integer.valueOf(3);
Integer a3=new Integer(3);
System.out.println("a1==a2:"+(a1==a2));
System.out.println("a1==a3:"+(a1==a3));
System.out.println("a2==a3:"+(a2==a3));
}
这样的结果并不意外,因为a1,a2是通过自动装箱创建的对象,因为值小于127,所以存储在缓存区,两者都未在堆内建立对象。所以说a1和a2都可以看作是缓存区内同一对象的引用。但是a3是通过new建立的对象,两个对象的“==”操作是判断是否指向同一个对象,所以这样的结果并不出乎意料
接下来测试下大于127的情况
public static void main(String[] args) {
Integer a1=300;
Integer a2 = Integer.valueOf(300);
Integer a3=new Integer(300);
System.out.println("a1==a2:"+(a1==a2));
System.out.println("a1==a3:"+(a1==a3));
System.out.println("a2==a3:"+(a2==a3));
}
三者都在堆内建立了对象,所以用==判断都不相同
测试下正好等于127的情况
public static void main(String[] args) {
Integer a1=127;
Integer a2 = Integer.valueOf(127);
Integer a3=new Integer(127);
System.out.println("a1==a2:"+(a1==a2));
System.out.println("a1==a3:"+(a1==a3));
System.out.println("a2==a3:"+(a2==a3));
}
public static void main(String[] args) {
Integer a1=-128;
Integer a2 = Integer.valueOf(-128);
Integer a3=new Integer(-128);
System.out.println("a1==a2:"+(a1==a2));
System.out.println("a1==a3:"+(a1==a3));
System.out.println("a2==a3:"+(a2==a3));
}
综上,对于上述情况,当取值为a -128<=a<=127的时候,自动装箱机制会在缓存区取一个对象给a引用,这时候无论是采取equals还是“==”方法,都能正确的判断两个对象值相等的情况。但是当值大于127或者小于-128的时候。利用自动装箱机制,将会在堆内随机内存生成不同的对象,这时候再用“==”判断的话,由于两个引用指向了不同的位置,所以必定返回false。文章开头提到的问题解决。
接下来了解下拆箱相关的问题,关于 拆箱,网上查到的资料,拆箱是根据包装其的xxxValue实现的,源码如下:
private final int value;
public int intValue() {
return value;
}
public byte byteValue() {
return (byte)value;
}
public short shortValue() {
return (short)value;
}
public float floatValue() {
return (float)value;
}
public double doubleValue() {
return (double)value;
}
其实实际中的应用如下,判断是否等于某个值的时候不需要通过xxxValue判断,只需要通过==即可实现自动拆箱,然后进行判断
public static void main(String[] args) {
Integer a1=200;
System.out.println("a1==a2:"+(a1==200));
System.out.println("a1==a2:"+(a1.intValue()==200));
}
a1==a2:true
a1==a2:true
总结大概如下,欢迎各位大佬指出不足的地方,本人很菜,共同进步