Java mutable对象和immutable对象的区别
今天读jdk源码中Map.java时看到一句话:
great care must be exercised if mutable objects are used as map keys;
第一次知道mutable对象这个概念,google了一下,维基百科定义如下:
“In object-oriented and functional programming, an immutable object (unchangeable[1] object) is an object whose state cannot be modified after it is created.[2] This is in contrast to a mutable object (changeable object) , which can be modified after it is created. In some cases, an object is considered immutable even if some internally used attributes change but the object's state appears to be unchanging from an external point of view. For example, an object that uses memoization to cache the results of expensive computations could still be considered an immutable object.”
在面向对象和函数式编程中,一个immutable对象(不可变对象)是指一旦创建之后状态不可改变的对象。
mutable对象(可变对象)是指创建之后也可以修改的对象。
在有些情况下,对象也被认为是不可变的(immutable),即,一个对象包含的内部使用的属性改变了,但从外部看对象的状态并没有改变。
例如,一个使用memoization来缓存复杂计算结果的对象仍然被看作是不可变(immutable)对象.
在面向对象编程中,String 以及其他的具体对象都被看作是不可变(immutable)对象,以提高可读性和运行效率。
不可变对象有几个优点:
线程安全
易于理解
比可变对象有更高的安全性
Java中不可变对象的经典例子就是String类的实例:
String s = "ABC";
s.toLowerCase();
toLowerCase()方法不会改变s中包含的数据“ABC”。而是创建一个新的String对象并将其初始化为“abc”,然后返回这个新对象的引用。
尽管String类声明中没有提供让它成为不可变对象的语法,但是,String类的方法中没有方法去改变一个String包含的数据,这就使得它是不可变的。
Java中关键字final用于声明原始数据类型(primitive types)和对象引用为不可变对象,但是它不能使对象本身变为不可变对象。
原始数据类型(primitive types)变量(int, long, short等)定义之后还可以再重新赋值,可以使用final阻止这样的赋值。
int i = 42; //int is of primitive type
i = 43; // OK
final int j = 42;
j = 43; // does not compile. j is final so can't be reassigned
仅仅使用final关键字还不能让引用类型(reference types)成为不可变对象,final只能阻止重新赋值。
final MyObject m = new MyObject(); //m is of reference type
m.data = 100; // OK. We can change state of object m (m is mutable and final doesn't change this fact)
m = new MyObject(); // does not compile. m is final so can't be reassigned
原始类型包装类(primitive wrappers)(Integer,Long, Short, Double, Float, Character, Byte, Boolean)也都是不可变的。
Java mutable 和 immutable类型
含义解释
immutable : variables that are assigned once and never reassigned.
mutable : When you assign to a variable or a field, 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.
基本类型及其封装对象类型都是不可变的
图形化解释 Snapshot Diagram
mutable:
immutable:
举例
例如String和StringBuilder:
1. String是immutable的,每次对于String对象的修改都将产生一个新的String对象,而原来的对象保持不变。
2. StringBuilder是mutable,因为每次对于它的对象的修改都作用于该对象本身,并没有产生新的对象。
如何保证自己创建的类是immutable类
- 所有成员都是private final。
- 不提供对成员的改变方法,setXX
- 确保所有的方法不会被重写。手段有两种:使用final Class(强不可变类),或者将所有类方法加上final(弱不可变类)。
- 如果某一个类成员不是原始变量(例如int,double)或者不可变类,必须通过在成员初始化或者使用get方法时要通过深度拷贝方法,来确保类的不可变。
优缺点
使用不可变类型,对其频繁修改会产生大量的临时拷贝(需要垃圾回收) ;
可变类型最 少化拷贝以提高效率。
使用可变数据类型,可获得更好的性能 ,也适合于在多个模块之间共享数据 。例如全局变量。
不可变类型更“安全”, 在其他质量指标上表现更好。
对可变类型可能造成的风险,我们通过防御式拷贝(深度拷贝),给客户端返回一个全新的可变类型的对象,大部分时候该拷贝不会被客户端修改, 可能造成大量的内存浪费。
深度拷贝
当只是引用传递或者根据对象的值创建新的值,称为“浅复制”,当原对象的发生改变时,根据上面方式创建的新对象的也会随之改变;
而如果采用深度复制,那是真正的复制了一份新的对象,新对象的与原对象不存在任何关联,原对象发生改变不会影响新对象。