java中import作用详解

package

C/C++ 的 #include会把所包含的内容在编译时添加到程序文件中,而java的import则不同。


这里我们先了解一下Java 的 package 到底有何用处。


package名称就像是我们的姓,而class名称就像是我们的名字 。package和package的附属关系用"."来连接,这就像是复姓。比如说 java.lang.String就是复姓 java.lang,名字為 String 的类别;java.io.InputStream 则是复姓 Java.io: Permanent & Contract Remote Work. ,名字為 InputStream的类别。


Java 会使用 package 这种机制的原因也非常明显,就像我们取姓名一样 ,光是一间学校的同一届同学中,就有可能会出现不少同名的同学,如果不取姓的话,那学校在处理学生资料,或是同学彼此之间的称呼,就会发生很大的困扰。相同的,全世界的 Java 类数量,恐怕比日本人还多,如果类别不使用package名称,那在用到相同名称的不同类时, 就会产生极大的困扰。所以package这种方式让极大降低了类之间的命名冲突。

可是现在问题来了,因為很多package的名称非常的长,在编程时,要使用一个类要将 多个包名.类名 完全写出,会让代码变得冗长,减低了简洁度。例如

显得非常麻烦,于是Sun公司就引入了import。


import

import就是在java文件开头的地方,先说明会用到那些类别。
接着我们就能在代码中只用类名指定某个类,也就是只称呼名字,不称呼他的姓。

首先,在程序开头写:

于是我们就可以在程序中这样写到:

一个java文件就像一个大房间,我们在门口写着在房间里面的class的姓和名字,所以在房间里面提到某个class就直接用他的名字就可以。例如:


System 就是指 java.lang.System,而 InputStream 就是指 java.io.InputStream。


但是如果一个java文件里面有多个同个“姓”,即包名相同的类(例如上面的InputStream,InputStreamReader,BufferedReader都是 java.io 中的类),我们一一写出显得比较繁杂,所以Sun就让我们可以使用

表示文件里面说到的类不是java.lang包的就是 java.io 包的。编译器会帮我们选择与类名对应的包。

那我们可不可以再懒一点直接写成下面声明呢?

历史告诉我们,这样是不行的。因為那些类别是姓 java.io 而不是姓 java。就像姓『诸葛』的人应该不会喜欢你称他為『诸』 先生吧。这样写的话只会将java包下的类声明,而不不会声明子包的任何类。

一开始说 import 跟 #include 不同,是因为import 的功能到此為止,它不像#include 一样,会将其他java文件的内容载入进来。import 只是让编译器编译这个java文件时把没有姓的类别加上姓,并不会把别的文件程序写进来。你开心的话可以不使用import,只要在用到类别的时候,用它的全部姓名来称呼它就行了(就像例子一开始那样),这样跟使用import功能完全一样。


import的两种导入声明

单类型导入(single-type-import)

(例:import java.util.ArrayList; )

按需类型导入(type-import-on-demand)

(例:import java.util.*;)

有如下属性:


java以这样两种方式导入包中的任何一个public的类和接口(只有public类和接口才能被导入)


上面说到导入声明仅导入声明目录下面的类而不导入子包,这也是为什么称它们为类型导入声明的原因。


导入的类或接口的简名(simple name)具有编译单元作用域。这表示该类型简名可以在导入语句所在的编译单元的任何地方使用.这并不意味着你可以使用该类型所有成员的简名,而只能使用类型自身的简名。

例如: java.lang包中的public类都是自动导入的,包括Math和System类.但是,你不能使用它们的成员的简名PI()和gc(),而必须使用Math.PI()和System.gc().你不需要键入的是java.lang.Math.PI()和java.lang.System.gc()。


程序员有时会导入当前包或java.lang包,这是不需要的,因为当前包的成员本身就在作用域内,而java.lang包是自动导入的。java编译器会忽略这些冗余导入声明(redundant import declarations)。即使像这样

import java.util.ArrayList;

import java.util.*;

多次导入,也可编译通过。编译器会将冗余导入声明忽略.


static import静态导入

在Java程序中,是不允许定义独立的函数和常量的。即什么属性或者方法的使用必须依附于什么东西,例如使用类或接口作为挂靠单位才行(在类里可以挂靠各种成员,而接口里则只能挂靠常量)。

于是J2SE 1.5里引入了“Static Import”机制,借助这一机制,可以用略掉所在的类或接口名的方式,来使用静态成员。static import和import其中一个不一致的地方就是static import导入的是静态成员,而import导入的是类或接口类型。


如下是一个有静态变量和静态方法的类

平时我们使用这些静态成员是用 类名.静态成员 的形式使用,即staticFieldsClass.staticField或者staticFieldsClass.staticFunction()。

现在用static import的方式:

这里有几个问题需要弄清楚:


Static Import无权改变无法使用本来就不能使用的静态成员的约束,上面例子的StaticTest和staticFieldsClass不是在同一个包下,所以StaticTest只能访问到staticFieldsClass中public的变量。使用了Static Import也同样如此。


导入的静态成员和本地的静态成员名字相同起了冲突,这种情况下的处理规则,是“本地优先。


不同的类(接口)可以包括名称相同的静态成员。例如在进行Static Import的时候,出现了“两个导入语句导入同名的静态成员”的情况。在这种时候,J2SE 1.5会这样来加以处理:


如果两个语句都是精确导入的形式,或者都是按需导入的形式,那么会造成编译错误。


如果一个语句采用精确导入的形式,一个采用按需导入的形式,那么采用精确导入的形式的一个有效。


大家都这么聪明上面的几个特性我就不写例子了。

按需导入机制

使用按需导入声明是否会降低Java代码的 执行效率 ?

绝对不会!

一、import的按需导入

编译之后的class文件 :

二、static import的按需导入

上面StaticNeedImportTest 类编译之后 :

附加

这是否意味着你总是可以使用按需导入声明?
是,也不是!

在类似Demo的非正式开发中使用按需导入声明显得很有用。

Sun的工程师一般不使用按需类型导入声明.这你可以在他们的代码中找到:

在java.util.Properties类中的导入声明:

可以看到他们用单类型导入详细的列出了需要的 java.io 包中的具体类型。

编辑于 2022-01-05 09:56