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

时间:2014-01-27 08:02:36

标签: java string immutability

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

Java String is special
Immutability of String in java

  1. 不变性: 现在,根据不变性,设计了String类,以便公共池中的值可以在其他位置/变量中重用。如果将String创建为

    ,这就很好

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

    这样的字符串

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

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

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

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

15 个答案:

答案 0 :(得分:105)

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

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


  

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

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

(为了迂腐,""的池条目在代码执行之前就已经创建了。事实上,它是在代码加载时创建的......或者甚至可能早于此代码。 )功能


  

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

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

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

  

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

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

  

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

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

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


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

答案 1 :(得分:42)

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

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

因为您在replace 上执行的每个可能的可变操作(如toLowerCaseString etcetra)都不会影响原始 String实例并返回新实例

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

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

Immutability允许字符串池或缓存

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

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

字符串实习或池化是Flyweight Design pattern

的一个示例

2)是的,它会像任何其他String一样被实习,因为空白String与其他String个实例一样String

参考文献:

答案 2 :(得分:18)

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

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

答案 3 :(得分:14)

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

2)是的。大概。我实际上并不是100%肯定,但情况确实如此。

答案 4 :(得分:7)

来自Java Oracle documentation

  

字符串是不变的; 他们的价值观无法改变   创建即可。

再次:

  

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

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

Stack Overflow上的相关文章:

关于对象池:对象池是一个java优化,它与immutable无关。

答案 5 :(得分:7)

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

一旦你以满足它的方式构建了字符串,你只需在其上调用toString()即可将其转换为可以传递给它的普通String库例程和其他仅使用String s。

的函数

此外,StringBuilderString都实现了CharSequence接口,因此如果要在自己的代码中编写可以使用可变字符串和不可变字符串的函数,可以声明它们采取任何CharSequence对象。

答案 6 :(得分:6)

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

答案 7 :(得分: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);

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

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

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

为什么会这样呢?

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

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

make as much stuff immutable as your can通常是一个不错的主意。

答案 8 :(得分:4)

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

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

希望这有助于您的查询。您可以随时检查String类的源代码,以便更好地理解链接:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/String.java

答案 9 :(得分:2)

1- 字符串不可变的。请参阅:

Is a Java string really immutable?

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

2 - 简答:将为空。

答案 10 :(得分:2)

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

您的问题的答案:

  1. 唯一的区别是:
    当字符串创建为 - {String a = "Hello World!";}时,只会创建一个对象 当它像 - {String b = new String("Hello World!");}一样创建时,会创建两个对象。第一个是因为您使用了'new'关键字,而第二个是 String 属性。

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

答案 11 :(得分:1)

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

相同,Integer是不可变的,因为无法修改它。

如何创建它无关紧要。

答案 12 :(得分:1)

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

答案 13 :(得分:1)

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

Now String B called“Test”.toUpperCase()which change the same object into“TEST”, so A will also be“TEST”`这是不可取的。

答案 14 :(得分:0)

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

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

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