i3geek.com
闫庚哲的个人博客

Java中Auto Box/Unbox容易踩到的“坑”,Integer

Java中的Auto Box/Unbox对写代码带来了便利性,但也挺容易就踩进“坑”里,主要是NPE,对象比较还有内存浪费。

自动装箱、拆箱

先解释下什么是自动装箱、拆箱,基本数据类型的自动装箱(autoboxing)、拆箱(unboxing)是自J2SE 5.0开始提供的功能。

这是最典型的Auto Box/Unbox的代码:

Integer i = 100; 
int j = i;

一般我们要创建一个类的对象实例的时候,我们会这样: Class a = new Class(parameter);

当我们创建一个Integer对象时,却可以这样:Integer i = 100; (注意:不是 int i = 100; )

实际上,执行上面那句代码的时候,系统为我们执行了:Integer i = Integer.valueOf(100); 

此即基本数据类型的自动装箱功能。自动拆箱则与其相反,int t = i; //拆箱,实际上执行了int t = i.intValue();

注:在运算时,也可以自动拆箱(System.out.println(i++);//i是Integer类型)

易入坑

Auto Box/Unbox在有些场景下容易产生NPE,例如假设有一个这样的方法:

public void execute(int code){}

假设调用方是从一个Map或其他地方获取到的Integer,如果忘记判断是否为null,直接传给execute的方法,就有可能导致NPE

下面这个关于Integer的Case也是比较常见的:

Integer i = 100; 
Integer j = 100; 
Integer m = 200; 
Integer n = 200; 
System.out.println(i == j); 
System.out.println(m == n);

其执行结果为:true,false
原因是Integer会cache -127~127的Integer对象,而不在这个范围的则会每次new Integer。


在JavaOne 2010大会上,还有一段关于Auto Box/Unbox带来的频繁YGC的案例代码,代码的关键信息如下:

public class A{
private int code;
public A(int code){
this.code = code;
}
public int get(){
return code;
}
}
public class Demo{
public statice void main(String[] args) throws Exception{
Map<Integer,A> map = new HashMap<Integer,A>();
// 往map里放1w个从1开始的A对象
...
while(true){
Collection values = map.values();
for(A a: values){
if(!map.containsKey(a.getId())){
// 不可能发生,所以可以随便打点什么
}
Thread.sleep(1);
}
}
}
}

在上面的代码中,其实只需要把A中的private int code和public int get中的int改为Integer,就可以避免频繁的YGC,因此在写代码时还是要稍微注意下Auto Box/Unbox带来的影响。

例子

通过下面的代码例子说明包装类:

public class Main {
    public static void main(String[] args) {
         
        Integer a = 1;
        Integer b = 2;
        Integer c = 3;
        Integer d = 3;
        Integer e = 321;
        Integer f = 321;
        Long g = 3L;
        Long h = 2L;
         
        System.out.println(c==d);
        System.out.println(e==f);
        System.out.println(c==(a+b));
        System.out.println(c.equals(a+b));
        System.out.println(g==(a+b));
        System.out.println(g.equals(a+b));
        System.out.println(g.equals(a+h));
    }
}

看下输出结果:

true
false
true
true
true
false
true

当 “==”运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。

第一个和第二个输出结果没有什么疑问。第三句由于  a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。

相关函数

通过学习Integer肯定要对以下三个函数产生困惑,valueof、parseInt、intValue,下面进行一一介绍

Integer.valueof()返回的是Integer的对象

初始化为指定 String /Int值的新的 Integer 对象。若该 String 不能作为 int 分析,则抛出异常。自动包装调用的函数,注意返回值是Integer

Integer.parseInt() 返回的是一个int的值

它的作用就是将形参 s 转化为整数,注意返回值是int

Integer.valueof().intValue();返回的也是一个int的值。

自动拆包调用的函数,注意返回值是int

赞(0)
未经允许不得转载:爱上极客 » Java中Auto Box/Unbox容易踩到的“坑”,Integer
分享到: 更多 (0)

评论 2

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
  1. #1

    Integer会cache -128至127.

    ethan1年前 (2017-12-04)回复
    • 对的!

      yan1年前 (2017-12-04)回复