我怎样才能通过引用正确传递一个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
 ...

为什么这样做?

有两个问题:

  1. Java是通过值传递的,而不是通过引用。 在方法中改变引用不会被反映到调用方法的传入引用中。
  2. 整数是不可变的。 没有像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); 

有两种方法可以通过引用

  1. 使用Apache Commons库中的org.apache.commons.lang.mutable.MutableInt 。
  2. 创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); 

相反,它是通过自动装箱完成的。