java
String str = "abc"; String str2 = "a"; String str3 = "bc"; System.out.println(str == str2 + str3); //fasle System.out.println(str == "a" + str3); //fasle System.out.println(str == "a" + "bc"); //true
第一个与第二个结果为什么会false?
第三个是2个字符串直接拼接会在常量池中生成新的对象(当对象已存在时候直接返回对象地址),所以str
的指向的对象和字符串拼接结果
所指向的对象是相同的,不知道这种理解是否有问题?
string str = "abc";
string str2 = "a";
string str3 = "bc";
Console.WriteLine(str == str2 + str3); //true
Console.WriteLine(str == "a" + str3); //true
Console.WriteLine(str == "a" + "bc"); //true
然而C#中的结果都为true,这2种语言对string类型的维护上有什么不同?求大神解答
@沙渺 正解
分析一下字节码就知道了。
java
// Compiled from StringDemo.java (version 1.5 : 49.0, super bit) public class collections.StringDemo { // Method descriptor #6 ()V // Stack: 1, Locals: 1 public StringDemo(); 0 aload_0 [this] 1 invokespecial java.lang.Object() [8] 4 return Line numbers: [pc: 0, line: 6] Local variable table: [pc: 0, pc: 5] local: this index: 0 type: collections.StringDemo // Method descriptor #15 ([Ljava/lang/String;)V // Stack: 5, Locals: 4 public static void main(java.lang.String[] args); 0 ldc <String "abc"> [16] 2 astore_1 [str] 3 ldc <String "a"> [18] 5 astore_2 [str2] 6 ldc <String "bc"> [20] 8 astore_3 [str3] 9 getstatic java.lang.System.out : java.io.PrintStream [22] 12 aload_1 [str] 13 new java.lang.StringBuilder [28] 16 dup 17 aload_2 [str2] 18 invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [30] 21 invokespecial java.lang.StringBuilder(java.lang.String) [36] 24 aload_3 [str3] 25 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [39] 28 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [43] 31 if_acmpne 38 34 iconst_1 35 goto 39 38 iconst_0 39 invokevirtual java.io.PrintStream.println(boolean) : void [47] 42 getstatic java.lang.System.out : java.io.PrintStream [22] 45 aload_1 [str] 46 new java.lang.StringBuilder [28] 49 dup 50 ldc <String "a"> [18] 52 invokespecial java.lang.StringBuilder(java.lang.String) [36] 55 aload_3 [str3] 56 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [39] 59 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [43] 62 if_acmpne 69 65 iconst_1 66 goto 70 69 iconst_0 70 invokevirtual java.io.PrintStream.println(boolean) : void [47] 73 getstatic java.lang.System.out : java.io.PrintStream [22] 76 aload_1 [str] 77 ldc <String "abc"> [16] 79 if_acmpne 86 82 iconst_1 83 goto 87 86 iconst_0 87 invokevirtual java.io.PrintStream.println(boolean) : void [47] 90 return Line numbers: [pc: 0, line: 8] [pc: 3, line: 9] [pc: 6, line: 10] [pc: 9, line: 11] [pc: 42, line: 12] [pc: 73, line: 13] [pc: 90, line: 15] Local variable table: [pc: 0, pc: 91] local: args index: 0 type: java.lang.String[] [pc: 3, pc: 91] local: str index: 1 type: java.lang.String [pc: 6, pc: 91] local: str2 index: 2 type: java.lang.String [pc: 9, pc: 91] local: str3 index: 3 type: java.lang.String }
现在一个个的分析:
ldc表示将int, float或String型常量值从常量池中推送至栈顶.
astore表示将栈顶引用型数值存入指定本地变量
那么
java
ldc <String "abc"> [16] 2 astore_1 [str]
表示 abc 在运行时常量池中间取出,并且将引用赋给str变量。
同理,str1,str2都一样。
那么这就说明一个问题:
类似于String str1="dddd"说明"dddd"字符串存在运行时常量池中,而不是存在堆中间。
然后看s2+s3:
java
new java.lang.StringBuilder [28] 16 dup 17 aload_2 [str2] 18 invokestatic java.lang.String.valueOf(java.lang.Object) : java.lang.String [30] 21 invokespecial java.lang.StringBuilder(java.lang.String) [36] 24 aload_3 [str3] 25 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [39] 28 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [43]
java运算符重载了加法。也就是字符串的加号是通过StringBuilder的append和toString来完成的。查看toString的源码:
java
public String toString() { // Create a copy, don't share the array return new String(value, 0, count); }
很显然是新生成了一个String,引用地址不一样,那么"=="判断当然为false。
然后看
java
"a" + str3
java
ldc <String "a"> [18] 52 invokespecial java.lang.StringBuilder(java.lang.String) [36] 55 aload_3 [str3] 56 invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [39] 59 invokevirtual java.lang.StringBuilder.toString() : java.lang.String [43] 62 if_acmpne 69 65 iconst_1 ``` 不用看。既然是StringBuilder那么也一定是false了、 最后"a"+"bc" ```java iconst_0 70 invokevirtual java.io.PrintStream.println(boolean) : void [47] 73 getstatic java.lang.System.out : java.io.PrintStream [22] 76 aload_1 [str] 77 ldc <String "abc"> [16] 79 if_acmpne 86
说明,对于这种情况,java编译器做了优化。直接存储在运行时常量池中间。那么当然为true。
那么C#。。。。不知道他们运行机制是咋样的。
java 编译期将"a"+"bc"优化为"abc"了
http://stackoverflow.com/questions/767372/java-string-equals-versus
http://stackoverflow.com/questions/1659097/why-would-you-use-string-eq...
Java中String不是基本类型是对象啊
对于Java:
-
==
比较变量是否指向同一个引用。 -
str2 + str3
和"a" + str3
必然都是临时生成的对象,所以不等。 -
"a" + "bc"
在编译阶段被优化为"abc"
存放在字符串常量表中,常量表中相同内容的只存一次,所以引用相同,比较相等。 - “同一字符串常量只存一次”,这个优化几乎所有的编译器都会做,但这仍然是一个依赖编译器的行为。——代码的语义依赖编译器有多可怕,这是不言而喻的。
- 当然对于Java而言,
==
比较字符串肯定是一个不良实践。Don't do this.
对于C#:
- C#重载了
string
类的==
运算符(MSDN: String.Equality Operator) - 所以C#中
==
就确实表示比较字符串的内容。 - 如果硬要比较两个引用变量是否指向同一个引用,就必须写成:
System.Object.ReferenceEquals(a, b)
在这个问题上,C#的实现明显是更优的——常用操作简明,不常用操作啰嗦。从而程序编写上语义自然,不易出错。