可以把 package(包)想象成文件系统里的文件夹,在电脑上,我们使用文件夹来组织文件,避免所有文件都散落在根目录下,导致混乱,同样,在 Java 项目中,我们使用 package 来组织类和接口,防止命名冲突,并管理代码结构。

package 是 Java 语言管理类的一个核心机制,它的主要作用可以总结为以下四点:
避免命名冲突
这是 package 最核心、最重要的作用。
问题场景:
假设你正在开发一个项目,需要定义一个 Date 类来处理日期,你使用了一个第三方库,这个库里恰好也有一个 Date 类,当你尝试使用 Date 这个名字时,编译器和 JVM 就会困惑:你到底想用的是哪个 Date 类?是项目自己定义的那个,还是第三方库里的那个?这就会导致编译错误。
解决方案:
通过 package,每个类都有一个唯一的、完整的“地址”,这个地址就是包名 + 类名。

- 你的项目中的
Date类可以放在com.mycompany.utils包下,它的完整名称是com.mycompany.utils.Date。 - 第三方库中的
Date类通常位于java.util包下,它的完整名称是java.util.Date。
你可以明确地告诉编译器你想使用哪一个:
// 使用 java.util 包中的 Date 类 java.util.Date utilDate = new java.util.Date(); // 使用 com.mycompany.utils 包中的 Date 类 com.mycompany.utils.Date myDate = new com.mycompany.utils.Date();
通过这种方式,即使类名相同,只要它们的包名不同,就不会发生冲突。
组织和分类代码
随着项目越来越大,类和接口的数量会急剧增加,如果没有 package,所有类都会在一个平级的目录下,查找和维护会变得非常困难。
package 提供了一种逻辑上的分层结构,让代码更有条理。

常见分类方式:
- 按功能模块划分:一个电商系统可以有
com.shop.product(商品模块),com.shop.order(订单模块),com.shop.user(用户模块)。 - 按层级划分:一个公司
mycompany的项目projectA,其包名可以设计为com.mycompany.projectA.controller,com.mycompany.projectA.service,com.mycompany.projectA.model等,清晰地体现了 MVC 架构。
这种组织方式不仅让代码结构清晰,也方便团队成员理解和协作。
访问权限控制
package 与访问修饰符(public, protected, default, private)紧密相关,共同决定了类、方法和成员变量的可见性。
public:可以被任何其他类访问,无论它在哪个包中。protected:可以被同一个包中的所有类,以及不同包中的子类访问。default(或包私有):只有同一个包中的类可以访问,这是package访问权限的关键,如果你不指定任何访问修饰符,那么它就具有默认的包访问权限。private:只能被定义它的类内部访问。
示例:
// 文件路径: src/com/example/Animal.java
package com.example;
public class Animal {
// public 方法,任何地方都可以访问
public void eat() {
System.out.println("Animal is eating.");
}
// default 方法,只有 com.example 包内的类可以访问
void sleep() {
System.out.println("Animal is sleeping.");
}
}
// 文件路径: src/com/example/Dog.java
package com.example;
public class Dog extends Animal {
public void makeSound() {
// 可以访问父类的 public 方法
eat();
// 可以访问父类的 default 方法,因为 Dog 和 Animal 在同一个包
sleep();
}
}
// 文件路径: src/com/example2/Cat.java
package com.example2;
import com.example.Animal;
public class Cat {
public void watch() {
Animal animal = new Animal();
// 可以访问 Animal 的 public 方法
animal.eat();
// 下面这行代码会编译错误!
// animal.sleep();
// 错误原因:sleep() 是 default 访问权限,Cat 类在 com.example2 包,
// 而 Animal 类在 com.example 包,不在同一个包,所以无法访问。
}
}
这个例子清晰地展示了 package 如何与访问权限协同工作,实现对代码细节的隐藏和保护。
提供可重用性
当我们将相关的类组织在同一个 package 中时,这个包本身就可以被视为一个功能单元,其他开发者可以很方便地导入这个包,并使用其中提供的公共类和接口,而无需关心其内部实现细节。
你只需要导入 java.util 包,就可以使用 List, Set, Map 等强大的集合工具类,极大地提高了代码的复用效率。
如何使用 package
-
声明包: 在 Java 源文件(
.java文件)的第一行(除了注释和import语句外)使用package关键字来声明包。package com.mycompany.projectA.model;
-
文件结构: 包声明必须与文件系统的目录结构严格对应,上面的
package声明,意味着这个.java文件必须存放在.../com/mycompany/projectA/model/目录下。 -
导入类: 在使用其他包中的类时,需要使用
import语句。import java.util.ArrayList; // 导入 ArrayList 类 import com.mycompany.projectA.model.User; // 导入 User 类 public class Main { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); User user = new User(); } } -
运行带有包的类: 在命令行中运行一个带有包的类时,需要使用完整的类名,并且从正确的根目录开始。
- 假设
Main类的完整路径是src/com/mycompany/projectA/Main.java。 - 你需要先编译:
javac src/com/mycompany/projectA/Main.java - 然后运行:
java com.mycompany.projectA.Main - 注意:运行时,
java命令需要从src目录的上一级目录执行,或者在src目录下执行java -cp . com.mycompany.projectA.Main。
- 假设
最佳实践
- 命名规范:包名通常全部使用小写字母,避免使用下划线,推荐使用倒置的域名作为包名的前缀,
com.google、org.apache,这样可以保证包名的唯一性。 - 目录结构清晰:保持包的目录结构与代码逻辑结构一致。
- 合理使用访问权限:尽量将类的实现细节(
default和private的成员)隐藏起来,只暴露必要的public接口,遵循封装原则。
| 作用 | 核心思想 | 好处 |
|---|---|---|
| 避免命名冲突 | 为类提供唯一标识(包名+类名) | 解决同名类问题,允许代码共存 |
| 组织代码 | 将相关类逻辑地分组 | 结构清晰,易于查找和维护 |
| 访问控制 | 结合 default 权限实现包内可见 |
实现信息隐藏,保护代码内部实现 |
| 提供可重用性 | 将功能打包,方便他人使用 | 提高开发效率,促进代码共享 |
package 是 Java 语言基石之一,它不仅是解决命名冲突的工具,更是构建大型、健壮、可维护软件项目的关键所在。
