Mutability和Immutability

  • 改变变量:使变量指向存储着另一个值的空间

  • 改变变量的值:变量指向的空间不变,变化的是存储的内容。

Immutability:不变性,一个重要的设计原则,设计ADT时尽量保证这个原则。

Immutable types:不可变的数据类型,当实例对象被创建以后,该对象的值就不可变化了,也就是该ADT中不能有mutator方法。

在编写程序的时候使用final关键字可以保证该变量不可再被改变,但不能保证该变量的值不变。所以,尽量使用final变量作为方法的输入参数、作为局部变量。

final类无法派生子类

final变量无法改变值/引用

final方无法被子类override(重写)

  • 比较immutable和mutable
    • 不变对象:一旦被创建,始终指向同一个值/引用
    • 可变对象:拥有方法可以修改自己的值/引用

一个例子:比较String和StringBuilder

在这个例子中,String和StringBuilder所达到的效果是相同的

/* String部分 */
String s = "a";	//开辟一个存储空间,里面存着字符a,s指向这块空间
s = s.concat("b"); //把字符a和字符b连接,然后把“ab”放在一个新的存储空间,最后让s指向这块空间

/* StringBuilder部分 */
StringBuilder sb = new StringBuilder("a"); //开辟一个存储空间,里面存着字符a
sb.append("b"); //取出a,然后与字符b连接,然后把“ab”仍然放在这块空间内,把原来的“a”覆盖了,sb的指向没变

在这个例子中,String和StringBuilder所达到的效果出现了差别

/* String部分 */
String s = "a";	//开辟一个存储空间,里面存着字符a,s指向这块空间,记为space1
String t = s;	//让t指向s所指向的空间即space1
s = s.concat("b"); //把字符a和字符b连接,然后把“ab”放在一个新的存储空间,记为space2,最后让s指向这块空间
//我们可以看到,现在s和t所指向的是两块不同的空间,空间中的内容也不一样,因此s和t的效果是不一样的

/* StringBuilder部分 */
StringBuilder sb = new StringBuilder("a"); //开辟一个存储空间,里面存着字符a
StringBuilder tb = sb;	//开辟一个存储空间,里面存着字符a
sb.append("b");	//取出a,然后与字符b连接,然后把“ab”仍然放在这块空间内,把原来的“a”覆盖了,sb的指向没变
//在这个情况下,由于从始至终只用到了一块存储空间,所以sb和tb的效果实际上是相同的

可变对象的优点:虽然mutable类型由于指向的是同一个存储区域,所以更改对象的内容后会在意想不到的位置产生意想不到的变化,所以更推荐使用Imutable的数据类型,但是使用不可变类型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收),比如依次将 ‘a’~‘z’ 连接到一个空字符串上,就会产生25个临时拷贝,而使用可变类型则最少化拷贝以提高效率。

使用可变数据类型,可获得更好的性能。但是在质量指标中,性能的优先级较低,所以即使mutable类型有这个优点也更倾向于选择imutable的类型。

也适合于在多个模块之间共享数据。在这里强烈不推荐使用Global variables。

例子:mutable潜在的风险,这种情况非常难以跟踪发现,也难以被其他开发者理解
 

/* @return the sum of the numbers in the list */
public static int sum (List<Integer> list) {
    int sum =0;
    for (int x: list) sum += x;
    return sum;
}

/* @return the sum of the absolute values of the numbers in the list */
public static int sumAbsolute (List<Integer> list) {
    // let's reuse sumo), because DRY, so first we take absolute values
    for (int i =0; i<list.size(); i++)
         list.set(i, Math.abs(list.get(i)));
    return sum(list);
}

//client
public static void main(String[] args) {
    List<Integer> my Data Arrays asList(-5, -3,-2);
    System.out.println(sumAbsolute(myData)); //期望值10,实际值10
    System.out.println(sum(myData)); //期望值-10,实际值10
}

解决的办法:通过防御式拷贝,给客户端一个副本,客户端即使对数据做了更改,也不会影响到自己。我们解决了外部对内部的无意改动,但为此付出的代价就是空间的浪费。而如果使用immutale类型的数据,就不存在这种风险。同时,我们编程的时候也要注意避免出现一个对象的多个引用,也就是说尽量不要让一个对象出现别名。

关于不可变类型的注意点:一般来说 immutable类的rep是不可更改的,但这也并非是绝对的。只要保证更改后它所表达的抽象含义没有更改, 它的数据域就可以被更改。这种更改一般在 observer等方法中完成,ADT中还是不能加入 mutator方法。例:一个表示分数的类,rep里有两个int,表示分子分母,创建时被初始化为了2和4,但是在调用Observer观察时被化简成了1和2,在外界看来它所表达的都是1/2,没有区别。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值