实例 求解 java String StringBuilder 底层原理?

weixin_38140000 2019-10-17 12:32:35
样例如下:
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方法,不理解,求指教。

谢谢!!!

...全文
812 33 打赏 收藏 转发到动态 举报
写回复
用AI写文章
33 条回复
切换为时间正序
请发表友善的回复…
发表回复
骚小孩呀 2019-10-24
  • 打赏
  • 举报
回复
看看源码呀,可能理解起来好点
weixin_45792238 2019-10-24
  • 打赏
  • 举报
回复
而如果在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;
轻解罗裳呀 2019-10-21
  • 打赏
  • 举报
回复
哈哈,大半夜的,睡觉了
qq_39936465 2019-10-21
  • 打赏
  • 举报
回复
引用 32 楼 dkwuxiang 的回复:
还要怎么说? 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
dkwuxiang 2019-10-21
  • 打赏
  • 举报
回复
引用 24 楼 qq_39936465 的回复:
[quote=引用 21 楼 dkwuxiang 的回复:] 不管是new StringBuilder(),还是append()也好,只要其中出现 字符串,比如new StringBuilder(“abdf”) 或者append("abdf"); “abdf”就会出现在常量池,StringBuilder 不能凭空变个字符串给你 你可以理解一下,String s = new String("asd");创建了几个对象,创建在哪里了
如果照你的理论,常量池有的对象就会false,那下面的问什么会true。 String s = new String("abc")+new String(“de”); System.out.println(s.intern()==s);//true[/quote] 还要怎么说? 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
郭超东 2019-10-21
  • 打赏
  • 举报
回复
已收藏
gz505 2019-10-20
  • 打赏
  • 举报
回复
底层原理对实际一些简单开发者来讲,没什么用途,尤其是这种使用较少的函数,都不如研究研究常用框架实际。
weixin_38140000 2019-10-19
  • 打赏
  • 举报
回复
引用 25 楼 qq_39936465 的回复:
你别问我创建了几个对象,创建再哪里 ? 你好好理解一下下面的不同。 String s1="abc"; String s2=new String("abc"); System.out.println(s1==s2);//false
说一下我的想法,String s = new String("asd");不考虑S前提下,如果字面量"asd"不在常量池中(常量池保存的也是引用),创建2个对象;如果在常量池中,创建一个对象;对于:String s1="abc"; 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的结果呢。有不对的地方,请各位批评指正,也请大家再看一下这个问题,谢谢~~~
大隐藏于寺 2019-10-19
  • 打赏
  • 举报
回复
引用 27 楼 weixin_38140000 的回复:
[quote=引用 25 楼 qq_39936465 的回复:]你别问我创建了几个对象,创建再哪里 ? 你好好理解一下下面的不同。 String s1="abc"; String s2=new String("abc"); System.out.println(s1==s2);//false
说一下我的想法,String s = new String("asd");不考虑S前提下,如果字面量"asd"不在常量池中(常量池保存的也是引用),创建2个对象;如果在常量池中,创建一个对象;对于:String s1="abc"; 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的结果呢。有不对的地方,请各位批评指正,也请大家再看一下这个问题,谢谢~~~[/quote] 回复得晚了,本来也想整理下jvms8和 jsl8中的原文来回答问题,但是有些问题你已经自己找到答案了. 现在说下你最后的问题,你引用的段话是 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.可以用以下的图来说明.
qq_39936465 2019-10-19
  • 打赏
  • 举报
回复
所以我说s3引用的地址根本不在常量池,而不管s3的值在常量池内有还是没有,s3.intern()的引用地址一定是常量池的地址,2者永远不会相等的,而碰巧楼主的s3的值常量池中是有的,因此我说第3句解释的有问题。
qq_39936465 2019-10-19
  • 打赏
  • 举报
回复
你别问我创建了几个对象,创建再哪里 ? 你好好理解一下下面的不同。 String s1="abc"; String s2=new String("abc"); System.out.println(s1==s2);//false
qq_39936465 2019-10-19
  • 打赏
  • 举报
回复
引用 21 楼 dkwuxiang 的回复:
不管是new StringBuilder(),还是append()也好,只要其中出现 字符串,比如new StringBuilder(“abdf”) 或者append("abdf"); “abdf”就会出现在常量池,StringBuilder 不能凭空变个字符串给你 你可以理解一下,String s = new String("asd");创建了几个对象,创建在哪里了
如果照你的理论,常量池有的对象就会false,那下面的问什么会true。 String s = new String("abc")+new String(“de”); System.out.println(s.intern()==s);//true
qq_39936465 2019-10-18
  • 打赏
  • 举报
回复
引用 12 楼 weixin_38140000 的回复:
现在的疑问是:
调用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话就和上面一样了。

只不过当你相加后的字符串如果在常量池中已经有和没有结果会不一样。
qybao 2019-10-18
  • 打赏
  • 举报
回复
java文档写的很清楚
https://docs.oracle.com/javase/8/docs/api/index.html

When 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





west_jing 2019-10-18
  • 打赏
  • 举报
回复
大牛些啊,学习了学习了
dkwuxiang 2019-10-18
  • 打赏
  • 举报
回复
引用 19 楼 qq_39936465 的回复:
我可能说的不是很准确看下源码就是知道了 public StringBuilder() { super(16); } public StringBuilder(String str) { super(str.length() + 16); append(str); } 所以 new StringBuilder(“12cd”)等价于 new StringBuilder().append("12cd")
不管是new StringBuilder(),还是append()也好,只要其中出现 字符串,比如new StringBuilder(“abdf”) 或者append("abdf"); “abdf”就会出现在常量池,StringBuilder 不能凭空变个字符串给你 你可以理解一下,String s = new String("asd");创建了几个对象,创建在哪里了
大隐藏于寺 2019-10-18
  • 打赏
  • 举报
回复
等我到老家再来好好理下怎么描述清楚点
qq_39936465 2019-10-18
  • 打赏
  • 举报
回复
我可能说的不是很准确看下源码就是知道了 public StringBuilder() { super(16); } public StringBuilder(String str) { super(str.length() + 16); append(str); } 所以 new StringBuilder(“12cd”)等价于 new StringBuilder().append("12cd")
qq_39936465 2019-10-18
  • 打赏
  • 举报
回复
引用 17 楼 dkwuxiang 的回复:
[quote=引用 11 楼 qq_39936465 的回复:] 楼上2位对第3句解释的都有问题,不是该字符串在常量池中已经存在了,常量池不存在的一样是false,是因为s3运算根本没有引用到常量池。

String s3 = new StringBuilder().append("34cd").toString();
	        System.out.println(s3.intern() == s3); //false
当.append("34dc")时,"34dc"就是先被创建到常量池,怎么能说没用到呢?然后结果是false,哪里不对吗?[/quote] 不是在常量池创建的,都说了因为空构造+append相当于 String s3=null;这里只是初始化了空间但是没有内容,然后append加入字符串时是直接写入了这个空间内,所以根本没有用到常量池,s3是堆上的地址,这时候你把s3放入常量池s3.intern()始终不会和s3相等。如果再多一个append就和1,2没区别了。 String s3 = new StringBuilder().append("34cd").append("56ef").toString();
dkwuxiang 2019-10-18
  • 打赏
  • 举报
回复
引用 11 楼 qq_39936465 的回复:
楼上2位对第3句解释的都有问题,不是该字符串在常量池中已经存在了,常量池不存在的一样是false,是因为s3运算根本没有引用到常量池。

String s3 = new StringBuilder().append("34cd").toString();
	        System.out.println(s3.intern() == s3); //false
当.append("34dc")时,"34dc"就是先被创建到常量池,怎么能说没用到呢?然后结果是false,哪里不对吗?
加载更多回复(13)

62,614

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧