“ new String()”也是不可变的吗?

我已经研究 Java 字符串有一段时间了

Java 字符串是特殊的
Java 中字符串的不变性

  1. 不变性: 现在,根据不可变性,String 类已经被设计成可以在其他地方/变量中重用 公共泳池中的值。如果将 String创建为

    String a = "Hello World!"; 但是,如果我创建 String like

    String b = new String("Hello World!"); why is this immutable as well? (or is it?). Since this has a dedicated heap memory, I should be able to modify this without affecting any other variable. So by design, was there any other reason why String as a whole is considered immutable? Or is my above assumption wrong?

  2. 我想问的第二件事是关于 普通字符串池普通字符串池

    String c = ""; 是否在池中创建了一个空项?

是否有任何帖子已经在这些? 如果有,可以有人分享的链接?

10179 次浏览

Java 库围绕任何 String对象都是不可变的这一约束进行了大量优化,而不管该对象是如何构造的。即使您使用 new创建 b,您传递该实例的其他代码也会将该值视为不可变的。这是 Value Object 模式的一个示例,所有的优点(线程安全,不需要制作私有副本)都适用。

空字符串 ""是一个合法的 String对象,就像其他任何对象一样,它只是碰巧没有内部内容,而且由于所有编译时常量字符串都是实际存在的,我将几乎保证某个运行时库已经将它添加到池中。

不管如何实例化字符串,字符串都是不可变的

1)简短的答案是 是的new String()也是不变的。

因为您在 String 不影响原件 String实例上执行的每个可能的 可变的操作可变的操作(如 replacetoLowerCase等)返回一个 新的例子

您可以在 Javadoc 中检查 String。公开的 String的每个 public方法都返回一个新的 String实例,并且不更改您调用该方法的当前实例。

这在多线程环境中非常有用,因为您不必每次传递或共享 String时都考虑可变性(有人会改变价值)。String可以很容易地成为最常用的数据类型,所以设计者祝福我们不要每次都考虑可变性,并且为我们省去了很多麻烦。

允许字符串池或缓存的不变性

正是由于不可变性属性,字符串的内部池才成为可能,因为当在其他地方需要相同的 String 值时,就会返回不可变的引用。如果 String是可变的,那么就不可能像这样共享 String来节省内存。

字符串的不可变性不是因为池,而是因为它具有更多的好处。

字符串实习或池就是 轻量级设计模式的一个例子

2)是的,它将像任何其他的 String实例一样被拘留,因为一个空白的 String实例也和其他的 String实例一样是一个 String实例。

参考文献:

new String()是一种产生 String的表达式... 而 String是不可变的,无论它是如何产生的。

(询问 new String()是否可变是毫无意义的。这是程序代码,不是值。但我认为那不是你真正的意思。)


如果我创建一个字符串对象作为 String c = "";是一个空项创建在池?

是的; 也就是说,为空字符串创建了一个条目。空 String没有什么特别之处。

(为了显得迂腐,在执行代码之前很久就创建了 ""的池条目。实际上,它是在加载代码时创建的... ... 甚至可能更早。)


因此,我想知道新的堆对象是否也是不可变的,..。

是的。但是不变性是 String 对象的一个基本属性。所有的 String对象。

您可以看到,String API 根本不提供用于更改 String任何方法。所以(除了一些使用反射的危险和愚蠢的 1技巧) ,你不能突变 String

如果是这样,目的是什么。

将 JavaString设计为不可变类的主要原因是简单性。如果核心字符串类提供了一个不可变的接口,那么它使得编写正确的程序和读取/推理其他人的代码变得更加容易。

第二个重要的原因是 String的不可变性对 Java 安全模型有着根本的影响。但我不认为这是最初的语言设计中的驱动程序... 在 Java 1.0和更早的版本中。

根据这个答案,我推测对同一个变量的其他引用是原因之一。请让我知道我的理解是否正确。

没有。它比那更根本。简单地说,所有 String对象都是不可变的。没有复杂的特例推理需要理解这一点。它只是 > > is < < 。

对于记录,如果您希望在 Java 中使用可变的“类字符串”对象,可以使用 StringBuilderStringBuffer。但这些类型与 String 不同。


1-这些技巧(IMO)之所以危险和愚蠢,是因为它们会影响字符串的值,而这些值可能通过字符串池被应用程序的其他部分共享。这可能会导致混乱... ... 下一个维护您代码的人几乎没有机会追踪到这种混乱。

1)不可变部分不是池的 因为,它只是使池成为可能。字符串通常作为参数传递给其他函数,甚至与其他线程共享; 使字符串不可变是一个设计决策,以便在这种情况下更容易推理。因此,无论您如何创建它们,Java 中的 是的-String总是不可变的(注意,在 Java 中可能有可变的字符串——只是不包含 String类)。

2)是的,有可能。我不是百分之百确定,但应该是这样的。

String 是不可变的,这意味着无论如何创建对象,都不能更改对象本身。至于第二个问题: 是的,它将创建一个条目。

来自 Java Oracle 文档:

字符串是常量; < strong > 它们的值在变为常量后不能更改 创建

再说一遍:

字符串缓冲区支持可变字符串 对象是不可变的,它们可以被共享。

一般来说: “一切原始的”(或相关的)对象是不变的(请接受我缺乏形式主义)。

关于堆栈溢出的相关文章:

关于对象池 : 对象池是一个 Java 优化,也与不可变性无关。

严格来说,这并不能回答你的问题,但是如果你的问题背后希望有一个可变的字符串,你可以操作,你应该检查一下 StringBuilder类,它实现了许多与 String完全相同的方法,但是也增加了方法来改变当前的内容。

一旦你已经构建了你的字符串,你满意它的方式,你只需要调用它的 toString(),以便将它转换成一个普通的 String,你可以传递给库例程和其他函数,只采用 String

而且,StringBuilderString都实现了 CharSequence接口,所以如果您想在自己的代码中编写既可以使用可变字符串又可以使用不可变字符串的函数,那么您可以声明它们以获取任何 CharSequence对象。

1)不可变性: 出于安全原因,如果使用 new 或其他方法创建字符串,则字符串将是不可变的

2)是的,字符串池中将有一个空条目。

使用代码可以更好地理解这个概念

    String s1 = new String("Test");
String s2 = new String("Test");
String s3 = "Test";
String s4 = "Test";


System.out.println(s1==s2);//false
System.out.println(s1==s3);//false
System.out.println(s2==s3);//false
System.out.println(s4==s3);//true

希望这对您的查询有所帮助。为了更好地理解 Link: http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/String.java,您总是可以检查 String 类的源代码

1-绳子就是 永恒不变看这个:

Java 字符串真的是不可变的吗?

所以你可以用很多方法来创建它。

2-简短的回答: 是的将是空的。

事实上,正好相反。

[ ... ] String类的设计使得公共池中的值可以在其他位置/变量中重用。

不,String类是不可变的,因此您可以安全地引用它的一个实例,而不必担心它会从程序的另一部分被修改。这就是为什么首先可以实现共享。

所以,考虑一下:

// this string literal is interned and referenced by 'a'
String a = "Hello World!";


// creates a new instance by copying characters from 'a'
String b = new String(a);

现在,如果只是创建对新创建的 b变量的引用,会发生什么情况?

// 'c' now points to the same instance as 'b'
String c = b;

假设您将 c(或者更具体地说,它引用的对象)传递给另一个线程上的一个方法,并继续在主线程上使用相同的实例。现在想象一下如果字符串是可变的会发生什么。

为什么会这样?

如果没有别的原因,那是因为不可变对象使多线程更简单,通常甚至更快。如果在不同的线程之间共享一个可变对象(即任何带有可变私有/公共字段或属性的有状态对象) ,则需要特别注意确保同步访问(互斥对象、信号量)。即使这样,您也需要特别注意确保所有操作的原子性。多线程很难。

关于性能影响,请注意,为了更改哪怕是一个字符,通常将整个字符串复制到一个新实例中,实际上比引入一个昂贵的上下文切换要快,因为需要同步结构来确保线程安全访问。正如您所提到的,不可变性还提供了实习的可能性,这意味着它实际上可以帮助减少内存使用。

对于 让尽可能多的东西不变来说,这通常是一个非常好的主意。

无论如何创建,创建的字符串始终是不可变的。

回答你的问题:

  1. 唯一的区别是:
    当字符串像—— {String a = "Hello World!";}那样创建时,只创建 一件物品
    当它被创建时,就像—— {String b = new String("Hello World!");}然后 两个物体被创建。第一个是因为使用了 “新”关键字,第二个是因为使用了 绳子属性。

  2. 是的,当然。将在池中创建一个空条目。

字符串是不可变的,因为它不提供修改它的方法。它的设计是为了避免任何篡改(它是最终的,底层数组不应该被触及...)。

同样,Integer 是不可变的,因为没有办法修改它。

如何创建它并不重要。

不变性不是 new的一个特征,而是 String类的一个特征。它没有 mutator 方法,所以它是不可变的。

 String A = "Test"
String B = "Test"

现在字符串 B called“ TEST”. toUpperCase () which change the same object into“ TEST”“ , soAwill also be“ TEST”‘这是不可取的。

请注意,在您的示例中,引用被更改,而不是它引用的对象,也就是说,作为引用的 b可能被更改并引用一个新对象。但是这个新对象是不可变的,这意味着在调用构造函数之后它的内容不会被更改。

您可以使用 b=b+"x";b=new String(b);更改字符串,变量 a的内容似乎会发生变化,但是不要混淆引用(这里是变量 b)的不可变性和它所引用的对象(想想 C 中的指针)。引用所指向的对象在创建后将保持不变。

如果需要通过更改对象的内容(而不是更改引用)来更改字符串,则可以使用 StringBuffer,它是 String 的可变版本。