为什么本地非最终变量会影响引用字段? Java的

时间:2013-03-04 10:02:49

标签: java reference pass-by-reference

我的程序中有以下代码:

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

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

4 个答案:

答案 0 :(得分:3)

Storage.getAdsForId(...)返回对同一List个对象的引用副本。因此,通过此引用调用会调用相同的列表。当您致电Storage.getAdsForId时,没有创建新的列表 - 只是对同一列表的新引用。

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

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

答案 1 :(得分:3)

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

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

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

答案 2 :(得分:1)

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

答案 3 :(得分:1)

如果是最终的,原始列表是否不受影响?我对此有疑问......这是同一个列表实例。为此,我要使用

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

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

return Collections.unmodifiableList(adsForIdList);

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