27
public class StackOverFlow {
    public static void main(String[] args) {
        ArrayList<String> al = new ArrayList<String>();
        al.add("A");
        al.add("B");
        markAsNull(al);
        System.out.println("ArrayList elements are "+al);

        String str = "Hello";
        markStringAsNull(str);
        System.out.println("str "+ str);
    }
    private static void markAsNull(ArrayList<String> str){
        str.add("C");
        str= null;
    }
    private static void markStringAsNull(String str){
        str = str + "Append me";
        str = null;
    }
}

这输出:

ArrayList elements are [A, B, C]
str Hello

在 的情况下ArrayList,将检索添加的元素。如果String方法调用对传递的字符串没有影响。JVM到底在做什么?谁能详细解释一下?

4

5 回答 5

21

在 Arraylist 字符串对象的情况下,添加的元素正在被检索。在 String 的情况下,方法调用对传递的 String 没有影响。

发生这种情况是因为 Java 是按值传递并且Strings 是不可变的

你打电话时

markAsNull(ArrayList<String> str)

新的名称引用是为指向str的同一个创建的。当您将其上的元素添加到同一对象时。稍后您放入,但该对象添加了新值并由.ArrayListaladdstrstrnulla1

你打电话时

markStringAsNull(String str)
{
    str = str + "Append me";
    // ...
}

该行通过附加给定的字符串str = str + "Append me";创建一个新String对象并将其分配给str. 但同样它只是对现在指向新创建的字符串的实际字符串的引用。(由于不变性)并且原始字符串没有改变。

于 2013-04-08T05:33:00.373 回答
5

这些markXAsNull方法将本地引用设置为null。这对存储在该位置的实际值没有影响。该main方法仍然有自己对值的引用,并且可以println使用这些引用。

此外,在进行字符串连接时,toString()在对象上被调用,这就是为什么 ArrayList 被输出为括号中的值列表。

于 2013-04-08T05:28:42.170 回答
5

摘自本书:SCJP - Sun Certified Programmer for Java 6 Study Guide (Katty Sierra - Bert Bates) 第 3 章目标 7.3 - 将变量传递给方法

Java 实际上是在单个 VM 中运行的所有变量的值传递。Pass-by-value 表示按变量值传递。这意味着,通过变量的副本传递!

传值的底线:被调用的方法不能改变调用者的变量,虽然对于对象引用变量,被调用的方法可以改变变量引用的对象。改变变量和改变对象有什么区别?对于对象引用,这意味着被调用的方法不能重新分配调用者的原始引用变量并使其引用不同的对象,或者为空。例如,在以下代码片段中,

void bar() {
Foo f = new Foo();
doStuff(f);
}
void doStuff(Foo g) {
g.setName("Boo");
g = new Foo();
}

重新分配 g 不会重新分配 f!在 bar() 方法的最后,创建了两个 Foo 对象,一个由局部变量 f 引用,一个由局部(参数)变量 g 引用。因为 doStuff() 方法具有引用变量的副本,所以它有一种方法可以获取原始 Foo 对象,例如调用 setName() 方法。但是,doStuff() 方法无法获取 f 引用变量。所以 doStuff() 可以改变 f 引用的对象中的值,但 doStuff() 不能改变 f 的实际内容(位模式)。换句话说,doStuff() 可以改变 f 所指对象的状态,但不能使 f 指代不同的对象!

于 2013-04-08T05:41:03.373 回答
2

Java 遵循传递值概念(Java 中没有传递引用)。因此,当您将字符串传递给函数时,它会将对该字符串的“引用副本”发送给函数。因此,即使您在函数中将变量设置为 null,当它返回给调用者时,它也仅引用其原始值。这就是为什么原始字符串无效的原因。

在 Arraylist 的情况下,引用的副本是指原始的 Arraylist(在字符串的情况下也是如此)。但是当你在 ArrayList 中添加一些东西时,它指的是原始对象,因此你可以看到效果。(尝试方法arraylist = new ArrayList(),您的原始arraylist 将保持原样)。

如果是字符串,当你这样做时

str=str + "abc";

Java 创建一个新的 String 对象,该对象将引用字符串“xyzabc”(例如 str="xyz"),并且“xyz”将有资格进行垃圾回收。但是由于“xyz”仍然有一个引用它的变量(原始字符串)不会被垃圾收集。但是一旦函数调用结束“xyzabc”就会进行垃圾收集。

讨论的总结是,只要引用指向同一个对象,您就可以在函数中进行更改,但是当您尝试更改引用(str=str+"abc")时,您正在引用方法中的新对象这样您的原始对象将保持原样。

于 2013-04-08T05:29:41.357 回答
1

在 Java 中,您可以创建一个对象,并由多个指针引用。在任何指针上调用 mutator 方法将有效地修改唯一对象,从而更新所有其他引用。

但是如果你在一个引用上调用变量赋值语句,只有那个指针会被改变,因为它不做任何对象方面的工作(这是我能解释的最好的......)。

将对象传递给参数将有效地复制引用,从而产生一个具有两个指针的对象 - 一个是全局的,另一个是本地的。

还有一点,因为String它是不可变的,所以您实际上会得到一个与原始对象不同的新对象(从您不得不说的事实来看a = a + "a"),这就是它不会修改原始字符串的原因。

于 2013-04-08T05:34:47.417 回答