记录一次 Integer 自动装箱的翻车经历

现场回顾:

1,在JNI开发过程中,遇到需要使用Integer进行参数传入传出的场景,比如结果为3。
2,在步骤1的jni层通过Integer传出的结果在一个八竿子打不着的地方使用Gson进行json字符串转换成实体对象的时候,所有int整形的变量都被赋值为3,但原始的值为0。

java接口

//不适用返回值传递的原因是,返回值被设计用来指示接口执行成功==0,其他为错误码。当成功时,再通过integer传递结果。
public native int func( Integer integer);

jni实现

   
extern "C"
JNIEXPORT jint JNICALL
Java_com_xxx_func(JNIEnv *env,jobject thiz,jobject jobjInteger) {

    uint result = 0;
 	//省略其他无关代码
 	...
 	//
	result = 3;
	
    jclass jcsInteger = env->FindClass("java/lang/Integer");
    jfieldID jfdValue = env->GetFieldID(jcsInteger, "value", "I");
    env->SetIntField(jobjInteger, jfdValue, result);
    
    return ret;
}

另外一个八竿子打不着的地方

public class Bean{
	 
	 private int type;
	     
	public int getType() {
        return type;
    }

    public void setType(int type) {
        this.type = type;
    }
}

调用(bug产生在这里)

public void demo(){
	
	//未通过new进行实例化,而是直接赋值,这里是代码段1。使用integerA = new Integer(0)后解决。
	Integer integerA = 0;
	func(integerA);//这里达到了目的,integerA成功被修改为3,并传递了出来。


	//另外一个八竿子打不着的地方,这里是代码段2
	String jsonString = "{"type": 0}";
	Bean bean  = new Gson().fromJson(jsonString,Bean.class);//内部肯定是发送了Integer自动装箱,源代码没找见,难道我分析的不对!!!!???
	//bean的type变量值,在这里不是期望的1,而是3!!!!
	//当然在项目里面的代码要比这里赋值的多,这篇文章是为了总结方便,把相关代码抽出来做成了案例方便回顾。

}

原因分析:

刚开始并未注意Integer,怀疑完Gson,怀疑多线程并发,怀疑完并发,怀疑jni,最后在怀疑一整晚人生后,痛定思痛,在使用排除法最后定位规律后,得出上面demo方法里面的规律,jni返回多少,json的整形就会修改多少。

解决方案:

排查代码后,一眼看出,integerA未进行实例化方式创建,使用Integer integerA = new Integer(0)后解决。

重拾基础:

问题虽然解决了,但是本着寻根溯源的宗旨,需要去彻底理解问题产生的原因,既然是用Integer出了问题,那就是自己使用Intger方法不当,还是基础不牢靠,才会被打回原形。

关键原因

-Integer提前将-128~127之间的Integer对象地址缓存在常量池中, Integer integer = 0 这样定义,会发生自动装箱,即Integer.valueOf(0),使用常量池缓存,不会创建新的对象,我们的这个BUG中,通过反射修改了常量值为3,造成开始常量值声明为0的,所有integer的值被修改。

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
  • 两段代码恰好都定义了Integer integer = 0; 在通过反射修改内部声明的value为3后,两个引用指向的值都被修改成了3。

你可能感兴趣的:(java,基础夯实)