恩,很同意楼主的说法。有些细小的概念是一定要澄清的。
补充说一下“String str = “Hello”; 等价于String str = new String(“Hello”);”这句话的错处。使用new关键字总是会分配内存建立新对象。String str = “Hello”不总是会创建新的str对象,如果内存中已经有内容为Hello的字符串,那么这句话只是把str这个对象变量指向它而已。只有内存中不存在内容为Hello的字符串,String str = “Hello”才等价于String str = new String(“Hello”)。因为后者总是建立新对象。也因为以上原因,使用new String("xxx")这种方式建立String对象是非常愚蠢的方式。String是非可变类,这种做法只是白白耗费内存而已。

解决方案 »

  1.   

    楼主和一楼说的都不错,俺也来补充几点:
    String是一个很特殊的东西,要想很好地去理解它,需要知道java怎么处理常量池的,好像在jvm规范中。另外在JLS中关于String也有描述(参见§3.10.5 String Literals)。
    * Literal strings within the same class (§8) in the same package (§7) represent references to the same String object (§4.3.1). 
    * Literal strings within different classes in the same package represent references to the same String object. 
    * Literal strings within different classes in different packages likewise represent references to the same String object. 
    * Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals. 
    * Strings computed at run time are newly created and therefore distinct. 
    * The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents.
    可以看看下面几个例子:
    <<
            String llo = "llo";
            String s1 = "Hello";
            String s2 = new String("Hello");        assertNotSame(s1, s2); // s1和s2不是一个实例,所以他们不等价
            assertSame(s1, s2.intern());        assertNotSame("Hello", "He"+llo); // 这样也不是一个实例,参见上面第5条
            assertSame("Hello", ("He"+llo).intern());
    >>
    再重申一下一楼的观点:
    String str = new String("xxxx")确实很愚蠢!除非你非要一个新的实例(但是这样的情况应该是极难见的)
      

  2.   

    可以看一下《Java Pitfalls》
      

  3.   

    我不认为用了new就总是在浪费内存,即使按照大家所认为的那样
    String s=new String("address1?");
    String t="address1?";
    t="address2?"
    恐怕这时候t还是得分配内存
      

  4.   

    TO kinzey34(无声之舞):
    按照你的写法,t这个时候是不需要分配内存的。JVM的常量池中已经有“address2?”
      

  5.   

    TO: xiaohaiz(老土进城,两眼通红) 
    你怎么知道常量池已经有了“address2?”了?难道我们只要用个 String s="XXXX",常量池里就已经为我们准备好了“XXXX”?
      

  6.   

    简单类型象int,double是传值的,
    而对象类型应该是传址的;
      

  7.   

    TO tangxc2003(糖糖) :对啊,这个常量池在编译的时候被决定。俺是不知道,但是JVM知道,呵呵。
    俺把虚拟机规范(vmspec)中关于常量池的介绍(3.5.5)贴在下面,大家来看看:
    <<
    A constant pool is a per-class or per-interface runtime representation of the constant_pool table in a Java class file (§4.4). It contains several kinds of constants, ranging from numeric literals known at compile time to method and field references that must be resolved at run time. The constant pool serves a function similar to that of a symbol table for a conventional programming language, although it contains a wider range of data than a typical symbol table.
    >>
    常量池的描述在编译时决定,常量池表在java的class文件中。它包含的常量就包含在java中的字符串常量,如"XXXX"。(当然还有别的信息)
    要理解这段,大家最好还是认真看看vmspec. :)
      

  8.   

    关于
    >>quot-------------------------------------------------------------
    : alienbat(死灵巫师) ( ) 信誉:100 
    String str = “Hello”不总是会创建新的str对象,如果内存中已经有内容为Hello的字符串,那么这句话只是把str这个对象变量指向它而已。只有内存中不存在内容为Hello的字符串,String str = “Hello”才等价于String str = new String(“Hello”)。
    >>-------------------------------------------------------------------
    这句话我理解是不对的;
    String str = “Hello”不会等价于String str = new String(“Hello”)。因为前者只在常量池中保证有对应的对象,而后者不但在常量池中保证有该对象,同时在非常量池的堆中也创键了一个对象,因而后者总会比前者多创建一个对象,因此两者在创建对象数目上是不会相同的。
      

  9.   

    顺便介绍一下这方面的知识来源:
    @see java.sun.com //可以下载vmspec & jls
    @see www.china-pub.com //可以订购<inside jvm>
      

  10.   

    NightRanger(流浪的人) 说得不错,确实如此。以前俺在帖子中写过
    String str = "AAA";和String str = new String("AAA");的反编译结果,大家看看:
    <<
    String str = "AAA";
    --------------------
       0:   ldc     #2; //String AAA
       2:   putstatic       #3; //Field str:Ljava/lang/String;
    >>
    <<
    String str = new String("AAA");
    ------------------------------
       0:   new     #2; //class String
       3:   dup
       4:   ldc     #3; //String AAA
       6:   invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
       9:   putstatic       #5; //Field str:Ljava/lang/String;
    >>
    大家看看差别。
      

  11.   

    我不懂JVM原理,大家可以看看SUN的API说明:
    http://java.sun.com/j2se/1.4.2/docs/api/java.lang 
    Class String
    java.lang.Object
      java.lang.StringAll Implemented Interfaces: 
    CharSequence, Comparable, Serializable --------------------------------------------------------------------------------public final class String
    extends Object
    implements Serializable, Comparable, CharSequence
    The String class represents character strings. All string literals in Java programs, such as "abc", are implemented as instances of this class. Strings are constant; their values cannot be changed after they are created. String buffers support mutable strings. Because String objects are immutable they can be shared. For example: 
         String str = "abc";
     
    is equivalent to: //这个单词该怎么翻译呢?
         char data[] = {'a', 'b', 'c'};
         String str = new String(data);
      

  12.   

    equivalent 的意思是:
    adj.相等的, 相当的, 同意义的
    n.等价物, 相等物
      

  13.   

    完全同意: kinzey34(无声之舞)
      

  14.   

    支持 xiaohaiz(老土进城,两眼通红) 兄弟的解释和引用,确实该有一个官方的,正规的表述。
      

  15.   

    不知道大家看过String的源代码没有,知道String内部是使用什么来表述字符串的吗?
    源代码很长了,俺就不全部帖了,也没有必要全贴。:)
    看看这个:
    <<
        /** The value is used for character storage. */
        private char value[];
    >>
    String内部就是使用char[]来存储字符串的。
    看看kinzey34(无声之舞) 给大家的例子
         char data[] = {'a', 'b', 'c'};
         String str = new String(data);
    @see String#String(char[])
    俺们来看看这个构造函数的源码:
    <<
        public String(char value[]) {
            this.count = value.length;
            this.value = new char[count];
            System.arraycopy(value, 0, this.value, 0, count);
        }
    >>
    是直接使用本地方法作的数组拷贝。和String str="AAA"是equal的,但是并不是一个实例。
    反编译这段看看:String str = new String(new char[]{'A','A','A'});
    <<
       0:   new     #2; //class String
       3:   dup
       4:   iconst_3
       5:   newarray char
       7:   dup
       8:   iconst_0
       9:   bipush  65
       11:  castore
       12:  dup
       13:  iconst_1
       14:  bipush  65
       16:  castore
       17:  dup
       18:  iconst_2
       19:  bipush  65
       21:  castore
       22:  invokespecial   #3; //Method java/lang/String."<init>":([C)V
    >>
    和常量池的做法不一样吧?
    所以 str.equals("AAA")但是str!="AAA"
      

  16.   

    1: str.equals('AAA')  equals 是判断内容是否相同,
    2:str!="AAA"         判断所分配的内存是否相同。
      

  17.   

    public class tt
    {
      public static void main(String args[])
      {
    String s=new String("address1?");
    String t="address1?";
    System.out.println("(s==t)="+(s==t));
      }
    }(s==t)=false
    Press any key to continue...
      

  18.   

    public class tt
    {
      public static void main(String args[])
      {
    //String s=new String("address1?");
    String s="address1?";
    String t="address1?";
    System.out.println("(s==t)="+(s==t));
      }
    }(s==t)=true
    Press any key to continue...
      

  19.   

    What will be the output when the following code is compiled and run (Assuming written inside main)?String s1 = new String("amit"); 
    System.out.println(s1.replace('m','r')); 
    System.out.println(s1); 
    String s3 = "arit"; 
    String s4 = "arit"; 
    String s2 = s1.replace('m','r'); 
    System.out.println(s2 == s3); 
    System.out.println(s3 == s4);arit 
    amit 
    false 
    trueA is correct. Since string class is immutable, s1 will remain "amit" even after calling replace() methods on it. What actually happens is that a new object is created on calling replace(). Comparing two strings with == operator checks for memory address to which the object reference is referring to. Java uses the concept of pooling. It maintains the pool of Java strings and when a new string is created, it first checks if it already exists in memory. If yes it picks it from the pool, that is what happening here.s2 and s4 points to the same memory area and thus s3==s4 will return true.
      

  20.   

    ttaomeng(我来瓜分) 的例子说清楚了。
      

  21.   

    ttaomeng(我来瓜分)的例子的确表明了
    String str = “Hello”; 和 String str = new String(“Hello”);
    在分配内存时的处理并不相同,JAVA虽然把它们都认做是相同的对象,却为前种方式作了节省内存的处理,感谢ttaomeng(我来瓜分)让我明白了这一点不过对于一般的字符串应用来说,我们关心的是它们的值是否相同,而不是它们的内存地址是否相同,所以 "String str = “Hello”; 等价于String str = new String(“Hello”);"
    也不是什么严重的错误,因为我们用的是 String.equals(String),而不是String==String来比较内容是否相同 关于 “使用new String("xxx")这种方式建立String对象是非常愚蠢的方式。String是非可变类,这种做法只是白白耗费内存而已” 的说法我还是不认同,从ttaomeng(我来瓜分)的例子可以看到,只有所有的字符串都采用
    String s="address1?";
    String t="address1?";的形式,而且给它们的字符串字面量都相同时,才可以节省内存。可问题是,想想看我们自己做过的程序,有多少情况下我们建立了这么多内容完全相同的不同String对象?我们省了多少内存?反正一般我的程序里的 String对象内容是不同的,也不很多,所以就算采用“高明”的方法来建立String对象,我也省不下多少内存,相反对于我这样的初学者来说,用new可以清楚的表明String是个(特殊的)对象
      

  22.   

    哈哈,我也看过那个文章,我回复的时候稍微反驳了一下。 没有像楼主这样彻底,哈,以后向你学习。 其实说白了吧。 java中的所有参数都是“传值”的。  先别否定我,等我说完。对于传一个对象进去。 然后在方法内改变了对象的状态,结果外面的对象也改变了,感觉上好像是传址。 其实想一想,我们传的是什么? 是对象吗?  不是。 其实我们传的是对象的引用。  引用和传址是不是一回事?  引用不也是记着对象的地址吗? 那为什么非要说是传值??  这里有一个微小的差别, 引用传递的时候,是复制了一个引用,然后传进去的。 也就是我所说的是传值的。 这个“值”指的是引用的值。 也就是说在方法里面有一个引用是传进来的。 但它和方法外面那个引用不是一个,是二个,但它们都指向同一个对象,所以通过任何一个引用来改变对象,再通过其它的引用访问时,也能“感觉”到改变。 所以感觉上就像是传址了。 但从引用的角度来说是“传值”不知道说明白没有。
      

  23.   

    TO  kinzey34(无声之舞) :
    还真是顽固啊,呵呵。好。难道下面两个例子还不能说明它们在效率上的差异吗?
    String str = "AAA"只需要一条ldc指令(push from contant pool)就结束了战斗,而String str = new String("AAA");花了多少指令,new,dup,ldc,invokespecial。不但浪费了CPU,还浪费了内存。
    <<
    String str = "AAA";
    --------------------
       0:   ldc     #2; //String AAA
       2:   putstatic       #3; //Field str:Ljava/lang/String;
    >>
    <<
    String str = new String("AAA");
    ------------------------------
       0:   new     #2; //class String
       3:   dup
       4:   ldc     #3; //String AAA
       6:   invokespecial   #4; //Method java/lang/String."<init>":(Ljava/lang/String;)V
       9:   putstatic       #5; //Field str:Ljava/lang/String;
    >>又及,不要告诉俺这点损失你觉得无所谓,1条2条可能没有关系,数据量大了呢?
    把下面的一段程序放在你的电脑上运行一下看看结果:
    <<
            long start = System.currentTimeMillis();
            for (int i=0;i<100000;i++) {String str = "AAA";}
            long end = System.currentTimeMillis();        long d1 = end-start;        start = System.currentTimeMillis();
            for (int i=0;i<100000;i++) {String str= new String("AAA");}
            end = System.currentTimeMillis();        long d2 = end-start;        System.out.println("1 cost : "+d1+" ms");
            System.out.println("2 cost : "+d2+" ms");
    >>
    10万次数据俺的机器是:
    1 cost : 5 ms
    2 cost : 30 ms
    100万次:
    1 cost : 10 ms
    2 cost : 140 ms
    1000万次:
    1 cost : 20 ms
    2 cost : 901 ms
    这样的差异已经很明显了。足以让大家去选择如何使用String。BTW, ligenlee(lee)的观点不能苟同,程序构建都弄不明白,学框架是白搭。
      

  24.   

    把相同的String str = "AAA";做10万到1000万,真有创意啊!不知这样的程序做出来有什么意义?
      

  25.   

    一会不来,这里好热闹,再次帮你们up.
    不过那几百个毫秒我不在乎,呵呵,我最多一个程序文件中用个100次撑死了,可是框架有了问题,我的系统就全over了,非被人骂不可。
      

  26.   

    TO tangxc2003(糖糖): 呵呵,创意就在于说明这个差异。 :)
      

  27.   

    不禁击节叫好!好~~好~~~~不这样争论,老鸟们也不会拿出真本事出来了,希望以后这样的大混战越多越好,我就拿着笔记本跟着后面狂抄,哈哈哈~~~不说两种创建String对象方法的效率
    但String str = new String(“Hello”);
    这样写法的好象很少用,明白String是对象应该很基本吧
    一般情况下没必要这么写
      

  28.   

    帖子里面的人都在讨论new String("xxx")是否有意义的问题呵呵,有意思String这个对象为什么要 public String(String original) 这个构造函数呢看看他的javadoc    /**
         * Initializes a newly created <code>String</code> object so that it 
         * represents the same sequence of characters as the argument; in other 
         * words, the newly created string is a copy of the argument string. 
         *
         * @param   value   a <code>String</code>.
         */只是重新创建了一个源String的拷贝而已所以既然了解了,如果没什么特别的用意,就没有必要刻意去用new String("xxx")当然即使用了也不是什么天大的错误。关于传参的问题这是我在另外一个帖子里面写的,拷贝过来占点篇幅:对于基本类型没问题,传值;对象传引用,这个引用是什么呢?也就是一个地址的数值。所以有人认为,java就是传值。这其实就是个概念问题,什么叫值,什么叫址。没有值,址用什么来体现。这个争论已经有很多年了,我认为,理解了就行了,不用去争论这些没有意义的东西。
      

  29.   

    new String(String argument)更多是当作匿名对象用的。
      

  30.   

    呵呵,String那里能作为匿名类使用?String本身就是final的,怎么匿名继承。“alemjann(裸睡)”的观点不知从何而来?