String类的不可变性及StringBuilder原理
来源:互联网 发布:淘宝卖家手机登录 编辑:程序博客网 时间:2024/05/29 18:12
String类不可变性的好处
String s="ABC";s.toLowerCase();
s.toLowerCase()并没有改变“ABC”的值,而是创建了一个新的String类“abc”,然后将新的实例指向变量s。
String类设置为不可变类的好处:
(1)提高Java字符串池(String Pool)的效率和安全性。当知道一个对象是不可变的,那么拷贝这个对象的内容时,就不用赋值它的本身而只是复制它的地址,复制地址需要很小的内存,效率也很高。
(2)不可变对象对于多线程是安全的,因为多线程的情况下,一个可变对象的值可能被其他进程改变,这样会造成不可预期的结果,而使用不可变对象就可以避免这种情况。
为什么将String设计成不可变类
(1)字符串常量池的需要
字符串常量池(String pool, String intern pool, String保留池) 是Java堆内存中一个特殊的存储区域, 当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。
如下面的代码所示,将会在堆内存中只创建一个实际String对象.
String s1 = "abcd"; String s2 = "abcd";
假若字符串对象允许改变,那么将会导致各种逻辑错误,比如改变一个对象会影响到另一个独立对象. 严格来说,这种常量池的思想,是一种优化手段.
String s1= "ab" + "cd"; String s2= "abc" + "d";
也许这个问题违反新手的直觉, 但是考虑到现代编译器会进行常规的优化, 所以他们都会指向常量池中的同一个对象.
(2)允许String对象缓存HashCode
Java中String对象的哈希码被频繁地使用, 比如在hashMap 等容器中。
字符串不变性保证了hash码的唯一性,因此可以放心地进行缓存.这也是一种性能优化手段,意味着不必每次都去计算新的哈希码. 在String类的定义中有如下代码:
private int hash;//用来缓存HashCode
(3)安全性
String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。例如你想加载java.sql.Connection类,而这个值被改成了myhacked.Connection,那么会对你的数据库造成不可知的破坏。
StringBuilder的实现原理
StringBuilder类也封装了一个字符数组,定义如下:
char[] value;
与String不同,它不是final的,可以修改。另外,与String不同,字符数组中不一定所有位置都已经被使用,它有一个实例变量,表示数组中已经使用的字符个数,定义如下:
int count;
StringBuilder继承自AbstractStringBuilder,它的默认构造方法是:
public StringBuilder() { super(16);}
调用父类的构造方法,父类对应的构造方法是:
AbstractStringBuilder(int capacity) { value = new char[capacity];}
也就是说,new StringBuilder()这句代码,内部会创建一个长度为16的字符数组,count的默认值为0。
append的实现
public AbstractStringBuilder append(String str) { if (str == null) str = "null"; int len = str.length(); ensureCapacityInternal(count + len); str.getChars(0, len, value, count); count += len; return this;}
append会直接拷贝字符到内部的字符数组中,如果字符数组长度不够,会进行扩展,实际使用的长度用count体现。具体来说,ensureCapacityInternal(count+len)会确保数组的长度足以容纳新添加的字符,str.getChars会拷贝新添加的字符到字符数组中,count+=len会增加实际使用的长度。
ensureCapacityInternal的代码如下:
private void ensureCapacityInternal(int minimumCapacity) { // overflow-conscious code if (minimumCapacity - value.length > 0) expandCapacity(minimumCapacity);}
如果字符数组的长度小于需要的长度,则调用expandCapacity进行扩展,expandCapacity的代码是:
void expandCapacity(int minimumCapacity) { int newCapacity = value.length * 2 + 2; if (newCapacity - minimumCapacity < 0) newCapacity = minimumCapacity; if (newCapacity < 0) { if (minimumCapacity < 0) // overflow throw new OutOfMemoryError(); newCapacity = Integer.MAX_VALUE; } value = Arrays.copyOf(value, newCapacity);}
扩展的逻辑是,分配一个足够长度的新数组,然后将原内容拷贝到这个新数组中,最后让内部的字符数组指向这个新数组,这个逻辑主要靠下面这句代码实现:
value = Arrays.copyOf(value, newCapacity);
toString实现
字符串构建完后,我们来看toString代码:
public String toString() { // Create a copy, don't share the array return new String(value, 0, count);}
基于内部数组新建了一个String,注意,这个String构造方法不会直接用value数组,而会新建一个,以保证String的不可变性。
- String类的不可变性及StringBuilder原理
- String类的不可变性
- String 类的不可变性
- Java的string类常量池及不可变性
- String的不可变性
- String的不可变性
- String的不可变性
- String的不可变性
- String类不可变性的好处
- String类不可变性的好处
- String类不可变性的好处
- String类不可变性的好处
- String对象的不可变性
- Java中String类不可变性的好处
- String类不可变性的好与坏
- String 的相等性和不可变性
- java之String对象的不可变性
- c#之string的不可变性2
- GridView无条目部分设置点击事件
- PullToRefresh+TabLayout+DrawerLayout+无限轮播集成使用
- DrawerLayout的布局及实现2
- 剑指offer2
- 环信集成流程说明
- String类的不可变性及StringBuilder原理
- QMessageBox
- flume User Guide翻译-logging raw data / plugin
- 如何将eclipse改成英文版
- java 注解的作用及使用方法详解
- Jmeter中常用的一些对字符串的处理代码参考
- Sublime 配置JAVA环境(控制台)
- Bagging与随机森林
- IntelliJ IDEA上创建Maven Web项目的时候,EL表达式无法解析异常