我想知道在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
。我对吗?
答案 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.x
,x
是myInteger.
的子对象。这就是您可以看到所做更改的原因。