java - String是不可变的。 究竟是什么意思?

这个问题在这里已有答案:

Java中字符串的不变性                                     24个答案

我在不可变字符串上编写了以下代码。

public class ImmutableStrings {

public static void main(String[] args) {

testmethod();

}

private static void testmethod() {

String a = "a";

System.out.println("a 1-->" + a);

a = "ty";

System.out.println("a 2-->" + a);

}

}

输出:

a 1-->a

a 2-->ty

这里变量String的值已经改变(许多人说不可变对象的内容不能改变)。 但究竟是什么意思说String是不变的? 你能否为我澄清一下这个话题?

来源:[https://docs.oracle.com/javase/tutorial/java/nutsandbolts/datatypes.html]

19个解决方案

336 votes

在继续进行不变性的大惊小怪之前,让我们先看一下String类及其功能,然后再做出任何结论。

这是String的工作原理:

String str = "knowledge";

像往常一样,这会创建一个包含String的字符串,并为其分配一个参考号String。够简单吗? 让我们执行更多功能:

String s = str; // assigns a new reference to the same string "knowledge"

让我们看看以下声明如何工作:

str = str.concat(" base");

这会将字符串String附加到String.但是等等,这怎么可能,因为"java"对象是不可变的? 令你惊讶的是,它是。

当执行上述语句时,VM取值String,即String并附加"java",给出值"knowledge base".现在,由于strs是不可变的,因此VM无法将此值分配给String,因此它创建一个新的 "base"对象,给它一个值concat,并给它一个引用str。

这里需要注意的一点是,虽然String对象是不可变的,但它的引用变量却不是。 所以这就是为什么在上面的例子中,引用了引用新形成的String对象。

在上面的例子中,我们有两个String对象:我们创建的第一个值为String,由"java"指向,第二个是"knowledge base",由str指向。但是,从技术上讲,我们有三个String对象, 第三个是在concat声明中的文字"base"。

关于字符串和内存使用的重要事实

如果我们没有另外的参考String到String怎么办? 我们本来会丢失那个"java".但它仍然会存在,但由于没有引用而被视为丢失。再看下面的一个例子

String s1 = "java";

s1.concat(" rules");

System.out.println("s1 refers to "+s1); // Yes, s1 still refers to "java"

发生了什么:

第一行非常简单:创建一个新的String String并参考"java"。

接下来,VM创建另一个新的String String,但没有是指它。 所以,第二个"java"即刻丢失。 我们无法达成它。

参考变量String仍指原始String "java"。

几乎每个应用于String对象以修改它的方法都会创建新的String对象。 那么,这些String对象去哪了? 嗯,这些存在于内存中,任何编程语言的关键目标之一就是有效利用内存。

随着应用程序的增长,String文字占用大面积内存非常常见,甚至可能导致冗余。 因此,为了提高Java的效率,JVM预留了一个称为“字符串常量池”的特殊内存区域。

当编译器看到String文字时,它会在池中查找final。 如果找到匹配项,则对新文字的引用将定向到现有的String,并且不会创建新的String对象。 现有的String只有一个参考。 这就是使String对象成为不可变的要点:

在String常量池中,final对象可能具有一个或多个引用。 如果几个引用指向同一个String甚至不知道它,那么如果其中一个引用修改了final值就不好了。 这就是为什么String对象是不可变的。

那么,现在你可以说,如果有人重写了String类的功能怎么办? 这就是String类被标记为final的原因,因此没有人可以覆盖其方法的行为。

roger_that answered 2019-03-28T23:39:45Z

170 votes

String是不可变的意味着您无法更改对象本身,但您可以更改对象的引用。

执行a = "ty"时,实际上是将a的引用更改为String literal "ty"创建的新对象。

更改对象意味着使用其方法更改其中一个字段(或者字段是公共字段而不是最终字段,以便可以从外部更新它们而无需通过方法访问它们),例如:

Foo x = new Foo("the field");

x.setField("a new field");

System.out.println(x.getField()); // prints "a new field"

在一个不可变的类中(声明为final,以防止通过继承进行修改)(它的方法不能修改它的字段,而且字段总是私有的并且建议是最终的),例如String,你不能改变当前的String但是你 可以返回一个新的String,即:

String s = "some text";

s.substring(0,4);

System.out.println(s); // still printing "some text"

String a = s.substring(0,4);

System.out.println(a); // prints "some"

Eng.Fouad answered 2019-03-28T23:40:51Z

18 votes

你正在改变a所指的内容。 试试这个:

String a="a";

System.out.println("a 1-->"+a);

String b=a;

a="ty";

System.out.println("a 2-->"+a);

System.out.println("b -->"+b);

您将看到a和b引用的对象未更改。

如果您想阻止代码更改哪个对象引用,请尝试:

final String a="a";

Greg Hewgill answered 2019-03-28T23:41:37Z

5 votes

字符串是new String(...),包含一系列UTF-16代码单元,该数组的偏移量为System.out.println(("hello" == "hello") + "," + (new String("hello") == "hello") + "," + (new String("hello") == new String("hello")));,长度为true,false,false。

例如。

String s

它为字符串引用创建空间。 分配副本引用,但不修改这些引用引用的对象。

你也应该意识到这一点

new String(s)

并没有真正做任何有用的事情。 它只是创建了另一个由相同数组,偏移量和长度支持的实例,如new String(...).很少有理由这样做,因此大多数Java程序员认为这是不好的做法。

Java双引号字符串(如new String(...))实际上是对实习System.out.println(("hello" == "hello") + "," + (new String("hello") == "hello") + "," + (new String("hello") == new String("hello")));实例的引用,因此true,false,false是对同一String实例的引用,无论它在代码中显示多少次。

“hello”创建一个池化的实例,new String(...)创建一个非池化的实例。 试试System.out.println(("hello" == "hello") + "," + (new String("hello") == "hello") + "," + (new String("hello") == new String("hello")));,你应该看到true,false,false

Lion answered 2019-03-28T23:42:42Z

4 votes

immutable意味着您不能更改同一引用的值。每次创建新引用都需要新的内存位置。例如:

String str="abc";

str="bcd";

这里,在上面的代码中,在存储器中有2个用于存储值的块。第一个用于值“abc”,第二个用于“bcd”。第二个值不代替第一个值。

这是不可变的。

Rakesh Patel answered 2019-03-28T23:43:25Z

3 votes

在您的示例中,变量a只是对字符串对象实例的引用。 当您说a = "ty"时,您实际上并未更改字符串对象,而是将引用指向字符串类的完全不同的实例。

Jon Newmuis answered 2019-03-28T23:43:58Z

3 votes

看这里

class ImmutableStrings {

public static void main(String[] args) {

testmethod();

}

private static void testmethod() {

String a="a";

System.out.println("a 1-->"+a);

System.out.println("a 1 address-->"+a.hashCode());

a = "ty";

System.out.println("a 2-->"+a);

System.out.println("a 2 address-->"+a.hashCode());

}

}

输出:

a 1-->a

a 1 address-->97

a 2-->ty

a 2 address-->3717

这表示无论何时修改不可变字符串对象a的内容,都将创建一个新对象。 即不允许更改不可变对象的内容。 这就是为什么对象的地址都不同的原因。

Rustam answered 2019-03-28T23:44:42Z

2 votes

您没有更改赋值语句中的对象,而是将一个不可变对象替换为另一个不可变对象。 对象String("a")不会更改为String("ty"),它将被丢弃,并且对ty的引用将被写入b。

相反,b表示可变对象。 你可以这样做:

StringBuffer b = new StringBuffer("Hello");

System.out.writeln(b);

b.append(", world!");

System.out.writeln(b);

在这里,您没有重新分配b:它仍然指向同一个对象,但该对象的内容已更改。

dasblinkenlight answered 2019-03-28T23:45:39Z

2 votes

您实际上是获取对新字符串的引用,字符串本身不会被更改,因为它是不可变的。 这是相关的。

看到

维基百科上的不可变对象

Jordan answered 2019-03-28T23:46:22Z

2 votes

不可变对象是一个对象,其状态在创建后无法修改。

所以a = "ABC"< - 不可变对象。 “a”表示对象的引用。并且,a = "DEF"< - 另一个不可变对象,“a”现在引用它。

分配字符串对象后,无法在内存中更改该对象。

总之,您所做的是将“a”的引用更改为新的字符串对象。

AntoineDeSaintExupery answered 2019-03-28T23:47:14Z

2 votes

String S1="abc";

S1.concat("xyz");

System.out.println("S1 is", + S1);

String S2=S1.concat("def");

System.out.println("S2 is", + S2);

这表明一旦创建了一个字符串对象,就无法更改它。 你需要创建新的EveryTime并放入另一个String。小号

Surya answered 2019-03-28T23:47:45Z

2 votes

我认为以下代码清除了差异:

String A = new String("Venugopal");

String B = A;

A = A +"mitul";

System.out.println("A is " + A);

System.out.println("B is " + B);

StringBuffer SA = new StringBuffer("Venugopal");

StringBuffer SB = SA;

SA = SA.append("mitul");

System.out.println("SA is " + SA);

System.out.println("SB is " + SB);

Ankit Sinha answered 2019-03-28T23:48:17Z

2 votes

Java String是不可变的,String将以对象的形式存储值。 因此,如果您指定值String a="a";,它将创建一个对象并将值存储在该对象中,如果您要分配值a="ty"意味着它将创建另一个对象存储该值,如果您想要清楚地理解,请检查has code 为String。

Selva answered 2019-03-28T23:48:52Z

1 votes

只有参考正在改变。 首先是a引用了字符串“a”,后来你将它改为“ty”。 字符串“a”保持不变。

Bhesh Gurung answered 2019-03-28T23:49:24Z

1 votes

在您的示例中,String首先引用b,然后引用"ty".您不会改变任何String实例; 你只是在改变String实例a所指的。 例如,这个:

String a = "a";

String b = a; // b refers to the same String as a

a = "b"; // a now refers to a different instance

System.out.println(b);

打印“a”,因为我们永远不会改变b指向的String实例。

ruakh answered 2019-03-28T23:50:13Z

1 votes

如果某个对象bar持有对可变对象foo的引用并将其某些状态封装在bar状态的可变方面,那将允许更改foo的那些方面的代码更改bar的状态的相应方面而不实际触及bar甚至 知道它的存在。 通常,这意味着使用可变对象封装其自身状态的对象必须确保不会将对这些对象的引用暴露给可能意外地改变它们的任何代码。 相比之下,如果bar保存对对象moo的引用并且仅使用身份以外的moo的不可变方面来封装其状态,则bar可以自由地将moo公开给外部代码,而不必担心外部代码可能对其执行的任何操作。

supercat answered 2019-03-28T23:50:53Z

1 votes

希望以下代码能澄清您的疑虑:

public static void testString() {

String str = "Hello";

System.out.println("Before String Concat: "+str);

str.concat("World");

System.out.println("After String Concat: "+str);

StringBuffer sb = new StringBuffer("Hello");

System.out.println("Before StringBuffer Append: "+sb);

sb.append("World");

System.out.println("After StringBuffer Append: "+sb);

}

在String Concat之前:你好

String Concat之后:你好

在StringBuffer追加之前:你好

StringBuffer追加后:HelloWorld

technolisha answered 2019-03-28T23:52:00Z

1 votes

可能上面提供的每个答案都是正确的,但我的答案是具体使用hashCode()方法,来证明点,像...一旦创建无法修改和修改将导致在不同的内存位置的新值。

public class ImmutabilityTest {

private String changingRef = "TEST_STRING";

public static void main(String a[]) {

ImmutabilityTest dn = new ImmutabilityTest();

System.out.println("ChangingRef for TEST_STRING OLD : "

+ dn.changingRef.hashCode());

dn.changingRef = "NEW_TEST_STRING";

System.out.println("ChangingRef for NEW_TEST_STRING : "

+ dn.changingRef.hashCode());

dn.changingRef = "TEST_STRING";

System.out.println("ChangingRef for TEST_STRING BACK : "

+ dn.changingRef.hashCode());

dn.changingRef = "NEW_TEST_STRING";

System.out.println("ChangingRef for NEW_TEST_STRING BACK : "

+ dn.changingRef.hashCode());

String str = new String("STRING1");

System.out.println("String Class STRING1 : " + str.hashCode());

str = new String("STRING2");

System.out.println("String Class STRING2 : " + str.hashCode());

str = new String("STRING1");

System.out.println("String Class STRING1 BACK : " + str.hashCode());

str = new String("STRING2");

System.out.println("String Class STRING2 BACK : " + str.hashCode());

}

}

OUTPUT

ChangingRef for TEST_STRING OLD : 247540830

ChangingRef for NEW_TEST_STRING : 970356767

ChangingRef for TEST_STRING BACK : 247540830

ChangingRef for NEW_TEST_STRING BACK : 970356767

String Class STRING1 : -1163776448

String Class STRING2 : -1163776447

String Class STRING1 BACK : -1163776448

String Class STRING2 BACK : -1163776447

Prabhukumar Devaraj answered 2019-03-28T23:52:38Z

1 votes

字符串是不可变的,这意味着,一旦创建了String对象的内容就无法更改。 如果要修改内容,则可以使用StringBuffer / StringBuilder而不是String。 StringBuffer和StringBuilder是可变类。

user095736 answered 2019-03-28T23:53:15Z

java 不可变_java - String是不可变的。 究竟是什么意思?相关推荐

  1. Java——final关键字,String为什么不可变

    目录 final是什么? 一.修饰类 二.修饰变量 三.修饰方法 String为什么不可变? 不知道大家有没有这样的疑问:我们在使用某些Java的类或者方法.属性(比如:String类)时,总是会发现 ...

  2. java 可变 不可变_java中的不可变类型的探究

    不可变类是指创建类的对象实例后,该实例的属性不能发生改变.常见的String就是不可变类.不可变类型的属性值不会发生改变,这在多线程编程的时候非常有用,不用担心对象的属性值被修改. 下面我们来看看如何 ...

  3. java 字符串驻留_java String 以及字符串直接量 与 字符串驻留池 ...

    结果输出 :fancydeepin这是怎么回事?不是说 String 是不可变的字符串吗?怎么这里又变了?是这样的,在这里 name 只是一个引用类型变量,并不是一个 String 对象,@1中创建了 ...

  4. java自制缓冲池_java String 缓冲池概念的举例说明

    口试中常会碰到String对象构造的题目.例如String s= new String("a");创建了几个对象. 下面根据代码具体解释一下各种String构造情况. 代码如下: ...

  5. java format用法_Java String format() 方法

    例如: String 类的 format 方法可以格式化日期和时间import java.util.Date; import java.util.Locale; public class Main { ...

  6. java split 坑_java String split 踩坑记

    split操作是出镜率非常高的一个方法, 但是我们使用中通常会使用两个类提供的split方法, 他们在入参类型一样, 但是效果却有一些差别, 稍不注意容易踩坑. java.lang.String#sp ...

  7. java竖线分割_Java String类的Split以竖线作为分隔符

    版权声明:本文为博主原创文章,转载请写明出处.https://blog.csdn.net/wo541075754/article/details/50668160 问题 Java中String类的Sp ...

  8. java equals 字符串_Java String 字符串 比较 == equals

    1.==该运算符表示指向字符串的引用是否相同,如t1==t2这种情况, 是因为在java中字符串的值是不可改变的,相同的字符串在内存中只会存一份, 所以t1和t2指向的是同一个对象,而t1==t4返回 ...

  9. java replace无效_Java String.replace()方法无效的原因及解决方式

    首先我们来看个例子 public class Demo1 { public static void main(String[] args) { String aa="abcd"; ...

最新文章

  1. [03] 处理注解:反射
  2. 数字大脑学术沙龙:“城市大脑与应急管理专题研讨会”成功召开
  3. PostgreSQL — 常规操作
  4. Kubernetes - - k8s - v1.12.3 动态存储管理GlusterFS及使用Heketi扩容GlusterFS集群
  5. Linux centos 集群下ssh无密码
  6. Android自动化测试之monkeyrunner基本要素(七)
  7. 打开.264后缀名格式的文件
  8. What you should do if you want to become more professional in career?
  9. java用mvvm,[Java教程]MVVM架构~使用boxy和knockoutjs实现编辑功能
  10. 常用函数式接口之Predicate
  11. 2.7万字还原行业面貌,《2019 AI金融风控行业研究报告》正式上线!...
  12. openstack 功能_OpenStack Ocata的新功能
  13. 安装 SQL Server 2005 时出现性能计数器要求安装错误的解决办法
  14. linuxoracle静默安装应答文件修改_Windows7重要更新补丁安装包 2020.01.17
  15. 探究streambuf
  16. Win10鼠标宏怎么设置?电脑设置鼠标宏的方法
  17. H3CSE园区-STP
  18. 无线网络稳定性测试方案
  19. NOI2021颓废记
  20. win10U盘插进电脑读不出来/不显示驱动怎么办?

热门文章

  1. 【改】[火光摇曳]神奇的伽玛函数(上)——markdown排版
  2. python 如何绘制分叉图
  3. 二开七色中文视频 图片 小说网站源码模板 苹果cms V10
  4. 夕阳红旅游团第四周报告
  5. TensorFlow学习--tf.summary.histogram与直方图仪表板/tensorboard_histograms
  6. 深度图转激光原理以及代码解析
  7. 学计算机的自述,计算机的自述作文范文
  8. Vue+Element-UI 上传图片,打开相机,相册
  9. java变量无法解析出现的原因_不明白为什么这个变量无法解析为一个类型? (JAVA)...
  10. 初识二维码 第二讲 二维码的结构