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}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章