对象的可变和不可变
可变对象:
对象进行操作后,该对象内部是发生变化的 可以这样认为,操作直接作用于该对象,并使该对象发生了变化 比如list,对list进行操作,list内部的内容是会变化的
不可变对象:
对象进行操作后,该对象内部是不发生变化的 可以这样认为,操作该对象之前先将其复制一份,然后对该复制的对象进行操作,原来的对象不发生变化 比如str,对str进行操作
关于String这个包装类来举例说明一下可变和不可变:mutable/immutable
String是immutable的,我们想对String类型的数据进行修改,那么我们是需要copy到中间对象进行操作的,所以如果我们用String去拼接字符串,中间会产生很多无用的中间对象;
所以我们用StringBuffer,StringBuffer是mutable的,所以我们直接用其append、add,其本质是一个线程安全的,其把所有修改数据的方法全部加上了synchronized,保证了线程安全;
而StringBuilder也是mutable的,跟StringBuffer比,只是取消了所有的synchronized锁
如果平时是少量的字符串拼接
String str = "aa"+"bb"+"cc";
JDK会自动帮我们优化,生成StringBuffer来拼接字符串,但是如果出现大量的字符串拼接,比如:
String str = "aa"+"bb"+"cc";
String str = "aa"+"bb"+"cc";
String str = "aa"+"bb"+"cc";
String str = "aa"+"bb"+"cc";
String str = "aa"+"bb"+"cc";
String str = "aa"+"bb"+"cc";
String str = "aa"+"bb"+"cc";
JDK会为我们进行很多次的new StringBuffer()操作
所以,进行大量的字符串拼接操作的时候,我们还是需要使用StringBuffer/StringBuilder
字符串常量池
String使用的太过频繁,所以Java引入了一个字符串常量池的概念
创建一个字符串时,首先会检查池中是否有值相同的字符串对象,如果有就直接返回引用,不会创建字符串对象;
如果没有则新建字符串对象,返回对象引用,并且将新创建的对象放入池中。
但是,通过new方法创建的String对象是不检查字符串常量池的,而是直接在堆中创建新对象,也不会把对象放入池中。
上述原则只适用于直接给String对象引用赋值的情况
String str1 = new String("a"); //不检查字符串常量池的
String str2 = "bb"; //检查字符串常量池的
tips:
在单线程的情况下有大量的字符串操作,我们使用StringBuild
在多线程的情况下游大量的字符串操作,我们使用StringBuffer
少量的字符串操作,直接用String