2013-07-27 101 views
38

我的朋友给我发了一个问题,他在一个模拟考试看见了Java认证有关字符串对象:Java中有多少个字符串对象?

String makeStrings(){ 
    String s = "HI"; 
    s = s + "5"; 
    s = s.substring(0,1); 
    s = s.toLowerCase(); 
    return s.toString(); 
} 

多少字符串对象调用此方法时会产生的呢? 考试给出的正确答案是3.但我认为它是五。

  1. “HI”
  2. “5”
  3. “HI5”
  4. “H”
  5. “H”

难道我错了吗?

+14

我怀疑区别在于“HI”和“5”已经在字符串池中,所以它们不会在每个方法调用中创建。 –

+1

@GrijeshChauhan我在SO meta上发了一个[post](http://meta.stackexchange.com/questions/190688/determining-whether-a-post-should-be-marked-as-a-duplicate#190689)。我认为我们应该投票关闭另一个,而不是结束这篇文章。也许我有偏见,但我认为这些答案更好... –

+1

@SteveP。我已投票表决已经重复选项... –

回答

46
String makeStrings() { 
    String s = "HI";   //String literal 
    s = s + "5";    //concatenation creates new String object (1) 
    s = s.substring(0,1);  //creates new String object (2) 
    s = s.toLowerCase();  //creates new String object (3) 
    return s.toString();  //returns already defined String 
} 

对于串联,创建一个新的字符串时,JVM使用StringBuilder,即:

s = new StringBuilder(s).append("5").toString(); 

toString()StringBuilder是:

public String toString() { 
    return new String(value, 0, count); //so a new String is created 
} 

substring创建一个新的String对象除非整个String被索引:

public String substring(int beginIndex, int endIndex) { 
    if (beginIndex < 0) { 
     throw new StringIndexOutOfBoundsException(beginIndex); 
    } 
    if (endIndex > count) { 
     throw new StringIndexOutOfBoundsException(endIndex); 
    } 
    if (beginIndex > endIndex) { 
     throw new StringIndexOutOfBoundsException(endIndex - beginIndex) 
    } 

    return ((beginIndex == 0) && (endIndex == count)) ? this : 
      new String(offset + beginIndex, endIndex - beginIndex, value); 
} 

toString()确实创建一个新的字符串:

public String toString() 
{ 
    return this; 
} 

toLowerCase()是一个相当长的方法,但我只想说,如果String已经在全部小写,它返回一个new String

鉴于提供的答案是3,正如Jon Skeet所建议的,我们可以假定两个字符串字面值已经在字符串池中。有关何时将字符串添加到池中的更多信息,请参阅Questions about Java's String pool

+1

Substring创建一个新的字符串对象,toString不会 – Joni

+0

'substring'会在这里创建一个对象。它只在你的索引处于原始字符串的边界处时才返回相同的字符串 - '[0,s.length() - 1]' –

+0

@Joni,我没有意识到它实际上调用了'toString()'串。我做了必要的改变(对于两者)。谢谢。 –

13

s = s + "5";

被翻译成:

String s = new StringBuilder(s).append("5").toString(); 

现在,创建一个对象。


s = s.substring(0,1); 

创建一个新字符串。


s = s.toLowerCase(); 

创建一个新对象。


return s.toString(); 

不创建一个字符串,它返回一个已经创建了一个。

+0

“s.toString();”返回相同的字符串?你确定? – Lokesh

+7

@loki yes ..'toString(){return this}' – sanbhat

9

其他一些答案确实有道理,但字符串文字呢?

String s = "HI"; 

对于字符串常量,当一个.java文件编译成.class文件,任何字符串文字说明以特殊的方式,就如同所有的常量。当一个类被加载时(注意加载发生在初始化之前),JVM通过类的代码并查找字符串文字。

当它找到一个时,它检查以查看等效字符串是否已经从堆中引用 。如果不是,它创建于 堆一个String实例,一旦引用到字符串对象进行存储在常数表

到对象的引用,该字符串在整个程序中文字的任何引用仅仅是替换为对字符串文字池引用的对象的引用。

因此,应该有四个Java对象,尽管当同一个方法被一次又一次调用,那么只有三个对象,如应用程序中字符串文字池包含文字“HI”。

此外,有关为什么当上述方法块exectued我们也可以检查它们是不同的字符串不同的哈希码(String是不可改变的。)

public static void main(String[] args) 
    { 
     NumberOfString str = new NumberOfString(); 
     String s = str.makeStrings(); 
     System.out.println(s.hashCode()); 
    } 

    public String makeStrings() 
    { 
     String s = "HI"; 
     System.out.println(s.hashCode()); 
     s = s + "5"; 
     System.out.println(s.hashCode()); 
     s = s.substring(0, 1); 
     System.out.println(s.hashCode()); 
     s = s.toLowerCase(); 
     System.out.println(s.hashCode()); 
     return s.toString(); 
    } 

你得到创建新对象的详细信息以下输出:

2305 
71508 
72 
104 
104 

我们不应该在上面的例子中计算字符串字面值对吗?

+0

根据OP问题给出的答案,我们可以假设它们已经在String池中。尽管如此,好的答案(+1)。 –

+1

原始问题指出“调用此方法时将创建多少个字符串对象?”,推断出该类已经加载并且方法正在从另一段代码中调用。根据你的解释,字符串文字是在加载类时创建的,并不是专门在调用此方法时创建的,因此不会计入有问题的总数。 – Pawn