Java package包的定义和使用(非常详细)
操作系统中有目录和文件的概念,其实在 Java 中,包和类的关系有点像目录和文件的关系,包就像是目录而类则像是文件。
一个 Java 项目中的包和类,展开后就是一个文件目录结构,如下图所示。

图 1 包与类
其中,com 是包(目录),backend 和 frontend 都是子包(子目录),frontend 包下有三个 Java 类(文件),而 backend 包下还有 core 和 util 两个子包,这两个子包下也分别有三个 Java 类。
如下图所示,JDK 内置了一个 String 类,我们自己再定义一个 String 类,那么当我们要使用这两个 String 类时会无法区分它们。

图 2 相同名称的类
其次,“包”机制能让项目结构更加清晰,从而方便维护。实际的 Java 项目中一般会包含大量类,小项目中可能有几十至上百个类,而大中型项目中往往有成百上千个类,如果将所有类都放到一起会是难以想象的。而通过包则可以按照逻辑划分将不同模块的 Java 类放到不同的包中,这样能使项目结构更加清晰,同时使开发维护工作也更方便。
最后,“包”机制还提供了访问控制功能,在 Java 中可以通过访问修饰符(如 protected、public 等)来控制类的访问范围,这个范围与包相关。
下面来看包定义的一个示例:
实际上包的层次数可以是任意的,包的层次间用“.”点号进行分隔,同时需要注意包名必须全部为小写。
下面看看导入包和类的语法,通过 import 关键字来完成导入,格式是:
下面看一个示例:

图 3 包目录1
如果 com.seaboat.test 包下的 Teacher 类要使用 Student 类则需要先将其导入,导入命令为“import com.seaboat. Student;”。假如不导入直接使用则会引发编译错误,具体错误消息为“Student cannot be resolved to a type”。
类似地,可以使用“import com.seaboat.*;”来实现类导入,这种星号实际上是一种通配符,所以需要在编译时进行类搜索工作,从而增加编译的时间。
如果要导入多个包和类,可以通过多行 import 语句来实现。例如下面的程序中,为了导入下图的 Student 类和 Grade 类,可以通过两行 import 语句来实现。

图 4 包目录2
下面简单介绍几个常见的内置包,包括 java.lang、java.io、java.net 以及 java.util:
一个 Java 项目中的包和类,展开后就是一个文件目录结构,如下图所示。

图 1 包与类
其中,com 是包(目录),backend 和 frontend 都是子包(子目录),frontend 包下有三个 Java 类(文件),而 backend 包下还有 core 和 util 两个子包,这两个子包下也分别有三个 Java 类。
为什么需要包
首先,假设不存在“包”的概念,那么会存在类名冲突的问题。所谓类名冲突,就是在使用时无法区分相同名称的类。如下图所示,JDK 内置了一个 String 类,我们自己再定义一个 String 类,那么当我们要使用这两个 String 类时会无法区分它们。

图 2 相同名称的类
其次,“包”机制能让项目结构更加清晰,从而方便维护。实际的 Java 项目中一般会包含大量类,小项目中可能有几十至上百个类,而大中型项目中往往有成百上千个类,如果将所有类都放到一起会是难以想象的。而通过包则可以按照逻辑划分将不同模块的 Java 类放到不同的包中,这样能使项目结构更加清晰,同时使开发维护工作也更方便。
最后,“包”机制还提供了访问控制功能,在 Java 中可以通过访问修饰符(如 protected、public 等)来控制类的访问范围,这个范围与包相关。
Java包的定义
要对一个类进行包定义非常简单,只需在类源文件(.java)的第一行添加包定义语句,具体的语法格式如下:package 包名;除了类,接口和枚举的包声明的格式也是一样的。
下面来看包定义的一个示例:
package com.seaboat; public class Student { int id; int age; String name; public Student(int id, int age, String name) { this.id = id; this.age = age; this.name = name; } public static void main(String[] args) { Student xiaoming = new Student(1, 10, "小明"); System.out.println(xiaoming.name + "," + xiaoming.age); } }可以看到,我们只需在第一行添加“package com.seaboat;”就能将 Student 类定义为 com.seaboat 包,该包包含了两个层次,com 是第一层,seaboat 是第二层,seaboat 是 com 的子包。
实际上包的层次数可以是任意的,包的层次间用“.”点号进行分隔,同时需要注意包名必须全部为小写。
Java包的导入
在实际开发过程中,会将不同的 Java 类按照功能模块或者业务逻辑进行划分,将属于某个模块的所有类统一放到一个包内。假如要在某个包的类中使用另一个包中的类,就需要先导入这个包,然后才能使用该包中的类。下面看看导入包和类的语法,通过 import 关键字来完成导入,格式是:
import 包.类; import 包.*;前者导入的是某个包中的具体某个类,而后者则是导入某个包中的所有类。
下面看一个示例:
package com.seaboat.test; import com.seaboat.Student; public class Teacher { public String name; public Student student; }com.seaboat 包下有一个 Student 类,如下图所示:

图 3 包目录1
如果 com.seaboat.test 包下的 Teacher 类要使用 Student 类则需要先将其导入,导入命令为“import com.seaboat. Student;”。假如不导入直接使用则会引发编译错误,具体错误消息为“Student cannot be resolved to a type”。
类似地,可以使用“import com.seaboat.*;”来实现类导入,这种星号实际上是一种通配符,所以需要在编译时进行类搜索工作,从而增加编译的时间。
如果要导入多个包和类,可以通过多行 import 语句来实现。例如下面的程序中,为了导入下图的 Student 类和 Grade 类,可以通过两行 import 语句来实现。

图 4 包目录2
package com.seaboat.test; import com.seaboat.Student; import com.seaboat.Grade; public class Teacher { public String name; public Student student; public Grade grade; }此外,如果我们不想使用 import 语句来导入包和类,可以通过全限定名的方式来使用某个类,即在使用类时以“包.类”的格式来声明。通过下面的程序大家就能明白如何使用全限定名的方式了。
public class Teacher2 { public String name; public com.seaboat.Student xiaoming = new com.seaboat.Student(1, 10, "小明"); }使用全限定名方式还能解决同名类的问题,比如我们在 com.seaboat.util 包下创建了一个 String 类,这个类与 Java 语言提供的 String 类同名,当我们要同时使用它们时可以通过全限定名来区分,例如:
public class Test { public static void main(java.lang.String[] args) { java.lang.String string = new java.lang.String(); com.seaboat.util.String string2 = new com.seaboat.util.String(); } }
Java内置包与自定义包
总体而言,我们可以把 Java 的包分为 Java 语言内置包和自定义包,内置包是 Java 为了方便开发者提供的语言层面的常用包,自定义包则是开发者根据实际需要自行创建的包。下面简单介绍几个常见的内置包,包括 java.lang、java.io、java.net 以及 java.util:
- java.lang 包:系统常用基础包,包括了很多 Java 运行时必不可少的类以及开发常用类,如 String、System 等。Java 会自动导入这个包,所以我们不必手动导入它;
- java.io 包:Java 语言的标准输入输出类包,包括文件流、输入输出流、字符字节流等;
- java.net 包:Java 语言提供的网络编程工具包,通过它能实现网络通信功能;
- java.util 包:Java 语言提供的常用工具包,包括集合框架、日期时间、国际化、时间模型、时区、位组、随机生成器等。