请考虑以下示例。
String str = new String();
str = "Hello";
System.out.println(str); //Prints Hello
str = "Help!";
System.out.println(str); //Prints Help!
现在,在Java中,String对象是不可变的。那么为什么可以为对象str
赋值“帮助!”。这与Java中字符串的不变性相矛盾吗?任何人都可以向我解释一下不变性的确切概念吗?
编辑:
确定。我现在得到它,但只是一个后续问题。以下代码如何:
String str = "Mississippi";
System.out.println(str); // prints Mississippi
str = str.replace("i", "!");
System.out.println(str); // prints M!ss!ss!pp!
这是否意味着再次创建了两个对象(“Mississippi”和“M!ss!ss!pp!”)并且引用str
在replace()
方法之后指向另一个对象?
答案 0 :(得分:286)
str
不是对象,它是对象的引用。 "Hello"
和"Help!"
是两个不同的String
个对象。因此,str
指向字符串。您可以更改指向的内容,但不能更改指向的内容。
获取此代码,例如:
String s1 = "Hello";
String s2 = s1;
// s1 and s2 now point at the same string - "Hello"
现在,我们可以对s1
做任何 1 ,这会影响s2
的价值。它们引用相同的对象 - 字符串"Hello"
- 但该对象是不可变的,因此无法更改。
如果我们这样做:
s1 = "Help!";
System.out.println(s2); // still prints "Hello"
这里我们看到变异对象和更改引用之间的区别。 s2
仍然指向与我们最初设置s1
指向的对象相同的对象。将s1
设置为"Help!"
只会更改引用,而它最初引用的String
对象保持不变。
如果字符串是可变的,我们可以这样做:
String s1 = "Hello";
String s2 = s1;
s1.setCharAt(1, 'a'); // Fictional method that sets character at a given pos in string
System.out.println(s2); // Prints "Hallo"
编辑以响应OP的编辑:
如果你看一下source code for String.replace(char,char)(也可以在你的JDK安装目录中的src.zip中找到 - 只要你想知道某些东西是如何工作的,那么你可以看一下专业技巧)你可以看到它的作用是以下内容:
oldChar
,请复制当前字符串,其中所有出现的oldChar
都替换为newChar
。 oldChar
,则返回当前字符串。是的,"Mississippi".replace('i', '!')
会创建一个新的String
对象。以下是:
String s1 = "Mississippi";
String s2 = s1;
s1 = s1.replace('i', '!');
System.out.println(s1); // Prints "M!ss!ss!pp!"
System.out.println(s2); // Prints "Mississippi"
System.out.println(s1 == s2); // Prints "false" as s1 and s2 are two different objects
您现在的作业是看看上面的代码在您将s1 = s1.replace('i', '!');
更改为s1 = s1.replace('Q', '!');
时的作用:)
1 实际上, 可以改变字符串(以及其他不可变对象)。它需要反思并且非常非常危险,除非你真的对破坏程序感兴趣,否则永远不要使用它。
答案 1 :(得分:23)
str
引用的对象可以更改,但实际的String
对象本身不能更改。
包含字符串String
和"Hello"
的{{1}}个对象无法更改其值,因此它们是不可变的。
"Help!"
个对象的不变性并不意味着指向该对象的引用不能改变。
可以阻止String
引用更改的一种方法是将其声明为str
:
final
现在,尝试将另一个final String STR = "Hello";
分配给String
将导致编译错误。
答案 2 :(得分:9)
Light_handle我建议您阅读Cup Size -- a story about variables和Pass-by-Value Please (Cup Size continued)。阅读上述帖子时,这将有很大帮助。
你读过它们吗?是。好。
String str = new String();
这会创建一个名为“str
”的新“遥控器”,并将其设置为值new String()
(或""
)。
e.g。在内存中创建:
str --- > ""
str = "Hello";
然后更改遥控器“str
”,但不会修改原始字符串""
。
e.g。在内存中创建:
str -+ ""
+-> "Hello"
str = "Help!";
然后更改遥控器“str
”,但不会修改原始字符串""
或遥控器当前指向的对象。
e.g。在内存中创建:
str -+ ""
| "Hello"
+-> "Help!"
答案 3 :(得分:7)
让我们把它分成几个部分
String s1 = "hello";
此语句创建包含 hello 的字符串并占用内存中的空间,即常量字符串池,并将其分配给引用对象 s1
String s2 = s1;
此声明将相同的字符串 hello 分配给新参考 s2
__________
| |
s1 ---->| hello |<----- s2
|__________|
两个引用都指向相同的字符串,因此输出相同的值,如下所示。
out.println(s1); // o/p: hello
out.println(s2); // o/p: hello
虽然字符串 不可变,但可以进行分配,因此 s1 现在会引用新值堆栈
s1 = "stack";
__________
| |
s1 ---->| stack |
|__________|
但是指向 hello 的 s2 对象会是什么样的呢。
__________
| |
s2 ---->| hello |
|__________|
out.println(s1); // o/p: stack
out.println(s2); // o/p: hello
由于String是不可变的 Java虚拟机不允许我们通过其方法修改字符串s1 。它将在池中创建所有新的String对象,如下所示。
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
注意,如果String是可变的,那么输出就是
out.println(s1); // o/p: stack overflow
现在你可能会惊讶为什么String要修改像 concat()这样的方法。以下片段将清除您的困惑。
s1 = s1.concat(" overflow");
这里我们将字符串的修改值分配回 s1 引用。
___________________
| |
s1 ---->| stack overflow |
|___________________|
out.println(s1); // o/p: stack overflow
out.println(s2); // o/p: hello
这就是为什么Java决定将 String作为最终类的原因,否则任何人都可以修改和更改string的值。 希望这会有所帮助。
答案 4 :(得分:6)
str
首次引用的字符串对象未被更改,您所做的只是make str
引用新的字符串对象。
答案 5 :(得分:5)
关于问题的替换部分,请尝试以下方法:
String str = "Mississippi";
System.out.println(str); //Prints Mississippi
String other = str.replace("i", "!");
System.out.println(str); //still prints Mississippi
System.out.println(other); // prints M!ss!ss!pp!
答案 6 :(得分:5)
String不会改变,对它的引用会。您将不可变性与final
字段的概念混为一谈。如果某个字段被声明为final
,则一旦分配该字段,就无法重新分配。
答案 7 :(得分:3)
不变性意味着实例化对象的值不能改变,你永远不能将“Hello”变成“Help!”。
变量str是对象的引用,当你为str指定一个新值时,你没有改变它引用的对象的值,你引用的是另一个对象。
答案 8 :(得分:3)
虽然java试图忽略它,但str
只不过是一个指针。这意味着当您第一次编写str = "Hello";
时,会创建一个str
指向的对象。当您通过编写str
重新分配str = "Help!";
时,会创建一个新对象,并且只要java感觉它就会收集旧的"Hello"
对象。
答案 9 :(得分:2)
使用:
String s = new String("New String");
s.concat(" Added String");
System.out.println("String reference -----> "+s); // Output: String reference -----> New String
如果你看到这里,我使用concat
方法来改变原始字符串,即&#34; New String&#34;用字符串&#34;添加了字符串&#34;,但我仍然得到了前面的输出,因此它证明你不能改变String类对象的引用,但是如果你通过StringBuilder类做这件事就可以了。它列在下面。
StringBuilder sb = new StringBuilder("New String");
sb.append(" Added String");
System.out.println("StringBuilder reference -----> "+sb);// Output: StringBuilder reference -----> New String Added String
答案 10 :(得分:2)
String类是不可变的,并且您不能更改不可变对象的值。 但是在String的情况下,如果你更改字符串的值,它将在字符串池中创建新字符串,而不是字符串引用该值而不是旧值。所以通过这种方式字符串是不可变的。 让我们举个例子,
String str = "Mississippi";
System.out.println(str); // prints Mississippi
它将创建一个字符串&#34; Mississippi&#34; 并将其添加到字符串池 所以现在str指向密西西比州。
str = str.replace("i", "!");
System.out.println(str); // prints M!ss!ss!pp!
但经过上述操作后, 将创建另一个字符串&#34; M!ss!ss!pp!&#34; 它将被添加到String池中。和 现在str指向M!ss!ss!pp !,而不是Mississippi。
所以通过这种方式当你改变字符串对象的值时,它将再创建一个对象并将其添加到字符串池中。
让我们再举一个例子
String s1 = "Hello";
String s2 = "World";
String s = s1 + s2;
以上三行将向字符串池中添加三个字符串对象
1)你好
2)世界
3)HelloWorld
答案 11 :(得分:2)
像Linus Tolvards所说:
谈话很便宜。给我看代码
看看这个:
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
}
}
输出
a==b? true
a: Mississippi
b: Mississippi
c==d? false
c: Mississippi
d: Mississippi
a==e? true
所以,请记住两件事:
答案 12 :(得分:0)
我将用一个简单的示例进行解释
考虑任何字符数组:例如char a [] = {'h','e','l','l','o'}; 和一个字符串: 字符串s =“ hello”;
在字符数组上,我们可以执行操作,例如使用迭代数组仅打印最后三个字母; 但是在字符串中,我们必须新建一个String对象并复制所需的子字符串,其地址将在新的字符串对象中。
例如
***String s="hello";
String s2=s.substrig(0,3);***
因此s2将带有“ hel”;
答案 13 :(得分:0)
如果HELLO
是您的字符串,则不能将HELLO
更改为HILLO
。此属性称为不变性属性。
您可以具有多个指针String变量来指向HELLO String。
但是,如果HELLO是char Array,则可以将HELLO更改为HILLO。例如
char[] charArr = 'HELLO';
char[1] = 'I'; //you can do this
编程语言具有不变的数据变量,因此可以用作键,值对中的键。
答案 14 :(得分:0)
Object string - 方法本身是“不可变的”。 此操作不会产生任何更改:“letters.replace(”bbb“,”aaa“);”
但是,分配数据确实会导致更改字符串内容的更改:
letters = "aaa";
letters=null;
System.out.println(letters);
System.out.println(oB.hashCode());
System.out.println(letters);
letters = "bbbaaa";
System.out.println(oB.hashCode());
System.out.println(letters);
//字符串Object的哈希码不会改变。
答案 15 :(得分:0)
答案已经很晚了,但是希望在Java
中添加来自String类作者的简明信息字符串是不变的;他们的价值观无法改变 创建。字符串缓冲区支持可变字符串。因为String 对象是不可变的,可以共享。
可以从此文档中获得任何更改字符串的内容,返回不同的对象(可以是新的或实习的和旧的)。 关于这个的不那么微妙的提示应该来自函数签名。 想想看,'为什么他们在一个对象上创建一个函数而不是状态?'。
STORED AS TEXTFILE
还有一个使这种行为明确的来源(来自替换功能文档)
返回替换所有出现的新字符串 带有newChar的字符串中的oldChar。
来源:https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#replace(char,%20char)
来源:String的JavaDoc。
答案 16 :(得分:0)
此处不变性意味着实例可以指向其他引用,但不会在原始引用处修改字符串的原始内容。 让我通过你给出的第一个例子来解释。 第一个str指的是“你好”,这是好的。 第二次它指向“帮助!”。 这里str开始指向“帮助!”并且“Hello”字符串的引用丢失了,我们无法取回它。
实际上,当str尝试修改现有内容时,将生成另一个新字符串,str将开始指向该引用。 因此,我们看到原始引用处的字符串未被修改,但在其引用时是安全的,并且对象的实例开始指向不同的引用,因此保持不变性。
答案 17 :(得分:0)
String是不可变的意味着您无法更改对象本身,但您可以更改对象的引用。当你调用a =“ty”时,实际上是将a的引用更改为由字符串文字“ty”创建的新对象。更改对象意味着使用其方法更改其中一个字段(或者字段是公共字段而不是最终字段,以便可以从外部更新它们而无需通过方法访问它们),例如:
Foo x = new Foo("the field");
x.setField("a new field");
System.out.println(x.getField()); // prints "a new field"
虽然在一个不可变类中(声明为final,以防止通过继承进行修改)(其方法不能修改其字段,并且字段始终是私有的并且建议为final),例如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"
答案 18 :(得分:0)
或者您可以尝试:
public class Tester
{
public static void main(String[] args)
{
String str = "Mississippi";
System.out.println(str); // prints Mississippi
System.out.println(str.hashCode());
str = str.replace("i", "!");
System.out.println(str); // prints M!ss!ss!pp!
System.out.println(str.hashCode());
}
}
这将显示哈希码如何更改。
答案 19 :(得分:0)
不变性我可以说是你无法改变String本身。假设你有String x,其值为“abc”。现在你不能改变字符串,也就是说,你不能改变“abc”中的任何字符。
如果你必须更改String中的任何字符,你可以使用字符数组并使其变异或使用StringBuilder。
String x = "abc";
x = "pot";
x = x + "hj";
x = x.substring(3);
System.out.println(x);
char x1[] = x.toCharArray();
x1[0] = 's';
String y = new String(x1);
System.out.println(y);
输出:
hj
sj
答案 20 :(得分:0)
字符串是不可变。这意味着我们只能更改参考。
String a = "a";
System.out.println("String a is referencing to "+a); // Output: a
a.concat("b");
System.out.println("String a is referencing to "+a); // Output: a
a = a.concat("b");
System.out.println("String a has created a new reference and is now referencing to "+a); // Output: ab
答案 21 :(得分:0)
在Java中,通常通过引用访问对象。在您的代码中,str是一个引用,首先分配给“Hello”(自动创建的对象或从constant pool获取),然后您分配了另一个对象“Help!”相同的参考。需要注意的一点是参考是相同的和修改的,但对象是不同的。您的代码中还有一件事就是访问了三个对象,
调用new String()即使它存在于字符串池中也会创建一个新对象,因此通常不应该使用它。要将从新String()创建的字符串放入字符串池,您可以尝试intern()
方法。
我希望这会有所帮助。
答案 22 :(得分:0)
对于那些想知道如何在Java中破坏String不变性的人......
<强>代码强>
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();
}
}
}
<强>输出强>
true
I am mutable
I am mutable
答案 23 :(得分:-1)
Java中的String in Immutable和Final只是意味着它无法更改或修改:
案例1:
class TestClass{
public static void main(String args[]){
String str = "ABC";
str.concat("DEF");
System.out.println(str);
}
}
输出:ABC
原因:对象引用str实际上并未更改为新对象 &#34; DEF&#34;已创建,它在池中,根本没有引用 (即丢失)。
案例2:
class TestClass{
public static void main(String args[]){
String str="ABC";
str=str.concat("DEF");
System.out.println(str);
}
}
输出:ABCDEF
原因:在这种情况下,str现在引用一个新对象&#34; ABCDEF&#34; 因此它打印ABCDEF,即先前的str对象&#34; ABC&#34;在没有参考的游泳池中丢失。
答案 24 :(得分:-1)
因为String是不可变的,所以如果你不将函数的返回值赋值给string.so,你的问题会将swap函数返回值赋值给s。
s = swap(s,n1,n2);然后字符串s的值将改变。
当我编写程序来获取一些排列字符串时,我也得到了未改变的值(尽管它没有给出所有的排列,但这是例如回答你的问题)
这是一个例子。
> 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();
> } }
但我没有从上面的代码中获取字符串的置换值。所以我将交换函数的返回值赋给字符串并获得了字符串的更改值。在分配返回值后,我得到了字符串的置换值。
/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; } }
答案 25 :(得分:-1)
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());
}
}
会产生这样的东西
1419358369 1419358369
103056 65078777
不可变对象的用途是,一旦分配了它的值就不要更改。 每当您尝试根据实现更改对象时,它将返回新对象。 注意:可以使用Stringbuffer而不是string来避免这种情况。
最后一个问题::您将在字符串池中有一个引用和2个字符串。 除非引用将指向m!ss!ss!pp!