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

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

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()); } } 

输出是:

应用输出

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

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

 String s = new String("xyz") 

而不是StringTest代码。

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

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

结果是:

在此输入图像描述

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

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

首先,这个问题确实在这里解决了这个问题: String Literal Pool是对String对象的引用的集合,还是对象的集合

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

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

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

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

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

回答:

1) 执行此操作后,将创建一个附加对象。

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

  • 如果"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”是interned并且是一个复合对象:一个char[]对象和String对象本身。 它由内存中的两个对象组成。

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

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

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

将为此创建两个对象:

 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 

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

  1. 使用new运算符,即

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

     String s2 = "abc"; 

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

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

即,当你写作

 String s2 = "abc"; 

JVM将查找字符串池并检查“abc”是否已存在。 如果存在,则将引用返回到已存在的字符串“abc”,并且不创建新对象,如果它不存在,则创建对象。

所以在你的情况下(一)

 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)方法查看内容是否相等。

  1. String s = new String(“xyz”);

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

现在,如果我们这样做

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

以上两个语句将创建两个对象。 第一行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";

如果我们执行上面的行,我们将有三个对象。

  • 第一个和前面提到的将在堆中创建两个对象,在String常量轮询中创建另一个。
  • 当第二行执行时,它检查字符串常量轮询
    并找到“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)); } } 

如果我们执行String s = new String("Brajesh"); ,应创建两个对象。 将在字符串文字池中创建一个对象,在堆区域中创建另一个对象。 但是如果我们已经有相同的字符串文字对象,那么只创建一个对象。 喜欢

 String s1 ="Brajesh"; String s = new String("Brajesh");//it will create only one object in heap area 

除此之外,还在char区域中创建了另一个对象,即char []的对象。 我附上了堆内存的快照。 在此处输入图像描述

根据编译器的智能程度创建2或3个对象。

然而,你的测试是垃圾,因为StringhashCode是基于String的内容,而不是它们的身份。 如果要检查标识,则应使用System.identityHashCode或仅使用==比较。

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

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

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

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

也许消息来源与你同在 。

  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进行比较,而不是与堆栈内存进行比较。

 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 =“a”String s2 =“a”; 2)创建一个新对象String s3 = new String(“a”); 好奇心,新对象字符串s2 =新字符串(“a”); 在以上所有代码中, 相同的char []将被复制.i:e char [] value 您可以在此处查看

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

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

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

在此处输入图像描述

@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

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

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

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

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

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

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

 String s = new String(“xyz”); 

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

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

情况1:

 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个对象。

有一种方法可以找到使用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 

注意:正如我们所知,实习方法始终会命中堆内存的字符串常量池。