包:库单元

在 Java 开发中,开发者不仅仅会使用自己写好的类,也会使用一些别人写好的类库中的类,那么当我们自己写的类名与别人写的类名冲突时,该如何处理呢?或者说如何避免类名的冲突。

首先,了解一下 Java 是如何组织代码的,当编译一个.java文件时,这个文件中的所有的类都会被编译并且各自生成一个.class文件,文件名与每个类名一致。所以,可能编译了很少的类,但是生成了大量的.class类。

Java 的可运行程序是将所有.class文件打包成一个Jar包,Java 解释器负责这些.class文件的查找,装载和解释。

我们将这样的一组java文件组合成的类库,其中每个文件都有一个 public 类和一些非 public 类。为了声明这些文件为同一组,使用package来声明:

package access;
public class firstClass{

}

package语句一定是在java文件的开头第一行,这样,就声明了这个类是在 access 下的,当其他位置要调用这个类的方法时,必须使用 access,或使用import导入,例如:

//用法1:
public class test(){
	public static void main(String [] args){
		access.firstClass first = new access.firstClass();
	}
}
//用法2
import access.*
public class test2(){
	public static void main(String [] args){
		firstClass first = new firstClass();
	}
}

总结一下 package 和 import 解决的问题
将单一的全局名字空间分割开,如此一来,不管多少人在开发,都不会出现类名冲突。

创建包名

既然上文中说使用关键字packageimport可以解决类名冲突的问题,那么在创建包的时候,如何命名才能实现所有人开发的类名都不冲突呢?

答案是以Internet域名作为package第一部分,当然,如果没有域名的话,尽量创造不可能与其他人重复的组合。

在确定合理的包名后,需要解决存储的问题,比较合理的做法是将包名映射到操作系统的层次化的文件结构中。因此,第二部分是将包名解析为系统中的一个目录,例如cn.ucc.controller,对应文件系统中目录:cn/ucc/controller

CLASSPATH 的作用

要理解CLASSPATH的作用,首先需要清楚 Java 解释器的运行过程:

  1. 找出环境变量CLASSPATH
  2. 编译器将编译时遇到的import语句的包名解析成路径
  3. 将的到的路径与CLASSPATH中存储的路径结合
  4. 得到完整的.class文件的路径

由此可以发现,CLASSPATH的作用是记录类库编译后的文件的根目录,并且可以有多个根目录。如:

CLASSPATH = .;D:\java\lib;C:\develop\simple

值得注意的是,在使用jar包时需要将jar包的完整路径写上,如:

CLASSPATH = .;D:\java\lib;C:\develop\test.jar

举个例子:

package cn.ucc.simple;
public class Vector{
	public Vector(){
		System.out.println("Vector");
	}
}
package cn.ucc.simple;
public class List{
	public List(){
		System.out.println("List");
	}
}

上述两个 Java 类均是在包cn.ucc.simple中,此时CLASSPATH的值是:

CLASSPATH = .;D:\java\lib;C:\doc\javat

利用import关键字使用上述两个类:

import cn.ucc.simple.*;
public class Test{
	public static void main(String [] args){
		Vector v = new Vector();
		List l = new List();
	}
}

当编译器遇到import cn.ucc.simple.*;时,就开始在CLASSPATH中的根目录下查找子目录cn\ucc\simple,找出符合的已编译的文件(Vector.class 和 List.class)。

类名冲突

import cn.ucc.simple.*;
import java.util.*
public class Test{
	public static void main(String [] args){
		Vector v = new Vector();//冲突
		List l = new List();
	}
}

上面段代码中,Vector 类在 import 的两个包下都存在,因此编译器无从知晓具体是使用哪个,便会报错,这个时候如果需要保留导入的一个包并使用另一个包下的 Vector,可以使用带包名的类:

import cn.ucc.simple.*;
public class Test{
	public static void main(String [] args){
		java.util.Vector v = new java.util.Vector();//不冲突
		List l = new List();
	}
}