string类不能改变的是对象内的成员变量值.
String不可变的好处以及为什么不可变:1.便于实现字符串常量池,节省空间.
在Java语言中,会大量使用到字符串,若每一册声明一个String创建一个新的String对象,这是多大的空间资源的浪费,Java中就提出了一个String Pool(字符串常量池)的概念,会在堆中开辟一块存储空间,当每次初始或一个String变量时,若该字符已经存在,就不会在创建新的字符串变量,而是返回已存在字符串的引用.
2.使多线程安全.
public class test {
// 不可变的String
public static String appendStr(String s) {
s += "bbb";
return s;//新的
}
// 可变的StringBuilder
public static StringBuilder appendSb(StringBuilder sb) {
return sb.append("bbb"); //还是原来的
}
当程序将StringBuffer当做参数传值时,因为Java对象参数传递的是引用,所以将传入的StringBuffer类型的参数进行操作改变时,就会将原来的String对象操作而改变,有时候并不希望改变传入参数的值而是使用,所以String不可变安全性就体现在这里.
在并发场景下,多个线程访问同一个资源,是安全的,不会引发竞争,但对资源进行操作时是不安全的,不可变对象不能被写,所以保证了多线程的安全.
3.避免安全问题
网络连接地址URL,文件路径path,数据库连接中字符常常作为参数,反射机制所需要的String参数,其不可变性可以保证连接的安全性,如果字符串可以随意改变,就有可能被改变字符串指向对象的值,会引起严重的不可挽回的损失.
4.加快字符串的处理速度
由于String是不可变的,保证了hashcode的唯一性,于是在创建对象时其hashcode就可以放心的缓存了,不需要重新计算,通常Map将String作为键的原因,处理速度高于其他对象.hashMap中的键往往也是使用String.总体来说,String不可变的原因包括设计考虑,效率优化,以及安全性三大方面.
String的值真的没有办法改变吗:还是有办法将String的值进行改变的:
//通过反射改变String底层数组值
public static void main(String[] args) throws Exception {
String s = "Hello World";
System.out.println("s = " + s); //Hello World
//获取String类中的value字段
Field valueFieldOfString = String.class.getDeclaredField("value");
//改变value属性的访问权限
valueFieldOfString.setAccessible(true);
//获取s对象上的value属性的值
char[] value = (char[]) valueFieldOfString.get(s);
//改变value所引用的数组中的第5个字符
value[5] = '_';
System.out.println("s = " + s); //Hello_World
System.out.println(s);//Hello_World
}
String对象创建的方式:
String s1="aaa";
String s2=new String("aaa");
方式一中,会先在常量池中查找"aaa"这个字符对象是否存在,若存在就将常量池中的指向这个字符对象,若不存在,才会常见一个新的字符串对象.
方式二中,不论常量池中是否已经存在"aaa"这个对象,都会创建一个新的对象.
下列例子中创建了几个对象:
1.
String s = new String("abc")创建了几个对象
情况1: 创建两个对象
String s = new String("abc");
直接new对象,此前在字符串常量池中不存在"abc",此种情况在堆中创建一个对象,然后在字符串常量池中创建一个对象
情况2: 创建一个对象
String s = "abc";
String s1 = new String("abc");
因为字符串常量池中已存在"abc",只需要在堆中创建即可.
2.
创建了一个对象,底层优化创建了一个StringBuilder对象,append()追多个字符串对象,只产生一个对象.
String st1 = "a" + "b" + "c";//只创建了一个字符串对象
String st2 = "abc";
System.out.println(st1 == st2);//true
3.
String st1 = "ab";
String st2 = "abc";
String st3 = st1 + "c";//一旦发生拼接中有变量,会重新创建一个字符串对象
System.out.println(st2 == st3);//false
String类中常用方法:
String();初始化一个新创建的String对象,使其表示一个空字符序列.
String("");初始化一个新创建的String对象,使其表示一个与参数相同的字符序列.
String(byte[] b ,0,length,chatset);通过使用指定chatset解码指定的byte子数组,构造一个新的String.
String(char[] b ,0,length);分配一个新的String,它包含取自字符组参数一个子数组的字符 .
String,StringBuffer,StringBuilder区别:String值不可变,
StringBuffer 值可变,线程安全的,
StringBuilder 值可变,线程不安全的



