“包”这个机制,类似于分组。主要作用是区分不同组内的同名类。例如,高三三班有一个“王五”,高二八班也有一个“王五”。高三三班和高三八班就是两个不同的包。
Java中的包(package)机制主要提供了类的多层命名空间,用于解决类的命名冲突、类文件管理等。
类似于C++中的命名空间
namespace
。
如果希望把一个类放在指定的包结构下,应该在Java源程序的第一个非注释行声明:
package packageName;
一旦如上声明,这个源文件中所有定位的类都属于这个包。
如果其他人要使用这个类,需要使用这个类的完整名称:包名+类名。
// 文件:Hello.java
package lee;
public class Hello
{
public static void main(String[] args)
{
// TODO
}
}
如果使用如下命令来编译
javac -d . Hello.java
按道理,-d是指定编译结果.class文件的位置,这里指定是当前目录(.),但实际上是在当前路径下创建了一个文件夹lee,里面包含Hello.class。
这是因为,Java规定位于包中的类,在文件系统中也必须有与包名层次相同的目录结构。
但是,如果直接使用javac Hello.java
不会创建lee文件夹。这样做也属于挑战智商的行为,一般还是推荐使用-d,让它形成层次目录结构。
在执行的时候,需要给出这个类的完整名称:
java lee.Hello
虚拟机在装载lee.Hello类时,会依次搜索CLASSPATH环境变量所指定的系列路径,查找这些路径下是否包含lee路径,并在lee路径下查找是否包含Hello.class文件。同一个包中的类不必位于相同路径下,只要它们都在CLASSPATH环境变量中即可。例如lee.Person可以在C盘中,lee.PersonTest可以在D盘中。
另外,除了类文件组织成层次结构外,源文件也要组织成对应的层次结构。此外,建议把源文件和class文件分开放:
把class文件放到某个目录下,并不意味这个目录成了这个类的包名。必须在Java源文件中声明package来指定。
Java包机制运行需要两个条件:
- 在源文件中打包(package packageName);
- class文件位于对应路径下(这个一般通过编译器来自动管理)。
对于包重名的情况,建议包也可以通过层级来组织,比如
org.crazyit.elearning.student.lee
云云。
package的使用有如下注意事项:
- 必须是源文件中非注释的第一句;
- 一个源文件只能指定一个包;
- 如果没有显示指定包,则类位于默认包下;
- 同一个包下的类可以自由访问。
// HelloTest.java
package lee;
public class HelloTest
{
public static void main(String[] args)
{
// 直接访问相同包下的类,不需要使用包前缀
var h = new Hello();
}
}
如果在lee包下再定义一个子包,那么子包中的类不能直接访问lee包中的类了。此外,在使用子包中的类时,要使用该类的全名(包名+类名),不能省略前面lee包的路径。
package lee.sub;
public class Apple{}
在lee.Hello
中使用Apple类时,必须用完整路径lee.sub.Apple
。
可以这么理解:父包和子包从逻辑上存在上下关系;但是从用法上没有任何关系。所以调用时要使用完整包名+类名。
package lee;
public class Hello
{
public static void main(String[] args)
{
var a = new lee.sub.Apple(); // 使用完整路径
}
}
为了简化对不用包的类的调用,Java引入了import
关键字。import
可以导入指定包层次下的某个类或全部类。
import
在package
之后,类定义之前;- 一个Java源文件只能有一个
package
,但是可以有多个import
。
import lee.sub.Apple; // 导入lee.sub中的Apple类
import lee.sub.* // 导入lee.sub中的所有类
使用import
后,在当前源文件中就不用再输入完整路径了。
这一条和C++中的
using namespace
类似。
Java默认所有源文件都导入了
java.lang
包下的所有类。所以String, System类不需要显示的import。
import语句可以简化编程,但并不是必需的。
JDK 1.5之后,还允许使用import
导入类成员变量、类方法,区别在于加上了static
修饰符:
import static package.subpackage.className.fieldName|methodName;
例如,使用import static
导入java.lang
包中System类和Math类的所有静态成员和静态方法(类成员和类方法):
import static java.lang.System.*;
import static java.lang.Math.*;
public class StaticImportTest
{
public static void main(String[] args)
{
// out是System的成员方法;
// PI是Math类的成员变量;
out.println(PI);
}
}
现在总结Java源文件的大致结构:
package 语句; // 0或者1条,位于文件非注释行的最开始
import | import static 语句; // 0条或者多条,位于package之后,第一个public类定义之前
public className | interfaceName | enumName; //0个或者1个public类、接口或枚举,注意一个源文件只能有1个public类
className | interfaceName | enumName; // 0个或多个普通类、接口或枚举,可以有,也可以没有