Java全栈知识(2)String类String常量池

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中字符串常量池保存哪里?永久代?方法区还是堆区?

  1. 运行时常量池(Runtime Constant Pool)是虚拟机规范中是方法区的一部分,在加载类和结构到虚拟机后,就会创建对应的运行时常量池;而字符串常量池是这个过程中常量字符串的存放位置。所以从这个角度,字符串常量池属于虚拟机规范中的方法区,它是一个逻辑上的概念;而堆区,永久代以及元空间是实际的存放位置。
  2. 不同的虚拟机对虚拟机的规范(比如方法区)是不一样的,只有 HotSpot 才有永久代的概念。
  3. HotSpot也是发展的,由于一些问题的存在,HotSpot考虑逐渐去永久代,对于不同版本的JDK,实际的存储位置是有差异的,具体看如下表格:

JDK版本

是否有永久代,字符串常量池放在哪里?

方法区逻辑上规范,由哪些实际的部分实现的?

jdk1.6及之前

有永久代,运行时常量池(包括字符串常量池),静态变量存放在永久代上

这个时期方法区在HotSpot中是由永久代来实现的,以至于这个时期说方法区就是指永久代

jdk1.7

有永久代,但已经逐步“去永久代”,字符串常量池、静态变量移除,保存在堆中;

这个时期方法区在HotSpot中由永久代(类型信息、字段、方法、常量)和(字符串常量池、静态变量)共同实现

jdk1.8及之后

取消永久代,类型信息、字段、方法、常量保存在本地内存的元空间,但字符串常量池、静态变量仍在堆中

这个时期方法区在HotSpot中由本地内存的元空间(类型信息、字段、方法、常量)和(字符串常量池、静态变量)共同实现

猜你喜欢

转载自blog.csdn.net/dghehe/article/details/129657466