关于java:为什么修改了ArrayList参数,但没有修改String参数?

Why is an ArrayList parameter modified, but not a String parameter?

本问题已经有最佳答案,请猛点这里访问。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
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;
    }
}

此输出:

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

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


In the case of Arraylist string objects the added elements are getting retrived. In case of String the method call has no effect on the String being passed.

它之所以发生,是因为Java是通过值传递的,EDCOX1是5不变的。

当你打电话

1
markAsNull(ArrayList<String> str)

名称为str的新引用是为al所指的同一ArrayList创建的。当add上的元素str被添加到同一个对象中时。后来你把str放在null上,但是对象增加了新的值,由a1指出。

当你打电话

1
2
3
4
5
markStringAsNull(String str)
{
    str = str +"Append me";
    // ...
}

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


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

此外,在进行字符串连接时,会对对象调用toString(),这就是将arraylist输出为其括号中的值列表的原因。


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

Java实际上是在单个运行的所有变量的传递值。虚拟机。传递值是指传递变量值。也就是说,把-变量!

传递值的底线:被调用方法无法更改调用方的变量,尽管对于对象引用变量,被调用的方法可以更改对象引用的变量。改变变量有什么不同改变目标?对于对象引用,这意味着被调用的方法不能重新分配调用方的原始引用变量并使其引用其他对象,或为空。例如,在下面的代码片段中,

1
2
3
4
5
6
7
8
void bar() {
Foo f = new Foo();
doStuff(f);
}
void doStuff(Foo g) {
g.setName("Boo");
g = new Foo();
}

重新分配G不会重新分配F!在bar()方法的末尾,两个foo对象已创建,一个由局部变量f引用,另一个由局部(参数)变量g。因为dostuf()方法具有引用变量,它可以获取原始foo对象,例如调用setname()方法。但是,dostuff()方法无法F引用变量。所以dostuff()可以更改对象f引用的值to,但dostuff()不能更改其他函数中f.的实际内容(位模式)words,dostuff()可以更改f引用的对象的状态,但它不能使F引用另一个对象!


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

在arraylist的情况下,引用的副本引用原始arraylist(在string的情况下也是相同的)。但是,当您向arraylist中添加某些内容时,它引用了原始对象,这样您就可以看到效果。(在方法arraylist=new arraylist()中尝试,您的原始arraylist将保持原样)。

如果是字符串,则在执行此操作时

str=str+"abc";

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

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


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

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

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

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