字符串实例化与字符串缓冲区实例化

4

我无法弄清楚这个问题:如果

String ab = "hello";        //straight initialization

String ab_1 = new String ("hello_1");  //initializing using new

两者都可以使用,但是

StringBuffer bfr = new StringBuffer("hi");   //works only with new

只有通过new关键字创建的对象才能使用该方法。

为什么String可以直接实例化,而StringBuffer需要使用new运算符呢?能否请有人解释一下主要原因。

8个回答

6

所有对象都需要使用 new 实例化。只有基本数据类型可以从字面量中实例化 (int i = 0;)。

唯一的例外是:

  • 字符串,其允许特殊的初始化构造方式:
   String s = "abc"; //can be instantiated from a literal, like primitives
  • null实例化:Object o = null;

它在Java语言规范#3.10中定义:

字面值是原始类型,字符串类型或null类型的值的源代码表示形式。

注意:数组也有专用的初始化模式,但不是字面量:

   int[][] a = { { 00, 01 }, { 10, 11 } };

4
另外,使用文字构造函数创建的字符串会进入字符串池,可以被重复使用。然而,使用 new 创建的字符串不会进入字符串池,StringBuffer 对象也不在其中。 - Love Hasija

2

使用 String s1 = "hello";String s2 = new String("hello"); 有微妙的区别。

 public static void main(String[] arg ) {
    String s1 = "Java";
    String s2 = "Java";
    String s3 = new String("Java");
    System.out.println(s1==s2); //true
    System.out.println(s1==s3); //false

    StringBuilder sb = new StringBuilder(25); //initial capacikacity
    sb = new StringBuilder(10);
    sb.append(s1).append(" uses immutable strings.");
    sb.setCharAt(20, 'S');
    System.out.println(sb);
}

在上述代码中,“Java”被称为字符串字面量。为了节省内存,在代码中两次出现时,它是相同的字符串字面量,因此s1和s2实际上引用内存中的同一个对象。虽然s1.equals(s3)为true,但它们不像上面所示的那样引用内存中的同一对象。
在实践中,我们总是使用.equals来比较字符串,并且它们是不可变的,因此我们不能轻易地更改s1引用的数据。但是,如果我们能够更改s1引用的数据,那么s2也会随之改变。
StringBuilder确实允许您修改基础数据:我们经常使用它将一个字符串附加到另一个字符串上,如上所示。我们可以庆幸的是,StringBuilder sb2 =“what?”是非法的,因为在StringBuilders的情况下,让它们中的两个引用相同的数据(意味着sb1==sb2)更可能导致问题,其中对sb1的更改会导致sb2发生意外更改。

1

1
 String ab = "hello";        //straight initialization
 String ac = "hello";  // create one more reference ac

String是一个特殊的情况,当你使用new关键字时,将创建一个新的String对象。请注意,对象始终在堆上 - 字符串池不是与堆分开的单独的内存区域。字符串池就像一个缓存。

这是因为Java中经常使用字符串,并且使用new关键字创建字符串对象很昂贵,因此Java引入了StringPool概念。

如果您声明一个变量ac具有相同的值,则Java不会创建新对象(字符串),它只会简单地引用已经存在于池中的相同对象(hello)。

 String ab_1 = new String ("hello_1");  //initializing using new

它将在内存中简单地创建对象,ab_1 将引用该对象。


1

在Java中,字符串是一种非常特殊的情况(在我看来这并不是一件好事,但这并不重要)。

与其他对象不同,字符串可以像常量一样直接实例化。当您这样做时,字符串常量将被添加到字符串常量池中,并像原始值一样处理。让我举个例子。

String a = "abc";
String b = "abc";

当您将a实例化为“原始”字符串时,它会被添加到池中,当您实例化b时,相同的对象将从池中返回,因此如果您执行以下操作:
a == b;

你会得到...true,因为它实际上是同一个对象。如果你用new实例化两个对象,你会得到false,因为你在比较两个不同对象的引用(new强制创建一个独立的对象)。


1

还有一个基于“存储”字符串的位置——内存或字符串常量池的区别。

为了使Java更加节省内存,JVM设置了一个特殊的内存区域,称为“字符串常量池”。当编译器遇到一个字符串字面值时,它会检查常量池,看看是否已经存在相同的字符串。如果找到匹配项,则将对新字面值的引用指向现有字符串,并且不会创建新的字符串字面值对象。(现有字符串只是具有额外引用。)

String s = "abc"; // creates one String object and one reference variable

在这个简单的情况下,“abc”将进入池中,s将引用它。
String s = new String("abc"); // creates two objects, and one reference variable

在这种情况下,因为我们使用了new关键字,Java将在正常(非池)内存中创建一个新的String对象,并且s将引用它。此外,字面值“abc”将被放置在池中。

0

String是一个可变类,具有内置构造函数,可以从字符串字面值创建String对象。

在String的情况下也没有异常(例如像原始类型一样创建它,例如int i = 0)。 String也执行构造函数来初始化以下内容(只是不同的是它是抽象的,不能直接看到):

String str = "ABC";

因为这里的“ABC”也代表一个字符串对象,程序员不能直接使用它,但它存在于字符串池中。当执行此语句时,JVM将内部调用私有构造函数,使用驻留在池中的“ABC”对象创建对象。

0

基本上,由于字符串在编程中使用非常频繁,Java提供了一种简便的方法来实例化一个字符串。

与其总是使用这个,

String str = new String ("hello");

Java使得这个成为可能。
String str = "hello";

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接