可变对象(immutable)和不可变对象(mutable)

Posted 申公的博客

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了可变对象(immutable)和不可变对象(mutable)相关的知识,希望对你有一定的参考价值。

可变对象(immutable)和不可变对象(mutable)

这个是之前一直忽略的一个知识点,比方说说起String为什么是一个不可变对象,只知道因为它是被final修饰的所以不可变,而没有抓住不可变三个字的重点:

1、不可变对象就是那些一旦被创建,它们的状态就不能被改变的对象,每次对它们的改变都是产生了新的对象

2、可变对象就是那些创建后,状态依然可以被改变的对象

举个例子:String和StringBuilder,String是不可变的,因为每次对String对象的修改都将产生一个新的String对象,而原来的对象保持不变;StringBuilder是可变的,因为每次对StringBuilder的修改都作用于该对象本身,并没有新的对象产生。如果这么说还不够清楚,截取两段源码,首先是String的concat方法,用户向已有的字符串后面拼接新的字符串:

技术分享
 1 public String concat(String str) {
 2     int otherLen = str.length();
 3     if (otherLen == 0) {
 4         return this;
 5     }
 6     char buf[] = new char[count + otherLen];
 7     getChars(0, count, buf, 0);
 8     str.getChars(0, otherLen, buf, count);
 9     return new String(0, count + otherLen, buf);
10     }
技术分享

看到第9行,new了一个新的String出来。然后看一下StringBuilder,StringBuilder最常用的应该就是append方法了,append一个字符串的时候会调用StringBuilder的父类AbstractStringBuilder的append方法:

技术分享
1 public AbstractStringBuilder append(String str) {
2         if (str == null) str = "null";
3         int len = str.length();
4         ensureCapacityInternal(count + len);
5         str.getChars(0, len, value, count);
6         count += len;
7         return this;
8     }
技术分享

第5行的这个value就是一个char型数组"char[] value;",每次对StringBuilder的操作都是对value的改变。

不可变的对象对比可变对象有两点优势:

1、保证对象的状态不被改变

2、不使用锁机制就能被其他线程共享

实际上JDK本身就自带了一些不可变类,比如String、Integer、Float以及其他的包装类,判断的方式就是看它们真正的那个对象是不是final的就好了。

我们自己也可以创建不可变对象,创建不可变对象应该遵循几个原则:

1、不可变对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象

2、不可变对象的所有属性应该都是final的

3、对象必须被正确地创建,比如对象引用在创建过程中不能泄露

4、对象应该是final的,以此来限制子类继承父类,以避免子类改变了父类的不可变特性

使用不可变类的好处:

1、不可变类是线程安全的,可以不被synchronized修饰就在并发环境中共享

2、不可变对象简化了程序开发,因为它无需使用额外的锁机制就可以在线程之间共享

3、不可变对象提高了程序的性能,因为它减少了synchronized的使用

4、不可变对象时可以被重复利用的,你可以将它们缓存起来,就像字符串字面量和整型数值一样,可以使用静态工厂方法来提供类似于valueOf这样的方法,它可以从缓存中返回一个已经存在的不可变对象,而不是重新创建一个

不可变对象虽然好,但是它有一个很大的缺点就是会制造出大量的垃圾,给垃圾收集带来很大的麻烦,由于它们不能被重用而且,所以不可变对象的使用依赖于开发人员合理的使用。另外,不可变对象也有一些安全问题,比如密码就建议不要用String,因为:

如果密码是以明文的形式保存成字符串 ,那么它将一直留在内存中,直到垃圾收集器把它清除。而由于字符创被放在字符串缓存池中以方便重用,所以它就可以在内存中被保留很长时间,而这将导致安全隐患,因为任何能够访问内存的人都可以清晰地看到文本中的密码,这也是为什么总是应该用加密的形式而不是明文来保存密码。由于字符串是不可变的,所以没有任何方式可以修改字符串的值,因为每次修改都将产生新的字符串,而如果使用char[]来保存密码,就可以将其中所有元素都设置为空或者是零。所以将密码保存到字符数组中很明显地降低了密码被窃的风险。

以上是关于可变对象(immutable)和不可变对象(mutable)的主要内容,如果未能解决你的问题,请参考以下文章

Python中的可变不可变对象和赋值技巧序列解包

可变于不可变对象分类

Python对象的拷贝

Java 的不可变类 (IMMUTABLE CLASS) 和 可变类 (MUTABLE CLASS)

二Python开发---8浅拷贝与深拷贝

Guava集合--Immutable(不可变)集合