当前位置: 首页 > Java > 正文

Java Auto Box/Unbox注意事项

1 星2 星3 星4 星5 星 (1 次投票, 评分: 5.00, 总分: 5)
Loading ... Loading ...
baidu_share

Java中的Auto Box/Unbox对写代码带来了便利性,但也挺容易就踩进”坑“里。

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

1
2
Integer i = 100;
int j = i;

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

1
public void execute(int code)

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

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

1
2
3
4
5
6
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 -128(-127)~127的Integer对象,而不在这个范围的则会每次new Integer。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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<A> 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带来的影响。

问:能讲讲sun.misc.Unsafe的用法吗?
答:通常直接使用到Unsafe的都是一些特殊场景,例如对性能要求很高,或内存消耗很大的等等。

常见的是使用Unsafe来做反射的优化、内存的管理。

代码中通常有很多需要通过反射get/set来操作bean中的field,如果操作非常频繁的话,可以通过Unsafe来进行优化(在eclipse里使用Unsafe时需要修改下配置,否则会不能import,修改方法:Window->Preference->Java->Compiler->Errors/Warnings->Deprecated and restricted API -> Forbidden Reference 修改成Warning或Ignore),主要是通过Unsafe.objectFieldOffset和Unsafe.getXXX(例如getInt)来实现,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class User {
 
    private String name;
 
    public String getName() {
        return name;
    }
 
    public void setName(String name) {
        this.name = name;
    }
 
 
}
 
import sun.misc.Unsafe;
import java.lang.reflect.Field;
 
public class UserTestForUnsafe {
 
    private static final Unsafe unsafe;
 
    static {
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } 
        catch (Exception e) {
            throw new RuntimeException("get unsafe fail", e);
        }
    }
 
    private static final Field nameField;
    static {
        try {
            nameField = User.class.getDeclaredField("name");
        } 
        catch (Exception e) {
            throw new IllegalStateException("get User.name error", e);
        }
    }
 
    public static final Field getNameField() {
        return nameField;
    }
 
    public static void main(String[] args) throws Exception{
        User user = new User();
        user.setName("bluedavy");
        long offset = unsafe.objectFieldOffset(nameField);
        System.out.println(unsafe.getObject(user, offset));
    }
 
}

关于使用Unsafe直接进行内存管理,可参考这篇文章:
http://highlyscalable.wordpress.com/2012/02/02/direct-memory-access-in-java/

本文固定链接: http://www.chepoo.com/java-auto-boxunbox-considerations.html | IT技术精华网

Java Auto Box/Unbox注意事项:等您坐沙发呢!

发表评论