当我们在java中使用concating两个字符串对象时,编译是如何工作的

class Simple{ public static void main(String args[]){ String s="Sachin"+" Tendulkar"; System.out.println(s);//Sachin Tendulkar } } 

编译器将其转换为:

  String s = (new StringBuilder()).append("Sachin").append(" Tendulkar").toString(); 

最后一行是什么意思?

Java编译器将在编译时将这两个字符串连接起来,这将导致以下指令

代码

 public class StringConcatInspection { public static void main(String[] args) { String s = "Sachin" + " Tendulkar"; } } 

它会编译成

  Last modified Jul 9, 2014; size 473 bytes MD5 checksum 5238429068ccae4ec199f4af970ad46e Compiled from "StringConcatInspection.java" public class StringConcatInspection SourceFile: "StringConcatInspection.java" minor version: 0 major version: 52 flags: ACC_PUBLIC, ACC_SUPER Constant pool: #1 = Methodref #4.#20 // java/lang/Object."":()V #2 = String #21 // Sachin Tendulkar #3 = Class #22 // StringConcatInspection #4 = Class #23 // java/lang/Object #5 = Utf8  #6 = Utf8 ()V #7 = Utf8 Code #8 = Utf8 LineNumberTable #9 = Utf8 LocalVariableTable #10 = Utf8 this #11 = Utf8 LStringConcatInspection; #12 = Utf8 main #13 = Utf8 ([Ljava/lang/String;)V #14 = Utf8 args #15 = Utf8 [Ljava/lang/String; #16 = Utf8 s #17 = Utf8 Ljava/lang/String; #18 = Utf8 SourceFile #19 = Utf8 StringConcatInspection.java #20 = NameAndType #5:#6 // "":()V #21 = Utf8 Sachin Tendulkar #22 = Utf8 StringConcatInspection #23 = Utf8 java/lang/Object { public StringConcatInspection(); flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 // Method java/lang/Object."":()V 4: return LineNumberTable: line 4: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this LStringConcatInspection; public static void main(java.lang.String[]); flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=2, args_size=1 0: ldc #2 // String Sachin Tendulkar 2: astore_1 3: return LineNumberTable: line 6: 0 line 7: 3 LocalVariableTable: Start Length Slot Name Signature 0 4 0 args [Ljava/lang/String; 3 1 1 s Ljava/lang/String; } 

编译器版本:1.7.0_60-ea

尊重萨钦

如果我理解正确,你会问到使用StringBuilder与普通的字符串连接有何不同(而不是某些编译器/ IDE优化)。

Java中的字符串是不可变的,这意味着一旦创建它们就无法修改它们。 因此,当您对给定字符串执行某些操作时 – 例如String.concat()String.replaceAll() ,它们乍一看它们将改变字符串的内部状态 – 您实际上是在创建一个派生的新字符串对象来自原始的不可变对象,因此必须在某处保存。

 String example = "This is a test "; example = example.trim(); // "This is a test" 

通常这样的操作并不是非常昂贵,但是如果你需要进行大量的字符串修改,那么使用这种方法最终会导致所有对象创建成本过高。

另一种方法是使用类似StringBuilder的类来表示可变字符序列。 使用此类,可以修改实例本身。 因此,与’String.concat()’相比,通过’StringBuilder.append()’方法连接的CPU /内存条件必须更便宜。 当然,一旦你完成了StringBuilder的构建,你通常会想要得到一个普通的String,因此它的toString()方法。

StringBuilder的另一个方便之处在于它使用方法链接,以便其方法返回自身,并允许您在一行中链接调用。

StringBuilder b = new StringBuilder(); 在b.append( “一”); 在b.append( “二”); 。在b.append( “三公”)追加( “四”);

这可以使某些事物更具可读性,例如

 StringBuilder sb = new StringBuilder(); sb.append(user.getLastName()).append(", ").append(user.getFirstName()); // Better than mixing String concatenation sb.append(user.getLastName() + ", " + user.getFirstName()); // Valid but defeats purpose of using StringBuilder 

当仅连接字符串文字时,编译器应重写为仅合并的文字。

 // source String s="Sachin"+" Tendulkar"; // compiler rewrite String s="Sachin Tendulkar"; 

但是,如果至少有一个非常量,那么我的理解是将使用StringBuilder来避免创建临时的一次性使用对象,这些对象只为垃圾收集器做额外的工作。

 // source String s = "Sachin"+" Tendulkar" + title; // compiler rewrite (approximately) String s = (new StringBuilder("Sachin Tendulkar")).append(title).toString(); 

无论是将文字传递给构造函数还是在两个字符串上使用.append都是我不知道的。 您可以编译代码然后尝试使用JAD进行反编译以找出JAD

根据Java语言规范,Java SE 5第3版,§15.28 :

String类型的编译时常量始终是“实例化”,以便使用String.intern方法共享唯一实例。

来自同一文件, §15.18.1 :

[串联连接运算符+]的结果是对String对象的引用(新创建, 除非表达式是编译时常量表达式(第15.28节) ),它是两个操作数字符串的串联。

强调我的。

因此,Java规范一直要求,至少从Java 5开始,通过连接常量形成的常量是内部字符串 ,因此直接优化为常量(不在运行时评估连接)。

还要注意,因为这是规范的要求, 所有兼容的实现必须实现这种实习行为; 它不仅仅是Sun / Oracle Java实现选择使用的优化。