资讯专栏INFORMATION COLUMN

关于String.intern()和new StringBuilder("").

derek_334892 / 3740人阅读

摘要:对比较返回是因为这个字符串在执之前已经出现过,字符串常量池中已经有它的引用了,不符合首次出现的原则,而计算机软件这个字符串则是首次出现的,因此返回。

在《深入理解Java虚拟机》书中,提到在jdk1.7的版本中用String.intern()返回引用。

public class RuntimeConstantPoolOOM {
    public static void main(String[]args) {
        String str1=new StringBuilder("计算机").append("软件").toString();
        System.out.println(str1.intern()==str1);
        String str2=new StringBuilder("ja").append("va").toString();
        System.out.println(str2.intern()==str2);
    }

}

运行结果:true false
书中给的解释是:

JDK 1.7(以及部分其他虚拟机,例如JRockit)的intern()实现不会再复制实例,只是在常量池中记录首次出现的实例引用,因此intern()返回的引用和由StringBuilder创建的那个字符串实例是同一个。对str2比较返回false是因为“java”这个字符串在执StringBuilder.toString()之前已经出现过,字符串常量池中已经有它的引用了,不符合“首次出现”的原则,而“计算机软件”这个字符串则是首次出现的,因此返回true。

现在的疑问是“java”这个字符串在常量池中什么时候存在了?
我最开始的猜想是“java”这个字符串是不是常驻在常量池中的?那为什么常驻在常量池中呢?Java虚拟机什么时候加载了“java”这个字符串?

最开始以为是StringBuilder的原因,查看了一下StringBuilder的源码,发现里面没有加载字符串常量,网上也找了关于intern()的,发现都只是对比JDK 1.6和JDK 1.7之间上面代码的运行结果比较,后来找了许久,终于找到一篇关于[String.intern()探究]: 的文章,发现要去查看System的源码.

java虚拟机会自动调用System类

/* register the natives via the static initializer.
 *
 * VM will invoke the initializeSystemClass method to complete
 * the initialization for this class separated from clinit.
 * Note that to use properties set by the VM, see the constraints
 * described in the initializeSystemClass method.
 */
在System类中的注释可以知道,调用了initializeSystemClass方法,在此方法中调用了Version对象的init静态方法
sun.misc.Version.init();
因此sun.misc.Version类会在JDK类库的初始化过程中被加载并初始化。
查看Version类定义的私有静态字符串常量如下:
private static final String launcher_name = "java";
private static final String java_version = "1.7.0_51";
private static final String java_runtime_name = "Java(TM) SE Runtime Environment";
private static final String java_runtime_version = "1.7.0_51-b13";
在初始化Version类时,对其静态常量字段根据指定的常量值做默认初始化,所以"java"被加载到了字符串常量池中,修改上面代码使字符串值为上面常量中的任意一个都会返回false。
String str2=new StringBuilder("1.7.0").append("_51").toString();
System.out.println(str2.intern()==str2);

这个问题解决了,然后我又发现了另外一个问题。除了这些在虚拟机加载时就初始化的常量,定义其他的字符串常量,比如“nihao”.

先运行这个代码
String str3 = new StringBuilder("ni").append("hao").toString();
System.out.println(str3==str3.intern());
通过上面的解释,运行结果为true.
在运行这个代码
String str3 = new StringBuilder("nihao").toString();
System.out.println(str3==str3.intern());
其结果是什么?应该还是true吧,毕竟通过上一个运行结果可以知道"nihao"这个字符串常量没有被预先加载到常量池中。
但是运行结果却是false.

我现在还没想通这个问题,StringBuilder的append方法没有改变字符串的引用地址,只是把其值改变了,为什么加了append返回的是true,没有加append却是false呢?如果在后面多加几个append返回的也是true。
也希望有人可以解答一下这个问题

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/67760.html

相关文章

  • String的内存模型,为什么String被设计成不可变的

    摘要:证明返回常量池中已存在的对象,不等于新建的对象。为什么要设计成一下内容来自发现百度的中文版本基本也是此文的翻译版。总之,安全性和字符串常量池缓存是被设计成不可变的主要原因。 String是Java中最常用的类,是不可变的(Immutable), 那么String是如何实现Immutable呢,String为什么要设计成不可变呢? 前言 关于String,收集一波基础,来源标明最后,不确...

    vspiders 评论0 收藏0
  • 关于 String.intern() 的思考

    摘要:我看到一个上的问题是关于的感觉比较有意思于是自己也去探索了一下有了一些自己的见解于是在此记录下来我们首先来看一个例子编程编程这个例子会输出什么呢有些读者朋友可能没有想到其实上面的例子在不同的版本中运行会有不同的结果的那么接下来我们来试一下吧 我看到一个 segmentfault 上的问题, 是关于 String.intern() 的, 感觉比较有意思, 于是自己也去探索了一下, 有了一...

    siberiawolf 评论0 收藏0
  • 深入研究Java String

    摘要:所以我决定先从类入手,深入的研究一番来开个好头。之所以会有以上的效果,是因为有字符串常量池的存在。同时运行时实例创建的全局字符串常量池中有一个表,总是为池中的每个字符串对象维护一个引用,所以这些对象不会被。 开始写 Java 一年来,一直都是遇到什么问题再去解决,还没有主动的深入的去学习过 Java 语言的特性和深入阅读 JDK 的源码。既然决定今后靠 Java吃饭,还是得花些心思在上...

    番茄西红柿 评论0 收藏0
  • Java内存区域及内存溢出

    摘要:直接通过可以造成本机内存溢出。小节内存区域描述异常程序计数器略略略虚拟机栈存放编译器可知的各种基本类型,对象引用和类型每个线程的栈大小堆存放对象实例最大值最小值运行时常亮池存放编译期生成的字面量和符号引用,运行期也能放入常量池。 堆溢出 Java堆用于存储对象实例,只要不断地创建对象,并且保证GC Roots到对象之间有可达路径避免垃圾回收,当到达最大堆的容量限制后就会产生Java.l...

    cheukyin 评论0 收藏0
  • 为什么不建议在for循环中使用"+"进行字符串拼接

    摘要:使用可以方便的对字符串进行拼接。该方法使用进行声明,说明是一个线程安全的方法。所以,阿里巴巴开发手册建议循环体内,字符串的连接方式,使用的方法进行扩展。但是,还要强调的是如果不是在循环体中进行字符串拼接的话,直接使用就好了。 摘要: 学习阿里巴巴Java开发手册。 原文:为什么阿里巴巴不建议在for循环中使用+进行字符串拼接 微信公众号:Hollis Fundebug经授权转载,...

    caoym 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<