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机制的童鞋一起讨论。

解决方案 »

  1.   

    String b = a + "t";这种情况没有显式的声明一个StringBuffer对象,编译器确实自动生成,存在了操作数栈中。
    实际上String类型的对象是不可以改变的,那么进行字符串拼接的时候,一般都会调用StringBuffer,即便是你没有声明一个Stringbuffer对象,JVM也会自动生成的
      

  2.   

    唉,这个问题实在不想再说啥了给你转一个文章,说的蛮透彻的Java运行环境有一个字符串池,由String类维护。执行语句String str="abc"时,首先查看字符串池中是否存在字符串"abc",如果存在则直接将"abc"赋给str,如果不存在则先在字符串池中新建一个字符串"abc",然后再将其赋给str。执行语句String str=new String("abc")时,不管字符串池中是否存在字符串"abc",直接新建一个字符串"abc"(注意:新建的字符串"abc"不是在字符串池中),然后将其付给str。前一语句的效率高,后一语句的效率低,因为新建字符串占用内存空间。String str = new String()创建了一个空字符串,与String str=new String("")相同。 public String intern()
    返回字符串对象的规范化表示形式。 
    一个初始为空的字符串池,它由类 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个。)
      

  3.   


    String b = "test";
    System.out.println(b == c.intern());//输出为true说明b和c都是在一个常量池中,lz的举例和分析应该是没错的。
      

  4.   

    个人认为:
    String b = a + "t"会创建一个新的String对象,b对象的值不变还是"tes";
    而String b = new StringBuffer("tes").append("t").toString()这种方法创建一个字符串放在b对象中,概念不同。
      

  5.   


    是够无聊,我相信你并没有弄懂这里面和JVM有关的知识。
      

  6.   


    输出为真不代表c也在常量池。c.intern()指向的是常量池里面的一个引用,c仍然在堆里面老实呆着
      

  7.   

    b == c.intern()你这是比较两个String对象,必然不相等!跟b里的内容是什么没有关系,跟是不是池里的也没有关系。而且b里的内容就是“test”! 你要用b.equals(c.intern())就是true了。跟JVM也没什么关系。建议还是先想想== 和equals方法两种不同的比较方式...........
      

  8.   

    是,string concatenation生成的串不是interned,它就相当于StringBuffer或StringBuilder append再toStringAll literal strings and string-valued constant expressions are interned.
      

  9.   

    再补充一下,无论有多少String对象 只要内容一样,那么池中有且只有一份该对象,所以b.intern() 和c.intern()返回的池中的对象是同一个。
      

  10.   

    操作符+用于字符串时进行了重载,基本可以推测是借用了String的concat方法
    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
      

  11.   

    再通过这个验证:String b = "tes" + "t";
    System.out.println(b.intern() == b);
    结果为true
      

  12.   

    上面是string值的常量表达式,属于例外吧
      

  13.   

    不是的啊
    c.intern()返回池中的字符串,那池中的字符串就相当于是常量"test",也就是说相当于双引号生成的,而
    b=a + "t"; 很明显相当于new生成的,生成一个新对象,两个对象不是同一个,你用==比较当然就是false了。
      

  14.   


    b == c.intern()你这是比较两个String对象,必然不相等。是吗?
      

  15.   


    这个是因为"tes" + "t"在编译时就能确定的,所以直接被编译成"test"放在常量池
      

  16.   


    恩,这和我猜的差不多,我知道答案了,thanks。BTW,论坛里面还是那么多浮躁的人啊
      

  17.   

    你的猜测差不多是对的,有一点是
    在JDK1.5之后,字符串拼接是调用StringBuilder的appen方法。b == c.intern() 
    一个在堆中,一个在字符串池中,内存地址显然不相等。
      

  18.   

    问题解决,结贴去。
    谢谢ls所有回复的人,不管正确or错误
      

  19.   

    我来试着解释一下,看对不对:
    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"。