String a = "tes";
String b = a + "t";
这个引用b指向的内容为"test"么?从下面的测试结果看来不是这样:
String c = new String("test");
System.out.println(b == c.intern());//输出为false
我们知道String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,
如果有则返回一个引用,没有则添加自己的字符串进进入常量池。从上面推测b指向的内容并非为"test",不然的话b == c.intern()应该输出true才对。
那么,引用b到底是一个什么东西?
从网上有人说的是编译器在编译时将String b = a + "t";
转化成String b = new StringBuffer("tes").append("t").toString();
也就是b指向的其实是堆上StringBuffer所占的内存单元,那么测试:
System.out.println(b.intern == c.intern());//输出为true以上为我猜测,请问我的猜测对吗?欢迎了解JVM机制的童鞋一起讨论。
String b = a + "t";
这个引用b指向的内容为"test"么?从下面的测试结果看来不是这样:
String c = new String("test");
System.out.println(b == c.intern());//输出为false
我们知道String的intern()方法会查找在常量池中是否存在一份equal相等的字符串,
如果有则返回一个引用,没有则添加自己的字符串进进入常量池。从上面推测b指向的内容并非为"test",不然的话b == c.intern()应该输出true才对。
那么,引用b到底是一个什么东西?
从网上有人说的是编译器在编译时将String b = a + "t";
转化成String b = new StringBuffer("tes").append("t").toString();
也就是b指向的其实是堆上StringBuffer所占的内存单元,那么测试:
System.out.println(b.intern == c.intern());//输出为true以上为我猜测,请问我的猜测对吗?欢迎了解JVM机制的童鞋一起讨论。
实际上String类型的对象是不可以改变的,那么进行字符串拼接的时候,一般都会调用StringBuffer,即便是你没有声明一个Stringbuffer对象,JVM也会自动生成的
返回字符串对象的规范化表示形式。
一个初始为空的字符串池,它由类 String 私有地维护。
当调用 intern 方法时,如果池已经包含一个等于此 String 对象的字符串(用 equals(Object) 方法确定),则返回池中的字符串。否则,将此 String 对象添加到池中,并返回此 String 对象的引用。
它遵循以下规则:对于任意两个字符串 s 和 t,当且仅当 s.equals(t) 为 true 时,s.intern() == t.intern() 才为 true。
String.intern();
再补充介绍一点:存在于.class文件中的常量池,在运行期间被jvm装载,并且可以扩充。String的intern()方法就是扩充常量池的一个方法;当一个String实例str调用intern()方法时,java查找常量池中是否有相同unicode的字符串常量,如果有,则返回其引用,如果没有,则在常量池中增加一个unicode等于str的字符串并返回它的引用。
例3:
String s0=”kvill”;
String s1=new String(“kvill”);
String s2=new String(“kvill”);
System.out.println(s0==s1);
S1.intern();
S2=s2.intern();
System.out.println(s0==s1);
System.out.prntln(s0==s1.intern());
System.out.println(s0==s2);
结果为:
False
False //虽然执行了s1.intern(),但它的返回值没有赋给s1
True
True
最后再破除一个错误的理解:
有人说,“使用String.intern()方法可以将一个String类保存到一个全局的String表中,如果具有相同值的unicode字符串已经在这个表中,那么该方法返回表中已有字符串的地址,如果在表中没有相同值的字符串,则将自己的地址注册到表中”如果把这个全局的String表理解为常量吃的话,最后一句话“如果在表中没有相同值的字符串,则将自己的地址注册到表中”是错的。
例4:
String s1=new String(“kvill”);
String s2=s1.intern();
System.out.println(s1==s1.intern());
System.out.println(s1+” ”+s2);
System.out.println(s2==s1.intern());
结果是:
False
Kvill kvill
True
我们没有声明一个”kvill”常量,所以常量池中一开始没有”kvill”的,当我们调用s1.intern()后就在常量池中新添加了一个”kvill”常量,原来的不在常量池中的”kvill”仍然存在,也就不是“把自己的地址注册到常量池中”了。
wuwu注释
例5:
String str1=”java”; //指向字符串池
String str2=”blog”; //指向字符串池String s=str1+str2; //s是指向堆中值为"javablog"的对象,+运算符会在堆中建立来两个String对象,这两个对象的值分别是"java" "blog". 也就是说从字符串池中复制这两个值,然后在堆中创建两个对象,然后再建立对象s,然后将"javablog"的堆地址赋给s. 这句共创建了?个String 对象!
System.out.println(s==”javablog”); //结果是false。
Jvm确实对型如String str1=”java”;的String对象放在常量池里,但是它是在编译时那么做的,而String s=str1+str2;是在运行时刻才能知道,也就是说str1+str2是在堆里创建的,所以结果为false了。
如果改成一下两种方式:
String s="java" + "blog"; //直接将"javablog"放入字符串池中,System.out.println(s==”javablog”); 的结果为true, 这个句子创建了?个String对象
String s=str1+ "blog"; //不放入字符串池,而是在堆中分配,System.out.println(s==”javablog”); 的结果为False, 这个句子创建了?个String对象
解答:
String s = new String("abc");创建了几个String对象?
String s = new String("abc");创建了几个String对象?引用变量与对象的区别;
字符串文字"abc"是一个String对象;
文字池(pool of literal strings)和堆(heap)中的字符串对象。一、引用变量与对象:除了一些早期的Java书籍和现在的垃圾书籍,人们都可以从中比较清楚地学习到两者的区别。
A aa;
这个语句声明一个类A的引用变量aa[我们常常称之为句柄],而对象一般通过new创建。所以题目中s仅仅是一个引用变量,它不是对象。二、Java中所有的字符串文字[字符串常量]都是一个String的对象。有人[特别是C程序员]在一些场合喜欢把字符串"当作/看成"字符数组,这也没有办法,因为字符串与字符数组存在一些内在的联系。事实上,它与字符数组是两种完全不同的对象。System.out.println("Hello".length());
char[] cc={'H','i'};
System.out.println(cc.length);三、字符串对象的创建:
由于字符串对象的大量使用(它是一个对象,一般而言对象总是在heap分配内存),Java中为了节省内存空间和运行时间(如比较字符串时,==比equals()快),在编译阶段就把所有的字符串文字放到一个文字池(pool of literal strings)中,而运行时文字池成为常量池的一部分。文字池的好处,就是该池中所有相同的字符串常量被合并,只占用一个空间。
我们知道,对两个引用变量,使用==判断它们的值(引用)是否相等,即指向同一个对象:String s1 = "abc" ;
String s2 = "abc" ;
if( s1 == s2 ) System.out.println("s1,s2 refer to the same object");
else System.out.println("trouble");这里的输出显示,两个字符串文字保存为一个对象。就是说,上面的代码只在pool中创建了一个String对象。现在看String s = new String("abc");语句,这里"abc"本身就是pool中的一个对象,而在运行时执行new String()时,
将pool中的对象复制一份放到heap中,并且把heap中的这个对象的引用交给s持有。ok,这条语句就创建了2个String对象。String s1 = new String("abc") ;
String s2 = new String("abc") ;
if( s1 == s2 ){ //不会执行的语句}这时用==判断就可知,虽然两个对象的"内容"相同(equals()判断),但两个引用变量所持有的引用不同,
上面的代码创建了几个String Object? (三个,pool中一个,heap中2个。)
String b = "test";
System.out.println(b == c.intern());//输出为true说明b和c都是在一个常量池中,lz的举例和分析应该是没错的。
String b = a + "t"会创建一个新的String对象,b对象的值不变还是"tes";
而String b = new StringBuffer("tes").append("t").toString()这种方法创建一个字符串放在b对象中,概念不同。
是够无聊,我相信你并没有弄懂这里面和JVM有关的知识。
输出为真不代表c也在常量池。c.intern()指向的是常量池里面的一个引用,c仍然在堆里面老实呆着
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
char buf[] = new char[count + otherLen];
getChars(0, count, buf, 0);
str.getChars(0, otherLen, buf, count);
return new String(0, count + otherLen, buf);
}该方法就是返回堆中新生成对象的,而不是the interned one从定义上讲:
String a = "tes";
String b = a + "t";a是literal,而b就不是literal,也不是string valued constant expression,因为a不是constant
详见:jls §3.10.5:http://java.sun.com/docs/books/jls/second_edition/html/lexical.doc.html#101083
System.out.println(b.intern() == b);
结果为true
c.intern()返回池中的字符串,那池中的字符串就相当于是常量"test",也就是说相当于双引号生成的,而
b=a + "t"; 很明显相当于new生成的,生成一个新对象,两个对象不是同一个,你用==比较当然就是false了。
b == c.intern()你这是比较两个String对象,必然不相等。是吗?
这个是因为"tes" + "t"在编译时就能确定的,所以直接被编译成"test"放在常量池
恩,这和我猜的差不多,我知道答案了,thanks。BTW,论坛里面还是那么多浮躁的人啊
在JDK1.5之后,字符串拼接是调用StringBuilder的appen方法。b == c.intern()
一个在堆中,一个在字符串池中,内存地址显然不相等。
谢谢ls所有回复的人,不管正确or错误
String b = a + "t";
String c = new String("test");
System.out.println(b == c.intern());此时常量池中没有"test",也就是b的内容"test"不是在常量池中,而c.intern()执行之后,在常量池中创建了"test",并返回它,当然b != c.intern()。System.out.println(b.intern() == c.intern());
当然输出true,因为b和c的内容相同,b.intern()和c.intern()返回的都是常量池中的test。也就是楼主的推测是对的:
String b = a + "t";不会在常量池中生成一个"test"。