深入理解java中的package关键字

时间:2021-11-17 17:41:46

以前只是模模糊糊的知道java的包访问权限,也理解package的作用,就是声明一个编译单元的群组。

但是,这次爆出了一个很奇怪的问题,是我们在编译android工程出现的。我们上层调用android的framework中的类,而framework中的类中被编译成不同的包,比如button类处于android.widget包,平常我们要加一个widget,我们也是放在源码相同的目录下,但这次其它同事放在了别的目录并提交了。提交的同事肯定经过自己的测试并验证通过没问题的。但是,我们全编译源码的时候,出现了找不到类的情况。很奇怪的原因,为什么单编译编译通过,全编译编译不过?

下面我来谈谈自己的理解。

这主要就是java的包访问权限了。

首先简要的介绍下java的package机制。

Java中的一个包就是一个类库单元,包内包含有一组类,它们在单一的名称空间之下被组织在了一起。这个名称空间就是包名。可以使用import关键字来导入一个包。例如使用import java.util.*就可以导入名称空间java.util包里面的所有类。所谓导入这个包里面的所有类,就是在import声明这个包名以后,在接下来的程序中可以直接使用该包中的类。

我们之所以要导入包名,就是要提供一个管理名称空间的机制。如果有两个类A类和B类都含有一个具有相同特征标记(参数列表)的方法f(),即便在同一段代码中同时使用这两个方法f(),也不会发生冲突,原因在于是不同的类对象调用,所以两个方法即便同名也不会发生冲突。但是如果类名称相互冲突又该怎么办呢?假设你编写了一个A类并安装到了一台机器上,而该机器上已经有一个其他人编写的A类,我们该如何解决呢?因为你如果想弄清楚一台机器上到底已经安装了那些类,并不是一件很容易的事情,所以名字之间总是有存在潜在的冲突的可能。在Java中对名称空间进行完全控制并为每个类创建唯一的标识符组合就成为了非常重要的事情。如果你要编写对于同一台机器上共存的其他Java程序友好的类库或程序的话,就需要考虑如何防止类名称之间的冲突问题。

当编写一个Java源代码文件时,此文件通常被称为编译单元。每个编译单元都必须有一个后缀名.java,而在编译单元内有且仅有一个public类,否则编译器就不会接受。该public类的名称必须与文件的名称相同(包括大小写,但不包括后缀名.java)。如果在该编译单元之中还有额外的类的话,那么在包之外的世界是无法看见这些类的,因为它们不是public类,而且它们主要用来为主public类提供支持。

    当编译一个.java文件(即一个编译单元)时,在.java文件中的每个类都会有一个输出文件,而该输出文件的名称与.java文件中每个类的名称相同,只是多了一个后缀名.class。因此在编译少量.java文件之后,会得到大量的.class文件。每一个.java文件编译以后都会有一个public类,以及任意数量的非public类。因此每个.java文件都是一个构件,如果希望许许多多的这样的构件从属于同一个群组,就可以在每一个.java文件中使用关键字package。而这个群组就是一个类库。

因此,尽管我们的代码与源码不在同一路径,但是,由于我们的包名相同,因此如果我们先编译了framework,那么在生成的android.widget包中,是有我们新加入的类的,那么我们导入android.widget包并引用新加入的类,是一点问题都没有的,因为包中的确含有我们新加入的类。但是,全编译的时候就会出问题了,因为我们新加入的类和源码的类不在同一目录,并不是同一时间被编译的。那么很有可能导致源码里的framework编译完了,然后开始编译上层,编译到调用处的时候,我们新加入的类还没有被编译,那么自然就出现了找不到类的错误了。

这次出错给我的感触很深,以前一直对package的作用不是很理解,也觉得他没多大的作用。因为我们编代码基本上都在同一目录,而且新加入的类都与源码的目录保持一致。这次编译出错使我对package的理解更深了一层,其实我们代码的路径不是最重要的,只要包名一致,那么就可以保证编译出来的class文件在同一个类库中。那么我们调用这个类库是一点问题都没有的。但是,放在android的源码的大环境下,如果不放在同一个目录,你不能保证编译的顺序。那么就会出现和我这次遇到的一样的错误,编译到调用处就会出错了。