1、String类的不可变性
我们打开源码
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
String对象里面的字符串其实就是存放在一个char[]类型的数组中,该数组是由final修饰的,并且没有提供任何的set方法共给用户使用,也就是说String类是不可变的。这样做有什么好处呢?
1、String的hash值经常被使用
我们经常使用String类型的字符串作为Hashmap的key,此时的hash值使用的就是String类中的存储hash的成员变量里面的值,而String里面的hash属性是通过第一次调用hashCode()方法计算出来存到属性中的。
2、字符串常量池的需要
如果一个 String 对象已经被创建过了,那么就会从 String Pool 中取得引用。只有 String 是不可变的,才可能使用 String Pool。
3、线程安全
因为Sting不可变,自然不用担心线程安全的问题
2、String Pool
String Pool也叫字符串常量池,其本质上是维护在堆上的一张hash表。
如下:
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); // true
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // false
}
如果我们调用new String()方法是直接创建了一个新的String对象,所以两次创建的都是不同的对象,
但是如果我们直接String s1 = "hello"这样创建字符串,本质上是在字符串常量池中创建了一个“hello”这么一个String对象在String Pool中,再使s3指向常量池中的内容。所以s4和s3的内容其实都是String Pool中“hello”的引用。所以相等。
2.1、修改字符串
如果我们调用
String str1 = "hello";
str1+=" world";
其实在字符串常量池中是,创建了“ world”对象,并且重新创建了一个新的String对象,内容是“hello world”,然后改变了str1的指向,从而改变了str1中的值,原来的“hello”还在String Pool中,此时的StringPool中一共有三个对象“hello”“ world”“hello world”。后续无用的会被JVM回收
注意:
我们在开发中,应该避免出现下列代码
String str = "hello" ;
for(int x = 0; x < 1000; x++) {
str += x ;
}
System.out.println(str);
因为会多次在String Pool创建中创建对象,影响效率。
2、Intern()方法
String str1 = new String("hello").intern() ;
String str2 = "hello" ;
System.out.println(str1 == str2);
// 执行结果
true
调用intern的本质就是把该字符串放入到String Pool中。
2.3、HotSpot中字符串常量池保存哪里?永久代?方法区还是堆区?
- 运行时常量池(Runtime Constant Pool)是虚拟机规范中是方法区的一部分,在加载类和结构到虚拟机后,就会创建对应的运行时常量池;而字符串常量池是这个过程中常量字符串的存放位置。所以从这个角度,字符串常量池属于虚拟机规范中的方法区,它是一个逻辑上的概念;而堆区,永久代以及元空间是实际的存放位置。
- 不同的虚拟机对虚拟机的规范(比如方法区)是不一样的,只有 HotSpot 才有永久代的概念。
- HotSpot也是发展的,由于一些问题的存在,HotSpot考虑逐渐去永久代,对于不同版本的JDK,实际的存储位置是有差异的,具体看如下表格:
JDK版本 |
是否有永久代,字符串常量池放在哪里? |
方法区逻辑上规范,由哪些实际的部分实现的? |
jdk1.6及之前 |
有永久代,运行时常量池(包括字符串常量池),静态变量存放在永久代上 |
这个时期方法区在HotSpot中是由永久代来实现的,以至于这个时期说方法区就是指永久代 |
jdk1.7 |
有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存在堆中; |
这个时期方法区在HotSpot中由永久代(类型信息、字段、方法、常量)和堆(字符串常量池、静态变量)共同实现 |
jdk1.8及之后 |
取消永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆中 |
这个时期方法区在HotSpot中由本地内存的元空间(类型信息、字段、方法、常量)和堆(字符串常量池、静态变量)共同实现 |