先分析在Java中的数据类型。
基本数据类型 | 对象数据类型 |
int,long,byte,short,char, float,double,boolean | Classes,interfaces,arrays,enums, annotations |
只有值,没有ID,与其他值无法区分 | 既有ID也有值 |
都是immutable类型 | 有些是mutable类型,有些是immutable |
在栈中分配内存 | 在堆中分配内存,ID存在栈中,指向数据 |
无法实现表达的统一 | 表达式与泛型的统一 |
实现代价低 | 代价昂贵 |
那什么是immutable和mutable类型的数据呢?
immutable类型的数据一旦被创建,就始终指向同一个值/引用。
String是immutable类型的典型例子。一旦被创建,一个String对象的值始终不变。
如果想要在String的结尾添加字符,也就是改变String类型变量的值,就必须生成一个新的String对象,使变量指向那个对象。
mutable类型数据拥有方法可以修改自己的值/引用。
比如StringBuilder,它本身有删除,嵌入和代替串中字符的方法。它可以直接修改对象的值,而不是像immutable类型数据,需要创建一个新的对象。
那immutable类型和mutable类型数据的差别在哪?
当只有一个引用指向该值时,这两者是没有区别的。因为修改后的值都是一样的。
但当有多个引用的时候,差异就出现了。
如下图
当另一个变量t指向与s相同的字符串对象,而另一个变量tb指向与sb相同的StringBuilder时,不可变对象和可变对象之间的差异变得更加明显。
如果使用immutable类型数据,对其频繁修改会产生大量的临时拷贝,因为每次修改都要创建一个新的对象,使变量指向它,这些临时拷贝最后都需要垃圾回收。
而使用mutable类型数据就没有这方面的担忧,因为它有方法直接修改对象的值,可以最少化拷贝来提高效率。
使用mutable类型数据,可获得更好的性能,在多个模块间共享数据也很方便。
经过上面的论述,那既然mutable类型的功能好像比immutable类型更加强大,那为什么还要强调使用immutable类型数据呢?
这就涉及到编程的安全性,编程首先便是考虑程序的安全性。
因为如果使用mutable类型数据,当多个引用同时指向同一个对象时,每个引用的操作都会改变对象的值,此时就容易使程序乱套。
如图,令t=s,然后对t进行操作,打印s,仍可以得到对t做出改变的结果。
而immutable类型数据就没有这种后顾之忧。当然,使用哪种数据类型需要根据当前情况考虑,如果是需要频繁修改的数据,当然是使用mutable类型数据更好。这需要看你程序的要求,对数据的要求更看重哪个质量指标。
再者,比如在使用Date这个mutable类型的对象,若直接return,就有可能导致函数外的操作能改变函数内的值,这就会发生错误。所以这种时候通常会使用一种叫做防御式拷贝的方法,给客户端返回一个全新的Date对象,即return new Date()。这样就能防止上面的情况。但大部分时候该拷贝不会被客户端修改,可能造成大量的内存浪费,这种情况下使用immutable类型,就可以节省频繁复制的代价了。
最后再提及一个final类型的变量。被final修饰的变量,始终只能指向同一个对象。如果final修饰的是一个immutable类型,由于immutable类型无法改变值,只能通过创建一个新的存储空间来存放新的值,故被final修饰后,它便无法改变其值,任何改变的操作都会报错。而若final修饰的是一个mutable类型数据,它便不能再指向另外一个对象了。