如何通过引用正确传递 Integer 类?

发布于 2024-09-11 16:43:54 字数 1043 浏览 6 评论 0 原文

我希望有人能为我澄清这里发生的事情。我在整数类中进行了一些研究,但由于整数覆盖 + 运算符,我无法弄清楚出了什么问题。我的问题在于这一行:

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);
        }
    }
}

这是我的预期输出:

Inc: 1
main: 1
Inc: 2
main: 2
Inc: 3
main: 3
Inc: 4
main: 4
Inc: 5
main: 5
Inc: 6
main: 6
...

这是实际输出。

Inc: 1
main: 0
Inc: 1
main: 0
Inc: 1
main: 0
...

为什么它会这样?

I am hoping that someone can clarify what is happening here for me. I dug around in the integer class for a bit but because integer is overriding the + operator I could not figure out what was going wrong. My problem is with this line:

Integer i = 0;
i = i + 1;  // ← I think that this is somehow creating a new object!

Here is my reasoning:
I know that java is pass by value (or pass by value of reference), so I think that in the following example the integer object should be incremented each time.

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);
        }
    }
}

This is my expected output:

Inc: 1
main: 1
Inc: 2
main: 2
Inc: 3
main: 3
Inc: 4
main: 4
Inc: 5
main: 5
Inc: 6
main: 6
...

This is the actual output.

Inc: 1
main: 0
Inc: 1
main: 0
Inc: 1
main: 0
...

Why is it behaving like this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

怪异←思 2024-09-18 16:43:54

有两个问题:

  1. 整数是按值传递,而不是按引用传递。更改方法内的引用不会反映到调用方法中传入的引用中。
  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";
}

There are two problems:

  1. Integer is pass by value, not by reference. Changing the reference inside a method won't be reflected into the passed-in reference in the calling method.
  2. Integer is immutable. There's no such method like Integer#set(i). You could otherwise just make use of it.

To get it to work, you need to reassign the return value of the inc() method.

integer = inc(integer);

To learn a bit more about passing by value, here's another example:

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";
}
好多鱼好多余 2024-09-18 16:43:54

上面的好答案解释了OP的实际问题。

如果有人需要传递需要全局更新的数字,请使用 AtomicInteger(),而不是创建建议的各种包装器类或依赖第 3 方库。

AtomicInteger() 当然主要用于线程安全访问,但如果性能影响不成问题,为什么不使用这个内置类。额外的好处当然是明显的线程安全性。

import java.util.concurrent.atomic.AtomicInteger

Good answers above explaining the actual question from the OP.

If anyone needs to pass around a number that needs to be globally updated, use the AtomicInteger() instead of creating the various wrapper classes suggested or relying on 3rd party libs.

The AtomicInteger() is of course mostly used for thread safe access but if the performance hit is no issue, why not use this built-in class. The added bonus is of course the obvious thread safety.

import java.util.concurrent.atomic.AtomicInteger
jJeQQOZ5 2024-09-18 16:43:54

整数是不可变的。您可以将 int 包装在自定义包装类中。

class WrapInt{
    int value;
}

WrapInt theInt = new WrapInt();

inc(theInt);
System.out.println("main: "+theInt.value);

The Integer is immutable. You can wrap int in your custom wrapper class.

class WrapInt{
    int value;
}

WrapInt theInt = new WrapInt();

inc(theInt);
System.out.println("main: "+theInt.value);
似狗非友 2024-09-18 16:43:54

有两种方法可以通过引用传递

  1. 使用 org.apache.commons.lang.mutable.MutableInt 来自 Apache Commons 库。
  2. 创建自定义类,如下所示

这是执行此操作的示例代码:

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

There are 2 ways to pass by reference

  1. Use org.apache.commons.lang.mutable.MutableInt from Apache Commons library.
  2. Create custom class as shown below

Here's a sample code to do it:

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;
    }
}

Output:

1
1
7
7
橙幽之幻 2024-09-18 16:43:54

您在这里看到的不是重载的 + 运算符,而是自动装箱行为。 Integer 类是不可变的,并且您的代码:

Integer i = 0;
i = i + 1;  

被编译器(自动装箱后)视为:

Integer i = Integer.valueOf(0);
i = Integer.valueOf(i.intValue() + 1);  

因此您的结论是正确的,即 Integer 实例已更改,但是不是偷偷摸摸的 - 它与 Java 语言的定义一致:-)

What you are seeing here is not an overloaded + oparator, but autoboxing behaviour. The Integer class is immutable and your code:

Integer i = 0;
i = i + 1;  

is seen by the compiler (after the autoboxing) as:

Integer i = Integer.valueOf(0);
i = Integer.valueOf(i.intValue() + 1);  

so you are correct in your conclusion that the Integer instance is changed, but not sneakily - it is consistent with the Java language definition :-)

夏了南城 2024-09-18 16:43:54

你在这里是正确的:

Integer i = 0;
i = i + 1;  // <- I think that this is somehow creating a new object!

第一:整数是不可变的。

其次:Integer 类不会覆盖 + 运算符,该行涉及自动拆箱和自动装箱(在旧版本的 Java 中,您会在上面的行中收到错误)。
当您编写 i + 1 时,编译器首先将 Integer 转换为(原始)int 以执行加法:自动拆箱。接下来,执行 i = 编译器将 int 转换为(新)整数:自动装箱。
所以 + 实际上被应用于原始的 int

You are correct here:

Integer i = 0;
i = i + 1;  // <- I think that this is somehow creating a new object!

First: Integer is immutable.

Second: the Integer class is not overriding the + operator, there is autounboxing and autoboxing involved at that line (In older versions of Java you would get an error on the above line).
When you write i + 1 the compiler first converts the Integer to an (primitive) int for performing the addition: autounboxing. Next, doing i = <some int> the compiler converts from int to an (new) Integer: autoboxing.
So + is actually being applied to primitive ints.

抱猫软卧 2024-09-18 16:43:54

我认为是自动装箱让你失望了。

这部分代码:

   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);
        }
    }

I think it is the autoboxing that is throwing you off.

This part of your code:

   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;
    }

Really boils down to code that looks like:

  public static Integer inc(Integer i) {
        i = new Integer(i) + new Integer(1);      
        System.out.println("Inc: "+i);
        return i;
    }

Which of course.. will not changes the reference passed in.

You could fix it with something like this

  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);
        }
    }
花之痕靓丽 2024-09-18 16:43:54

如果您将 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;
  }

,您将看到它总是打印“false”。
这意味着加法创建了一个新的 Integer 实例并将其存储在本地变量 i 中(“本地”,因为 i 实际上是所传递的引用的副本),留下了变量调用方法不变。

Integer 是一个不可变的类,这意味着您不能更改它的值,但必须获取一个新实例。在这种情况下,您不必像这样手动执行此操作:

i = new Integer(i+1); //actually, you would use Integer.valueOf(i.intValue()+1);

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

If you change your inc() function to this

 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;
  }

then you will see that it always prints "false".
That means that the addition creates a new instance of Integer and stores it in the local variable i ("local", because i is actually a copy of the reference that was passed), leaving the variable of the calling method untouched.

Integer is an immutable class, meaning that you cannot change it's value but must obtain a new instance. In this case you don't have to do it manually like this:

i = new Integer(i+1); //actually, you would use Integer.valueOf(i.intValue()+1);

instead, it is done by autoboxing.

掐死时间 2024-09-18 16:43:54

1 ) 仅引用的副本作为值发送到形式参数。当形参变量被赋予其他值时,对于该整型对象,形参的引用发生变化,但实参的引用保持不变。

公共类 UnderstandingObjects {

public static void main(String[] args) {

    Integer actualParam = new Integer(10);

    changeValue(actualParam);

    System.out.println("Output " + actualParam); // o/p =10

    IntObj obj = new IntObj();

    obj.setVal(20);

    changeValue(obj);

    System.out.println(obj.a); // o/p =200

}

private static void changeValue(Integer formalParam) {

    formalParam = 100;

    // Only the copy of reference is set to the formal parameter
    // this is something like => Integer formalParam =new Integer(100);
    // Here we are changing the reference of formalParam itself not just the
    // reference value

}

private static void changeValue(IntObj obj) {
    obj.setVal(200);

    /*
     * obj = new IntObj(); obj.setVal(200);
     */
    // Here we are not changing the reference of obj. we are just changing the
    // reference obj's value

    // we are not doing obj = new IntObj() ; obj.setValue(200); which has happend
    // with the Integer

}

}

类 IntObj {
整数a;

public void setVal(int a) {
    this.a = a;
}

}

1 ) Only the copy of reference is sent as a value to the formal parameter. When the formal parameter variable is assigned other value ,the formal parameter's reference changes but the actual parameter's reference remain the same incase of this integer object.

public class UnderstandingObjects {

public static void main(String[] args) {

    Integer actualParam = new Integer(10);

    changeValue(actualParam);

    System.out.println("Output " + actualParam); // o/p =10

    IntObj obj = new IntObj();

    obj.setVal(20);

    changeValue(obj);

    System.out.println(obj.a); // o/p =200

}

private static void changeValue(Integer formalParam) {

    formalParam = 100;

    // Only the copy of reference is set to the formal parameter
    // this is something like => Integer formalParam =new Integer(100);
    // Here we are changing the reference of formalParam itself not just the
    // reference value

}

private static void changeValue(IntObj obj) {
    obj.setVal(200);

    /*
     * obj = new IntObj(); obj.setVal(200);
     */
    // Here we are not changing the reference of obj. we are just changing the
    // reference obj's value

    // we are not doing obj = new IntObj() ; obj.setValue(200); which has happend
    // with the Integer

}

}

class IntObj {
Integer a;

public void setVal(int a) {
    this.a = a;
}

}

独﹏钓一江月 2024-09-18 16:43:54

我们可以使用 Apache Commons Mutable Int

public static Integer inc(MutableInt i) {
        i.increment();    
        System.out.println("Inc: "+i.getValue());
        return i;
}

public static void main(String[] args) {
        MutableInt integer = new MutableInt(0);
        for (int i =0; i<10; i++){
            inc(integer);
            System.out.println("main: "+integer.getValue());
        }
}

这会产生输出:

Inc: 1
main: 1
Inc: 2
main: 2
Inc: 3
main: 3
Inc: 4
main: 4
Inc: 5
main: 5
Inc: 6
main: 6
Inc: 7
main: 7
Inc: 8
main: 8
Inc: 9
main: 9
Inc: 10
main: 10

We can do this using Apache Commons Mutable Int

public static Integer inc(MutableInt i) {
        i.increment();    
        System.out.println("Inc: "+i.getValue());
        return i;
}

public static void main(String[] args) {
        MutableInt integer = new MutableInt(0);
        for (int i =0; i<10; i++){
            inc(integer);
            System.out.println("main: "+integer.getValue());
        }
}

This produces output:

Inc: 1
main: 1
Inc: 2
main: 2
Inc: 3
main: 3
Inc: 4
main: 4
Inc: 5
main: 5
Inc: 6
main: 6
Inc: 7
main: 7
Inc: 8
main: 8
Inc: 9
main: 9
Inc: 10
main: 10
孤蝉 2024-09-18 16:43:54

对于这个问题,已经有很多答案了。
大多数人都建议包装类..但我们确实不需要创建一个新类。有一种方法没有人提供。

并对问题进行了讨论。但在提供我的解决方案之前我会先简要介绍一下。

问题的产生是因为虽然有一个 Integer 类
这确实是通过引用传递的,但是对象是通过引用传递的
仅当您分配通过的新值时才可以通过方法访问
参考丢失。 Integer类不提供任何方法
允许修改该值。在参数上使用赋值符号
变量意味着丢失传递给参数的引用。

我的解决方案比使用包装类更简单。我们可以只使用大小为 1 的数组。

    public class PassIntByReference
{
    public static void main(String[] args) throws IOException
    {


        Integer intObj = 0;
        int[] arr = new int[1];

        System.out.println("intObj="+intObj);
        System.out.println("arr[0]="+arr[0]);

        usingIntegerClass(intObj);
        passByRefUsingArray(arr);

        System.out.println("After modifier .. Function call");
        System.out.println("intObj="+intObj);
        System.out.println("arr[0]="+arr[0]);

    }
    public static void usingIntegerClass(Integer immutableRef)
    {
        immutableRef = 1;//This creates a new object and original reference remains unModified.

    }

    public static void passByRefUsingArray(int[] arrOfSize1)
    {
        arrOfSize1[0]=1;
    }
}

输出

intObj=0
arr[0]=0
After modifier .. Function call
intObj=0
arr[0]=1

A lot of answers have been given to this problem.
Most people have suggested wrapper classes.. but we really do not need to create a new class. There is one method which no one has provided.

And problem has been discussed. But I will brief before providing my solution.

Problem arises from the fact that though there is a class Integer
which is indeed passed by reference but objects passed by reference
are only accessed via methods if you assign a new value that passed
reference is lost. Integer class does not provide any methods that
allow to modify that value. Using assignment sign on parameter
variable means losing the reference that was passed to parameter.

My solution is simpler than using wrapper class. We can just use array of size 1.

    public class PassIntByReference
{
    public static void main(String[] args) throws IOException
    {


        Integer intObj = 0;
        int[] arr = new int[1];

        System.out.println("intObj="+intObj);
        System.out.println("arr[0]="+arr[0]);

        usingIntegerClass(intObj);
        passByRefUsingArray(arr);

        System.out.println("After modifier .. Function call");
        System.out.println("intObj="+intObj);
        System.out.println("arr[0]="+arr[0]);

    }
    public static void usingIntegerClass(Integer immutableRef)
    {
        immutableRef = 1;//This creates a new object and original reference remains unModified.

    }

    public static void passByRefUsingArray(int[] arrOfSize1)
    {
        arrOfSize1[0]=1;
    }
}

output

intObj=0
arr[0]=0
After modifier .. Function call
intObj=0
arr[0]=1
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文