String s = new String(“xyz”)。执行这行代码后,有多少个对象被生成?

时间:2013-10-30 01:47:53

标签: java jvm

这个采访问题的共同商定答案是代码创建了两个对象。但我不这么认为;我写了一些代码来确认。

public class StringTest {
    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "a";
        String s3 = new String("a");
        System.out.println("s1: "+s1.hashCode());
        System.out.println("s2: "+s2.hashCode());
        System.out.println("s3: "+s3.hashCode());
    }
}

输出结果为:

Application output

这是否意味着只创建了一个对象?

重申:我的问题是以下代码创建了多少个对象:

String s = new String("xyz")

而不是StringTest代码。

受@Don Branson的启发,我调试了以下代码:

public class test {
    public static void main(String[] args) {
        String s = new String("abc");
    }
}

结果是:

Enter image description here

s的id为84,“abc”的id为82.这究竟是什么意思?

21 个答案:

答案 0 :(得分:19)

除了您使用的JVM / JRE之外,还有哪些错误。更好的是不要担心像这样的事情。有关任何更正/关注,请参阅评论部分。

首先,这个问题确实在这里提到了这个问题: Is String Literal Pool a collection of references to the String Object, Or a collection of Objects

所以,这是每个人在这个问题上的指南。

...

给定以下代码行:String s = new String(“xyz”)

有两种方法可以看到这个:

(1)代码行执行时会发生什么 - 它在程序中运行的字面时刻?

(2)声明创建了多少Objects的净效应是什么?

答案:

1)在执行之后,创建了一个额外的对象。

a)当JVM加载包含此行代码的"xyz"时,会创建并实现String class

  • 如果某个其他代码中的"xyz"已经在实习池中,则该文字可能不会生成新的String对象。

b)创建新的String s后,内部char[]是实习"xyz"字符串的副本。

c)这意味着,当行执行时,只创建了一个额外的对象。

事实是,只要加载了类并且在运行此代码段之前,就会创建"xyz"对象。

......下一个场景......

2)代码创建了三个对象(包括实习"a"

String s1 = "a";
String s2 = "a";
String s3 = new String("a");

a)s1和s2只是被引用,而不是对象,并且它们在内存中指向相同的String

b)“a”是实体并且是复合对象:一个char[]对象和String对象本身。它由内存中的两个对象组成。

c)s3,new String("a")再生一个对象。新String("a")不会复制“a”的char[],它只会在内部引用它。这是方法签名:

public String2(String original) {
        this.value = original.value;
        this.hash = original.hash;
}

一个实习String ("a")等于2 Objects。一个new String("a")等于另一个对象。代码的净效应是三个对象。

答案 1 :(得分:8)

将为此创建两个对象:

String s = new String("abc");

一个在堆中,另一个在"字符串常量池" (SCP)。引用s将始终指向s,并且SCP区域中不允许GC,因此SCP上的所有对象将在JVM关闭时自动销毁。

例如:

这里通过使用堆对象引用,我们通过调用intern()

获得相应的SCP对象引用
String s1 = new String("abc");
String s2 = s1.intern(); // SCP object reference
System.out.println(s1==s2); // false
String s3 = "abc";
System.out.println(s2==s3); //True s3 reference to SCP object here

答案 2 :(得分:4)

有两种方法可以在Java中创建字符串对象:

  1. 使用new运算符,即

    String s1 = new String("abc");
    
  2. 使用字符串文字,即

    String s2 = "abc";
    

  3. 现在字符串分配在时间和内存上都很昂贵,因此JVM(Java虚拟机)执行一些任务。什么任务?

    请参阅,无论何时使用new运算符,都会创建对象,并且JVM不会查找字符串池。它只是要创建对象,但是当您使用字符串文字创建字符串对象时,JVM将执行查找字符串池的任务

    即,当你写

    String s2 = "abc";
    

    JVM将查找字符串池并检查" abc"已存在或不存在。如果它存在,则将引用返回到已存在的字符串" abc"并且没有创建新对象,如果它不存在,则创建一个对象。

    所以在你的情况下 (a)中

    String s1 = new String("abc");
    
    • 由于使用了new,因此创建了对象

    (b)中

    String s2 = "abc";
    
    • 使用字符串文字创建一个对象" abc"不在 字符串池,因此创建了对象。

    (c)中

    String s2 = "abc";
    
    • 再次使用字符串文字和" abc"在字符串池中,和 因此,不会创建对象。

    您也可以使用以下代码查看:

    class String_Check
    {
        public static void main(String[] n)
        {
            String s1 = new String("abc");
            String s2 = "abc";
            String s3 = "abc";
            if (s1==s2)
                System.out.println("s1==s2");
            if(s1==s3)
                System.out.println("s1==s3");
            if(s2==s3)
                System.out.println("s2==s3");
        }
    }
    

    我希望这会有所帮助......请注意==用于查看对象是否相等,并使用equals(Object)方法查看内容是否相等。

答案 3 :(得分:2)

有很多随机答案,所以我有信心我的面试官也不太确定:) :)

我做了很多研究,发现哈希码不是内存地址,调试时的变量没有给出内存地址。因此,这些参数可能会造成混淆。

答案 4 :(得分:2)

  1. String s = new String(" xyz");

  2. 上面一行将创建两个对象,一个在堆中,另一个在String常量池中。

    现在如果我们这样做

    1. String s = new String("xyz");
    2. String s1 ="xyz";
    3. 以上两个语句将创建两个对象。 第一行String s = new String("xyz");将创建两个如上所述的对象 在第一行中,当String s = "xyz";执行时,如果存在相同的内容对象,则检查字符串常量池,因为第一行在字符串常量池中使用" xyz"它返回相同的引用,不会创建其他对象。

      如果我们将这四条线放在一起,如下所述。

      1. String s2 = new String("xyz");
      2. String s3 ="xyz";
      3. String s4 = new String("xyz");
      4. String s5 ="xyz";
      5. 如果我们执行上述行,我们将有三个对象。

        • 第一个和前面提到的将在堆中创建两个对象 另一个在String constant poll中。
        • 当第二行执行时,它检查字符串常量轮询
          并找到" xyz"所以它返回相同的对象,所以直到第二个 我们有两个对象。
        • 当第三行执行时,它将在堆中创建一个新对象 因为new运算符在堆中创建对象所以直到第三行 有3个对象。
        • 当第四行执行时,它检查字符串常量轮询
          并找到" xyz"所以它返回相同的对象,所以第四行 我们有三个对象。

        关于intern()方法的奖金

          

        当在String对象上调用intern()方法时,它看起来像   池中此String对象包含的字符串,如果是字符串   在那里发现然后返回池中的字符串。否则,这个   String对象被添加到池中以及对此String的引用   返回对象。

        
        
        public class TestString {
        
            public static void main(String[] args) {
                String s1 = "Test";
                String s2 = "Test";
                String s3 = new String("Test");
                final String s4 = s3.intern();
                System.out.println(s1 == s2);
                System.out.println(s2 == s3);
                System.out.println(s3 == s4);
                System.out.println(s1 == s3);
                System.out.println(s1 == s4);
                System.out.println(s1.equals(s2));
                System.out.println(s2.equals(s3));
                System.out.println(s3.equals(s4));
                System.out.println(s1.equals(s4));
                System.out.println(s1.equals(s3));
            }
        
        }
        
        
        

        
        
        //Output
        true
        false
        false
        false
        true
        true
        true
        true
        true
        true
        
        
        

        通过在新的字符串对象上应用实习方法来查看实习生的魔力。  实习生在这里应用,以便检查是否"测试"是否可以在字符串常量池中使用,因为"测试"在String常量池中可用,它将返回相同的对象,因此s3具有与s1s2相同的引用,并将所有结果都设为true

        
        
        public class TestString {
        
            public static void main(String[] args) {
                  String s1 = "Test";
        	        String s2 = "Test";
        	        String s3 = new String("Test").intern(); 
        	        final String s4 = s3.intern();
        	        System.out.println(s1 == s2);
        	        System.out.println(s2 == s3);
        	        System.out.println(s3 == s4);
        	       System.out.println(s1 == s3);
        	        System.out.println(s1 == s4);
        	        System.out.println(s1.equals(s2));
        	        System.out.println(s2.equals(s3));
        	        System.out.println(s3.equals(s4));
        	        System.out.println(s1.equals(s4));
        	        System.out.println(s1.equals(s3));
            }
        
        }
        
        
        

        
        
        true
        true
        true
        true
        true
        true
        true
        true
        true
        true
        
        
        

答案 5 :(得分:2)

如果执行return personal.GetSubfolders (false).Where (x => X.Name != "Calendar" && x.Name != "Contacts").ToList (); ,则将创建两个对象。一个对象将在字符串文字池中创建,另一个将在堆区域中创建。 但是,如果我们已经有相同的字符串文字对象,则仅创建一个对象。 像

String s = new String("Brajesh");

除此附加对象外,还在char []对象的堆区域中创建。我在这里附加了堆内存的快照。 enter image description here

答案 6 :(得分:1)

创建2或3个对象,具体取决于编译器的智能程度。

然而,您的测试是垃圾,因为hashCode的{​​{1}}是基于String的内容,而不是基于他们的身份。如果您想检查身份,则应使用StringSystem.identityHashCode比较。

允许编译器和运行时(非强制)尽可能优化字符串创建。因此,他们通过对您拥有的三个字符串使用单个文字来优化文字字符串。 无论如何,==运算符必须返回 new 对象(即新分配的对象)。 如果使用静态方法new,则可以在运行时进行字符串优化。但我不知道当前的JRE是否实际应用了任何缓存(检查哈希表可能比分配新的String.valueOf更昂贵)

答案 7 :(得分:0)

2 个物体由:

String s = new String("xyz");

第一次在堆内存(字符串池)中创建新的字符串对象

第二次将 "xyz" 放入字符串常量池

答案 8 :(得分:0)

java.lang.String会覆盖hashCode()方法,以便该值取决于字符串的内容

因此,hashCode()不会告诉您有关实例数量的任何信息。它可以是相同的字符串,也可以是没有单字节共享的另一个实例。 equals()相同。这解释了你的输出。

使用System.identityHashCode(..)进行此类研究。

may the source be with you

答案 9 :(得分:0)

如果new String()创建2个对象(一个在堆中,一个在字符串池中),那么.intern方法的用途是什么?

  在String对象上调用的

intern()方法查找字符串   如果找到字符串,则此String对象包含在池中   然后从池中返回字符串。不然这样   将String对象添加到池中,并对此String进行引用   对象被返回。

答案 10 :(得分:0)

        String s1="Pune";
        String s2="Mumbai";
        String s3="Pune";
        String s4=new String("Mumbai");
        System.out.println("S1 :"+s1.hashCode());  //S1 :2499228
        System.out.println("S2 :"+s2.hashCode());  //S2 :-1979126203
        System.out.println("S3 :"+s3.hashCode());  //S3 :2499228
        System.out.println("S4 :"+s4.hashCode());  //S4 :-1979126203
        System.out.println(s2==s4);     // false

正如我们在上面的程序中看到的,我们分别获得了s2和s4的类似哈希码,尽管我们使用==运算符得到了错误。 ==运算符用于参考比较。

在" String s4 = new String(" Mumbai")"创建了两个对象,一个在堆内存中,一个在堆栈内存中。因此,s2与堆内存中创建的s4进行比较,而不是堆栈内存。

答案 11 :(得分:0)

与新的String(“ <>”)被调用后发生的确切情况相混淆,我找到了这个线程。您对哈希码比较的理解在技术上并不正确。

int hashCode()在String类中已被覆盖,并且它根据String文字的内容返回一个值。

String s1 = new String(“ Hello”); 字符串s2 =新的String(“ Hello”);

因此s1.hashCode()= s2.hashCode()= anyStringOfContent_“ Hello” .hashCode()

**/** Cache the hash code for the string */
private int hash; // Default to 0
public int hashCode() {
    int h = hash;
    if (h == 0 && value.length > 0) {
        char val[] = value;
        for (int i = 0; i < value.length; i++) {
            **h = 31 * h + val[i];**
        }
        hash = h;
    }
    return h;
}**

现在仅要解释为什么这样做,您实际上可以阅读Kathy Sierra的书,该书很好地解释了开发人员以这种方式进行操作的方法(基本上,所有返回true的对象都应返回相同的equals()方法的值) )。

答案 12 :(得分:0)

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 []并且每次都会被复制,同时内容被实例化,是的,它会将数据存储在String Constant Pool中。 1)将取自SCP String s1 =&#34; a&#34;字符串s2 =&#34; a&#34 ;; 2)创建一个新对象String s3 = new String(&#34; a&#34;);好奇心,新对象字符串s2 =新字符串(&#34; a&#34;);在以上所有代码中相同的char []将被复制.i:e char [] value You can check here

答案 13 :(得分:0)

如果在调试模式下在eclipse中运行以下代码,我们将了解如何使用String string = new String("manoj");创建多少个对象,内部将在String类构造函数中创建String str = "manoj"。 将鼠标悬停在参考上之后,只需检查 id 即可,如下面的屏幕截图所示。 ScreenShot

public static void main(String[] args)
{
    String str = "atul";
    String string = new String("manoj");
    String string2 = "manoj";
    System.out.println(str == string);
}

答案 14 :(得分:-1)

我在Eclipse调试器中运行它。在该上下文中,创建了两个对象,一个具有id 17,另一个具有22:

enter image description here

答案 15 :(得分:-1)

@Giulio,你是对的。 String s3 = new String(“abc”);在堆中创建两个对象,其中一个在参考s3中,另一个在SCP中(没有参考)。 现在String s2 =“abc”;不会在SCP中创建任何新对象,因为SCP中已存在“abc”。

    String s1 = "abc";
    String s2 = "abc";
    String s3 = new String("abc");
    String s4 = s3.intern();
    System.out.println("s1: "+System.identityHashCode(s1));
    System.out.println("s2: "+System.identityHashCode(s2));
    System.out.println("s3: "+System.identityHashCode(s3));
    System.out.println("s4: "+System.identityHashCode(s4));

O / P:s1:366712642,     s2:366712642,     s3:1829164700,     s4:366712642

由于我没有资格发表评论,我在这里写了。

答案 16 :(得分:-2)

  

String s = new String(“ xyz”);

以上代码中创建了多少个对象?

以上代码中仅创建了一个对象,该对象位于堆内存中。

不是两个对象.....

如果创建了两个对象,则一个对象位于堆内存(新运算符)中,而另一个对象位于字符串常量池(字符串文字)中(如果您使用字符串文字在值以下存储)

  

字符串s1 =“ xyz”;

它不会在字符串常量池中返回对象 s 的引用。它将在字符串常量池中以 s1 创建新对象。

如何?

我们可以通过使用==运算符(s == s1)来检查引用类型来进行检查。 如果 s 已存储在String Constant Pool中,则它为true,在这种情况下输出为false。

所以结论是上面的代码创建了一个对象。

答案 17 :(得分:-2)

Java中有一个名为字符串池的概念。字符串池(字符串实习池)是Java堆中的特殊存储区域。创建字符串并且池中已存在该字符串时,将返回现有字符串的引用,而不是创建新对象并返回其引用。

所以String s = new String(“xyz”)它将创建两个对象。

  1. 第一个对象将在Java永久堆内存中创建,作为我们传递的参数的一部分 - &#34; XYZ&#34;。它将在String Literal Pool中创建。

  2. 第二个对象将在Java堆内存中创建 - 它将作为new运算符的一部分创建。

答案 18 :(得分:-3)

有一种方法可以使用new关键字(String s1=new String("Rajesh"))找到创建的对象数。

public class Rajesh {
    public static void main(String[] args){
        String s1=new String("Rajesh");
        System.out.println(s1+s1.intern());
    }
}

输出:

RajeshRajesh //s1=Rajesh+s2.intern()=Rajesh

注意:我们知道intern方法总是命中堆内存的字符串常量池。

答案 19 :(得分:-3)

我使用hashcode()方法查找创建的字符串对象的数量。 hashcode()方法将存储在引用变量中的数据摘要为单个哈希值。

<强> CASE1:

String s="

Fred";
System.out.println(s.hashCode());

s=s+"47";
System.out.println(s.hashCode());

s=s.substring(2,5);
System.out.println(s.hashCode());

s=s.toUpperCase();
System.out.println(s.hashCode());

s=s.toString();
System.out.println(s.hashCode());

输出为:

Fred--2198155         //1st object ----------------  String s="Fred"

Fred47--2112428622    //2nd object ----------------  s=s+"47"

ed4--100213           //3rd object ----------------  s=s.substring(2,5)

ED4--68469            //4th object ----------------  s=s.toUpperCase()

ED4--68469            //this is retrieved from the string constant pool -------- s=s.toString();

总共创建了4个对象。

案例2:

String s="FRED";
System.out.println(s.hashCode());

s=s+"47";
System.out.println(s.hashCode());

s=s.substring(2,5);
System.out.println(s.hashCode());

s=s.toUpperCase();
System.out.println(s.hashCode());

s=s.toString();
System.out.println(s.hashCode());

输出为:

FRED--2166379       //1st object ----------------  String s="Fred" 

FRED47--2081891886  //2nd object ----------------  s=s+"47"

ED4--68469          //3rd object ----------------  s=s.substring(2,5)

ED4--68469          //this is retrieved from the string constant pool -------  s=s.toUpperCase()

ED4--68469          //this is retrieved from the string constant pool -------- s=s.toString() 

总共创建了3个对象。

答案 20 :(得分:-3)

仅仅因为所有哈希码都相同并不意味着您正在查看同一个对象。创建了两个对象。让我们打破这个。

String s = new String(“xyz”);

在“new String(”xyz“)'部分中,地址返回到新字符串”xyz“。当你说'String s ='时,这会将返回的地址分配给这个对象,以便它们指向同一个地方,但是新的字符串和字符串s是两个单独的对象。