代码之家  ›  专栏  ›  技术社区  ›  Rory McGurty

对一个对象的更改会无意中更改另一个对象

  •  1
  • Rory McGurty  · 技术社区  · 4 年前

    我试图在java中使用列表,我的代码中出现了以下错误(见下文)。我创建了一个新的列表对象L1并将其传递给函数f。在函数f中,我创建了L1的副本并将其命名为L2。我在L2上执行加法操作,并从函数返回。返回后,L1的值发生了变化。这怎么可能?据我所知,L1的作用域不延伸到函数f,所以L1不能在函数f中改变。L2是L1的副本,是它自己的对象,所以对它的任何改变都不应该改变L1。有人能给我解释一下吗?

    import java.util.List;
    import java.util.ArrayList;
    
    public class HelloWorld{
    
         public static void main(String []args){
            List<String> L1 = new ArrayList<String>();
            L1.add("H");
            L1.add("I");
            System.out.println(L1);
            
            f(L1);
            
            System.out.println(L1);
         }
         
         public static void f(List<String> myList){
             List<String> L2 = new ArrayList<String>();
             L2 = myList;
             
             L2.add("!");
             return;
         }
    }
    

    哪个打印出来

    [H, I]
    [H, I, !]
    
    2 回复  |  直到 4 年前
        1
  •  1
  •   chriptus13    4 年前

    问题是,你正在创建一个新的 List L2 然后用以下内容覆盖引用 L2 = myList 你应该添加每个 String 价值来自 myList 相反。试试这个。

    public static void f(List<String> myList) {
        List<String> L2 = new ArrayList<>();
        for(String str : myList) L2.add(str);
        L2.add("!");
    }
    

    自从 列表 是一个对象,它作为方法的引用传递 f 因此,如果你这样做 L2=myList 你只是这么说 L2 指向相同 列表 作为 myList .阅读更多 here .

    你也不必 return 明确在 void 方法。

    最后注意你的方法 f 实际上并没有多大作用,因为它只会创建一个本地新的 列表 而你却不归还。

        2
  •  1
  •   Nathan Hughes    4 年前

    传入的列表不是列表的副本,它是一个引用,它指向列表。

    您将一个对列表的引用传递给一个方法,该引用仍然指向原始列表L1。然后将其分配给L2,现在L2指向原始列表。所以,不,你不是无意中改变了第二个对象,是同一个对象。

    你可以将一个引用传递给一个方法,它仍然指向它在该方法外部指向的同一个对象。

    对于按值传递引用的语言来说,这是正常的。C、JavaScript和Lisp的行为都与此类似。