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

Som*_*guy 73 java string immutability

我一直在研究Java String.以下问题基于以下帖子

Java String是java中特殊
的String不可变性

  1. 不可变性:现在,通过不变性,String类被设计为公共池中的值可以在其他位置/变量中重用.如果String被创建为, 则这很好

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

    String b = new String("Hello World!"); 为什么这也是不变的?(或者是吗?).由于这有一个专用的堆内存,我应该能够修改它而不影响任何其他变量.因此,通过设计,还有其他任何原因,为什么String整体被认为是不可改变的?或者我的上述假设是错误的?

  2. 我想问的第二件事是关于常见的字符串池.如果我创建一个字符串对象为

    String c = ""; 是在池中创建的空条目?

这些帖子上已经有帖子吗?如果是这样,有人可以分享链接吗?

Ste*_*n C 105

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

(询问是否new String()可变是荒谬的.它是程序代码,而不是价值.但我认为这不是你真正的意思.)


如果我创建一个字符串对象,就像String c = "";在池中创建的空条目一样?

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

(为了迂腐,在""代码执行之前很久就创建了get的pool条目.实际上,它是在代码加载时创建的......或者甚至可能早于此代码.)


所以,我想知道新的堆对象是否也是不可变的,...

是的.但不变性是String对象的基本属性.所有String对象.

你看,StringAPI根本没有提供任何改变方法String.所以(除了使用反射的一些危险和愚蠢的1个技巧),你不能改变a String.

如果是的话,目的是什么?

Java String被设计为不可变类的原因是简单性.如果核心字符串类提供不可变的接口,它可以更容易地编写正确的程序,并读取/推理其他人的代码.(至少,正如我所理解的那样,这就是这个设计决策的基本原理.)

通过答案,我认为其他对同一变量的引用是其中一个原因.如果我理解这一点,请告诉我.

不,它比这更重要.简单地说,所有String对象都是不可变的.理解这一点并不需要复杂的特殊情况推理.它只是>>是<<.

为了记录,如果你想在Java中使用可变的"类似字符串"的对象,你可以使用StringStringBuilder.但这些是String的不同类型.


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


Nar*_*hai 42

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

1)简短回答是肯定的,new String()也是不可改变的.

因为每一个可能的可变操作(比如replace,toLowerCase您执行上etcetra)String 不影响原有的 String实例,并返回一个新的实例.

你可以在Javadoc中查看String.暴露的每个public方法String都返回一个新String实例,并且不会改变您调用该方法的当前实例.

这在多线程环境中非常有用,因为每次传递或共享时都不必考虑可变性(有人会更改值)String.String很容易成为最常用的数据类型,因此设计师们祝福我们所有人不要每次都考虑可变性并为我们带来很多痛苦.

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

由于不可变性属性,字符串的内部池是可能的,因为当在其他某个地方需要相同的String值时,则返回该不可变引用.如果String本来是可变的那么就不可能String像这样分享s来节省内存.

字符串的不可变性不是因为汇集,而是不可变性带来了更多的好处.

字符串实习或池化是Flyweight设计模式的一个示例

2)是的,它会像其他任何String一样被实习,因为空白StringString和其他String实例一样多.

参考文献:


chr*_*ke- 18

String无论对象是如何构造的,Java库都围绕任何对象不可变的约束进行了大量优化.即使您创建b使用new其他代码,您传递实例将把值不变.这是Value Object模式的一个示例,并且所有优点(线程安全,无需进行私有复制)都适用.

空字符串""是一个合法的String对象,就像其他任何东西一样,它恰好没有内部内容,并且由于所有编译时常量字符串都被实现,我几乎可以保证某些运行时库已经将它添加到了池.

  • @Rakesh:如果你真正问的是什么(我不确定你是如何表达它的话)是通过什么精确的机制`new String()`是不可变的,那么它很简单,因为`String`没有实现用于更改其内容的公共方法,因此根本不存在用于更改其内容的接口. (4认同)
  • @Rakesh:你的假设"内存不会被其他地方使用"是错误的.您可以将对同一对象的引用传递给几十个线程,如果它保证是不可变的,它会更安全. (4认同)
  • @Rakesh:正如chrylis所写,它允许程序的其他部分使用字符串来承担其不变性,从而使它们免于同步或制作字符串的私有副本,否则可能会在其背后进行更改. (2认同)

Cub*_*bic 14

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

2)是的.大概.我实际上并不完全确定,但情况确实如此.


ven*_*iac 7

Java Oracle文档:

字符串是不变的; 它们的值在创建后无法更改.

然后再次:

字符串缓冲区支持可变字符串.因为String对象是不可变的,所以可以共享它们.

一般来说:"所有原始"(或相关)对象都是不可变的(请接受我缺乏形式主义).

Stack Overflow上的相关文章:

关于对象池:对象池是一个java优化,它与不可变也无关.


Dol*_*000 7

这不是你的问题的严格答案,但如果你的问题背后是希望有可操作的可变字符串,你应该检查出StringBuilder类,它实现了许多完全相同的方法,String但也添加了方法改变当前的内容.

一旦你以一种你满足它的方式构建你的字符串,你只需调用toString()它就可以将它转换为普通的String,你可以传递给库例程和其他只带Strings的函数.

此外,双方StringBuilderString实现了CharSequence接口,所以如果你想编写自己的代码功能,可以同时使用可变和不可变的字符串,你可以宣布他们采取的任何CharSequence物体.


Kic*_*ick 6

字符串是不可变的意味着无论您如何创建它都无法更改对象本身.对于第二个问题:是的它将创建一个条目.


Gro*_*roo 5

实际上,这是另一种方式.

[...] 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);
Run Code Online (Sandbox Code Playgroud)

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

// 'c' now points to the same instance as 'b'
String c = b;
Run Code Online (Sandbox Code Playgroud)

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

无论如何,这是为什么?

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

关于性能影响,请注意,通常将整个字符串复制到新实例中以便更改单个字符,实际上比由于确保线程安全访问所需的同步构造而导致昂贵的上下文切换更快.正如您所提到的,不变性还提供了实习可能性,这意味着它实际上可以帮助减少内存使用.

一般来说,制作尽可能多的东西是一个不错的主意.