【问题标题】:Understanding how Java handles objects when used as method parameters了解 Java 在用作方法参数时如何处理对象
【发布时间】:2014-02-05 23:46:24
【问题描述】:

我一直在玩 Java,但不能完全理解 Java 方法如何处理传递给它们的对象。

例如,在下面的代码中,我创建了一些“容器”对象实例,其中包含另一个对象和一个原语。当我将此“容器”对象传递给方法时,我可以更改容器实例中保存的对象,或者直接修改它的值,或者使用 new 运算符构造一个新对象并替换其原始对象。这些更改是永久性的,因为在方法外检查时,它们的值是新对象的值。

让我非常困惑的是,虽然我可以通过方法更改 Containers 内部对象,但我不能实际更改容器本身。我的意思是,如果我将一个容器传递给一个方法并尝试通过 new 运算符的交换或赋值来改变它。

下面是我用来测试修改对象实例属性然后修改实际实例本身的代码。

class InsideRef{
    char myChar;
    InsideRef(char newVal){
        myChar = newVal;
    }
}

class Container{
    InsideRef myInRef = null;
    int myPrimitive = 0;
    Container(char innerChar, int innerPrim){
        myInRef = new InsideRef(innerChar);
        this.myPrimitive = innerPrim;
    }
    public void myDetails(){
        System.out.format("Container.%s => myPrimitive -> %d || myInRef => %s -> %c.%n",
        this.hashCode(),this.myPrimitive,this.myInRef.hashCode(),this.myInRef.myChar);
    }
}

class AttribRefModder{
    public static void ModObjRefVal(Container toEdit){
        toEdit.myInRef.myChar = 'Z';
    }
    public static void ModNewObjReference(Container toEdit){
        toEdit.myInRef = new InsideRef('Y');
    }
}

class RefSwapper{
    public static void RefSwap(Container A, Container B){
        System.out.println("Swapping....");
        System.out.print("OBJECT A -> ");
        A.myDetails();
        System.out.print("OBJECT B -> ");
        B.myDetails();
        Container temp = A;
        A = B;
        B = temp;
        System.out.print("SWAPPED A -> ");
        A.myDetails();
        System.out.print("SWAPPED B -> ");
        B.myDetails();
        System.out.println("Exiting....");
    }
    public static void RefNew(Container A){
        System.out.println("Assigning Reference New Object....");
        A = new Container('V',999);
        System.out.print("NEW C REF -> ");
        A.myDetails();
        System.out.println("Exiting....");
    }
}

public class ReferenceModding{
    public static void main(String[] args){
        System.out.println("-----------MODDING INNER REFS----------");
        Container C1 = new Container('A', 111);
        System.out.print("ORIGINAL A -> ");
        C1.myDetails();
        AttribRefModder.ModObjRefVal(C1);
        System.out.print("MODDED A.Ref -> ");
        C1.myDetails();
        AttribRefModder.ModNewObjReference(C1);
        System.out.print("NEW A.Ref -> ");
        C1.myDetails();
        System.out.println("----------SWAPPING REFERENCES----------");
        Container C2 = new Container('B',222);
        RefSwapper.RefSwap(C1, C2);
        System.out.print("OBJECT A -> ");
        C1.myDetails();
        System.out.print("OBJECT B -> ");
        C2.myDetails();
        System.out.println("----------ASSIGN NEW OBJECTS----------");
        Container C3 = new Container('C',333);
        System.out.print("OBJECT C -> ");
        C3.myDetails();
        RefSwapper.RefNew(C3);
        System.out.print("OBJECT C -> ");
        C3.myDetails();
    }
}

如果我发布的代码过多,我深表歉意。只是我整天都在玩Java,这个对象参数业务真的让我感到困惑。我不明白为什么 Java 方法允许我编辑新对象并将其分配给容器类中保存的 InsideRef 引用,但不允许我对实际容器类执行相同的操作。

感谢您提供的任何帮助。

【问题讨论】:

标签: java parameters reference parameter-passing pass-by-reference


【解决方案1】:

当我将这个“容器”对象传递给方法时

您需要了解的第一件事是不可能“传递”“对象”。 Java 中唯一的类型是原始类型和引用类型。这意味着每个值要么是原始值,要么是引用。 “引用”是指向对象的指针。 Container 类型是一个引用类型——它是指向对象的指针类型,具体来说,是指向类 Container 实例的指针。

【讨论】:

    【解决方案2】:

    我认为混乱来自RefSwap中的这一部分:

    Container temp = A;
    A = B;
    B = temp;
    

    在 Java 中,这只影响该方法中的变量 AB。当方法返回到调用它的位置时,它不会改变对象AB 指向的对象。所以在main 中,对象C1C2 仍然指代相同的Container 对象,它们不会互换。

    【讨论】:

      【解决方案3】:

      java 中的参数传递始终是按值传递的。这意味着在赋值左侧有一个参数在方法调用之外根本没有影响;它只是更改了您在方法调用堆栈上复制的引用(指针)的值,因此您在方法的其余部分中丢失了该值。

      例如,PL/SQL 有一个真正的通过引用传递的参数,如果你在那里声明

      -- True pass-by-reference
      procedure my_procedure(my_integer out integer) is
      begin
        my_integer := 6;
      end;
      

      在过程调用之后你会看到你传递的整数已经改变了它的值。但是,java 不支持这种参数传递。在 C 中想想这样的事情:

      // Just stepping on the values on the method call stack
      void my_function(int* my_integer) {
         my_integer = 0;
      }
      

      这会改变指针引用的整数的值吗?不,它只是处理指针值。这就是你用你的

      A = new Container('V',999);
      

      我希望这对你有任何帮助:)

      【讨论】:

        【解决方案4】:

        你说的对Java的特点。

        在幕后,你传递了一个对象的引用——内存地址是一个很好的模型。因此,您可以更改该引用所引用的任何内容。

        但是你不能改变调用者认为的“那个对象”——调用者有一个包含传递给你的地址的内存地址,你不能改变它。因此,无论您做什么,都无法更改引用的对象,只能更改该对象“内部”的内容。

        【讨论】:

        • 准确地说,你传递了一个引用按值(即引用的副本),这就是为什么方法不能更改调用者的引用。
        猜你喜欢
        • 1970-01-01
        • 2021-10-21
        • 2016-12-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-25
        相关资源
        最近更新 更多