一個簡單的字符串比較,如下四種寫法有什麼區別呢?java
寫法一:segmentfault
public class TestClass { public void test() { "str1".equals("str2"); } public static void main(String[] args) { new TestClass().test(); } }
寫法二:工具
public class TestClass { public void test() { String str = "str1"; str.equals("str2"); } public static void main(String[] args) { new TestClass().test(); } }
寫法三:性能
public class Constans { public static final String str1 = "str1"; } public class TestClass { public void test() { String str = Constans.str1; str.equals("str2"); } public static void main(String[] args) { new TestClass().test(); } }
寫法四:優化
public class TestClass { public void test() { String str = "str1"; String st2 = "str2"; str.equals(st2); } public static void main(String[] args) { new TestClass().test(); } }
以上四種寫法基本上同樣,但若是咱們非要摳一下區別呢,咱們經過javap -c -l TestClass.class
來看一下它們的區別。this
寫法一:spa
Compiled from "TestClass.java" public class com.example.demo.compile.TestClass { public com.example.demo.compile.TestClass(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/example/demo/compile/TestClass; public void test(); Code: 0: ldc #2 // String str1 2: ldc #3 // String str2 4: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 7: pop 8: return LineNumberTable: line 11: 0 line 12: 8 LocalVariableTable: Start Length Slot Name Signature 0 9 0 this Lcom/example/demo/compile/TestClass; public static void main(java.lang.String[]); Code: 0: new #5 // class com/example/demo/compile/TestClass 3: dup 4: invokespecial #6 // Method "<init>":()V 7: invokevirtual #7 // Method test:()V 10: return LineNumberTable: line 15: 0 line 16: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 args [Ljava/lang/String; }
重點關注 test()
下的Code
那一段。code
寫法二:blog
Compiled from "TestClass.java" public class com.example.demo.compile.TestClass { public com.example.demo.compile.TestClass(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/example/demo/compile/TestClass; public void test(); Code: 0: ldc #2 // String str1 將常量「str1」壓入棧 2: astore_1 // 將棧中的「str1」pop出,賦值給str 3: aload_1 // 將str的引用值壓入棧 4: ldc #3 // String str2 6: invokevirtual #4 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 9: pop 10: return LineNumberTable: line 11: 0 line 12: 3 line 13: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 this Lcom/example/demo/compile/TestClass; 3 8 1 str Ljava/lang/String; public static void main(java.lang.String[]); Code: 0: new #5 // class com/example/demo/compile/TestClass 3: dup 4: invokespecial #6 // Method "<init>":()V 7: invokevirtual #7 // Method test:()V 10: return LineNumberTable: line 16: 0 line 17: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 args [Ljava/lang/String; }
能夠看到,在test
方法裏,寫法二
比寫法一
多了對str
局部變量的操做。
關於方法裏的幾個命令的做用,見下圖:
圖片來源:《大話+圖說:Java字節碼指令——只爲讓你懂》圖片
寫法三:
Compiled from "TestClass.java" public class com.example.demo.compile.TestClass { public com.example.demo.compile.TestClass(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/example/demo/compile/TestClass; public void test(); Code: 0: ldc #3 // String str1 2: astore_1 3: aload_1 4: ldc #4 // String str2 6: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 9: pop 10: return LineNumberTable: line 11: 0 line 12: 3 line 13: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 this Lcom/example/demo/compile/TestClass; 3 8 1 str Ljava/lang/String; public static void main(java.lang.String[]); Code: 0: new #6 // class com/example/demo/compile/TestClass 3: dup 4: invokespecial #7 // Method "<init>":()V 7: invokevirtual #8 // Method test:()V 10: return LineNumberTable: line 16: 0 line 17: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 args [Ljava/lang/String; }
寫法二
和寫法三
徹底同樣,這是爲何呢?
咱們用反編譯工具打開寫法三
的class
文件。
從class
文件反編譯後了內容上看,寫法三
的String str = Constans.str1
在編譯後會被轉成String str = "str1"
,轉以後跟寫法二
同樣了。
java在編譯的時候,會作一些性能上的優化,好比:爲了減小運行時的棧調用,將var = 常量字段
直接編譯成var = 常量字段的值
。
寫法四:
Compiled from "TestClass.java" public class com.example.demo.compile.TestClass { public com.example.demo.compile.TestClass(); Code: 0: aload_0 1: invokespecial #1 // Method java/lang/Object."<init>":()V 4: return LineNumberTable: line 8: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/example/demo/compile/TestClass; public void test(); Code: 0: ldc #3 // String str1 2: astore_1 3: ldc #4 // String str2 5: astore_2 6: aload_1 7: aload_2 8: invokevirtual #5 // Method java/lang/String.equals:(Ljava/lang/Object;)Z 11: pop 12: return LineNumberTable: line 11: 0 line 12: 3 line 13: 6 line 14: 12 LocalVariableTable: Start Length Slot Name Signature 0 13 0 this Lcom/example/demo/compile/TestClass; 3 10 1 str Ljava/lang/String; 6 7 2 st2 Ljava/lang/String; public static void main(java.lang.String[]); Code: 0: new #6 // class com/example/demo/compile/TestClass 3: dup 4: invokespecial #7 // Method "<init>":()V 7: invokevirtual #8 // Method test:()V 10: return LineNumberTable: line 17: 0 line 18: 10 LocalVariableTable: Start Length Slot Name Signature 0 11 0 args [Ljava/lang/String; }
寫法四
是在寫法二
基礎上,又多了對str2
局部變量的操做。