java import 導入包時,我們需要注意什麼呢?

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你好,我是看山。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這篇文章起因是 code review 時和同事關於 import 導入聲明的分歧。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"用過 IDEA 的都知道,默認情況下,通過 import 導入類時,當數量達到設置數量(類 5 個、靜態變量 3 個),就會改爲按需導入方式,也就是使用使用*號摺疊導入。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"同事建議不要採用按需導入,要使用單類型導入 (single-type-import)。而我是覺得既然 IDEA 作爲宇宙級的 IDE,不會在這種地方出現紕漏,所以想繼續按照 IDEA 默認配置來。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"所以總結一下這兩種方式的差異。如果對 java import 不熟悉,可以從 ","attrs":{}},{"type":"link","attrs":{"href":"https://javagoal.com/java-import-package/","title":""},"content":[{"type":"text","text":"這裏","attrs":{}}]},{"type":"text","text":" 看看。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"import 的兩種導入聲明","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在 java 中,通過 import 導入類的方式有兩種:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"bulletedlist","content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"單類型導入 (single-type-import),例如 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import java.io.File","attrs":{}}],"attrs":{}},{"type":"text","text":":這種方式比較容易理解,而且大部分時候我們用的都是這種方式。通過明確指明類和接口路徑,將他們導入進來。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按需類型導入 (type-import-on-demand),例如 ","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import java.io.*","attrs":{}}],"attrs":{}},{"type":"text","text":":通過通配符","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"*","attrs":{}}],"attrs":{}},{"type":"text","text":"定義導入方式,但是並不是直接導入這個包下的所有類,而是可以導入所有類。也就是說,如果需要就導入,不需要就不導入。","attrs":{}}]}],"attrs":{}}],"attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"有如下屬性:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"java 以這樣兩種方式導入包中的任何一個","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"public","attrs":{}}],"attrs":{}},{"type":"text","text":"的類和接口(只有 public 類和接口才能被導入)","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"上面說到導入聲明僅導入聲明目錄下面的類而不導入子包,這也是爲什麼稱它們爲類型導入聲明的原因。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"導入的類或接口的簡名(simple name)具有編譯單元作用域。這表示該類型簡名可以在導入語句所在的編譯單元的任何地方使用。這並不意味着你可以使用該類型所有成員的簡名,而只能使用類型自身的簡名。例如:java.lang 包中的 public 類都是自動導入的,包括","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Math","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"System","attrs":{}}],"attrs":{}},{"type":"text","text":"類。但是,你不能使用它們的成員的簡名","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"PI()","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"gc()","attrs":{}}],"attrs":{}},{"type":"text","text":", 而必須使用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Math.PI()","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"System.gc()","attrs":{}}],"attrs":{}},{"type":"text","text":". 你不需要鍵入的是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.lang.Math.PI()","attrs":{}}],"attrs":{}},{"type":"text","text":"和","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.lang.System.gc()","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"程序員有時會導入當前包或","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.lang","attrs":{}}],"attrs":{}},{"type":"text","text":"包,這是不需要的,因爲當前包的成員本身就在作用域內,而","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.lang","attrs":{}}],"attrs":{}},{"type":"text","text":"包是自動導入的。java 編譯器會忽略這些冗餘導入聲明 (redundant import declarations)。","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"按需導入機制","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"按需類型導入在大部分情況用起來更加方便,一個通配符可以導入包下的所有類,就不用費勁寫一堆導入了。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"但是,根據能量守恆,在敲代碼時節省下來的能量,必然會在其他地方消耗。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"比如,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Date","attrs":{}}],"attrs":{}},{"type":"text","text":"類,如果完全使用按需類型導入,可以寫做","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import java.util.*","attrs":{}}],"attrs":{}},{"type":"text","text":"。當這個類恰好需要,","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"PrepareStatement","attrs":{}}],"attrs":{}},{"type":"text","text":"時,又需要加上","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import java.sql.*","attrs":{}}],"attrs":{}},{"type":"text","text":"導入,這個時候,編譯器不知道","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Date","attrs":{}}],"attrs":{}},{"type":"text","text":"類是要用","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.util","attrs":{}}],"attrs":{}},{"type":"text","text":"包裏的還是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.sql","attrs":{}}],"attrs":{}},{"type":"text","text":"裏面的了,就會報出","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Reference to 'Date' is ambiguous, both 'java.util.Date' and 'java.sql.Date' match","attrs":{}}],"attrs":{}},{"type":"text","text":"異常,也就是所說的","attrs":{}},{"type":"text","marks":[{"type":"strong","attrs":{}}],"text":"命名衝突","attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"解決辦法就是指明","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"Date","attrs":{}}],"attrs":{}},{"type":"text","text":"類的全路徑,也就是使用單類型導入:","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import java.util.Date","attrs":{}}],"attrs":{}},{"type":"text","text":"。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"除了命名衝突,還有一些不太明顯的缺點:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"編譯速度:因爲按需導入機制的特性,需要在 CLASSPATH 下找到所有符合包名的類,在編譯時會消耗性能。在小項目中,這個速度可以忽略。如果在大項目中,就會有明細差異。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"可讀性:在使用 IDE 開發過程中,我們很少會在","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import","attrs":{}}],"attrs":{}},{"type":"text","text":"中查看類的路徑。但是如果需要我們在其他環境編輯文件,比如 vim,從","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import","attrs":{}}],"attrs":{}},{"type":"text","text":"查看類的路徑就很便捷了。","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"導入不需要的類會發生什麼呢","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從理性講,java 編譯器一定會在這裏做優化,不會把不需要的導入聲明加入到 class 文件中,但是之前沒有看到哪裏有說明,所以動手做一下實驗:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"先定義 java 類:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"package cn.howardliu;\n\n// 需要用到的單類型導入\nimport java.util.Date;\n// 需要用到的按需類型導入\nimport java.math.*;\n// 不需要用到的單類型導入\nimport java.sql.PreparedStatement;\n// 不需要用到的按需類型導入\nimport java.awt.*;\n\npublic class Main {\n private Date date1;\n private BigDecimal num1;\n\n public void test(){\n Date date2 = new Date();\n BigDecimal num2 = new BigDecimal(0);\n }\n}","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"通過命令","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"javac Main.java","attrs":{}}],"attrs":{}},{"type":"text","text":"編譯,然後通過","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"javap -verbose Main.class","attrs":{}}],"attrs":{}},{"type":"text","text":"查看編譯結果:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"class"},"content":[{"type":"text","text":"Classfile /path/to/Main.class\n Last modified 2021-1-31; size 439 bytes\n MD5 checksum 81e13559f738197b4875c2c2afd6fc41\n Compiled from \"Main.java\"\npublic class cn.howardliu.Main\n minor version: 0\n major version: 52\n flags: ACC_PUBLIC, ACC_SUPER\nConstant pool:\n #1 = Methodref #7.#19 // java/lang/Object.\"\":()V\n #2 = Class #20 // java/util/Date\n #3 = Methodref #2.#19 // java/util/Date.\"\":()V\n #4 = Class #21 // java/math/BigDecimal\n #5 = Methodref #4.#22 // java/math/BigDecimal.\"\":(I)V\n #6 = Class #23 // cn/howardliu/Main\n #7 = Class #24 // java/lang/Object\n #8 = Utf8 date1\n #9 = Utf8 Ljava/util/Date;\n #10 = Utf8 num1\n #11 = Utf8 Ljava/math/BigDecimal;\n #12 = Utf8 \n #13 = Utf8 ()V\n #14 = Utf8 Code\n #15 = Utf8 LineNumberTable\n #16 = Utf8 test\n #17 = Utf8 SourceFile\n #18 = Utf8 Main.java\n #19 = NameAndType #12:#13 // \"\":()V\n #20 = Utf8 java/util/Date\n #21 = Utf8 java/math/BigDecimal\n #22 = NameAndType #12:#25 // \"\":(I)V\n #23 = Utf8 cn/howardliu/Main\n #24 = Utf8 java/lang/Object\n #25 = Utf8 (I)V\n{\n public cn.howardliu.Main();\n descriptor: ()V\n flags: ACC_PUBLIC\n Code:\n stack=1, locals=1, args_size=1\n 0: aload_0\n 1: invokespecial #1 // Method java/lang/Object.\"\":()V\n 4: return\n LineNumberTable:\n line 12: 0\n\n public void test();\n descriptor: ()V\n flags: ACC_PUBLIC\n Code:\n stack=3, locals=3, args_size=1\n 0: new #2 // class java/util/Date\n 3: dup\n 4: invokespecial #3 // Method java/util/Date.\"\":()V\n 7: astore_1\n 8: new #4 // class java/math/BigDecimal\n 11: dup\n 12: iconst_0\n 13: invokespecial #5 // Method java/math/BigDecimal.\"\":(I)V\n 16: astore_2\n 17: return\n LineNumberTable:\n line 17: 0\n line 18: 8\n line 19: 17\n}\nSourceFile: \"Main.java\"","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從 class 文件內容可以看出:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"按需類型導入方式在 class 文件中的表現形式,與按類型導入一樣,也會找到需要的類導入,不會導入包中的所有類。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"不需要的類導入聲明,最終都會被優化掉,不會出現在 class 文件中。","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"java 中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import","attrs":{}}],"attrs":{}},{"type":"text","text":"與 C 語言中的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"include","attrs":{}}],"attrs":{}},{"type":"text","text":"不同,不會將導入聲明的類寫入到 class 文件中,各自還是獨立的 class 文件。","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"JDK 推薦哪種方式","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"JDK 絕對是 java 編程的標杆,我們很多都可以從 JDK 中學習:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"codeblock","attrs":{"lang":"java"},"content":[{"type":"text","text":"import java.io.IOException;\nimport java.io.PrintStream;\nimport java.io.PrintWriter;\nimport java.io.InputStream;\nimport java.io.OutputStream;\nimport java.io.Reader;\nimport java.io.Writer;\nimport java.io.OutputStreamWriter;\nimport java.io.BufferedWriter;\nimport java.security.AccessController;\nimport java.security.PrivilegedAction;\n\nimport sun.util.spi.XmlPropertiesProvider;","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"這是","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"java.util.Properties","attrs":{}}],"attrs":{}},{"type":"text","text":"中的 import 聲明,可以看出,使用了單類型導入聲明,所以,在沒有其他要求的情況下,我們儘量還是使用單類型導入。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":2},"content":[{"type":"text","text":"文末思考","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"numberedlist","attrs":{"start":"1","normalizeStart":1},"content":[{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":1,"align":null,"origin":null},"content":[{"type":"text","text":"java 的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import","attrs":{}}],"attrs":{}},{"type":"text","text":"是類導入聲明,不會將文件寫入到編譯後的 class 文件中","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":2,"align":null,"origin":null},"content":[{"type":"text","text":"java 的","attrs":{}},{"type":"codeinline","content":[{"type":"text","text":"import","attrs":{}}],"attrs":{}},{"type":"text","text":"有兩種導入方式:單類型導入、按需類型導入","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":3,"align":null,"origin":null},"content":[{"type":"text","text":"按需類型導入只會在編譯過程中有性能損失,在運行期與單類型導入無差別","attrs":{}}]}],"attrs":{}},{"type":"listitem","content":[{"type":"paragraph","attrs":{"indent":0,"number":4,"align":null,"origin":null},"content":[{"type":"text","text":"JDK 源碼中,大部分使用了單類型導入。","attrs":{}}]}],"attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"horizontalrule","attrs":{}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"你好,我是看山,公衆號:看山的小屋,10 年老後端,Apache Storm、WxJava、Cynomys 開源貢獻者。主業:程序猿,兼職:架構師。遊於碼界,戲享人生。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章