【问题标题】:Java - Object state does not change after method call [duplicate]Java - 方法调用后对象状态不会改变[重复]
【发布时间】:2012-08-01 20:18:53
【问题描述】:

初学者 java 问题,但我无法理解在下面的示例中按值调用(或引用)是如何工作的 -

我的自定义字符串对象在退出方法后,为什么字符串值没有被修改。 ?与 Date 等其他类相同。

public class StringMadness {

public static void main(String[] args) {
    String s = "Native String";
    CustomStringObject cs = new CustomStringObject();
    System.out.println("Custom String Before: " + cs.str);
    hello(cs);
    System.out.println("Custom String After: " + cs.str);

    System.out.println("Native String Before: " + s);
    hello(s);
    System.out.println("Native String After: " + s);
}

private static void hello(String t) {
    t = "hello " + t;
}

private static void hello(CustomStringObject o) {
    o.str = "hello " + o.str;
  }
}

class CustomStringObject {

String str = "Custom String";
}

【问题讨论】:

    标签: java pass-by-reference pass-by-value


    【解决方案1】:

    比较这两种方法:

    private static void hello(String t) {
        t = "hello " + t;
    }
    
    private static void hello(CustomStringObject o) {
        o.str = "hello " + o.str;
    }
    

    在第一种情况下,您为t 分配了一个新值。这对调用代码没有影响——你只是改变了一个参数的值,所有的参数在 Java 中都是按值传递的。

    在第二种情况下,您要为o.str 分配一个新值。这改变了o 的值所引用的对象 字段的值。调用者看到该更改,因为调用者仍然拥有对该对象的引用。

    简而言之:Java 总是使用按值传递,但您需要记住,对于类,变量(或实际上任何其他表达式)的值是引用,而不是对象。你不需要使用参数传递来看到这个:

    Foo foo1 = new Foo();
    Foo foo2 = foo1;
    foo1.someField = "changed";
    System.out.println(foo2.someField) // "changed"
    

    这里的第二行将foo1 的值复制到foo2 - 这两个变量引用同一个对象,因此您使用哪个变量来访问它并不重要。

    【讨论】:

    • 啊……我明白了。谢谢....最初,我的hello(CustomStringObject o ) 正在实例化一个新实例,就像o = new CustomStringObject() 一样,我注意到在 hello 方法之外,实例化从未有任何效果。这就是引发问题的原因。
    • @Jon 很好的解释。 Foo 示例有帮助。 +1
    • 如果调用者传递空引用,那么我观察到被调用者修改不会反映给调用者。为什么会这样?
    • @feelgoodandprogramming:出于与更改参数本身完全相同的原因,调用者不可见。引用按值传递...
    • 如何处理像布尔这样的“原始”对象?如果我将这个对象传递给一个方法,然后将其设置在方法之外,那么该方法之外的设置现在是否适用于方法内部?
    【解决方案2】:

    这两种方法之间有一个重要区别:使用hello(String),您尝试将reference更改为String,而使用hello(CustomObject),给定一个参考,您使用引用更改对象的成员。

    hello(String) 引用String。在函数中,您尝试更改引用指向的对象,但您只是更改了引用的 pass-by-value 副本。因此,您的更改不会反映在方法之外。

    hello(CustomObject) 获得一个对象引用的副本,然后您可以使用它来更改实际对象。将此视为更改对象的内容。因此,您的更改会反映在调用方中。

    给定一个对象的引用,您可以使用它的公开方法/字段来更改对象

    【讨论】:

    • 另外,请注意字符串是不可变的。当您调用 hello(String) 时,会传递字符串地址的副本(例如:0xABC4)。在该函数中,当您为字符串分配新值时,这将为该字符串生成一个新地址(例如:0xABC5),该地址将不会在您的函数之外看到。
    【解决方案3】:

    因为对于字符串,您只是更改了本地参数引用。

    【讨论】:

      【解决方案4】:

      t 将指向新对象并仅限于方法,因此更改在外部不可见。

      第二种情况,你改变的值会被更新为对象,所以这些改变在方法调用之后是可见的。

      【讨论】:

      • 这并不是因为字符串是不可变的。即使这是一个 StringBuilder,为参数分配一个新值也不会影响调用者...
      • @JonSkeet:同意。更新了我的答案。
      【解决方案5】:

      不起作用,因为 String 是不可变对象

      【讨论】:

        猜你喜欢
        • 2013-05-06
        • 1970-01-01
        • 2018-11-05
        • 1970-01-01
        • 2018-08-17
        • 2020-08-26
        • 1970-01-01
        相关资源
        最近更新 更多