【软件构造】Mutable类型与Immutable类型


1.前言

在软件构造这门课中,对mutable类型和immutable类型的深入理解,有助于后续ADT、可维护性、可复用性的学习,因此我们有必要对其进行详细的分析说明。

我们首先明确的是,mutable类型和immutable类型均属于ADT的范围,二者关系如下图:

【软件构造】Mutable类型与Immutable类型-LMLPHP


2.概念

immutable类:类的实例创建后成员变量值不变,若修改后,引用会指向一个实例对象。

mutable类:类的实例创建后可以通过类的方法就地修改值。


3.常见immutable类与mutable类

常见immutable类:String类;基本数据类型与其封装数据类型,如int、char、Interger、Boolean;Scanner类;经过 Collections.unmodifiableList/Map/Set() 方法处理后的集合。

常见mutable类:StringBuilder、StringBuffer、Map类、Collection类。


4.代码实践

考虑如下代码:

String str=new String("123");
str.concat("4");
System.out.println(str);

 输出结果为:

【软件构造】Mutable类型与Immutable类型-LMLPHP

为什么结果不是“1234”呢?

我们知道,java数据类型分为基本数据类型和对象数据类型(引用类型),后者类型的对象会按引用传递,这个引用,本质上是一个指针,指向存储在堆里的对象实体。所以,对于这样的变量,有着直接修改被指向的数据值让引用重新指向一个新对象两种方式。

而对于immutable类型,一旦该类初始化为一个新对象,其指向的堆中的值不可以修改,除非让其指向新的堆位置。所以,上述contact()会使用str引用指向的值重新创建一个新的对象,而不是修改str指向的对象的值。

其相应代码快照图为:

【软件构造】Mutable类型与Immutable类型-LMLPHP

再考虑如下代码:

StringBuffer strbuf1= new StringBuffer("123");
StringBuffer strbuf2=strbuf1;
strbuf1.append("4");
System.out.println(strbuf1);
System.out.println(strbuf2);

输出为:

【软件构造】Mutable类型与Immutable类型-LMLPHP

这里Stringbuffer为mutable类型,调用其成员方法append时,可以在引用所指向的堆中直接修改值,故输出均为“1234”。

其代码快照图为:

【软件构造】Mutable类型与Immutable类型-LMLPHP

对于mutable类型的对象,若有多个引用,其中某一个引用对对象的值修改时,由于所有引用指向同一个对象,所以在其他引用的值被“偷偷地改变了”,而这种改变,往往是被忽略的,因此会有潜在的危险性。

比如如下代码:

strbuf2.append("5");
System.out.println(strbuf1);

在输出strbuf2时,输出结果也是“12345”。

此外在函数调用时,对于mutable类也会出现非法篡改的情况:

 1     public static StringBuilder addstr(StringBuilder p){
 2         p.append("d");
 3         return p;
 4     }
 5     public static void main(String[] args) {
 6         StringBuilder str=new StringBuilder("abc");
 7         System.out.println(str);
 8         addstr(str);
 9         System.out.println(str);
10     }

其输出为:

【软件构造】Mutable类型与Immutable类型-LMLPHP


5.针对immutable类非法篡改的解决方案

方案一:defensive copy

在传参之前,或者在函数体内修改传入参数之前,拷贝一个新的对象

 1     public static StringBuilder addstr(StringBuilder p){
 2         StringBuilder copy=new StringBuilder(p);
 3         copy.append("d");
 4         return copy;
 5     }
 6     public static void main(String[] args) {
 7         StringBuilder str=new StringBuilder("abc");
 8         System.out.println(str);
 9         StringBuilder str1=addstr(str);
10         System.out.println(str);
11         System.out.println(str1);
12     }

输出为:

abc
abc
abcd

方案二:使用相应的immutable类替换mutable类的引用

 1     public static String addstr(String p){
 2         return p+"de";
 3     }
 4     public static void main(String[] args) {
 5         String str=new String("abc");
 6         System.out.println(str);
 7         String str1=addstr(str);
 8         System.out.println(str);
 9         System.out.println(str1);
10     }

输出为:

abc
abc
abcde

6.总结

immutable类更加安全,在软件构造过程中同时使用immutable类型的类,保证变量的值始终不变,可以重复使用,但对其修改需要进行大量拷贝,浪费时间与存储空间;

mutable类的修改不会造成空间的浪费,适合作为共享数据使用,但对其修改一定要谨慎进行。


完结~感谢阅读~☺️☺️☺️

05-09 16:41