为什么是ArrayList参数被修改,而不是String参数


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到底在做什么?有人能详细解释一下吗?

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

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

当你呼叫

markAsNull(ArrayList<String> str)

al指向的同一个ArrayList创建了一个名为str的新引用。当你在stradd一个元素时,它会被添加到同一个对象中。之后,您将str添加到null,但对象添加了新值,并由a1指向。

当你呼叫

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

str = str + "Append me";行通过附加给定字符串并将其赋值给str来创建一个新的String对象。但是它只是对实际字符串的引用现在指向新创建的字符串。(由于不可变性)和原始字符串不被改变。

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

同样,当做字符串连接时,toString()在对象上被调用,这就是为什么ArrayList作为括号中值的列表输出的原因。

摘自本书:SCJP - Sun Certified Programmer for 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引用,另一个由因为doStuff()方法有一个引用变量,它有一种方法来获得原始Foo对象,例如调用setName()方法。但是,doStuff()方法没有办法到达引用变量f。所以doStuff()可以改变f引用的对象中的值但是doStuff()不能改变f的实际内容(位模式)换句话说,doStuff()可以改变f所引用对象的状态,但它不能使f指向不同的对象!

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

对于Arraylist, copy of reference指向原始的Arraylist(对于string也是一样)。但是当你向数组列表中添加一些东西时,它会指向原始对象这样你就能看到效果了。(try in method arraylist=new arraylist(),你原来的数组列表将保持原样)

对于字符串,当您执行

str=str + "abc";

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

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

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

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

将对象传递给形参将有效地复制引用,从而得到一个具有两个指针的单个对象——一个全局指针,另一个局部指针。

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

相关内容

  • 没有找到相关文章

最新更新