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

java对象引用在方法中更改,并理解结果,java,object,Java,Object,跟踪程序输出 In second v.i:15 In first v.i:20 为什么在这两种情况下都不是15。传递值的对象,然后在第二个方法中更改对象引用。第二个方法应该是15,在第一个方法中看起来也应该是15 public class Test { /** * @param args */ class Value{ public int i = 15; } public static void main(String[]

跟踪程序输出

In second v.i:15
In first v.i:20
为什么在这两种情况下都不是15。传递值的对象,然后在第二个方法中更改对象引用。第二个方法应该是15,在第一个方法中看起来也应该是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();
        v.i = 25;
        second(v);
        System.out.println("In First v.i:" + v.i);
    }
    public void second(Value v){
        v.i = 20;
        Value val = new Value();
        v = val;
        System.out.println("In second v.i:" + v.i);
    }
}

当您将
v
传递到
second
时,通过值传递的是存储在
v
中的引用。回想一下java只具有引用类型,而不是C++之类的对象类型。在
second
内,当您执行
v.i=20时
,它会更改本地变量
v
first
中引用的同一对象。即使参数
v
second
内重新分配,该更改仍然存在。当您将
v
传递到
second
时,通过值传递的是存储在
v
中的引用。回想一下java只具有引用类型,而不是C++之类的对象类型。在
second
内,当您执行
v.i=20时
,它会更改本地变量
v
first
中引用的同一对象。即使参数
v
second

内重新分配,这种变化仍然存在。让我试着用一个类比来解释一下

假设你的价值对象是一个真实的对象——一张写有数字15的纸。你把那张纸给了一个叫“第一”的朋友。 首先把那张纸放在一边,得到一张新的纸,上面写着15,但是划掉15,在上面写着25


他把这第二张纸给了另一个叫“第二”的朋友。第二个拿着那张纸,划掉第一个写的25,改为写20。然后,他拿起另一张纸,上面写着数字“15”,向你展示——你看到了数字15。然后你让第一个给你看他给第二个的那张纸。你看,上面写着“20”

让我试着用一个类比来解释

假设你的价值对象是一个真实的对象——一张写有数字15的纸。你把那张纸给了一个叫“第一”的朋友。 首先把那张纸放在一边,得到一张新的纸,上面写着15,但是划掉15,在上面写着25


他把这第二张纸给了另一个叫“第二”的朋友。第二个拿着那张纸,划掉第一个写的25,改为写20。然后,他拿起另一张纸,上面写着数字“15”,向你展示——你看到了数字15。然后你让第一个给你看他给第二个的那张纸。您可以看到,它说“20”

Java方法实现是
通过调用
[在对象的情况下,引用]
,但不完全是
通过引用调用

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

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

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

请看下面的示例以了解差异。为了进一步解释,我加入了一个
third'和一个
4th`方法

public class Test {
    class Value {
        int i = 15;
    }

    public void second( Value v ) {
        System.out.println( " 2.1.1: entered: v.i = " + v.i ); // 25
        System.out.println( " 2.1.2: v.hashCode() = " + v.hashCode() + "; v = " + v.toString() );

        v = new Value();
        v.i = 9;
        System.out.println( " 2.2.1:   new V: v.i = " + v.i ); // 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: v.i = " + v.i ); // 25
        System.out.println( " 3.1.2:  v.hashCode() = " + v.hashCode() + "; v = " + v.toString() );

        v = new Value();
        v.i = 9;
        System.out.println( " 3.2.1:  created: v.i = " + v.i ); // 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: v.i = " + v.i ); // 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();
        //**********************************/

        v.i = 45;
        System.out.println( " 4.2.1:changed: v.i = " + v.i ); // 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; v.i = " + v.i ); // 15
        v.i = 25;
        System.out.println( "1.2.2: changed: v.i = " + v.i ); // 25
        System.out.println();

        System.out.println( "1.3.1: before calling second(v) ..." );
        System.out.println( " v.i = " + v.i + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() );
        second( v );
        System.out.println( "1.3.2: returning from second(v) ..." );
        System.out.println( " v.i = " + v.i + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() );
        System.out.println();

        System.out.println( "1.4.1:  before calling third(v) ..." );
        System.out.println( " v.i = " + v.i + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() );
        v = third( v );
        System.out.println( "1.4.2:  returning from third(v) ..." );
        System.out.println( " v.i = " + v.i + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() );
        System.out.println();

        System.out.println( "1.5.1: before calling fourth(v) ..." );
        System.out.println( " v.i = " + v.i + ", v.hashCode() = " + v.hashCode() + "; v = " + v.toString() );
        v = fourth( v );
        System.out.println( "1.5.2: returning from fourth(v) ..." );
        System.out.println( " v.i = " + v.i + ", 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; v.i = 15
1.2.2: changed: v.i = 25

1.3.1: before calling second(v) ...
 v.i = 25, v.hashCode() = 1671711; v = Test$Value@19821f
 2.1.1: entered: v.i = 25
 2.1.2: v.hashCode() = 1671711; v = Test$Value@19821f
 2.2.1:   new V: v.i = 9
 2.2.2: v.hashCode() = 11394033; v = Test$Value@addbf1
1.3.2: returning from second(v) ...
 v.i = 25, v.hashCode() = 1671711; v = Test$Value@19821f

1.4.1:  before calling third(v) ...
 v.i = 25, v.hashCode() = 1671711; v = Test$Value@19821f
 3.1.1:  entered: v.i = 25
 3.1.2:  v.hashCode() = 1671711; v = Test$Value@19821f
 3.2.1:  created: v.i = 9
 3.2.2:  v.hashCode() = 4384790; v = Test$Value@42e816
1.4.2:  returning from third(v) ...
 v.i = 9, v.hashCode() = 4384790; v = Test$Value@42e816

1.5.1: before calling fourth(v) ...
 v.i = 9, v.hashCode() = 4384790; v = Test$Value@42e816
 4.1.1:entered: v.i = 9
 4.1.2:v.hashCode() = 4384790; v = Test$Value@42e816
 4.2.1:changed: v.i = 45
 4.2.2:v.hashCode() = 4384790; v = Test$Value@42e816
1.5.2: returning from fourth(v) ...
 v.i = 45, v.hashCode() = 4384790; v = Test$Value@42e816
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

如果您确实希望在调用的方法中保存对对象
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.1.1: entered: ...
1.2.1: created; v.i = 15
1.2.2: changed: v.i = 25

1.3.1: before calling second(v) ...
 v.i = 25, v.hashCode() = 1671711; v = Test$Value@19821f
 2.1.1: entered: v.i = 25
 2.1.2: v.hashCode() = 1671711; v = Test$Value@19821f
 2.2.1:   new V: v.i = 9
 2.2.2: v.hashCode() = 11394033; v = Test$Value@addbf1
1.3.2: returning from second(v) ...
 v.i = 25, v.hashCode() = 1671711; v = Test$Value@19821f

1.4.1:  before calling third(v) ...
 v.i = 25, v.hashCode() = 1671711; v = Test$Value@19821f
 3.1.1:  entered: v.i = 25
 3.1.2:  v.hashCode() = 1671711; v = Test$Value@19821f
 3.2.1:  created: v.i = 9
 3.2.2:  v.hashCode() = 4384790; v = Test$Value@42e816
1.4.2:  returning from third(v) ...
 v.i = 9, v.hashCode() = 4384790; v = Test$Value@42e816

1.5.1: before calling fourth(v) ...
 v.i = 9, v.hashCode() = 4384790; v = Test$Value@42e816
 4.1.1:entered: v.i = 9
 4.1.2:v.hashCode() = 4384790; v = Test$Value@42e816
 4.2.1:changed: v.i = 45
 4.2.2:v.hashCode() = 4384790; v = Test$Value@42e816
1.5.2: returning from fourth(v) ...
 v.i = 45, v.hashCode() = 4384790; v = Test$Value@42e816
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
希望这对你有帮助

其他参考资料:


  • Java方法实现是
    callby
    [reference to,如果是对象,]
    value
    ,但不完全是
    callby reference

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

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

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

    请看下面的示例以了解差异。我包括了一个
    third'和一个
    4th`方法来解释