Immutability of Strings in Java
考虑the following example。P></
1 2 3 4 5 6 7 |
现在,在Java字符串对象是immutable。那么如何为对象指定值
编辑:P></
好吧。现在我要它,但只是一个后续问题。尾巴:the following about是什么P></
1 2 3 4 5 |
does that are created this对象的二次均值("密西西比"和的"M!SS!SS!聚丙烯(PP)!"
以该代码为例:
现在,我们不能对
如果我们这样做:
1 2 |
这里我们看到了改变一个对象和改变一个引用之间的区别。
如果字符串是可变的,我们可以这样做:
1 2 3 4 |
编辑以响应OP的编辑:
如果您查看string.replace的源代码(char,char)(也可以在JDK安装目录的src.zip中找到——只要您想知道某个东西是如何工作的,就需要查看pro提示)您可以看到它的作用是:
- 如果当前字符串中出现一个或多个
oldChar ,则复制当前字符串,其中oldChar 的所有出现都替换为newChar 。 - 如果当前字符串中不存在
oldChar ,则返回当前字符串。
是的,
1 2 3 4 5 6 |
你现在的功课是看看如果你把
1实际上,可以改变字符串(和其他不变的对象)。它需要深思熟虑,非常非常危险,除非你真的有兴趣破坏程序,否则永远不应该使用它。
包含字符串
防止
1 |
现在,试图将另一个
轻手柄,我建议你读一下杯子的尺寸——一个关于变量和传递值的故事(杯子尺寸继续)。这对阅读上面的文章有很大帮助。
你读过吗?对。很好。
这将创建一个新的"远程控制",称为"
例如,在内存中,这会创建:
1 | str --- >"" |
1 | str ="Hello"; |
然后更改遥控器"
例如,在内存中,这会创建:
1 2 | str -+ "" +->"Hello" |
1 | str ="Help!"; |
然后更改遥控器"
例如,在内存中,这会创建:
1 2 3 | str -+ "" | "Hello" +->"Help!" |
我们把它分成几个部分吧
1 |
此语句创建包含hello的字符串并占用内存中的空间,即常量字符串池中的空间,并将其分配给引用对象s1。
1 |
此语句将相同的字符串hello赋给新引用s2
1 2 3 4 | __________ | | s1 ---->| hello |<----- s2 |__________| |
两个引用都指向同一个字符串,因此输出的值如下所示。
1 2 | out.println(s1); // o/p: hello out.println(s2); // o/p: hello |
虽然字符串是不可变的,但可以进行赋值,因此s1现在将引用新的值堆栈。
1 2 3 4 5 | s1 ="stack"; __________ | | s1 ---->| stack | |__________| |
但是,对于指向hello的s2对象,它将保持原样。
1 2 3 4 5 6 7 | __________ | | s2 ---->| hello | |__________| out.println(s1); // o/p: stack out.println(s2); // o/p: hello |
由于字符串是不可变的,所以Java虚拟机不允许我们用它的方法修改字符串S1。它将在池中创建所有新的字符串对象,如下所示。
1 2 3 4 5 6 7 8 9 10 | s1.concat(" overflow"); ___________________ | | s1.concat ----> | stack overflow | |___________________| out.println(s1); // o/p: stack out.println(s2); // o/p: hello out.println(s1.concat); // o/p: stack overflow |
注意,如果字符串将是可变的,那么输出将是
1 | out.println(s1); // o/p: stack overflow |
现在您可能会惊讶为什么String有这样的方法来修改Cuto()。下面的片段会消除你的困惑。
1 | s1 = s1.concat(" overflow"); |
这里,我们将字符串的修改值赋值返回到S1引用。
1 2 3 4 5 6 7 8 | ___________________ | | s1 ---->| stack overflow | |___________________| out.println(s1); // o/p: stack overflow out.println(s2); // o/p: hello |
这就是为什么Java将字符串定义为最终类的原因,否则任何人都可以修改和更改字符串的值。希望这会有点帮助。
字符串不会更改,对它的引用将更改。您将不可变性与
关于问题的替换部分,请尝试以下操作:
1 2 3 4 5 6 |
不变性意味着实例化对象的值不能更改,您永远不能将"hello"转换为"help!".
变量str是对一个对象的引用,当您为str分配一个新值时,您不会更改它引用的对象的值,而是引用另一个对象。
虽然Java试图忽略它,但EDCOX1的0个""只不过是一个指针。这意味着当你第一次写
用途:
1 2 3 |
如果在这里看到,我使用
1 2 3 | StringBuilder sb = new StringBuilder("New String"); sb.append(" Added String"); System.out.println("StringBuilder reference ----->"+sb);// Output: StringBuilder reference -----> New String Added String |
字符串类是不可变的,不能更改不可变对象的值。但在字符串的情况下,如果更改字符串的值,它将在字符串池中创建新的字符串,而不是对该值的字符串引用。所以字符串是不可变的。举个例子,
它将创建一个字符串"Missippi",并将其添加到字符串池中所以现在str指向密西西比。
1 2 |
但在上述操作之后,另一个字符串将被创建为"m!SS!SS!PP!"它将被添加到字符串池中。和现在str指向m!SS!SS!PP!不是密西西比州。
因此,通过这种方式,当您更改字符串对象的值时,它将再创建一个对象,并将其添加到字符串池中。
再举一个例子
上面这三行将向字符串池添加三个字符串对象。
1)你好,BR/>2)世界< BR>3)地狱世界
就像莱纳斯·托尔瓦兹说的:
Talk is cheap. Show me the code
看看这个:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public class Test{ public static void main(String[] args){ String a ="Mississippi"; String b ="Mississippi";//String immutable property (same chars sequence), then same object String c = a.replace('i','I').replace('I','i');//This method creates a new String, then new object String d = b.replace('i','I').replace('I','i');//At this moment we have 3 String objects, a/b, c and d String e = a.replace('i','i');//If the arguments are the same, the object is not affected, then returns same object System.out.println("a==b?" + (a==b) ); // Prints true, they are pointing to the same String object System.out.println("a:" + a ); System.out.println("b:" + b ); System.out.println("c==d?" + (c==d) ); // Prints false, a new object was created on each one System.out.println("c:" + c ); // Even the sequence of chars are the same, the object is different System.out.println("d:" + d ); System.out.println("a==e?" + (a==e) ); // Same object, immutable property } } |
输出是
1 2 3 4 5 6 7 | a==b? true a: Mississippi b: Mississippi c==d? false c: Mississippi d: Mississippi a==e? true |
所以,记住两件事:
- 字符串是不可变的,直到应用一个操作并创建新的方法(C&D D案例)。
- 如果两个参数相同,则替换方法返回相同的字符串对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public final class String_Test { String name; List<String> list=new ArrayList<String>(); public static void main(String[] args) { String_Test obj=new String_Test(); obj.list.add("item");//List will point to a memory unit- i.e will have one Hashcode value #1234 List<String> list2=obj.list; //lis1 also will point to same #1234 obj.list.add("new item");//Hashcode of list is not altered- List is mutable, so reference remains same, only value in that memory location changes String name2=obj.name="Myname"; // name2 and name will point to same instance of string -Hashcode #5678 obj.name ="second name";// String is Immutable- New String HAI is created and name will point to this new instance- bcoz of this Hashcode changes here #0089 System.out.println(obj.list.hashCode()); System.out.println(list2.hashCode()); System.out.println(list3.hashCode()); System.out.println("==========="); System.out.println(obj.name.hashCode()); System.out.println(name2.hashCode()); } } |
会制造出这样的东西
十四亿一千九百三十五万八千三百六十九十四亿一千九百三十五万八千三百六十九
十万三千零五十六六千五百零七万八千七百七十七
不可变对象的目的是,一旦分配了值,就不应该更改它的值。每次尝试根据实现更改新对象时,它都会返回新对象。注意:可以使用StringBuffer而不是String来避免这种情况。
或者你可以尝试:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
这将显示哈希代码如何更改。
字符串是不可变的。这意味着我们只能更改引用。
1 2 3 4 5 6 7 8 |
在爪哇中,对象通常通过引用访问。在您的代码中,str是一个引用,它首先被分配给"hello"(一个自动创建的对象或从常量池中提取的对象),然后您分配给另一个对象"help!"同一参考。要注意的一点是,引用是相同的,并且是修改过的,但是对象是不同的。在代码中还有一件事,您访问了三个对象,
调用new string()将创建一个新对象,即使它存在于字符串池中,因此通常不应使用它。要将从新字符串()创建的字符串放入字符串池,可以尝试使用
我希望这有帮助。
对于那些想知道如何打破爪哇字符串不变性的人…
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | import java.lang.reflect.Field; public class StringImmutability { public static void main(String[] args) { String str1 ="I am immutable"; String str2 = str1; try { Class str1Class = str1.getClass(); Field str1Field = str1Class.getDeclaredField("value"); str1Field.setAccessible(true); char[] valueChars = (char[]) str1Field.get(str1); valueChars[5] = ' '; valueChars[6] = ' '; System.out.println(str1 == str2); System.out.println(str1); System.out.println(str2); } catch (NoSuchFieldException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } |
产量
1 2 3 | true I am mutable I am mutable |
Java中的字符串在不可变的和最终的意思是它不能被改变或修改:
案例1:
1 2 3 4 5 6 7 |
Output: ABC
Reason: The object reference str is not changed in fact a new object
"DEF" is created which is in the pool and have no reference at all
(i.e lost).
案例2:
1 2 3 4 5 6 7 |
Output: ABCDEF
Reason: In this case str is now referring to a new object"ABCDEF"
hence it prints ABCDEF i.e. previous str object"ABC" is lost in pool with no reference.
字符串是不可变的,意味着不能更改对象本身,但可以更改对对象的引用。当您调用a="ty"时,实际上是将a的引用更改为字符串"ty"创建的新对象。更改对象意味着使用其方法更改其某个字段(或者字段是公共的,不是最终的,这样可以从外部更新它们,而无需通过方法访问它们),例如:
1 2 3 | Foo x = new Foo("the field"); x.setField("a new field"); System.out.println(x.getField()); // prints"a new field" |
在不可变类(声明为final,以防止通过继承进行修改)中(其方法不能修改其字段,而且字段始终是私有的,建议为final),例如string,不能更改当前字符串,但可以返回新字符串,即:
1 2 3 4 5 |
这里不可变意味着实例可以指向其他引用,但字符串的原始内容不会在原始引用处修改。让我用你给出的第一个例子来解释。第一个str指的是"你好",直到现在还可以。第二次指向"帮助!".这里str开始指向"帮助!""hello"字符串的引用丢失,我们无法恢复。
实际上,当str试图修改现有内容时,将生成另一个新字符串,str将开始指向该引用。所以我们看到原始引用处的字符串没有被修改,但在其引用处是安全的,并且对象的实例开始指向不同的引用,所以不可变性是保持不变的。
答案太晚了,但是想从Java中的String类的作者那里给出一个简洁的消息
Strings are constant; their values cannot be changed after they are
created. String buffers support mutable strings. Because String
objects are immutable they can be shared.
从这个文档中可以得出,任何更改字符串、返回不同对象(可以是新的、实习的和旧的)的操作。关于这个的不那么微妙的提示应该来自函数签名。想想看,"为什么他们让一个对象上的函数返回一个对象而不是状态?".
1 |
还有一个使此行为显式的源(来自替换函数文档)
Returns a new string resulting from replacing all occurrences of
oldChar in this string with newChar.
资料来源:http://DOCS.Oracle .COM/JavaSe/ 7 /DOCS/API/Java/Lang/String。HTML替换(char,%20char)
- 作者李博因顿
- 作者阿瑟·范霍夫
- 作者Martin Buchholz
- 作者乌尔夫·齐比斯
来源:字符串的javadoc。
对象字符串-方法本身被设置为"不可变"。此操作不产生更改:"letters.replace"("bbb","aaa");
但是,分配数据确实会导致字符串内容发生更改:
1 2 3 4 5 6 7 8 |
//字符串对象的哈希代码不变。
因为字符串是不可变的,所以如果不将函数的返回值赋给字符串,则不会发生更改。因此,在您的问题中,请将交换函数的返回值赋给S。
s=交换(s,n1,n2);则字符串s的值将更改。
在编写程序以获取一些排列字符串时,我也得到了不变的值(虽然它没有给出所有排列,但这是为了回答您的问题)
下面是一个例子。
1 2 3 4 5 6 7 8 9 | > import java.io.*; public class MyString { public static void > main(String []args)throws IOException { BufferedReader br=new > BufferedReader(new InputStreamReader(System.in)); String > s=br.readLine().trim(); int n=0;int k=0; while(n!=s.length()) { > while(k<n){ swap(s,k,n); System.out.println(s); swap(s,k,n); k++; } > n++; } } public static void swap(String s,int n1,int n2) { char temp; > temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s); > sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString(); > } } |
但我没有从上面的代码中获取字符串的排列值,因此我将swap函数的返回值分配给了字符串,并得到了字符串的更改值。在分配返回值之后,我得到了字符串的排列值。
1 2 3 4 5 6 7 | /import java.util.*; import java.io.*; public class MyString { public static void main(String []args)throws IOException{ BufferedReader br=new BufferedReader(new InputStreamReader(System.in)); String s=br.readLine().trim(); int n=0;int k=0; while(n!=s.length()){ while(k<n){ s=swap(s,k,n); System.out.println(s); s=swap(s,k,n); k++; } n++; } } public static String swap(String s,int n1,int n2){ char temp; temp=s.charAt(n1); StringBuilder sb=new StringBuilder(s); sb.setCharAt(n1,s.charAt(n2)); sb.setCharAt(n2,temp); s=sb.toString(); return s; } } |
如果
您可以有多个指针字符串变量来指向hello字符串。
但如果hello是char数组,那么可以将hello改为hillo。如,
1 2 | char[] charArr = 'HELLO'; char[1] = 'I'; //you can do this |
编程语言具有不变的数据变量,因此它可以用作键、值对中的键。
我可以说,不变性是您不能更改字符串本身。假设您有字符串x,其值为"abc"。现在您不能更改字符串,也就是说,您不能更改"abc"中的任何字符。
如果必须更改字符串中的任何字符,可以使用字符数组并对其进行变异,或者使用StringBuilder。
1 2 3 4 5 6 7 8 9 10 |
输出:
1 2 | hj sj |