杰瑞科技汇

Java package 如何正确编译?

核心思想

package 的核心思想是 解决命名冲突实现代码的模块化管理,它就像你电脑里的文件夹,把不同用途的文件(代码)分门别类地存放起来。

Java package 如何正确编译?-图1
(图片来源网络,侵删)

你可能会有两个不同的项目,都创建了一个名为 User 的类,如果没有包,它们就会放在同一个目录下,导致冲突,但有了包,一个可以是 com.projectA.model.User,另一个是 com.projectB.entity.User,它们就可以完美共存。


声明包

在 Java 源代码文件(.java 文件)的开头,使用 package 关键字来声明该文件所属的包。

语法:

package <包名>;

规则:

Java package 如何正确编译?-图2
(图片来源网络,侵删)
  • 必须是源文件的第一行非注释代码。
  • 一个文件只能声明一个包。
  • 包名通常使用小写字母,以避免与类名、接口名冲突。
  • 包名采用 倒置的域名 命名法,这是一种约定俗成的规范,可以确保包名的唯一性,如果你的域名是 mycompany.com,那么你的包名可以是 com.mycompany.project.utils

示例: 创建一个名为 com.example.model 的包,并在其中创建一个 Person.java 文件。

// File: Person.java
package com.example.model;
public class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public void sayHello() {
        System.out.println("Hello, my name is " + name + " and I am " + age + " years old.");
    }
}

目录结构

Java 编译器有一个硬性规定:包的声明必须与文件在文件系统中的目录结构完全一致

这意味着:

  • 包名 com.example.model 对应的目录结构是 com/example/model/
  • Person.java 文件必须存放在 com/example/model/ 这个目录下。

一个典型的项目结构可能如下:

Java package 如何正确编译?-图3
(图片来源网络,侵删)
my_project/
└── src/
    └── com/
        └── example/
            └── model/
                └── Person.java  // 包声明: package com.example.model;

编译

编译 Java 带包的文件时,关键在于如何告诉编译器你的源文件根目录在哪里,编译器需要根据 package 声明来创建正确的目录结构。

在源文件所在的目录下编译

假设你的当前工作目录是 my_project/src/

  1. 打开终端/命令行,进入 my_project/src 目录。
    cd /path/to/my_project/src
  2. 执行 javac 命令,编译器会读取 Person.java 中的 package 声明,并自动在当前目录下创建 com/example/model/ 子目录,然后将编译后的 .class 文件放进去。
    javac com/example/model/Person.java
  3. 编译成功后,你会看到生成的目录结构:
    my_project/
    └── src/
        └── com/
            └── example/
                └── model/
                    ├── Person.java
                    └── Person.class  // 新生成的字节码文件

在任意目录下编译(更常用、更规范)

这是更推荐的做法,因为它与构建工具(如 Maven, Gradle)的工作方式一致。

假设你的项目结构是:

my_project/
└── src/
    └── com/
        └── example/
            └── model/
                └── Person.java
  1. 打开终端,进入项目的根目录 my_project/

    cd /path/to/my_project
  2. 使用 -d 选项指定输出目录。 表示当前目录,src 是源代码根目录。

    javac -d . src/com/example/model/Person.java
    • javac: 编译器命令。
    • -d .: 告诉编译器将生成的 .class 文件及其包目录结构输出到当前目录 () 下。
    • src/com/example/model/Person.java: 要编译的源文件路径。
  3. 编译成功后,你会看到在 my_project 根目录下生成了一个 com 文件夹:

    my_project/
    ├── src/
    │   └── com/
    │       └── example/
    │           └── model/
    │               └── Person.java
    └── com/                  <-- 编译器生成的
        └── example/
            └── model/
                └── Person.class

    这种方式非常清晰,src 用于存放源代码,com (或其他你指定的目录,如 target) 用于存放编译后的产物。


运行

运行带包的类时,同样需要指定完整的包名,你不能直接进入 com/example/model 目录去运行 Person.class,因为这样会找不到类。

命令格式:

java <完整包名>.<类名>

继续上面的例子:

你的当前工作目录仍然是 my_project/Person.class 文件位于 my_project/com/example/model/Person.class

  1. 错误的做法:

    # 错误!找不到或无法加载主类 Person
    java com.example.model.Person

    为什么会错?因为 java 命令会在当前目录 (my_project) 下寻找 com/example/model/Person.class,但实际路径是 my_project/com/example/model/Person.class,它找不到。

  2. 正确的做法: 你需要告诉 java 命令,你的类的根目录是哪里,使用 -classpath (或其简写 -cp) 选项。

    # 正确!
    java -classpath com com.example.model.Person
    • -classpath com: 告诉 java 虚拟机,类的根目录是 my_project/com
    • com.example.model.Person: 告诉虚拟机要运行哪个类。

    运行后,控制台会输出:

    Hello, my name is null and I am 0 years old.

    (因为没有传递构造参数,所以是默认值)

    或者,更通用的做法: 如果你的编译输出目录是当前目录 (),-classpath 就应该是 。

    java -classpath . com.example.model.Person

使用其他包中的类

当你想在 com.example.model 包中的 Person 类里,使用 java.util 包里的 ArrayList 时,你需要:

  1. 使用全限定名

    package com.example.model;
    import java.util.ArrayList; // 更推荐的方式
    public class Person {
        private String name;
        // ...
        public void listHobbies() {
            // 使用全限定名
            java.util.ArrayList<String> hobbies1 = new java.util.ArrayList<>();
            // 或者使用 import 语句(更推荐)
            ArrayList<String> hobbies2 = new ArrayList<>();
        }
    }
  2. 使用 import 语句 import 语句写在 package 声明之后,类定义之前,它只是帮你简化了代码,告诉编译器去哪里找这个类,并不会影响运行时的行为。

    // File: Person.java
    package com.example.model;
    import java.util.ArrayList; // 导入 ArrayList 类
    public class Person {
        // ...
    }

总结与最佳实践

步骤 命令/操作 说明
目录结构 src/com/example/model/Person.java 包名必须与目录结构一致。
编译 javac -d . src/com/example/model/Person.java 在项目根目录编译,-d . 指定输出目录为当前目录。
运行 java -classpath com com.example.model.Person 使用 -classpath 指定类的根目录,然后用完整包名+类名运行。
导入类 import java.util.ArrayList; package 之后,类定义之前,使用 import 简化代码。

现代开发工具的做法:

在实际开发中,我们几乎不会手动使用 javacjava 命令,我们会使用 MavenGradle 这样的构建工具。

  • Maven: 你只需要在 pom.xml 中定义项目坐标(GroupId, ArtifactId, Version),Maven 会自动管理包结构。
    • groupId 对应包名的前几部分(如 com.example)。
    • artifactId 对应项目名。
    • Maven 会强制你将源代码放在 src/main/java 目录下,编译后的输出在 target/classes 目录下。
    • 你只需在 src/main/java 下按照包名创建目录结构即可。
    • 运行时,Maven 会自动设置好 classpath,你只需运行 mvn compilemvn exec:java 等命令即可。

手动编译和运行是理解 Java package 机制的最佳途径,而掌握构建工具则是现代 Java 开发的必备技能。

分享:
扫描分享到社交APP
上一篇
下一篇