样例如下:
String s1 = new StringBuilder("12").append("ab").toString();
System.out.println(s1.intern() == s1); //true String s2 = new StringBuilder("ja").append("va").toString();
System.out.println(s2.intern() == s2); //false String s3 = new StringBuilder().append("12ab").toString();
System.out.println(s3.intern() == s3); //false问题:
1、s1 不应该是指向堆内存的地址吗? 为什么会与 s1.intern() 指向相同?
2、s1、s2 调用形式形同,结果不同?
3、s1、s3不同?看StringBuilder带String的构造方法,也是调用的append方法,不理解,求指教。谢谢!!!
String s1 = new StringBuilder("12").append("ab").toString();
System.out.println(s1.intern() == s1); //true String s2 = new StringBuilder("ja").append("va").toString();
System.out.println(s2.intern() == s2); //false String s3 = new StringBuilder().append("12ab").toString();
System.out.println(s3.intern() == s3); //false问题:
1、s1 不应该是指向堆内存的地址吗? 为什么会与 s1.intern() 指向相同?
2、s1、s2 调用形式形同,结果不同?
3、s1、s3不同?看StringBuilder带String的构造方法,也是调用的append方法,不理解,求指教。谢谢!!!
并且,如果只看反编译的结果可能达不到这种细节的粒度
但是看一下字节码可能会有帮助,不过我懒得看了. javap -verbose xxx.class另外可以和java6的情况比较一下.
s3.intern() == s3,由于 s1的时候已经将这个字符串加入常量池了,所以也是false;
你把上面三句可以理解为下面的语句
s1=new String("12")+new String("ab");
s2=new String("ja")+new String("va");
s3=new String("12ab");
String a = "a";
String b = "a";
String c = "a"; System.out.println(a.intern() == a);
System.out.println(a.intern() == b);
System.out.println(a.intern() == c); //输出了 true true true
这里可以从专业的JVM 开发大牛 R大 的以前帖子里面了解到,https://www.iteye.com/topic/1112592?page=3,搜索:RednaxelaFX.注意看下R大的回答.再来说下String.intern()方法.比如说String实例"demo"来调用这个方法时,会查看池中所有引用的String实例有没有与"demo"相同,也就是用equals方法比较为true,如果没有,就保存"demo"的引用并返回"demo"的引用,如果有,就返回保存的引用.这个可以去看源码的注释.根据上面知道的在JDK 8 HotSpot VM环境下来分析下楼主的这几个问题.
1.
String s1 = new StringBuilder("12").append("ab").toString();
System.out.println(s1.intern() == s1); //true通过StringBuilder 拼接了"12"和"ab" 得到了"12ab", 此时堆内存中的首次出现了"12ab"这个String实例.这个实例是在堆中的young generation里面的, s1保存的是对这个实例的引用,调用s1.intern()时,池中无与"12ab"相同内容的字符串实例引用,所以保存这个引用并返回了引用.使用 == 判断引用地址是否相等,结果为true
2.
String s2 = new StringBuilder("ja").append("va").toString();
System.out.println(s2.intern() == s2); //false4楼的同学已经提到了,Version类定义的私有静态字符串常量有String实例"java",会在jvm启动的时候加载到运行时常量池中.通过StringBuilder会生成一个新的String实例"java"保存在堆内存中,s2保存对这个实例的引用.虽然常量池中引用的实例"java"也在堆内存中,但是并不是同一个实例,相应的他们的引用也不一样,调用s2.intern()时返回的是Version类加载到运行时常量池中的引用,而不是s2指向的.此时用 == 判断引用地址是否相同,结果为false.3.
String s3 = new StringBuilder().append("12ab").toString();
System.out.println(s3.intern() == s3); //false这个例子的分析与第二种情况类似,在常量池中已经有了"12ab"实例的引用,这个引用就是append()方法传入的"12ab"的引用 StringBuilder创建一个新的实例,在堆内存中,s3保存此实例的引用,与常量池中的引用不一致,通过== 判断,结果为false.
至于你后面说把3放到1前面,所有的结果都为false了,通过上面的分析答案是很明显.类同2,3情况分析.
String a = "12";
String b = "ab";
String s1 = new StringBuilder(a).append(b).toString();
这样常量池里就已经存在了“12”“ab”,但是不存s1的“12ab”,所以s1==s1.intern(),为true;再看
String s3 = new StringBuilder().append("12ab").toString();
分解为:
String a = "12ab";
String s3 = new StringBuilder().append(a).toString();
这时常量池已经存在“12ab”,s3的12ab已经存在了
System.out.println(s3.intern() == s3); //false
回归正题:
我又写了2个单独的测试类:
1、
public class Test1 {
public static void main(String[] args) {
String s1 = new StringBuilder("12").append("ab").toString();
System.out.println(s1.intern() == s1); //true }
}
2、public class Test2 {
public static void main(String[] args) {
String s1 = new StringBuilder().append("12ab").toString();
System.out.println(s1.intern() == s1); //false
}
}区别就是调用了不同的构造方法,一个true、一个false。这个也请看一下,谢谢。
如果对1、2各追加一个append方法,结果都是true了
1、public class Test1 {
public static void main(String[] args) {
String s1 = new StringBuilder("12").append("ab").append("cd").toString();
System.out.println(s1.intern() == s1); //true }
}
2、public class Test2 {
public static void main(String[] args) {
String s1 = new StringBuilder().append("12ab").append("cd").toString();
System.out.println(s1.intern() == s1); //true
}
} }
}
现在的疑问是:
调用StringBuilder空参的append和有参的结果不一样,当追加至2个append方法时,结果一样,都是true。
请各位再给看一下。
其实我前面已经说过了当StringBuilder非空参时,如第1句,第2句
相当于下面的语句,如果后面还有append就再加一个new String
s1=new String("12")+new String("ab");
s2=new String("ja")+new String("va");而StringBuilder空参时,如第3句如果后面只有一个append的话就相当于
s3=new String("12ab");这时候只是相当于赋值语句,不会在常量池中计算,所以不会引入常量池的值。
如果后面再加append话就和上面一样了。只不过当你相加后的字符串如果在常量池中已经有和没有结果会不一样。
String a = "12";
String b = "ab";
String s1 = new StringBuilder(a).append(b).toString();
这样常量池里就已经存在了“12”“ab”,但是不存s1的“12ab”,所以s1==s1.intern(),为true;再看
String s3 = new StringBuilder().append("12ab").toString();
分解为:
String a = "12ab";
String s3 = new StringBuilder().append(a).toString();
这时常量池已经存在“12ab”,s3的12ab 你还是没有理解这个分析;
String s3 = new StringBuilder().append("12ab").toString();
这样时,常量池会先出现“12ab”,而这时s3是“12ab”,发现常量池已经存在该字符,不会重新创建了,s3.intern()返回的是常量池地址,s3是new StringBulider()的内存地址,所以是false;String s3 = new StringBuilder("12").append("ab").append("ed").toString();
这种时候
常量池中出现的是 “12”“ab”“ed”,而此时s3是“12abed”,发现常量池中不存在该字符,然后将new StringBuilder()内存地址记录下来,s3.intern()返回的是new StringBuilder()的内存地址,所以true;
当.append("34dc")时,"34dc"就是先被创建到常量池,怎么能说没用到呢?然后结果是false,哪里不对吗?
当.append("34dc")时,"34dc"就是先被创建到常量池,怎么能说没用到呢?然后结果是false,哪里不对吗?不是在常量池创建的,都说了因为空构造+append相当于 String s3=null;这里只是初始化了空间但是没有内容,然后append加入字符串时是直接写入了这个空间内,所以根本没有用到常量池,s3是堆上的地址,这时候你把s3放入常量池s3.intern()始终不会和s3相等。如果再多一个append就和1,2没区别了。
String s3 = new StringBuilder().append("34cd").append("56ef").toString();
super(16);
}
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
所以 new StringBuilder(“12cd”)等价于 new StringBuilder().append("12cd")
https://docs.oracle.com/javase/8/docs/api/index.htmlWhen the intern method is invoked, if the pool already contains a string equal to this String object as determined by the equals(Object) method, then the string from the pool is returned. Otherwise, this String object is added to the pool and a reference to this String object is returned.如果常量池已存在和字符串对象的内容一样的对象话,则返回常量池对象(的引用)
如果常量池里还不存在的话,就把字符串对象加到常量池,并返回该对象的引用根据文档
String s1 = new StringBuilder("12").append("ab").toString();
String s2 = "12ab"; //在intern方法前后,加上常量池“12ab”对象引用的代码,就明白了
System.out.println(s1.intern() == s1); //true
//String s2 = "12ab";至于s3和s1为什么不一样
String s3 = new StringBuilder().append("12ab").toString(); //因为在这里常量池就已经创建“12ab”了,不管你换成什么内容,除非把字符串拆开写,和s1一样
System.out.println(s3.intern() == s3); //false
String s = new String("abc")+new String(“de”);
System.out.println(s.intern()==s);//true
你好好理解一下下面的不同。
String s1="abc";
String s2=new String("abc");
System.out.println(s1==s2);//false
String s2=new String("abc");
System.out.println(s1==s2);//false ,我理解的是字符串字面量是一种隐式创建String对象,S1指向的是常量池,s2指向的是堆,== 比较地址,所以false。我翻看了一下java se8 虚拟机规范《第5章 5.1 运行时常量池》里面一段话是这么解释的:“为了得到字符常量,Java虚拟机需要检查CONSTANT_String_info结构中的码点序列。如果某String实例所包含的Unicode码点序列与CONSTANT_String_info结构所给出的序列相同,而之前又曾在该实例上面调用过String.intern()方法,那么此次字符常量获取的结果将是一个指向相同String实例的引用。否则,会创建一个新的String实例,其中包含由CONSTANT_String_info结构所给出的Unicode码点序列;字符常量获取的结果是指向那个新String实例的引用。最后,新String实例的intern()方法被Java虚拟机自动调用。”,就是这个"新String实例的intern()方法被Java虚拟机自动调用",那在new StringBuilder时,StringBuilder里面也是return new String...,按理说intern方法已经被执行了,为什么会得到false的结果呢。有不对的地方,请各位批评指正,也请大家再看一下这个问题,谢谢~~~
现在说下你最后的问题,你引用的段话是 jvm 运行时常量池从Class文件的常量池(constant pool)中获取字符串字面常量(String literal) 引用的对象的方法,注意是仅仅针对String literal ,那什么又是String literal,在jsl8的3.10.5 中有定义: String literal是用由双引号括起来的0个或者多个字符构成的,这些字符可以是转义字符.,原文是:"A string literal consists of zero or more characters enclosed in double quotes.Characters may be represented by escape sequences (§3.10.6)".就是平时开发中很常见的直接出现在代码中的"0001"类似的字符串.碰到了String literal,用你说的这个方法,先检查常量池中是否有内容相同的String 实例的引用,如果有就直接返回这个引用,如果没有,在堆中新建一个String 实例,返回这个实例的引用,jvm自动调用intern()方法把这个引用保存到常量池中.所以直接用"123"给多个String 变量赋值,全部变量用 == 判断时一直都是true,这段代码就像我之前回复的那样.现在来说你问的为什么调用String s2 = new String ("asd");s1 == s2 为false,在s2变量赋值时,new 操作符会在堆中再重新开辟一块内存,创建一个String 实例,这个实例的内容与"asd"所指向的String 实例 是完全一样的,但是这两个实例的内存地址是完全不同的.所以用 == 判断的时候,结果为false.可以用以下的图来说明.
String s = new String("abc")+new String(“de”);
System.out.println(s.intern()==s);//true还要怎么说?
String s = new String("abc")+new String(“de”);
常量池中创建 “abc”“de”两个字符串,s 最后的值是“abcde”,s.intern()发现常量池中不存在该值,赋值s的现有内存地址保存在常量池作为“abcde”的引用,所以s.intern()==s是true;
String s = new String("abc")+new String(“de”);
System.out.println(s.intern()==s);//true还不明白做个测试,
String s = new String("abc")+new String(“de”);
String s1 = “abcde”;//这里将abcde在常量池创建
System.out.println(s.intern()==s);//false //发现常量池中存在“abcde”s.intern()返回常量池现有引用,所以是false
我最后一次回复你,我一直说的是第3句的解释,不知道为何又被你扯回第2句去了。
String s1=new StringBuilder("12").append("34").toString();
System.out.println(s1.intern() == s1); //true
String s2=new StringBuilder("5678").toString();
System.out.println(s2.intern()==s2);//false
而如果在JDK1.6中,intern()方法会把首次遇到的字符串实例复制到永久代中,返回的也是永久代中这个字符串的实例的引用,而StringBulder创建的字符串实例在Java堆上,所以必然不是同一个引用,(s1.intern() == s1)的结果将返回false。s2.intern() == s2时,由于java这个字符串已经存在在常量池了,java虚拟机会自动调用System类,System类会调用了initializeSystemClass方法,从而在此方法中调用了Version对象的init静态方法sun.misc.Version.init();Version类定义的私有静态字符串常量有这个 java ,所以是false
s3.intern() == s3,由于 s1的时候已经将这个字符串加入常量池了,所以也是false;