Java中容易踩到的”坑”系列之Auto Box/Unbox

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

例如这是最典型的Auto Box/Unbox的代码:
[code]
Integer i = 100;
int j = i;
[/code]
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 -128(-127)~127的Integer对象(感谢@yongchun的纠正),而不在这个范围的则会每次new Integer。

在JavaOne 2010大会上,还有一段关于Auto Box/Unbox带来的频繁YGC的案例代码,代码的关键信息如下:
[code]
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 map = new HashMap();
// 往map里放1w个从1开始的A对象

while(true){
Collection values = map.values();
for(A a: values){
if(!map.containsKey(a.getId())){
// 不可能发生,所以可以随便打点什么
}
Thread.sleep(1);
}
}
}
}
[/code]
在上面的代码中,其实只需要把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)来实现,代码如下:
[code]
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));
}

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

=============================
欢迎关注微信公众号:hellojavacases

关于此微信号:
用于分享Java的一些小知识点,Java问题排查的Cases,Java业界的动态,以及和大家一起互动讨论一些Java的场景和问题。

公众号上发布的消息都存放在http://hellojava.info上。