在面试中常常遇到这样的问题:一、什么是不可变对象。不可变对象有什么好处。在什么情景下使用它,或者更具体一点,java的String类为何要设置成不可变类型?java
一、不可变对象,顾名思义就是建立后的对象不能够改变,典型的例子有java中的String类型。面试
二、相比于可变对象,不可变对象有不少优点:sql
(1)不可变对象能够提升String Pool(字符串常量池)的效率和安全性。若是你知道一个对象是不可变更 ,那么须要拷贝的对象的内容时就不用复制它自己二只是复制它的地址,复制地址(一般一个指针的大小)须要很小的内存,效率也很好。二对于其余引用同一个对象的其余变量也不会形成影响。数据库
(2)可不变对象对于多线程滴安全的,由于在多线程同事进行的状况下,一个可变对象的值极可能被其余线程改变这样会形成不可预期的结果么人使用不可变对象就能够避免这种状况出现。编程
java将String设成不可变最大的缘由是效率和安全。缓存
那么不可变类型究竟是怎么实现的呢?安全
在java中考虑到各类因素,须要综合到内存,数据结构以及安全的方面的考虑,在下文中,我会为各类缘由作一个总结。性能优化
一、字符串常量池的须要网络
字符串常量池是java堆内存中一个特殊的存储区域,当建立一个String对象,假如此字符串值已经存在于常量池中,则不会建立一个新的对象,而是引用已经存在的对象。数据结构
代码以下:
String s1 = "ABC";
String s2 = "ABC";
在java中内存分为堆内存和栈内存,堆内存存放的是对象,栈内存存储对象的引用,字符串“ABC”存放在堆内存中,而s1,s2做为对象的引用则存放在栈内存中,原理以下:
堆内存 栈内存
String对象 "ABC"______ s1 String变量的引用
|______ s2
假设:字符串对象容许改变,那么将会致使各类逻辑错误。好比改变一个对象却影响到另一个独立的对象。
思考一下:一下代码,s1和s2还会指向同一个对象吗?
String s1 = "AB"+"C";
String s2 = "A"+"BC";
也许不少新手都会以为不是指向同一个对象,可是考虑到现代编译器会进行常规的优化因此他们都会指向常量池中的同一个对象。
二、运行String对象缓存HashCode
java中String对象的哈希码被频繁的使用,好比在HashMap的容器中。
字符串不变性保证了hash码的惟一性,所以能够放心的进行缓存,这也是一种性能优化手段,意味着没必要每次都取计算新的哈希码,在String类的定义中有以下代码:
private int hash;//用来缓存HashCode
三、安全性
String被许多的Java类(库)用来当作参数,例如 网络链接地址URL,文件路径path,
还有反射机制所须要的String参数等, 倘若String不是固定不变的,将会引发各类安全隐患。
整体来讲, String不可变的缘由包括 设计考虑,效率优化问题,以及安全性这三大方面.
事实上,这也是Java面试中的许多 "为何" 的答案。
四、String类不可变的好处
String是全部语言中最经常使用的一个类。咱们知道在Java中,String是不可变的、final的。Java在运行时也保存了一个字符串池(String pool),这使得String成为了一个特别的类。
String类不可变性的好处
1.只有当字符串是不可变的,字符串池才有可能实现。字符串池的实现能够在运行时节约不少heap空间,由于不一样的字符串变量都指向池中的同一个字 符串。但若是字符串是可变的,
那么String interning将不能实现(译者注:String interning是指对不一样的字符串仅仅只保存一个,即不会保存多个相同的字符串。),由于这样的话,若是变量改变了它的值,那么
其它指向这个值的变量 的值也会一块儿改变。
2.若是字符串是可变的,那么会引发很严重的安全问题。譬如,数据库的用户名、密码都是以字符串的形式传入来得到数据库的连 接,或者在socket编程中,主机名和端口都是以字
符串的形式传入。由于字符串是不可变的,因此它的值是不可改变的,不然黑客们能够钻到空子,改变字符 串指向的对象的值,形成安全漏洞。
3.由于字符串是不可变的,因此是多线程安全的,同一个字符串实例能够被多个线程共享。这样便不用由于线程安全问题而使用同步。字符串本身即是线程安全的。
4.类加载器要用到字符串,不可变性提供了安全性,以便正确的类被加载。譬如你想加载java.sql.Connection类,而这个值被改为了myhacked.Connection,那么会对你的数据库形成
不可知的破坏。
5.由于字符串是不可变的,因此在它建立的时候hashcode就被缓存了,不须要从新计算。这就使得字符串很适合做为Map中的键,字符串的处理速度要快过其它的键对象。这就是HashMap
中的键每每都使用字符串。
2、既然知道String类型不可变的好处和做用那么大,那么是否就不须要可变类型了呢?
固然不是,当你须要向字符串插入或修改的时候,Sting不可变类型就显得足襟见肘,这时候就须要一个可变的字符串类型:StringBuffer。
StringBuffer与String同样,都表明字符串,可是因为StringBuffer内部实现的方式和String不一样,因此StringBuffer在处理字符串的时候
不产生新的对象,在内存使用上要因为String类。