1.源码中String类不可变的原因
String类的源码
public final class String{//用字符数组来存数值private final char value[];}
虽然String是final类型的类,且value也是final类型的数组,但这不是String不可变的根本原因,String不可变是因为value是private,且并没有提供对外的get和set
final关键字修饰作用
修饰类:表明该类不可被继承,类中的所有成员方法都隐式的被指定为final方法
修饰方法:不可被重写,JVM会尝试将其内联,以提高运行效率
修饰变量:不可被改变,修饰引用变量表示引用不可变,引用指向的内容可变
修饰常量:在编译阶段会存入常量池中
2.如何改变String类的内容
String的值是不能被改变的,可能有人看到下面这个会觉得String的内容有被改变。但是实际上在内存中没改变的不是String 的值而是改变了引用的对象。然后将指针指向新的String 对象。所以经常改变内容的字符串最好不要用String,因为每次生成对象都会对系统性能产生影响。如果要改变字符串的内容,可以用StringBuffer与StringBuilder.
1.第一种
public static void main(String[] args) {String s = "hey";
s=s+" how are you";
System.out.println(s);}//输出的值为 “hey how are you”
2.第二种对于引用类型,会在堆中创建实例,然后对象的具体指只在栈中进行保存。堆种保存的值栈种保存的值栈种对应的引用地址。
String中的value是引用类型,所以可以做到在不改变堆中引用的情况下,改变栈中具体的值即可
代码示例
public class StringModifyTest {public static void main(String[] args) throws Exception{// 1. mock一个StringString str = "Melodfy";// 2. 通过反射过去对象// 反射获取对象的三种方法// 1. Class.forName("类全限定名")// 2. 类名.class()// 3. 实例.getClass()Class<? extends String> strCls = str.getClass();// 3. 通过反射获取指定参数,得到一个属性对象Field field = strCls.getDeclaredField("value");// 私有属性要设置可访问,否则访问操作时异常filed.setAccessible(true); // 4. 根据属性对象获取参数的具体指char[] result = (char[])field.get(str);// 5. 验证输出结果Systemg,out.println("变更前结果:");for(char c : result) {System.out.print(c);}// 6. 操作变更,然后输出验证System.out.println("变更后结果:");char[2] = 'w';for(char c : result) {System.out.print(c);}}
}
3.不可变的好处和坏处
1)线程安全性(好处)
在并发场景下,多个线程读取同一个资源,是不会引发竞态条件的。只有在对资源进行些操作时才有危险。不可变对象不能被修改,所以是线程安全的。
2)节省空间,提高效率(好处)
String还有字符串常量池的属性,on和two两个变量指向的是同一个地址。在大量使用字符串的情况下,可以节省内存空间,提高效率。
String的不可变条件是必要条件,要是内存中字符串内容能够改来改去,这么做就完全没有意义。
3)修改性能不高(坏处)
由于String 的不可变性,每次对String 类型进行改变的时候,都会生成一个新的String 对象,然后将指针指向新的String对象。
4.总结:
1.保存字符串的数组被final修饰且为私有的,并且String类没有提供/暴露修改这个字符串的方法
2.String类被final修饰导致其不能被继承,进而避免了子类破坏String 不可变