为什么修改了ArrayList参数,但没有修改String参数?

时间:2013-04-08 05:23:32

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

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的情况下,方法调用对传递的String没有影响。 JVM到底在做什么?任何人都可以详细解释一下吗?

5 个答案:

答案 0 :(得分:21)

  

对于Arraylist字符串对象,添加的元素将被重新获得。在String的情况下,方法调用对传递的String没有影响。

这是因为Java是按值传递而String是不可变的

致电时

markAsNull(ArrayList<String> str)

str指向的同一ArrayList创建名称al的新引用。当您add str上的某个元素时,它会被添加到同一个对象中。稍后您将str添加到null,但该对象已添加新值,并由a1指向。

致电时

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

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

答案 1 :(得分:5)

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

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

答案 2 :(得分:2)

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

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

如果是字符串,则执行

str = str +“abc”;

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

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

答案 3 :(得分:2)

来自本书:SCJP - Sun认证Java 6程序员学习指南(Katty Sierra - Bert Bates)第3章目标7.3 - 将变量传递给方法

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

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

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中的值 to,但doStuff()不能改变f的实际内容(位模式)。其他 单词,doStuff()可以改变f引用的对象的状态,但它不能 make f指的是另一个对象!

答案 4 :(得分:1)

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

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

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

还有一点,因为String是不可变的,你实际上会得到一个与原始对象不同的新对象(因为你必须说a = a + "a"),这就是为什么它不会修改原始字符串。