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


73

我学习Java字符串已有一段时间了。以下问题基于以下帖子

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

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

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

    String b = new String("Hello World!"); 为什么这也是不变的?(或者是?)。由于它具有专用的堆内存,因此我应该能够在不影响任何其他变量的情况下对其进行修改。因此,从设计String的角度来看,还有其他原因为什么整体上是不变的?还是我的上述假设错了?

  2. 我想问的第二件事是关于通用字符串池的信息。如果我创建一个字符串对象为

    String c = ""; 在池中创建一个空条目?

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


1
我认为最好首先了解具有不可变对象的有用性,而不仅仅是字符串,Google随机点击是javapractices.com/topic/TopicAction.do?Id=29。如果您知道一个类是不可变的,则它在许多方面都变得更加容易使用。
RemcoGerlich 2014年



3
不要将“不变性”与“公共池”相混淆-它们不是一回事。您很可能创建自己的类型,不可变的类型,而不使用“公共池”。您的问题不是“问题”,而是一系列错误的陈述。
亚伦·麦克戴2014年

在担心其他问题之前,请先关注基础知识。如果您对不变性没有清晰的了解,那么您不应该学习字符串池。不变性是学习语言的不可或缺的关键概念。
trevor-e 2014年

Answers:


105

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

(问是否new String()可变是不明智的。这是程序代码,而不是值。但是我认为那不是您的真正意思。)


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

是; 即,为空字符串创建一个条目。空没什么特别的String

(为方便起见,将""在执行代码之前很长时间就创建用于的池条目。实际上,它是在加载代码时创建的……甚至早于此。)


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

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

您会发现,StringAPI根本没有提供任何更改的方法String。因此(除了使用反射的一些危险和愚蠢的1招),您不能对进行突变String

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

JavaString被设计为不可变类的主要原因是简单性。如果核心字符串类提供了不可变的接口,则可以更轻松地编写正确的程序以及阅读/理解其他人的代码。

第二个重要原因是的不​​可改变性String对Java安全模型具有根本的影响。但是我认为这不是原始语言设计的驱动程序……在Java 1.0及更低版本中。

按照答案,我认为对同一变量的其他引用是原因之一。如果我理解正确,请告诉我。

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

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


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


42

字符串是不可变的,与如何实例化无关

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

因为每一个可能的可变操作(如replacetoLowerCaseetcetra),您执行上String 不会影响原有的 String实例,并返回你一个新的实例

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

这在多线程环境中非常有帮助,因为您不必每次通过或共享环境时都考虑可变性(有人会更改值StringString可以很容易地成为最常用的数据类型,因此设计人员祝福我们所有人不要每次都考虑可变性,从而为我们省去了很多麻烦。

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

因为不可变性属性,所以内部字符串池是可能的,因为当在其他位置需要相同的String值时,将返回不可变引用。如果String本来就可变,那么就不可能String像这样共享s来节省内存。

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

字符串实习或合并是Flyweight Design模式的一个示例

2)是的,它将像其他任何东西String一样被扣留,因为空白与其他实例String一样多。StringString

参考文献:


值得补充的是,如果出现以下情况,则任何类型都是不可变的:所有数据成员均为private,并且所有方法均未修改任何内容。做出String不可变类的决定只是语言设计者的设计决定,并不构成核心语言的“特殊”部分。
2014年

@Basilevs不确定您是真正搜索过还是只是vgrepp。
克莱里斯-cautiouslyoptimistic-

18

Java库针对任何String对象都是不可变的约束进行了严格的优化,无论该对象如何构造。即使您创建了busing new,您传递给该实例的其他代码也会将该值视为不可变的。这是“值对象”模式的示例,所有优点(线程安全,无需创建私有副本)都适用。

空字符串与其他任何东西一样""都是合法String对象,它恰好没有内部内容,而且由于所有编译时常量字符串都被插入,因此我实际上保证某些运行时库已导致将其添加到池。


让我感到困惑的是,String使用'new String()'创建一个将在堆中创建专用条目的方法,对吗?那么,如果不会在其他任何地方使用该内存,在这里如何解释不变性呢?只是想了解,不要轻描淡写。
有些人

2
@Rakesh:就像chrylis所写的那样,它允许使用字符串的程序的其他部分承担其不变性,这使他们摆脱了同步或制作字符串的私人副本之类的责任,否则这些字符串可能会在后台被更改。
Dolda2000

4
@Rakesh:如果您真正要问的(我不太确定您的措辞是)是由哪种精确机制构成new String()的,那么这很简单,因为String没有实现更改其内容的公共方法,因此根本不存在用于更改它的接口。
Dolda2000

4
@Rakesh:您认为“内存将不会在其他任何地方使用”是错误的。您可以将对同一对象的引用传递给数十个线程,如果保证它是不可变的,则它会更加安全。
Groo 2014年

14

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

2)是的。大概。我实际上不是100%确信,但事实确实如此。


7

严格来说,这并不是对您问题的答案,但是,如果在您的问题背后是希望拥有可操作的可变字符串,那么您应该检查一下StringBuilder该类,该类实现了许多完全相同的方法,String但也向其中添加了方法更改当前内容。

一旦以对字符串满意的方式构建了字符串,就可以简单地调用toString()它,以将其转换为String可以传递给仅包含Strings的库例程和其他函数的普通字符串。

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


我特别在寻找String的不变性。无论如何,这是一个很好的见解。
有些人

7

Java Oracle文档中

字符串是常量;它们的值创建后就无法更改

然后再次:

字符串缓冲区支持可变字符串。由于String对象是不可变的,因此可以共享它们。

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

堆栈溢出的相关文章:

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


6

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


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(或更具体地说,它所引用的对象)到另一个线程上的方法,然后继续在主线程上使用相同的实例。现在想象一下,如果字符串可变,将会发生什么。

为什么会这样呢?

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

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

通常,使尽可能多的东西不变是一个很好的主意。


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



2

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

您的问题的答案:

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

  2. 是肯定的。池中将创建一个空条目。


1

字符串是不可变的,因为它没有为您提供修改字符串的手段。这是为了避免任何篡改而设计的(这是最终的,不应该触摸基础数组……)。

相同地,Integer是不可变的,因为没有办法对其进行修改。

创建它并不重要。


1

不变性不是new该类的功能,它是该类的功能String。它没有mutator方法,因此是不可变的。


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

现在,字符串B called“ Test” .toUpperCase()which change the same object into“ TEST”, so一个will also be“ TEST”`是不希望的。


你能解释一下吗?
Fabio

“测试”已由许多参考变量引用,因此,如果其中任何一个更改了值,其他变量将自动受到影响。
Surya Rawat 2014年

0

请注意,在您的示例中,引用已更改,而不是引用的对象,即b,引用可能已更改并引用了新对象。但是那个新对象是不可变的,这意味着它的内容不会在调用构造函数之后的“之后”改变。

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.