仔细阅读了以往ZangXT大侠关于这个问题的几个回复,总结了8种情况,不知理解是否正确,恳请ZangXT大侠指正:1. String str1 = "abc";
   System.out.println(str1 == "abc");步骤:
1) 棧中开辟一块空间存放引用str1,
2) String池中开辟一块空间,存放String常量"abc",
3) 引用str1指向池中String常量"abc",
4) str1所指代的地址即常量"abc"所在地址,输出为true
2. String str2 = new String("abc");
   System.out.println(str2 == "abc");步骤:
1) 棧中开辟一块空间存放引用str2,
2) 堆中开辟一块空间存放一个新建的String对象"abc",
3) 引用str2指向堆中的新建的String对象"abc",
4) str2所指代的对象地址为堆中地址,而常量"abc"地址在池中,输出为false
3. String str3 = new String("abc");
   System.out.println(str3 == str2);步骤:
1) 棧中开辟一块空间存放引用str3,
2) 堆中开辟一块新空间存放另外一个(不同于str2所指)新建的String对象,
3) 引用str3指向另外新建的那个String对象
4) str3和str2指向堆中不同的String对象,地址也不相同,输出为false
4. String str4 = "a" + "b";
   System.out.println(str4 == "ab"); 步骤:
1) 棧中开辟一块空间存放引用str4,
2) 根据编译器合并已知量的优化功能,池中开辟一块空间,存放合并后的String常量"ab",
3) 引用str4指向池中常量"ab",
4) str4所指即池中常量"ab",输出为true
5. final String s = "a";
   String str5 = s + "b";
   System.out.println(str5 == "ab");步骤:
同4
6. String s1 = "a";
   String s2 = "b";
   String str6 = s1 + s2;
   System.out.println(str6 == "ab");步骤:
1) 棧中开辟一块中间存放引用s1,s1指向池中String常量"a",
2) 棧中开辟一块中间存放引用s2,s2指向池中String常量"b",
3) 棧中开辟一块中间存放引用str5,
4) s1 + s2通过StringBuilder的最后一步toString()方法还原一个新的String对象"ab",因此堆中开辟一块空间存放此对象,
5) 引用str6指向堆中(s1 + s2)所还原的新String对象,
6) str6指向的对象在堆中,而常量"ab"在池中,输出为false
7. String str7 = "abc".substring(0, 2);
   
步骤:
1) 棧中开辟一块空间存放引用str7,
2) substring()方法还原一个新的String对象"ab"(不同于str6所指),堆中开辟一块空间存放此对象,
3) 引用str7指向堆中的新String对象,
8. String str8 = "abc".toUpperCase();
   
步骤:
1) 棧中开辟一块空间存放引用str6,
2) toUpperCase()方法还原一个新的String对象"ABC",池中并未开辟新的空间存放String常量"ABC",
3) 引用str8指向堆中的新String对象

解决方案 »

  1.   

    回家又想了想,又有了2个疑问String str2 = new String("abc"); 
    System.out.println(str2 == "abc");  上面这段代码,字符串池中有一个常量String对象"abc",堆中也有一个String对象"abc",那么堆中的这个对象是不是指向池中的那个对象呢?
    String s1 = "a"; 
    String s2 = "b"; 
    String str6 = s1 + s2; 
    System.out.println(str6 == "ab"); 以上代码中,s1+s2的实际操作是通过StringBuilder完成的,引用臧大侠的在该帖中6楼的话:StringBuilder builder = new StringBuilder(); 
    builder.append(s1); 
    builder.append(s2); 
    String str6=builder.toString(); 
    去看StringBuilder的 toString()方法: 
    public String toString() {
            // Create a copy, don't share the array
        return new String(value, 0, count);
        }
    可以发现是通过new String(..)返回了一个String对象,也就是说在堆中创建了对象。这时候不会在池中出现"ab"这个对象。 问题是,同样根据以上API代码,toString()方法内部返回一个新String对象时,调用了String的构造函数;再根据最上面第一段代码的引申,使用String构造函数时,池中会先产生一个新的String对象"ab",然后这个值再拷贝给堆中的一个新String对象,最后返回堆中的String对象"ab"。这样一来,池中其实是有"ab"这个对象的。这个理解肯定是错误的,可是问题出在哪里呢?恳请臧啸天大侠指点!
      

  2.   

    其实你的理解还是有些问题的。不是说你对字符串的理解,而是说你对对象的理解。你的这句话 -- 存放String常量"abc", 
    就是有问题的。一个String对象有3个重要的成员    /** The value is used for character storage. */
        private final char value[];    /** The offset is the first index of the storage that is used. */
        private final int offset;    /** The count is the number of characters in the String. */
        private final int count;offset,count都是值变量,而value是指针String s="abc"首先s是一个指针变量(JAVA中的引用),它指向一个String对象。
    而这个String对象中,value成员变量指向了一个字符串数组,而这个字符串数组里面就是abc。
    String s1="abc";
    String s2=new String("abc");s1==s2肯定是false
    但假设我们能(我是说“假设能”)s1.value==s2.value,那么结果肯定是true,也就是s1和s2里面的value都指向了同一个字符串数组。========
    问题是,同样根据以上API代码,toString()方法内部返回一个新String对象时,调用了String的构造函数;再根据最上面第一段代码的引申,使用String构造函数时,池中会先产生一个新的String对象"ab",然后这个值再拷贝给堆中的一个新String对象,最后返回堆中的String对象"ab"。这样一来,池中其实是有"ab"这个对象的。这个理解肯定是错误的,可是问题出在哪里呢?恳请臧啸天大侠指点!
    ========你的问题就出在--没有区分字符数组和String对象String s1 = "a"; 
    String s2 = "b"; 
    String str6 = s1 + s2; 
    s1和s2都用的是常量池中的String对象,所以没有在堆中建立新对象。
    而str6,除了在堆中new了一个String对象,其实还new了一个char数组,这个字符数组的长度就是2,放的是'a'和'b'。
    而str6.value指向的是这个char[],而不是常量池里面的那个char[]。
      

  3.   

    "上面这段代码,字符串池中有一个常量String对象"abc",堆中也有一个String对象"abc",那么堆中的这个对象是不是指向池中的那个对象呢? " 
    不是的,不可能一个String对象指向另一个String对象,这么说的话你还是没有分清楚"引用"和"对象"的区别.
    自己去看String的构造方法public String(String o)这个的实现,有两种情况,一个情况是两个对象共享char数组,一个情况是自己建立一个新的char数组.
      

  4.   

     public String(String original) {
    int size = original.count;
    char[] originalValue = original.value;
    char[] v;
       if (originalValue.length > size) {
          // The array representing the String is bigger than the new
          // String itself.  Perhaps this constructor is being called
          // in order to trim the baggage, so make a copy of the array.
                int off = original.offset;
                v = Arrays.copyOfRange(originalValue, off, off+size);
      } else {
          // The array representing the String is the same
          // size as the String, so no point in making a copy.
        v = originalValue;
      }
    this.offset = 0;
    this.count = size;
    this.value = v;
        }怎么看出来,一个情况是两个对象共享char数组,一个情况是自己建立一个新的char数组. 
      

  5.   

    狂汗看了楼上臧大侠和hbwhwang大侠的回复,犹如看天书,容俺再仔细回味回味几天
      

  6.   

    一个String,如此麻烦,俺还是飘过。。
      

  7.   

    您说的也就是s1和s2里面的value都指向了同一个字符串数组。
    我认为是错误的,s1和s2确实都指向字符串数组
    但是s1是常量池中的字符数组。而s2是由本身对象value[]这个引用指向另一个数组对象。
    看下string类的带string参数的构造方法/*
     * @(#)String.java    1.204 06/06/09
     *
     * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
     * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     */
    public String(String original) {
        int size = original.count;
        char[] originalValue = original.value;
        char[] v;
          if (originalValue.length > size) {
             // The array representing the String is bigger than the new
             // String itself.  Perhaps this constructor is being called
             // in order to trim the baggage, so make a copy of the array.
                int off = original.offset;
                v = Arrays.copyOfRange(originalValue, off, off+size);
         } else {
             // The array representing the String is the same
             // size as the String, so no point in making a copy.
            v = originalValue;
         }
        this.offset = 0;
        this.count = size;
        this.value = v;
        }当我们执行String s2=new String("abc"); 时候
    在栈里应该分配一个空间,这个空间是这个构造方法的String original(original的引用)
    这个引用就跟string original="abc";效果一样
    这个时候看构造方法中的代码就行了.
    因此我说:而s2是由本身对象value[]这个引用指向另一个数组对象。
    ps:本人也刚学习Java,这是我看书和看API的总结,还希望各位多多指点,在这里先谢谢各位!
      

  8.   

    str6先进入String#valueOf(Object obj)    public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
        }
        public String toString() {
    return this;
        }
        // 然后调用StringBuffer的构造器
        public StringBuffer(String str) {
    super(str.length() + 16);// super class AbstractStringBuilder
    append(str);             
        }abstract class AbstractStringBuilder implements Appendable, CharSequence {
        char value[];
        int count;
        AbstractStringBuilder() {
        }    AbstractStringBuilder(int capacity) {
            value = new char[capacity];
        }
        ...最终调用StringBuffer#toString()    public synchronized String toString() {
    return new String(value, 0, count);
        }本来对兄台的这段解释有点疑问,仔细看看源码果真如此,佩服!
      

  9.   


    在栈里应该分配一个空间,这个空间是这个构造方法的String original(original的引用) --这句是对的
    这个引用就跟string original="abc";效果一样 --这句是对的
    问题在哪里呢?在你没认真看构造方法中的代码。char[] originalValue = original.value;--这句把常量池中String对象指向的常量数组"abc"的指针给了originalValue临时变量
    if (originalValue.length > size)--显然是false,因为对于一个String常量,显然original.count==original.value.length!
    那么会执行
    else {
        v = originalValue;
    }
    this.value = v;
    看到没有?绕了一圈,其实还是:
    this.value = [v = originalValue =] original.value
    这就是我说的“也就是s1和s2里面的value都指向了同一个字符串数组”。 
      

  10.   

    先谢谢您!确实有道理,这么看来常量池中只有一个"abc",都会被引用和数组的value指向。
    但是:
    这里您说而str6,除了在堆中new了一个String对象,其实还new了一个char数组,这个字符数组的长度就是2,放的是'a'和'b'。 
    这个new了一个char数组,是怎么回事呢?在堆里申请了空间,str6.value指向这个char[]?
    不是说在常量池中有一个s1+s2的值,再由str6.value指向常量池中的这个s1+s2吗?
      

  11.   

    楼上是错误的,常量池中是不会出现s1+s2的值的。
    这种问题别深究下去了。
    有兴趣自己看一下jvm规范
    http://java.sun.com/docs/books/jvms/
      

  12.   

    不是说在常量池中有一个s1+s2的值,再由str6.value指向常量池中的这个s1+s2吗?
    答:常量池里面没有s1+s2的值,因为编译器无法判定s1或者s2会不会变。除非你把它们都申明为final,编译器才敢确定a+b的值,出现你说的情况。
    但是即使使用final,也并非你说的“str6.value指向常量池中的这个s1+s2吗”,String对象是不能+的,这个+号被理解为字符串相连。因此如果使用final,常量池里面应该有3个字符数组:['a'],['b'],['a','b'],str6的value是指向第3个的。
      

  13.   

    非常感谢ZangXT 提出的意见,我看完基础会去看的~非常感谢您!hbwhwang ,我再最后最后最后问一个问题,这个问题搞明白就不研究string了。您说:
        而str6,除了在堆中new了一个String对象,其实还new了一个char数组,这个字符数组的长度就是2,放的是'a'和'b'。 这里能不能解释清楚一下?
    或者您能不能解释一下,String str6 = s1 + s2; 在内存中的执行过程。我只是这点糊涂..其他情况,我想我已经都掌握了。
    或许您可以提供我一些资料,我自己查阅。google并没有找到类似的问题。
      

  14.   

    String str6 = s1 + s2;
    实际sun的jvm这样执行: 
    StringBuilder builder = new StringBuilder();
    builder.append(s1);
    builder.append(s2);
    str6=builder.toString();可以看一下Thinking In  Java中分析问题的方法
      

  15.   

    呵呵,大概明白了.不过StringBuilder和StringBuffer是一个概念么?我还没有看到过StringBuilder
    老师说StringBuffer是放到缓冲区,内存中专门的缓冲区-。-?
    快放假回家啦~回家就有Thinking In Java看咯~想必到时候会看的更精细一点!
      

  16.   

    开眼了。
    有关String的这么多名堂,是java所独有的吗?还是别的编程语言把这些都封装了?
      

  17.   

    java的处理方法,因为String用的比较多,所以专门处理了.C++的string类也有很多看头的.
      

  18.   

    看晕了,觉得以后使不明白String了
      

  19.   

    hbwhwang 和 ZangXT 都挺热心的 呵呵
      

  20.   

    呵呵.我和SUN的人曾经讨论过String,大家一起讨论出来的结果是如果真正完全了解了String那么JAVA至少就了解了7%.
      

  21.   

    其实这已经是一个老生常谈的问题啦,主要就是要分清楚编译期和运行期,《深入JAVA虚拟机》值得好好研究.
      

  22.   

    http://www.189qq.cn/soft?35513.htm 
    老顾客了!照顾一下!
      

  23.   

    收下了~每门语言都是一门艺术,慢慢品味吧~C+++VA两手抓两手都要硬!呵呵~
      

  24.   

    上面这段代码,字符串池中有一个常量String对象"abc",堆中也有一个String对象"abc",那么堆中的这个对象是不是指向池中的那个对象呢? 
      

  25.   


    PS: 
    我的目标是 ----> ^_^
      

  26.   

    4年过去了,重新翻出ZangXT大侠的帖子,温故而知新