Java中String和字符串常量池

本文简单讲解Java中那些String引用和字符串常量池的关系

什么是String

什么是字符串常量池

String

根据源码可知,字符串String对象的底层是char[]数组来存储数据,也就是String对象中包括了char[]数组对象。

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
		
  	....
}

那么我们来捋清字符串和字符数组的关系

  1. 字符数组在JVM的存在形式
char[] arr = new char[]{'0','1'};

JVM会在堆空间生成一个数组对象,arr变量就指向这个数组对象

  1. 字符串在JVM的存在形式
String s = "01";

JVM会先在字符串常量池里找到"01"对应的字符串对象

如果没有找到那么就会创建字符串对象和字符数组对象,并会存到字符串常量池,然后将字符串对象引用赋值给变量s;

如果找到就拿到“01”字符串对象的引用,直接赋值给变量s。

当给s修改字符串内容时,

s = "10"

由于字符串对象中的char[]是final修饰的,也就是不可变的,因此当s改变字符串的时候,会重新赋值,而不是去修改原字符。也就是JVM会创建新的“10”字符串对象和字符数组对象,然后把新的字符串对象的引用赋值s。

注意区分两个东西:字符串对象,字符数组对象;字符串对象中引用着字符数组对象;

字符串常量池

全局字符串常量池,也就是在整个JVM环境是共享的,起到缓存的作用,缓存的是字符串String对象,存储的形式是KV对(HashTable)。字符串String是最常用的数据类型之一,为了避免字符串频繁创建和销毁的开销,使用字符串常量池对字符串对象复用,能够提高程序执行的性能。JVM在读写字符串的时候都会先从全局字符串常量池中操作,有就直接用,没有则创建。

String引用和字符串常量池之间的那些事

接下来,我们结合例子说明常见的几种情况

例子1:

String s = "01";

JVM会先在字符串常量池找到“01”字符串对象,

如果找不到就创建新的“01”字符串对象和字符数组对象,然后再把字符串对象引用赋值s;

如果找到了就拿到“01”的字符串对象的引用,赋值给变量s。

例子2:

String s = new String("01");

// 使用到String的构造方法如下:
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

JVM先会定位到“01”变量,然后从字符串常量池中找到“01”,

如果找到就拿到字符串对象的引用赋值给original;

如果找不到就会在堆空间创建“01”的字符串对象和字符数组对象,把字符串对象引用赋值给original;

然后执行到new String(),在堆空间创建新字符串对象并把引用赋值给s,又从字符串常量池找到“01”,拿到的就是字符串对象original,最后把字符串对象original的char[]的引用赋值给新字符串对象s的char[]。

例子3:

String s1 = "01";
String s2 = "01";

s1:JVM会定位到“01”,然后从字符串常量池中找,如果找到直接用,把字符串对象引用赋值给s1;如果找不到则创建“01”的字符串对象和字符数组对象,再把字符串对象引用赋值给s1;

s2:JVM也会定位到“01”,然后从字符串常量池中找,已经存在了“01”字符串对象,那么把直接赋值s2。

所以当s1==s2比较时,结果会返回true

例子4:

String s1 = new String("01");
String s2 = new String("01");

// 使用到String的构造方法如下:
public String(String original) {
    this.value = original.value;
    this.hash = original.hash;
}

JVM会先定位到“01”,然后在字符串常量池中找,如果找到直接拿到引用,赋值给original,如果没有找到则创建“01”新的字符串对象和字符数组对象,然后把字符串对象引用赋值给original;

s1:执行第一个new String(),创建新字符串对象并把引用赋值给s1,然后从字符串常量池中找到“01”的字符串对象,在“01”字符串对象中找到字符数组对象引用,赋值给s1字符串对象的字符数组对象

s2:同理s1

Java中String和字符串常量池_第1张图片

结尾

好啦,以上就是本文的全部内容。学习底层知识可以帮忙我们平常在使用它或遇到困难时,有比较好的理论基础和认知,甚至利于分析问题根本。一起加油吧!

你可能感兴趣的