java对象引用在方法中更改并理解结果

以下程序输出

In second vi:15 In first vi:20 

为什么它在两种情况下都不是15.在第二种方法中传递了Value的Object,然后在第二种方法中更改了对象引用。第二种方法应该是它应该是15并且看起来像在First方法中它应该也是15

 public class Test { /** * @param args */ class Value{ public int i = 15; } public static void main(String[] args) { Test t = new Test(); t.first(); } public void first(){ Value v = new Value(); vi = 25; second(v); System.out.println("In First vi:" + vi); } public void second(Value v){ vi = 20; Value val = new Value(); v = val; System.out.println("In second vi:" + vi); } } 

Java方法实现是call by [reference to, in case of objects,] value call by reference而不是call by reference

您正在传递对象Value v表示内联,方法范围变量v指的是在方法first()创建的对象v 。 这意味着对v引用的同一对象的任何修改也将反映在调用结束时。 但是,在second方法中,您正在为Value创建一个新对象,但指向方法范围变量v 。 此新对象的内存位置与方法参数中传递的内容位置不同。 要识别差异,请检查使用其引用变量创建的对象的hashCode

因此,在方法second更改v的实例变量将不会返回给方法的调用者,除非该方法返回更改的对象。 您的方法在此处返回void

大多数情况下,程序员会对调用者和被调用方法中使用的相同引用名称感到困惑。

请查看以下示例以了解其中的差异。 我已经包括了third' and a第四种方法,以进一步解释它。

 public class Test { class Value { int i = 15; } public void second( Value v ) { System.out.println( " 2.1.1: entered: vi = " + vi ); // 25 System.out.println( " 2.1.2: v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); v = new Value(); vi = 9; System.out.println( " 2.2.1: new V: vi = " + vi ); // 9 System.out.println( " 2.2.2: v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); } // second(v) public Value third( Value v ) { System.out.println( " 3.1.1: entered: vi = " + vi ); // 25 System.out.println( " 3.1.2: v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); v = new Value(); vi = 9; System.out.println( " 3.2.1: created: vi = " + vi ); // 9 System.out.println( " 3.2.2: v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); return v; } // third(v) public Value fourth( final Value v ) { System.out.println( " 4.1.1:entered: vi = " + vi ); // 9 System.out.println( " 4.1.2:v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); /********************************** // The final local v can't be assigned. It must be blank and not using a compound assignment. // meaning, you are not allowed to change its memory location, // but can alter its content, if permitted // v = new Value(); //**********************************/ vi = 45; System.out.println( " 4.2.1:changed: vi = " + vi ); // 45 System.out.println( " 4.2.2:v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); return v; } // fourth(v) public void first() { System.out.println( "1.1.1: entered: ..." ); Value v = new Value(); System.out.println( "1.2.1: created; vi = " + vi ); // 15 vi = 25; System.out.println( "1.2.2: changed: vi = " + vi ); // 25 System.out.println(); System.out.println( "1.3.1: before calling second(v) ..." ); System.out.println( " vi = " + vi + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); second( v ); System.out.println( "1.3.2: returning from second(v) ..." ); System.out.println( " vi = " + vi + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); System.out.println(); System.out.println( "1.4.1: before calling third(v) ..." ); System.out.println( " vi = " + vi + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); v = third( v ); System.out.println( "1.4.2: returning from third(v) ..." ); System.out.println( " vi = " + vi + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); System.out.println(); System.out.println( "1.5.1: before calling fourth(v) ..." ); System.out.println( " vi = " + vi + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); v = fourth( v ); System.out.println( "1.5.2: returning from fourth(v) ..." ); System.out.println( " vi = " + vi + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() ); } // first() public static void main( String ... a ) { Test _this = new Test(); _this.first(); } // psvm(...) } // class Test 

运行上面的示例时,您可能会看到如下输出:

 1.1.1: entered: ... 1.2.1: created; vi = 15 1.2.2: changed: vi = 25 1.3.1: before calling second(v) ... vi = 25, v.hashCode() = 1671711; v = Test$Value@19821f 2.1.1: entered: vi = 25 2.1.2: v.hashCode() = 1671711; v = Test$Value@19821f 2.2.1: new V: vi = 9 2.2.2: v.hashCode() = 11394033; v = Test$Value@addbf1 1.3.2: returning from second(v) ... vi = 25, v.hashCode() = 1671711; v = Test$Value@19821f 1.4.1: before calling third(v) ... vi = 25, v.hashCode() = 1671711; v = Test$Value@19821f 3.1.1: entered: vi = 25 3.1.2: v.hashCode() = 1671711; v = Test$Value@19821f 3.2.1: created: vi = 9 3.2.2: v.hashCode() = 4384790; v = Test$Value@42e816 1.4.2: returning from third(v) ... vi = 9, v.hashCode() = 4384790; v = Test$Value@42e816 1.5.1: before calling fourth(v) ... vi = 9, v.hashCode() = 4384790; v = Test$Value@42e816 4.1.1:entered: vi = 9 4.1.2:v.hashCode() = 4384790; v = Test$Value@42e816 4.2.1:changed: vi = 45 4.2.2:v.hashCode() = 4384790; v = Test$Value@42e816 1.5.2: returning from fourth(v) ... vi = 45, v.hashCode() = 4384790; v = Test$Value@42e816 

如果你真的想要在被调用的方法中保存对象instanceVariableV所做的更改,比如说fifth() ,另一种可能性就是将v声明为实例变量

以下示例将解释差异。

 public class Test { Value instanceVariableV = null; // v // rest of other variables and methods here // ... public void fifth() { System.out.println( " 5.1.1:entered: instanceVariableV = " + instanceVariableV ); // null // null, hence no hashCode(), and no toString() will work // let us create an instance of Value instanceVariableV = new Value(); System.out.println( " 5.2.1:created: instanceVariableV = " + instanceVariableV ); // Test$Value@9304b1 System.out.println( " 5.2.2: instanceVariableV.i = " + instanceVariableV.i ); // 15 System.out.println( " 5.2.3: hashCode = " + instanceVariableV.hashCode() ); // 9634993 instanceVariableV.i = 20; System.out.println( " 5.3.1:changed: instanceVariableV.i = " + instanceVariableV.i ); // 20 System.out.println( " 5.3.2: hashCode = " + instanceVariableV.hashCode() ); // 9634993 // not changed } // fifth() public void first() { // continuation of code System.out.println( "1.6.1: before calling fifth() ..." ); System.out.println( " instanceVariableV = " + instanceVariableV ); fifth(); System.out.println( "1.6.2: returning from fifth() ..." ); System.out.println( " instanceVariableV = " + instanceVariableV ); if ( instanceVariableV != null ) { // must be different from the one when created new System.out.println( " .i = " + instanceVariableV.i ); // this won't differ System.out.println( " .hashCode() = " + instanceVariableV.hashCode() ); } } // first() public static void main( String ... a ) { // ... System.out.println( "\r\nmain(...): vInstanceVariable = " + _this.instanceVariableV ); if ( _this.instanceVariableV != null ) { // must be different from the one when created new System.out.println( " .i = " + _this.instanceVariableV.i ); // this won't differ System.out.println( " .hashCode() = " + _this.instanceVariableV.hashCode() ); } } // psvm(...) 

当您使用上面的扩展示例运行时,您可能会看到如下输出:

 1.6.1: before calling fifth() ... instanceVariableV = null 5.1.1:entered: instanceVariableV = null 5.2.1:created: instanceVariableV = Test$Value@9304b1 5.2.2: instanceVariableV.i = 15 5.2.3: hashCode = 9634993 5.3.1:changed: instanceVariableV.i = 20 5.3.2: hashCode = 9634993 1.6.2: returning from fifth() ... instanceVariableV = Test$Value@9304b1 .i = 20, .hashCode() = 9634993 main(...): vInstanceVariable = Test$Value@9304b1 .i = 20 .hashCode() = 9634993 

希望这对你有所帮助。

其他参考:

  1. Java是通过引用传递还是通过值传递?
  2. java是通过引用传递的吗? (关于SO的post)

v传递给second ,按值传递的是存储在v引用 。 回想一下,Java只有引用类型,而不是像C ++这样的对象类型。 在second ,当你执行vi = 20; ,它首先更改局部变量v引用的同一对象。 即使参数vsecond内部重新分配,该更改仍然存在。

让我试着用类比来解释

想象一下,你的价值对象是一个真实的对象 – 一张写有数字15的纸。 你把那张纸给了一个名叫“First”的朋友。 首先把那张纸放在一边然后拿一张新纸,上面写着15张纸,然后将15张纸翻过去,然后写上25张。

他把这张第二张纸给另一位名叫“Second”的朋友。 第二张拿出那张纸,翻出了First写的25,并在其上写了20。 然后他拿起又一张数字为“15”的纸,向你展示 – 你看到的是15号。然后你问First给你看他给Second的那张纸。 你看它说“20”

Java中的所有参数都是值参数。 当您调用方法second :JVM将v对象复制到v1v1具有与v相同的指针。 当你分配

1,当你设置vi = 20vv1的值变为20(因为这些对象具有相同的指针)

2, v = new Value() ;

这意味着你分配v1 = new Value(); 此时,v1和v有不同的指针。

在调用方法second ,v的值为20(步骤1)。