‘变与不变‘

本文深入探讨了编程中可变(mutable)与不可变(immutable)的概念,以Java为例,通过String和StringBuilder的对比来阐述。作者指出String是不可变的,而StringBuilder是可变的,分析了它们在内存中的变化,并通过代码示例验证了这一特性。同时,文章还提到了不变量(invariants)在ADT设计中的重要性,以及如何通过final关键字确保引用不可变。最后,讨论了Mutator方法的作用及其返回类型的意义。

变与不变

本人愚笨,对于课程第四章所讲的mutable和immutable的概念(包括与不变性相关的概念)还是有些模糊,写此文以分清二者的区别,加深对相关概念的理解。

Mutable and immutable type/reference

首先区分一下改变一个变量和改变一个变量的值的区别,PPT中是这么解释的:

//When you assign to a variable, you’re changing where the variable’s arrow 
//points.You can point it to a different value.
//改变一个变量:将该变量指向另一个存储空间。
//When you assign to the contents of a mutable value – such as an array or list 
//– you’re changing references inside that value.
//改变一个变量的值:将该变量当前指向的存储空间中写入一个新的值。

举个例子,在下图中,s和t就是变量,而string类型的a、ab以及abc对应于变量的值。
请添加图片描述

不变数据类型(immutable type):是一组值以及可以对其执行的操作,且一旦被创建其值不能改变。
不变引用(immutable reference):一旦确定其指向的对象,不能再被改变指向其他对象。
区别这两个概念可以用第二张图来解释,不知道我当时为什么不能理解这俩概念。对于String类型,它是一个不可变数据类型,所以一旦创建了一个String对象并将其赋值,这个对象的值就不能改变。图中用一个变量s指向该String对象,当用t同样指向该对象且进行concat操作时,由于String类型的不变性以及相关程序的输出(如下图),所以可以断定一定是在对t重新赋值后创建了一个新的String对象并将t指向此对象。

//String类型对象测试
String s = "a";
String t = s + "b";
System.out.println(s);
System.out.println(t);
//StringBuilder类型对象测试
StringBuilder sb = new StringBuilder("a");
StringBuilder tb = sb.append("b");
System.out.println(sb);
System.out.println(tb);

与String相对应的,StringBuilder则是一个可变数据类型。同样,运行相关程序后,可以由前面的知识得知,我们的期望和结果是相匹配的——tb先对sb指向的StringBuilder对象进行append操作,然后指向sb所指向的对象,并没有重新创建一个StringBuilder对象。
请添加图片描述
同一章的PPT中,说到基本数据类型是不可变的,这也许能用上面的例子来解释,但是我还想在代码层面试一下是不是真的。其实我一开始用C语言试过,但是发现输出的地址相比之下是一样的。而后改为用Java语言检验。

int i = 0;
int j = System.identityHashCode(i);
i = 2;
int k = System.identityHashCode(i);
System.out.println(j);
System.out.println(k);

结果符合预期:
请添加图片描述
对于可变的引用,若想让引用不可变,可以使用final关键字,如:

final int t = 10;

编译器进行静态类型检查时,如判断final变量首次赋值后发生了改变,会提示错误。

Invariants

不变量:程序在任何时候总是true的性质。上文所说的Immutable就是一个典型的不变量。
ADT需要始终保持其不变量。PPT上对此的一个例子是对于程序员不想让其改变的一种数据类型,如果此数据类型不是一个不变量,则在每一次使用此数据类型操作后都必须进行值的检查,这样未必也太麻烦了。比如一个String类型变量如果使用public修饰,则其的相关操作对于客户端来说是直接可以使用的,此时要求其不变的规约已经被打破了,这个表示泄露让客户端的其他用户也受到影响,这是我们不希望看到的。
上面所说的表示泄露就是指用户不通过我们ADT提供的方法,就可以修改ADT的内容,或者用户能够把ADT内期望不可变的元素与外界建立联系。
除了Immutability,Rep Invariants也是一个重要的ADT不变量。表示不变性是指某个具体的“表示”是否是“合法的”,或者说是所有表示值的一个子集,包含了所有合法的表示值。在写实验二的时候,在rep旁边都会写一个RI,根据RI把这种选择和解释明确写到代码当中。

Mutator

变值器,改变对象属性的方法,用正则表达式就是T+, t* → void | t | T。变值器的使用我觉得没必要过多赘述,主要还是注意Mutator的返回类型可能为空也可能不为空(变值器通常返回void,如果返回值为void,则必然意味着它改变了对象的某些内部状态;返回为非空时,如set里的add函数就是返回boolean来反映是否修改成功)。21年的一个题考过这个知识点。
请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值