Immutable Integer包装到另一个类中(按值调用)

时间:2018-05-02 09:22:12

标签: java integer immutability

我想知道在Java中按值调用并尝试了一些代码。

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        Integer integer = 4;
        System.out.println(integer);
        test.change(integer);
        System.out.println(integer);
    }

    public void change(Integer integer) {
        integer++;
    }
}

由于java是按值调用的,我的输出类似于:

4
5

但是它打印了

4
4

然后我了解到Integers是不可变的,所以我的方法“change”创建了值为5的新Integer,而main中的“integer”仍然是4。

然后我写下了以下课程:

public class Test {
    public static void main(String[] args) {
        Test test = new Test();
        MyInteger myInteger = new MyInteger();
        myInteger.x = 4;
        System.out.println(myInteger.x);
        test.change(myInteger);
        System.out.println(myInteger.x);
    }
    public void change(MyInteger myInteger) {
        myInteger.x++;
    }
} 
class MyInteger {
    Integer x;
}

现在输出就像我预期的那样

4
5

这是我的论文:

方法change(Integer integer)通过创建新的不可变myInteger来代替Integer来修改Integer x对象,并且此方法中的参数myInteger指向同一个实例始终为MyInteger。我对吗?

2 个答案:

答案 0 :(得分:2)

让我们做一点分析并深入研究两种方法的字节码。

public class Main {

    public static void main(String[] args) {
        Main test = new Main();
        Integer integer = 4;
        System.out.println(integer);
        test.change(integer);
        System.out.println(integer);
    }

    public void change(Integer integer) {
        integer++;
    }
}

change方法的字节码是:

public change(Ljava/lang/Integer;)V
   L0
    LINENUMBER 14 L0
    ALOAD 1
    ASTORE 2
    ALOAD 1
    INVOKEVIRTUAL java/lang/Integer.intValue ()I // gets value of wrapped int
    ICONST_1 // load 1 into stack
    IADD // add 1 to your value
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; // return
    DUP
    ASTORE 1
    ASTORE 3
    ALOAD 2
    POP

正如您所看到的,该值会增加并且会丢失,因为它未被返回。所以我们需要返回这个值才能得到结果,对吗?

有人可以认为以下代码的输出为4, 5

public class Main {
    public static void main(String[] args) {
        Main test = new Main();
        Integer integer = 4;
        System.out.println(integer);
        Integer integerNew = test.change(integer);
        System.out.println(integerNew);
    }

    public Integer change(Integer integer) {
        return integer++;
    }
}

输出为4, 4

为什么呢?因为这是后增量。您增加了一个新的Integer并返回了旧的public change(Ljava/lang/Integer;)Ljava/lang/Integer; L0 LINENUMBER 14 L0 ALOAD 1 ASTORE 2 // store the copy of the first Integer (4) ALOAD 1 // INVOKEVIRTUAL java/lang/Integer.intValue ()I // get value of first ICONST_1 // load 1 IADD // add 1 INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; // get modified value Integer (5) DUP ASTORE 1 // save it ASTORE 3 ALOAD 2 // load the copy Integer (4) ARETURN // return it 。分析字节码证实了这一点:

Integer

因此,我们可以看到public Integer change(Integer integer) { return ++integer; } 的值增加了......并且再次丢失,因为我们在增加之前的状态中返回值。

我们正在寻找

预增量

public change(Ljava/lang/Integer;)Ljava/lang/Integer;
   L0
    LINENUMBER 14 L0
    ALOAD 1 // Integer (4)
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ICONST_1 // load 1
    IADD // becomes Integer (5)
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    DUP
    ASTORE 1 // save it
    ARETURN // return Integer (5)

字节码是:

public class Main {

    public static void main(String[] args) {
        Main test = new Main();
        MyInteger myInteger = new MyInteger();
        myInteger.x = 4;
        System.out.println(myInteger.x);
        test.change(myInteger);
        System.out.println(myInteger.x);
    }

    public void change(MyInteger integer) {
        integer.x++;
    }
}

class MyInteger {
    Integer x;
}

该值在返回之前递增,我们可以接受它。

分析最后一个案例:

public change(Lcom/test/MyInteger;)V
   L0
    LINENUMBER 15 L0
    ALOAD 1
    ASTORE 2
    ALOAD 2 
    GETFIELD com/test/MyInteger.x : Ljava/lang/Integer; // get Integer (4)
    ASTORE 3 // store copy of Integer value (4)
    ALOAD 2
    ALOAD 2
    GETFIELD com/test/MyInteger.x : Ljava/lang/Integer; // get Integer (4)
    INVOKEVIRTUAL java/lang/Integer.intValue ()I
    ICONST_1 
    IADD // add 1
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer; 
    DUP_X1
    PUTFIELD com/test/MyInteger.x : Ljava/lang/Integer; // PUT Integer (5) BACK!
    ASTORE 4 
    ALOAD 3
    POP

及其字节码:

PUTFIELD

正如您所看到的,从MyInteger行开始,Integer实例开始持有对值为5的{​​{1}}的引用。

希望这有助于您了解有关该主题的更多详细信息。

答案 1 :(得分:1)

你是对的。 myInteger.x++实际上是在改变值,因为Integer x实例变量是共享的。

Infact myInteger.x++还会创建一个新的不可变Integer实例。但是您可以通过test变量访问此新实例,因此您永远不会忘记它的更改。

作为避免混淆的拇指规则我会说:

  

当你将一个参数传递给某个函数时,它并不重要   你直接用它做什么,但你用它的子对象做什么。

在您的情况下:myInteger.xxmyInteger.的子对象。这就是您可以看到所做更改的原因。