问题来源:
在使用Java做某个实验的时候,有一个方法需要传入一个整数类型的值并在方法内修改值的大小。1
显然,使用int是不可行的,于是本着“只要传入引用类型就可以在方法内修改它的值”的误区,使用了Integer类型。
但事实上执行上述changeInteger()方法后的,integer的值还是1。
IDEA也给出了警告,在方法内修改后的是never used的。
原因
在Integer类型的源码中,我找到了如下一段:
可以看到Integer类型的值是被final修饰了的,也就是说一旦创建就不能改变。
我们再做一个测试,该测试返回一个Integer类型的引用每次改变后的哈希值:
class Values{
static void changeInteger(Integer aInteger){
System.out.println("a传给方法中的aInteger后:" + System.identityHashCode(aInteger));
aInteger += 1;
System.out.println("aInteger在方法内改变后:" + System.identityHashCode(aInteger));
}
}
public class AboutInteger {
public static void main(String[] args){
Integer a = 0;
System.out.println("a改变前:" + System.identityHashCode(a));
Values.changeInteger(a);
System.out.println("a传入方法后:" + System.identityHashCode(a));
a = 3;
System.out.println("a第一次改变后:" + System.identityHashCode(a));
}
}
结果为:
a改变前:793589513
a传给方法中的aInteger后:793589513
aInteger在方法内改变后:1349393271
a传入方法后:793589513
a第一次改变后:1338668845
可以看到a传给方法后,相当于是a和aInteger都引用了哈希值为793589513的这个对象,但是一旦a或者aInteger被改变后,它们所引用的对象就与原来不同了。所以在方法中改变了aInteger,相当于是在方法中把aInteger重新指向了一个新的对象,当然不能改变原来的a值了。
再看一段源码:
首先,给Integer类型赋值相当于执行了:a = Integer.valueof(int i),而在源码中,可以看到如果i在常量池范围内(一般是-128到127),就会从常量池中获取一个Integer对象,如果不在范围内,就会new一个对象。不管是哪种情况,都不再是原来所引用的对象了。
总结
改变Integer类型的值,不是像一般的引用类型一样执行一个Object.value = 1,因为在Integer中value被final修饰,不能改变。事实上是执行了Integer.valueof(int i)方法,该方法无论如何都会创建一个新的对象。
解决方案
- 使用返回值将必须在方法中修改的Integer类型返回
- 封装一个类myInteger{Integer value;}
参考文章:
【1】 以Integer类型传参值不变来理解Java值传参
【2】 工作中Integer类型传参遇到的陷阱
【3】 Integer参数传递问题
文章内容部分参考网络。主要作为自己整理的笔记使用。理解上表达上可能存在错误,欢迎指正。 ↩︎