String 对象是对 char 数组进行了封装实现的对象,主要有两个成员:char 数组,hash 值
String 对象的不可变性
在实现代码中 String 类被final 关键字修饰了,而且变量 char 数组也被 final 修饰了。都知道类被final 修饰代表该类不可继承,而 char[] 被 final+private 修饰,代表 String 对象不可被更改。Java 实现了这个特性叫做 String 对象的不可变性,即 String 对象一旦创建成功,就不能再对它进行改变
这样做的好处是什么?
- 保证 String 对象的安全性,假设String 对象是可变的,那么 String 对象将可能被恶意修改
- 保证 hash 属性值不会被频繁变更,确保了唯一性,使得类似 HashMap 容器才能实现响应的 key-value 缓存功能
- 可以实现字符串常量池。在 Java 中,通常有两种字符串创建方式,一种通过字符串的方式创建,如 String str = “abc”,另一种是通过 new 的方式创建,如 String str = new String("abc)
String 的创建方式及内存分配的方式
1、String str = “abc”;
当代码中使用这种方式创建字符串对象是,JVM 首先会检查该对象是否在字符串常量池中,如果在,那么就返回该字符串的引用,否则新字符串就会再常量池进行创建。这种方式可以减少同一个值的字符串的重复创建,节约内存(str 只是个引用)
2、String str = new String("abc")
首先在编译类文件时,“abc”常量字符串会被放入进常量结构中,在类加载时“abc”将会在常量池进行创建,其次,在调用new 时,JVM命令将会调用 String 的构造函数,同时引用常量池中的“abc” 字符串,在堆内存中创建一个String对象,最后 str 将引用 String 对象
3、使用new ,对象会创建在堆里,同时赋值的话,会在常量池中创建一个字符串对象,复制到堆中。
具体的赋值过程是现将常量池中压入栈中,在使用 String 的构造方法时,会拿到栈中的字符串做为构造函数的参数。
这个构造函数式一个 char数组的赋值过程,而不是new 出来的,所以是引用常量池中的字符串对象,存在引用关系
public class Location {
private String city;
private String region;
}
4、String str = "ab"+"cd"+"ef";
在编码过程中,字符串的拼接很常见。前面说过 String 对象是不可变的,如果我们使用String对象相加,拼接想要的字符串,会不会产生多个对象呢?例如代码:String str = "ab"+"cd"+"ef";
分析代码可以知道:首先会变成 ab 对象,在生成 abcd 对象,最后生成 abcdef 对象,从理论上来说,这段代码是低效的。
但是编译器会在编译的时候会自动优化这段代码,优化为:String str= "abcdef";
5、大循环使用
intern
String 的 intern 方法,如果常量池中有相同值,就会重复使用该对象,返回对象的引用。
- new String()会在内存中创建一个 a 的 String 对象 , king 在常量池中创建
- 在调用 intern方法之后,会在常量池中查找是否有等于该字段对象的引用,有就返回引用。
- 调用 new String()会在堆内存中创建一个 b 的 String 对象
- 在调用 intern 方法之后,回去常量池中查找是否有等于该字符串的引用,有就返回引用
所以 a 和 b 引用的是同一个对象

