java中package语句通过反向域名+业务域分层命名(如com.example.ecommerce.order.api)定义类全限定名并避免冲突,须严格匹配目录结构;jdk9+模块系统用module-info.java进一步控制包可见性;团队需统一规范并自动化检查。

在 Java 中,package 语句是定义类所属命名空间的唯一方式,它直接决定了类的全限定名(Fully Qualified Name),从而从根本上避免类名冲突。关键不在于“怎么写 package”,而在于“怎么设计包结构”——这需要结合项目规模、模块职责和团队协作来规划。
包名必须与目录结构严格一致
Java 强制要求 package 声明必须匹配物理路径。例如:
- 若类声明为
package com.example.user.auth;,则该文件必须放在src/com/example/user/auth/目录下; - 编译器和 JVM 会按此路径查找类,路径错、包名错、类加载就会失败;
- IDE(如 IntelliJ 或 Eclipse)通常自动同步,但手动移动文件后务必检查 package 声明是否更新。
采用反向域名 + 业务域分层命名
这是业界通用且最稳妥的约定,兼顾唯一性与可读性:
- 前缀用公司/组织的反向域名(如
com.github.myorg或org.springframework),确保全局唯一; - 后续层级按业务维度递进,常见模式:
domain → module → layer → type; - 例如:
com.example.ecommerce.order.api(对外接口)、com.example.ecommerce.order.infra.persistence(持久化实现); - 避免使用模糊词如
util、common单独作包名,应限定作用域:com.example.shared.validation比com.example.common更清晰。
模块化项目中用 module-info.java 进一步约束可见性
在 JDK 9+ 的模块系统中,package 是基础,但 module-info.java 才真正控制“谁能看到哪些包”:
- 即使两个模块都有
com.example.service.UserService,只要它们属于不同命名模块,就不会冲突; - 通过
exports com.example.service;显式开放包,未导出的包对外不可见; - 配合
requires声明依赖,能提前发现循环依赖或非法跨层调用。
团队协作需统一规范并自动化检查
再好的命名规则,缺乏落地机制也会失效:
- 在代码风格文档中明确包命名层级含义(如
app表示启动入口,infra表示基础设施); - 用 Checkstyle 或 SonarQube 配置规则,禁止出现
com.example.util这类宽泛包名; - 新模块加入时,由架构组评审包路径是否符合领域边界,避免
order模块里混入payment相关包。
包不是越深越好,也不是越短越妙。核心是让包名成为可读的“路径说明书”——看到 com.example.billing.invoice.render.pdf,就该清楚这是开票模块中负责 PDF 渲染的组件,且不会和邮件渲染、HTML 渲染产生命名重叠。设计得当,一个 package 语句就是一道轻量却可靠的隔离墙。










