String
String 为什么不可变
java.lang.String 源码
字符串的本质是一个byte[] 的字节数组(jdk 11),在jdk8中是一个字符数组
public final class String implements Serializable, Comparable<String>, CharSequence {
@Stable
private final byte[] value;// 字符串本质
private final byte coder;
private int hash;
private static final long serialVersionUID = -6849794470754667710L;
static final boolean COMPACT_STRINGS = true;
private static final ObjectStreamField[] serialPersistentFields = new ObjectStreamField[0];
public static final Comparator<String> CASE_INSENSITIVE_ORDER = new String.CaseInsensitiveComparator();
static final byte LATIN1 = 0;
static final byte UTF16 = 1;
}
由此可见在java
语言中String
是一个final
关键字修饰的类,该类不可以具有子类,不可以修改。注意String
是一个引用型类,不是基本数据类型。基本数据类型只有一下8个byte
,short
,int
,float
,double
,long
,char
,boolean
,与此相对应的是其8大包装类类型
注意所谓的字符串不可变,指的是字符串的内容不可变,而不是字符串的引用不能变
1、由于字符串使用范围广,防止SQL注入等,保证安全
2、多线程中,只有不变的对象和值是线程安全的,可以在多个线程中共享数据。
3、由于字符串 hashcode 属性不会变更,保证了唯一性,使得类似 HashMap,HashSet 等容器才能实现相应的缓存功能。由于 String 的不可变,避免重复计算 hashcode,只要使用缓存的 hashcode 即可,这样一来大大提高了在散列集合中使用 String 对象的性能。
4、当字符串不可变时,常量池才具有意义,减少了重复的,相同字面量的字符串,共享内存,节省堆内存空间,
String 的创建
String s1 = "hello";
String s2 = new String("hello");
char[] ch = new char[]{'h','e','l','l','o'};
String s3 = new String(ch);
String s4 = String.valueOf(10);
字符串的字面量就是一个字符串对象
s1方式创建的字符串,查询堆中是否存在,如果不存在就产生一个对象,开辟一个常量池,将其存储,后续使用直接去常量池中取字符串。
String对象一旦被创建就是固定不变的了,对String对象的任何改变都不影响到原对象,相关的任何修改操作都会生成新的对象.
每当我们创建字符串常量时,JVM会首先检查字符串常量池,如果该
字符串已经存在常量池中,那么就直接返回常量池中的实例引用。如果字符串不存在常量池中,就会实例化该字符串并且将其放到常量池中。由于String字符串的不可变性。因此常量池中一定不存在两个相同的字符串
字符串对象的手动入池
public native String intern();
调用intern方法会将当前字符串引用指向的对象保存到字符串常量池中,有两种情况:
1.若当前常量池中已经存在了该对象,则不再产生新的对象,返回常量池中的String对象
2.若当前常量池中不存在该对象,则将该对象入池,返回入池后的地址。
String s1 = "hello";
String s2 = new String("hello");
System.out.println(s1 == s2);
s2 = s2.intern();
System.out.println(s1 == s2);
第一次输入false,第二次输出true,注意手动入池方法具有返回值,需要替换原先对象,即是入池成功
常量池
两种形态:静态常量池和运行时常量池。
静态常量池,即*.class文件中的常量池,class文件中的常量池不仅仅包含字符串(数字)。字面量,还包含类、方法的信息,占用class文件绝大部分空间。
运行时常量池,则是jvm虚拟机在完成类装载操作后,将class文件中的常量池载入到内存中,并保存在方法区中,我们常说的常量池,就是指方法区中的运行时常量池。
字符串池的优点
避免了相同内容的字符串的创建,节省了内存,省去了创建相同字符串的时间,同时提升了性能;
字符串池的缺点
牺牲了JVM在常量池中遍历对象所需要的时间