29

对于这个面试问题,普遍同意的答案是代码创建了两个对象。但我不这么认为;我写了一些代码来确认。

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,这到底是什么意思?

4

21 回答 21

24

以下错误取决于您使用的 JVM/JRE。无论如何,最好不要担心这样的事情。有关任何更正/疑虑,请参阅评论部分。

首先,这个问题确实询问了这里解决的问题: 字符串文字池是对字符串对象的引用的集合,还是对象的集合

因此,这是每个人在此问题上的指南。

...

鉴于这行代码:String s = new String(“xyz”)

有两种看待这种情况的方法:

(1) 当代码行执行时会发生什么——它在程序中运行的那一刻?

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

回答:

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

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

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

b)String s创建 new 时,内部是内部字符串char[]的副本。"xyz"

c) 这意味着,当该行执行时,只创建了一个附加对象。

事实是"xyz",一旦加载类并且在此代码部分运行之前,该对象就会被创建。

...下一个场景...

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

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

Stringa) s1 和 s2 只是被引用,而不是对象,它们在内存中指向同一个。

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

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

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

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

于 2013-10-30T02:06:53.317 回答
8

为此将创建两个对象:

String s = new String("abc");

一个在堆中,另一个在“字符串常量池”(SCP)中。引用s会一直指向s,并且不允许在SCP区域进行GC,所以在JVM关闭的时候,SCP上的所有对象都会被自动销毁。

例如:

这里通过使用堆对象引用,我们通过调用 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
于 2014-12-08T15:22:04.893 回答
3

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

  1. 使用 new 运算符,即

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

    String s2 = "abc";
    

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

看,每当你使用new操作符时,对象就会被创建,JVM 不会在字符串池中查找。它只是要创建对象,但是当您使用字符串文字来创建字符串对象时,JVM 将执行在字符串池中查找的任务

即,当你写

String s2 = "abc";

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

所以在你的情况下(a)

String s1 = new String("abc");
  • 由于newis used 对象被创建

(二)

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

于 2015-04-16T12:33:13.013 回答
3
  1. 字符串 s = new String("xyz");

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

现在如果我们这样做

  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";

如果我们执行上述行,我们将拥有三个对象。

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

通过在新字符串对象上应用实习生方法来了解实习生的魔力。此处应用了实习生,因此它将检查“Test”是否在字符串常量池中可用,因为“Test”在字符串常量池中可用,它将返回相同的对象,因此s3具有相同的引用s1并且 s2将获得所有结果真实的

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

于 2018-06-11T13:08:07.010 回答
2

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

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

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

于 2018-07-05T14:12:51.677 回答
2

有这么多随机的答案,所以我相信我的面试官也不会很确定:) :)

我研究了很多,发现hashcode不是内存地址,调试时的变量没有给出内存地址。因此,这些参数可能会令人困惑。

于 2020-04-01T22:20:11.853 回答
1

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

尽管如此,您的测试是垃圾,因为hashCodesString是基于 的内容String,而不是基于它们的身份。如果要检查身份,则应使用System.identityHashCode或仅==进行比较。

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

于 2013-10-30T01:58:25.747 回答
1
        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 进行比较,而不是与堆栈内存进行比较。

于 2016-10-18T15:12:49.263 回答
1
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[] 并且每次都会复制相同的内容并实例化它,是的,它会将数据存储在字符串常量池中。1) 将取自 SCP 字符串 s1 = "a" 字符串 s2 = "a";2) 创建一个新对象 String s3 = new String("a"); 好奇心,新对象字符串 s2=new String("a"); 在上述所有代码中,相同的 char[] 将被复制。i:e char[] 值 您可以在此处查看

于 2017-05-13T16:08:47.163 回答
0

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

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

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

愿源与你同在。

于 2015-10-01T11:12:49.707 回答
0

@Giulio,你是对的。字符串 s3 = new String("abc"); 创建两个对象,一个在堆中,引用为 s3,另一个在 SCP(无引用)。现在 String s2 = "abc"; 不会在 SCP 中创建任何新对象,因为“abc”已经存在于 SCP 中。

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

运单:s1:366712642,s2:366712642,s3:1829164700,s4:366712642

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

于 2018-01-12T07:39:54.663 回答
0

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

public static void main(String[] args)
{
    String str = "atul";
    String string = new String("manoj");
    String string2 = "manoj";
    System.out.println(str == string);
}
于 2018-09-18T12:56:17.643 回答
0

对调用 new String("<>") 后究竟发生了什么感到困惑,我找到了这个线程。不过,您对哈希码比较的理解在技术上并不正确。

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

字符串 s1 = 新字符串(“你好”);字符串 s2 = 新字符串(“你好”);

所以 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() 方法的对象都应该返回相同的 hashCode() 值)。

于 2018-11-15T21:24:59.940 回答
0

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

在 String 对象上调用的 intern() 方法在池中查找此 String 对象包含的字符串,如果在那里找到该字符串,则返回池中的字符串。否则,将此 String 对象添加到池中并返回对该 String 对象的引用。

于 2019-02-05T05:42:06.163 回答
0

2个物体由:

String s = new String("xyz");

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

"xyz"字符串常量池第二名

于 2020-12-27T10:35:13.423 回答
-1

我在 Eclipse 调试器中运行它。在这种情况下,创建了两个对象,一个 id 为 17,另一个为 22:

在此处输入图像描述

于 2013-10-30T01:59:28.620 回答
-2

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

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

  1. 第一个对象将在 Java 永久堆内存中创建,作为我们传递的参数的一部分 - “XYZ”。它将在字符串文字池中创建。

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

于 2015-07-01T10:56:58.453 回答
-3

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

String s = new String(“xyz”);

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

于 2013-10-30T02:01:43.507 回答
-3

我使用该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 个对象。

案例二:

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

于 2015-11-20T13:08:15.340 回答
-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 方法总是命中堆内存的字符串常量池。

于 2017-02-09T09:20:47.087 回答
-3

字符串 s = new String("xyz");

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

上面的代码中只创建了一个对象,即在堆内存中。

不是两个对象.....

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

字符串 s1 = "xyz";

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

如何?

我们可以通过使用 == 运算符 (s == s1) 来检查引用类型。如果s已经存储在字符串常量池中,则它给出 true,在这种情况下输出为 false。

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

于 2018-10-24T14:29:59.413 回答