我怎样才能通过引用正确传递一个Integer类?
我希望有人能够澄清我在这里发生的事情。 我在整数类中挖了一会儿,但是因为整数覆盖了+
运算符,我不知道什么是错的。 我的问题是这条线:
Integer i = 0; i = i + 1; // ← I think that this is somehow creating a new object!
这里是我的推理:我知道java传递值( 或通过引用的值传递 ),所以我认为在下面的例子中,整数对象应该每次增加。
public class PassByReference { public static Integer inc(Integer i) { i = i+1; // I think that this must be **sneakally** creating a new integer... System.out.println("Inc: "+i); return i; } public static void main(String[] args) { Integer integer = new Integer(0); for (int i =0; i<10; i++){ inc(integer); System.out.println("main: "+integer); } } }
这是我的预期输出:
公司:1 主要:1 公司:2 主要:2 Inc:3 主要:3 公司:4 主要:4 Inc:5 主要:5 公司:6 主要:6 ...
这是实际产出。
公司:1 主:0 公司:1 主:0 公司:1 主:0 ...
为什么这样做?
有两个问题:
- Java是通过值传递的,而不是通过引用。 在方法中改变引用不会被反映到调用方法的传入引用中。
- 整数是不可变的。 没有像
Integer#set(i)
这样的方法。 否则你可以利用它。
要使其工作,您需要重新分配inc()
方法的返回值。
integer = inc(integer);
为了更多地了解通过价值传递,这里是另一个例子:
public static void main(String... args) { String[] strings = new String[] { "foo", "bar" }; changeReference(strings); System.out.println(Arrays.toString(strings)); // still [foo, bar] changeValue(strings); System.out.println(Arrays.toString(strings)); // [foo, foo] } public static void changeReference(String[] strings) { strings = new String[] { "foo", "foo" }; } public static void changeValue(String[] strings) { strings[1] = "foo"; }
Integer是不可改变的。 你可以在你的自定义包装类中包装int。
class WrapInt{ int value; } WrapInt theInt = new WrapInt(); inc(theInt); System.out.println("main: "+theInt.value);
有两种方法可以通过引用
- 使用Apache Commons库中的org.apache.commons.lang.mutable.MutableInt 。
- 创build自定义类,如下所示
这是一个示例代码来做到这一点:
public class Test { public static void main(String args[]) { Integer a = new Integer(1); Integer b = a; Test.modify(a); System.out.println(a); System.out.println(b); IntegerObj ao = new IntegerObj(1); IntegerObj bo = ao; Test.modify(ao); System.out.println(ao.value); System.out.println(bo.value); } static void modify(Integer x) { x=7; } static void modify(IntegerObj x) { x.value=7; } } class IntegerObj { int value; IntegerObj(int val) { this.value = val; } }
输出:
1 1 7 7
你在这里看到的不是超载+
oparator,而是自动装箱行为。 Integer
类是不可变的,你的代码是:
Integer i = 0; i = i + 1;
被编译器(在自动装箱后)看作:
Integer i = Integer.valueOf(0); i = Integer.valueOf(i.intValue() + 1);
所以你的结论是正确的, Integer
实例改变了,但不是偷偷摸摸 – 它符合Java语言的定义:-)
上面解释OP的实际问题的好答案。
如果有人需要传递需要全局更新的数字,请使用AtomicInteger(
)而不是创buildbuild议的或依赖于第三方库的各种包装类。
AtomicInteger(
)当然主要用于线程安全访问,但如果性能没有问题,为什么不使用这个内置的类。 额外的好处当然是明显的线程安全。
import java.util.concurrent.atomic.AtomicInteger
你在这里是正确的:
Integer i = 0; i = i + 1; // <- I think that this is somehow creating a new object!
首先:整数是不变的。
其次:Integer类不覆盖+
运算符,在该行涉及autounboxing和autoboxing(在旧版本的Java中,上面的代码会出错)。
编写i + 1
,编译器首先将整数转换为一个(原始) int
用于执行加法:autounboxing。 接下来,执行i = <some int>
编译器将从int
转换为(新)Integer:自动装箱。
所以+
实际上被应用于原始的int
s。
我认为这是自动装箱,把你扔掉。
这部分代码:
public static Integer inc(Integer i) { i = i+1; // I think that this must be **sneakally** creating a new integer... System.out.println("Inc: "+i); return i; }
真的归结为这样的代码:
public static Integer inc(Integer i) { i = new Integer(i) + new Integer(1); System.out.println("Inc: "+i); return i; }
哪一个当然..不会改变通过的参考。
你可以用这样的东西解决它
public static void main(String[] args) { Integer integer = new Integer(0); for (int i =0; i<10; i++){ integer = inc(integer); System.out.println("main: "+integer); } }
如果你改变你的inc()函数
public static Integer inc(Integer i) { Integer iParam = i; i = i+1; // I think that this must be **sneakally** creating a new integer... System.out.println(i == iParam); return i; }
那么你会看到它总是打印“假”。 这意味着添加会创build一个新的Integer实例并将其存储在局部variablesi(“local”,因为我实际上是传入的引用的副本),从而使调用方法的variables保持不变。
整数是一个不可变的类,这意味着你不能改变它的值,但必须获得一个新的实例。 在这种情况下,你不必这样手动:
i = new Integer(i+1); //actually, you would use Integer.valueOf(i.intValue()+1);
相反,它是通过自动装箱完成的。