【问题标题】:Why local non-final variable affects the referenced field? Java为什么局部非最终变量会影响引用的字段?爪哇
【发布时间】:2013-02-18 09:13:08
【问题描述】:

我的程序中有以下代码:

...
private void generateStack() {
    List<AdvertisementsModel> adsForModel = Storage.getAdsForId(model.getId());
    ...
    adsForModel.clear();
...

Storage 是具有静态字段和方法的静态类。 generateStack 方法在另一个类和实例对象中。如果asdForModel 引用不是最终的,为什么adsForModel.clear(); 会影响Storage 类中的列表?

【问题讨论】:

  • 如果是final的话,原名单不受影响吗?我对此表示怀疑......这是同一个列表实例。为此,我将使用 new ArrayList&lt;AdvertisementsModel&gt;(Storage.getAdsForId(model.getId()));,它创建一个新实例,如果可能,修改 Storage 类以返回原始列表的 UnmofifiableList。
  • 在列表上使用final只会使列表引用不可变而不是列表内容
  • @ChristopheRoussy,非常好的笔记,谢谢。

标签: java reference pass-by-reference


【解决方案1】:

Storage.getAdsForId(...) 返回对同一 List 对象的引用的副本。所以通过这个引用调用相同的列表。当您调用 Storage.getAdsForId 时,不会创建新列表 - 只是对同一列表的新引用。

因此,最好明确返回 ImmutableList 或在 Storage.getAdsForId(...) 中制作列表的防御性副本并返回副本。

请注意,当AdvertisementsModel 可变时,您需要进行深层复制,否则您将在不同级别遇到相同问题。 (否则您现在可能有一个列表副本,但两个列表仍然包含对相同 AdvertisementsModel 对象的引用,并且更改它们仍然会影响 Storage 中的列表内容。)

【讨论】:

    【解决方案2】:

    如果是final的话,原名单不受影响吗?我对此表示怀疑......这是同一个列表实例。为此,我会使用

    new ArrayList<AdvertisementsModel>(Storage.getAdsForId(model.getId()));
    

    这会创建一个新的列表实例,如果可能,修改 Storage 类以返回原始列表的 UnmofifiableList:

    return Collections.unmodifiableList(adsForIdList);
    

    我更喜欢这个,因为这个解决方案不会在每次调用时创建一个新的 List 实例,接收代码有责任决定是否需要创建它。但是,在多线程环境中,如果原始列表可能被修改,这可能会导致 ConcurrentModificationExceptions - 因此在这种情况下,在 getter 本身中创建列表的“防御性副本”更为明智。一定要记住,那么对副本的修改不会影响原始列表...

    【讨论】:

      【解决方案3】:

      Java 是按值传递的(引用的)。因此,如果Storage.getAdsForId(model.getId()) 返回一个静态存储在 Storage 中的引用,那么在实例中对其调用clear() 也会影响Storage 中的同一个列表。你可以这样做:

      return new ArrayList<AdvertisementsModel>(Storage.getAdsForId(model.getId()));
      

      改为返回列表的副本,以避免影响存储中的列表。当然,修改此列表中的元素仍会影响Storage 中列表中存在的相同元素。为避免这种情况,您必须深度复制列表中的每个元素。

      【讨论】:

      • 为什么要复制,而不是使用 Collections.unmodifiableList 来包装原始列表?
      • 好吧,一方面,你不能在它上面调用 clear() 方法(如上面的代码中所述)。
      【解决方案4】:

      getAdsForId 应该返回列表的副本,否则它将返回一个引用并且在列表上调用clear 将清空原始列表。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2015-07-20
        • 2011-08-13
        • 1970-01-01
        • 1970-01-01
        • 2020-08-14
        • 2011-08-18
        • 1970-01-01
        • 2022-07-06
        相关资源
        最近更新 更多