代码之家  ›  专栏  ›  技术社区  ›  Matt McCormick

为什么最终对象可以修改?

  •  77
  • Matt McCormick  · 技术社区  · 16 年前

    我在正在处理的代码库中遇到了以下代码:

    public final class ConfigurationService {
        private static final ConfigurationService INSTANCE = new ConfigurationService();
        private List providers;
    
        private ConfigurationService() {
            providers = new ArrayList();
        }
    
        public static void addProvider(ConfigurationProvider provider) {
            INSTANCE.providers.add(provider);
        }
    
        ...
    

    INSTANCE 声明为 final . 为什么对象可以添加到 实例 ?这不应该使使用final无效吗?(没有)。

    我假设答案与指针和内存有关,但我想确定一下。

    7 回复  |  直到 16 年前
        1
  •  146
  •   Gama11 zzapper    7 年前

    final 只需制作对象 参考 不变的。通过这样做,它指向的对象是不可变的。 INSTANCE 不能引用另一个对象,但它引用的对象可能会更改状态。

        2
  •  33
  •   Community Mohan Dere    9 年前

    最终的和不变的是不同的。

    final != immutable

    这个 final 关键字用于确保引用不被更改(即不能用新引用替换其引用)。

    但是,如果属性为self是可修改的,那么可以执行刚才描述的操作。

    例如

    class SomeHighLevelClass {
        public final MutableObject someFinalObject = new MutableObject();
    }
    

    如果我们实例化这个类,我们将无法将其他值赋给属性 someFinalObject 因为它是 最终的 .

    所以这是不可能的:

    ....
    SomeHighLevelClass someObject = new SomeHighLevelClass();
    MutableObject impostor  = new MutableObject();
    someObject.someFinal = impostor; // not allowed because someFinal is .. well final
    

    但是,如果它自身的对象是这样可变的:

    class MutableObject {
         private int n = 0;
    
         public void incrementNumber() {
             n++;
         }
         public String toString(){
             return ""+n;
         }
    }  
    

    然后,可以更改该可变对象包含的值。

    SomeHighLevelClass someObject = new SomeHighLevelClass();
    
    someObject.someFinal.incrementNumber();
    someObject.someFinal.incrementNumber();
    someObject.someFinal.incrementNumber();
    
    System.out.println( someObject.someFinal ); // prints 3
    

    这与您的帖子具有相同的效果:

    public static void addProvider(ConfigurationProvider provider) {
        INSTANCE.providers.add(provider);
    }
    

    在这里,您没有更改实例的值,而是在修改其内部状态(via,providers.add方法)

    如果要防止类定义发生如下更改:

    public final class ConfigurationService {
        private static final ConfigurationService INSTANCE = new ConfigurationService();
        private List providers;
    
        private ConfigurationService() {
            providers = new ArrayList();
        }
        // Avoid modifications      
        //public static void addProvider(ConfigurationProvider provider) {
        //    INSTANCE.providers.add(provider);
        //}
        // No mutators allowed anymore :) 
    ....
    

    但是,这可能没有多大意义:)

    顺便问一下,你也必须 synchronize access to it 基本上是出于同样的原因。

        3
  •  24
  •   Jon Skeet    16 年前

    误解的关键在于你问题的标题。这不是 对象 这是最后一个,这是 变量 . 变量的值不能更改,但其中的数据可以更改。

    始终记住,当您声明引用类型变量时,该变量的值是引用,而不是对象。

        4
  •  11
  •   Theo    16 年前

    最终的 只是意味着不能更改引用。如果实例声明为final,则不能将其重新分配给其他引用。对象的内部状态仍然是可变的。

    final ConfigurationService INSTANCE = new ConfigurationService();
    ConfigurationService anotherInstance = new ConfigurationService();
    INSTANCE = anotherInstance;
    

    会引发编译错误

        5
  •  6
  •   defectivehalt    16 年前

    曾经一次 final 变量已分配,它始终包含相同的值。如果A 最终的 变量保存对对象的引用,然后对象的状态可以通过对该对象的操作更改,但该变量始终引用同一对象。这也适用于数组,因为数组是对象;如果 最终的 变量保存对数组的引用,然后可以通过对数组的操作更改数组的组件,但该变量始终引用同一数组。

    Source

    这是导游 making an object immutable .

        6
  •  4
  •   Gama11 zzapper    7 年前

    最终的和不变的不是同一回事。“最终”表示不能重新分配引用,因此您不能说

    INSTANCE = ...
    

    不可变意味着不能修改对象本身。一个例子是 java.lang.String 班级。不能修改字符串的值。

        7
  •  2
  •   Gama11 zzapper    7 年前

    Java没有内置于语言中的不可变概念。没有办法将方法标记为一个变异体。因此,语言无法强制对象不可变。