一个案例
有以下案例:
public class Main {
\
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
String s3 = new String("hello");
String s4 = new String("hello");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s3 == s4);
}
}
输出结果为:
true
false
false
为什么s1和s2是同一个对象为什么呢?
在Java程序中,类似于:1,2,3,3.14,“hello"等字面类型的常量经常频繁使用,为了使程序的运行速度更快更节省内存,Java为8种基本数据类型和String类都提供了常量池。
这个字符串常量池底层就是一个哈希表, 名为StringTable. 也就是一个String对应一个Value.
什么是池?
池是编程中经常有的概念, 是一种重要的提升效率的方式, 我们会经常遇到各种池, 例如线程池, 内存池等等.
为了节省空间, 和提高运行效率, java中引用了:
Class文件常量池
运行时常量池
字符串常量池
字符串常量池
这个字符串常量池底层使用c/c++写的, 我们在jvm中去看源码的话,会发现其实这个常量池是一个StringTable的类, 实际上是一个固定大小的哈希表
String对象的创建
一个完整的String对象:
![](https://i-blog.csdnimg.cn/blog_migrate/ea9f946e46e476f629461e6581ef37a8.png)
可以看到里面有一个字符数字, 然后还有一个hash值, 其中这个hash值为0.
![](https://i-blog.csdnimg.cn/blog_migrate/2e2675a6df37833e33b550e1353ef7bd.png)
图解
那么对于字符串常量池, 字符串的存储又是怎么样的呢?
直接使用字符串常量赋值
public class Main {
public static void main(String[] args) {
String s1 = "hello";
String s2 = "hello";
System.out.println( s1 == s2); // true
}
}
运行结果为True;
首先String s1是在main方法中的, 理论上他是一个局部变量,是一个字符串的引用, 他是被放在栈区的.
![](https://i-blog.csdnimg.cn/blog_migrate/41f37585177ea724f6aa295bbf28a1b9.png)
首先创建字符串S1, 将其放入常量池, 然后创建s2, 然后遍历哈希表, 发现哈希表对应的哈希值已经存在, 所以直接将这个s2引用指向s1的那个字符串.
![](https://i-blog.csdnimg.cn/blog_migrate/f651ff37f9aad359a1782229562cca66.png)
只要是new的对象,都是唯一的
intern方法
intern方法是一个本地(native)方法, 底层使用c++实现, 看不到其实现的源代码, 该方法的作用是手动将创建的String对象, 添加到常量池当中去.
一个例子:
public class Main {
public static void main(String[] args) {
char[] ch = new char[] {'a','b','c'};
String stringCh = new String(ch);
String s1 = "abc";
System.out.println(s1 == stringCh);
}
}
提问: 在创建了stringCh对象只有, 常量池当中有abc字符串吗?
看运行结果:
![](https://i-blog.csdnimg.cn/blog_migrate/d52a05671f2caf9e5d7a9e27d8f4b6d2.png)
结果为false, 说明stringCh中的"abc"没有被放入常量池, 此时再和s1比较的话, 就是两个不同的对象.
如果使用intern将其加入到常量字符串呢?
public class Main {
public static void main(String[] args) {
char[] ch = new char[] {'a','b','c'};
String stringCh = new String(ch);
stringCh.interm();
String s1 = "abc";
System.out.println(s1 == stringCh);
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/41ae321cab9519ac20a8c1ec55e1948b.png)
结果为true, 可以看到intern方法把stringCh放入了常量池.