简介
本篇文章首先简单的介绍一下数组,因为数组与字符串有着不可分割的关系。随后会对字符串做详尽的讲解,包括字符串的创建原理、字符串常量池、字符串内存结构、字符串拼接,intern等内容
数组
数组最大长度
想要知道数组最大可以为多长,就要先知道数组的长度是怎么记录的?
数组长度是存储在对象头里,且用无符号4字节表示,所以最大是 2的32次方减1
数组与字符串的关系
字符串
字符串常量池
字符串常量池,在jvm中对应的是StringTable,它继承了hashtable,也就是说底层数据结构是hashtable
既然是hashtable那就肯定要生成,key与value
key的生成
key的生成规则是,用内容与长度进行hash运算,再用得到的hash值转为key
value的生成
value的生成规则是将java的实例(对应c++的instance OopDesc)封装成HashtableEntry
内存结构
下面用常见几段代码分析各个情况的内存结构
第一种
看这段代码的内存结构
String str1 = "123";
证明
有了以上的理论我们借助idea看点实际的东西
工具使用
单步运行后内存变化,可以看到char[]多了一个
第二种
public static void main(String[] args) {
String str1 = "123";
String str2 = "123";
System.out.println(str1 == str2);
}
证明
从内存变化中可以看到 str2并没有创建新的cha[]与string,换言之,str1与str2指向的是同一个对象。
第三种
public static void main(String[] args) {
String str1 = new String("123");
String str2 = new String("123");
System.out.println(str1 == str2);
}
证明
== 与equals
了解了内存结构后这个问题就简单了, == 是比较是否为同一个对象,equals是比较值
字符串拼接
第一种
public static void main(String[] args) {
String str1 = "1" +"2";
}
反编译class可以看到这种情况被编译优化了
第二种
public static void main(String[] args) {
String str1 = "1";
String str2 = "2";
String s3 = str1 + str2;
}
通过字节码可以看到是用StringBuilder进行append
第三种
public static void main(String[] args) {
final String str1 = "1";
final String str2 = "2";
String s3 = str1 + str2;
}
这种情况会被当成常量,也会被编译器优化
综合练习
String三个参数的构造函数
通过内存变化可见(右下角),使用三个参数的构造函数创建字符串没有创建常量池的引用的,所以str2 = “12”时还会生成一个String对象与一个char[]
intern
public static void main(String[] args) {
String str = new String(new char[]{'1','2'},0,2);
str.intern();
String str2 = "12";
System.out.println(str == str2);
}
将上述例子稍作改动,加一个str.intern,最终结果就变成了true,因为intern的执行过程是,先看字符串常量池中有没有该字符串的引用,如果有则直接返回,否则创建引用。
以上就是本篇文章要分享的全部内容,拜拜~。