字符串常量池
String str1 = "hh";
String str2 = "hh";
String str3 = "hh";
System.out.println(str1 == str2);//true
System.out.println(str2 == str3);//true
我们应该知道String是一个引用类型,str1 str2 str3 内存储的是这个"hh"的地址,这就说明了str1 str2 str3 指向了同一个对象。也就说str2 str3 在堆内没有创建一个新的内存空间. 实际上在jvm的底层实际会自动维护一个对象池,也就是字符串常量池
如果采用了直接赋值的模式进行对String类的对象进行实例化对象,那木这个字符串内容,会直接保存在这个对象池中.
如果下次直接使用直接赋值模式申明String类对象,此时的对象池中如果有这个内容,将直接进行引用,如果没有,开辟新的字符串对象后将其保存在对象池中以供下次使用.
========================================================
String str1 = "hh";
String str2 = new String("hh");
System.out.println(str1 == str2);//false
而若是用上述这个方法来创建一个新的String类,就会再次开辟一个堆内存空间,也就是会同时有两个堆内存空间来存储这个相同内容的字符串.比较浪费存储空间.
因此我们引用intern()来解决这个问题
String str1 = "hh";
String str2 = new String("hh").intern();
System.out.println(str1 == str2);//true
这种做法会让在使用String的构造方法的时候会在String对象池中查找是否有个内容的对象,如果有就会直接引用,若果没有则会在堆上直接开辟一个内存空间来存放这个内容,并且加在对象池中
字符串不可变
1. 方便实现字符串对象池. 如果 String 可变, 那么对象池就需要考虑何时深拷贝字符串的问题了.
2. 不可变对象是线程安全的.
3. 不可变对象更方便缓存 hash code, 作为 key 时可以更高效的保存到 HashMap 中.
String是一个类,内部是基于char[]来实现的,但是String类中并没有提供set方法来改变内部的字符数组.
我们按住ctrl+右键进入内部可以发现
这个是char无法改变的,
当我们想修改一个字符串的时候,大多都是借助原来的字符串创建一个新的字符串.
String str1 = "hh";
str1 = "a" + str1.substring(1);
System.out.println(str1);
//ah
我们要是想直接在原来的字符串上进行修改,使用反射这种方法来破坏封装,访问一个内部的private成员来改变.
String str1 = "hh";
//获取String类中的value字段,这个value和String原码中的value是匹配的
Field valueField = String.class.getDeclaredField("value");//这个要处理异常
//将这个字段的访问属性设为true
valueField.setAccessible(true);
//把str1中的value属性获取到
char[] value = (char[]) valueField.get(str1);//这个也要处理异常
//修改
value[0] = 'a';
System.out.println(str1);//ah