文章目录
1 String的基本特性
-
string: 字符串,使用一对“”引起来表示
-
String s1 = “ayugudu”;
-
String s2 = new String(“ayugudu”);
-
String 声明为final的,不可被继承
-
String 实现了Serializable接口:表示字符串是支持序列化的。
-
String 实现了Comparable接口:表示string可以比较大小
-
String 在jak8以前内部定义了final char[] value 用于存储字符串数据,jdk9改为byte[]。(不在使用char[]来存储,改成byte[]加上编码标记,节约了空间)
注意:基于string的一些类同样发生了修改如 StringBuffer,StringBuilder
2 String 具有不可变性
-
当对字符串重新赋值时,需要重写指定内存区域赋值。
-
当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值。
-
当调用string的replace方法修改指定字符或字符串时也需要重新指定内存区域赋值
-
String的String pool 是一个固定大小的HashTable默认大小是1009(jdk6之前,jdk7默认值就是60013),如果放进String pool 的String非常多,就会造成hash冲突,导致链表很长,调用String.intern性能会下降。
-
使用
-XX:StringTableSize
可设置String Table的长度
字符串常量池中是不会存储相同的字符串的
3 string 内存分配
String 类型的常量池比较特殊,主要使用的方法有两种
-
直接使用双引号声明出来
-
使用String提供的intern()方法
3.1 StringTable 为什么要调整
jdk1.7 之前将永久代中的字符冲常量池与静态变量存方法到永久代中,之后存放到堆中。
-
permSzie 默认比较小
-
永久代垃圾回收频率较低
容易出现oom.
4 . 字符串拼接操作
-
常量与常量的拼接结果在常量池,原理是编译器优化。
-
常量池中不会存在相同的内容的常量
-
只要其中有一个是变量,结果就在堆中,变量拼接的原理是stringbuilder
-
如果拼接的结果调用intern()方法,则主动将常量池中还没有的字符串对象放入池中,并返回此对象地址。
/**
* @program: jvmDemo
* @description:
* @author: wfg
* @create: 2021-06-19 18:09
*/
public class Test14 {
public static void main(String[] args) {
String s1 = "ayu";
String s2 ="gudu";
String s3 = "ayugudu";
//如果拼接的符号前后出现了变量,则相当于在堆空间中 new string(),具体的内容为拼接的结果
String s4 = s1+s2;
// 编译期优化
String s5 ="ayu"+"gudu";
//true
System.out.println(s5==s3);
//false
System.out.println(s5==s4);
}
}
String s4 = s1+s2;
的执行细节:
-
StringBuilder s = new StringBuilder();
-
s.append(“a”);
-
s.append(“b”);
-
s.toString() —>类似于new String(“ab”)
final String t1 ="wf";
final String t2 = "g";
final String t3 ="wfg";
String t4 = t1+t2;
//true
System.out.println(t4==t3);
如果拼接符号左右两边都是字符串常量或常量引用,则仍然使用编译期优化,即非stringbuilder的方式
5 . intern()的使用
如果不是用双引号声明的String 对象,可以使用string提供的intern方法:intern方法会从字符串常量池中查询当前字符串是否存在,若不存在就会将当前字符串放入常量池中。
interned string 就是确保字符串在内存里只有一份拷贝,这样可以节约内存空间,注意这个值会被存放在字符串内部
6 new String()问题
new String(a)
创建了两个对象首先判断常量池中有没有此对象,没有就在常量池中新建此对象并将引用付给堆中新建对象。
String s= new String("a")+new String("b")
```创建了几个对象?
- 对象1: new StringBuilder()
- 对象2: new String(“a”)
- 对象3: 常量池中的“a”
- 对象4:new String(“b”)
- 对象5 常量池中的“b”
深入剖析 stringBuilder的tostring()
- 对象6:new String(“ab”)
**注意,tostring的调用在字符串的常量池中,没有生成ab**
## intern()的问题
```java
/**
* @program: jvmDemo
* @description:
* @author: wfg
* @create: 2021-05-30 13:19
*/
public class Test4 {
public static void main(String[] args) {
String s1= new String("ayugudu");
s1.intern();//调用此方法之前,字符串常量池中已经存在"ayugudu"
String s2="ayugudu";
System.out.println(s1==s2);// jdk6:false jdk7:false
String t1 = new String("wf")+new String("g");//t1记录变量的地址为 new String("wfg"),执行完后字符串常量池中没有wfg
t1.intern();//在字符串常量池中生成“11” 理解:
// jdk6:创建了一个新的对象“wfg”,拥有新的地址
// jdk7:此时常量中没有创建“wfg”,而是创建一个指向堆空间中 new String(“wfg”)的地址
String t2 ="wfg";// t2 所使用的是 上一行代码在字符串常量池中生成的地址
System.out.println(t1==t2);// jdk6:false jdk7:true
}
}
总结:
-
jdk1.6中,将这个字符串对象尝试放入常量池中
- 如果常量池中有,则不会放入,返回已有的对象的地址
- 如果没有 ,会把此对象复制一份,放入常量池中。并返回常量池中的对象地址
-
jdk1.7起,将这个字符串尝试放入串池
-
如果串池中有,则不会放入
-
如果没有,则会把对象的引用地址复制一份,放入串池,并返回串池中的引用地址。
-
对于程序中大量存在的字符串,尤其是其中存在很多重复字符串时,使用intern()可以节省内存空间