字符串常量池&字符串常量的几种创建方式及其位置

从JDK7开始,字符串常量池被移到了堆区中,因此Java程序中的字符串常量对象要么在堆区的字符串常量池之中,要么在堆区的字符串常量池之外。为了做区分,下文将堆区的字符串常量池区域称为字符串常量池,将堆区字符串常量池之外的区域称为堆区。

一.使用字面量直接赋值

String str = "abc";

1.使用这种方式可以保证字符串常量池中一定有字符串对象"abc"。

2.执行步骤:

(1)通过值比较的方式判断字符串常量池中是否有字符串对象"abc":

(2)若有,则直接将该对象的引用返回给str;

(3)若没有,则先在字符串常量池中创建字符串对象"abc",再将其引用返回给str。

3.特征:

(1)字符串常量池一定会有声明的字面量的字符串对象。

(2)创建对象的个数:[0,1]:

        a.只有当字符串常量池没有"abc"对象,才会创建。即字符串常量池中的对象具有唯一性。

        b.全程与堆区无关。

(3)返回的是指向字符串常量池中的引用。

二.使用new String(字面量)

String str = new String("abc");

1.使用这种方式可以保证字符串常量池和堆区都会有字符串对象"abc"。

2.执行步骤:

(1)通过值比较的方式判断字符串常量池中是否有字符串对象"abc":

(2)若有,则继续执行;

(3)若没有,则会在字符串常量池中创建字符串对象"abc";

(4)在堆区中创建字符串对象"abc",并将其引用返回。

3.特征:

(1)字符串常量池和堆中都一定会有声明的字面量的字符串对象。

(2)创建对象的个数:[1,2]。

       a.只有当字符串常量池没有"abc"对象,才会创建。即字符串常量池中的对象具有唯一性。

        b.一定会在堆区创建一个"abc"对象。因此堆区可能会有多个"abc"对象,每执行一次new String("abc")就会创建一个"abc"对象。

(4)返回的是指向堆中的引用。

三.使用new String(变量)

String str = new String(str1);

1.这种方式只会在堆中创建字符串对象。

2.解析:

(1)字符串常量池的来源链是:字节码文件的静态常量池——>静态常量池中的所有字符串常量——>堆中的字符串常量池。

(2)即字符串常量池中的数据都是在编译阶段确定的。像上述这种创建字符串对象的方式,由于涉及到变量,因此需要在在运行时动态构建,无法加入字符串常量池。

(3)str指向堆中的对象。

四.使用StringBuilder

String str = new StringBuilder().append("a").append("b").toString();

1.对于作为方法参数传递的字面量"a"、"b",因为其已经显示声明,在编译阶段是可知的,因此字符串常量池中会加入对象"a"、"b"。

(1)上述对显示声明的字面量字符串对象作为参数传递,可以拆解成以下步骤

String stra = "a";
String strb = "b";
String str = new StringBuilder().append(stra).append(strb).toString();

也就是说字符串对象"a"和"b"会被加入到字符串常量池中,但不会在堆中创建对象。

(2)由此可知,使用字面量作为参数传递时,该字面量也会被作为字符串对象加入到字符串常量池中。

2.上述语句的执行结果是str = "ab";但同样地,由于涉及到类的调用和方法的执行,因此对字符串对象"ab"是在运行时动态构建的,其只能在堆中创建对象,无法加入到字符串常量池中。

3.str指向堆中的对象。

4.验证:使用jclasslib查看编译成的字节码文件中的静态常量池

 

 可以看到,该类的静态常量池中唯二的两个字符串常量就是"a"和"b",它们会被作为字符串常量对象加入到字符串常量池中,而没有"ab"。

五.使用"+"连接

1.等号右边全为字面量

String str = "a" + "b";

(1)在编译阶段,编译器会对全由字面量构成的连接时进行优化,称为常量折叠。优化结果为

String str = "ab";

(2)字符串"a"和"b"由于常量折叠而丢失,不会创建任何对象。最终会为优化后得到的字符串"ab"在字符串常量池中创建对象。

(3)str指向字符串常量池中的对象

(4)验证:

可以看到,静态常量池中只有一个字符串常量"ab"。

2.等号右边至少含有一个变量

String str = str1 + "a" + "b";

 (1)只要涉及到对变量的操作,就只能在运行时动态构建。

(2)上述语句会在字符串常量池中创建字符串对象"a"和"b",会在堆中创建字符串对象作为动态执行的结果。

(3)str指向堆中的对象。

六.总结

1.对于代码中所有的字面量字符串,无论是显式声明用于构建String对象还是用于传参,只要不被常量折叠,就一定会为其在字符串常量池中创建对象。

2.对于通过调用类、变量、方法等方式动态构建的字符串,由于需要运行时动态构建,因此无法加入字符串常量池,只能在堆中创建对象。

七.String类提供的intern()方法

intern()方法会将字符串常量对象放到字符串常量池中,并返回其引用。对于不同的JDK版本有不同的实现:

///str是一个在堆中的字符串对象
System.out.println(str.intern() == str);

1.JDK6及之前的intern()方法:

(1)如果str在字符串常量池中,则返回其位于字符串常量池中的引用。输出结果为false。

(2)如果str不在字符串常量池中,则在字符串常量池中创建一个str并返回其引用。输出结果为false。

(3)str是对堆中的引用,而str.intern()只会返回字符串常量池中的引用,因此结果一定是false。

2.JDK7及之后的方法:

(1)如果str在字符串常量池中,则返回其位于字符串常量池中的引用。输出结果为false。

(2)如果str不在字符串常量池中,则在字符串常量池中存入对堆中str的引用,并将该引用返回,而不会再创建一个对象。输出结果为true。

(3)str是对堆中的引用,而str.intern()的返回结果有两种可能:对字符串常量池中的引用、对字符串常量池的引用——>对堆中对象的引用。

  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值