强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

Java 完全指南 / 03 - Hello World:编译运行、项目结构、JAR 打包

03 - Hello World:编译运行、项目结构、JAR 打包

手动编译运行

最简单的 Hello World

// 文件名: HelloWorld.java
public class HelloWorld {
    public static void main(String[] args) {
        System.out.println("Hello, World!");
    }
}
# 编译:将 .java 编译为 .class 字节码
javac HelloWorld.java

# 运行:JVM 加载并执行字节码
java HelloWorld

# 输出:
# Hello, World!

⚠️ 类名必须与文件名完全一致(包括大小写)。public class HelloWorld 必须放在 HelloWorld.java 中。

main 方法签名解析

public static void main(String[] args)
                         
                         └── 参数字符串数组接收命令行参数
                └──────────── 方法名固定为 main
            └───────────────── 返回类型void
      └──────────────────────── static无需创建对象即可调用
└─────────────────────────────── publicJVM 需要访问此方法

JDK 21 也支持简化写法(无需 public 类):

// JDK 21+ 通过 JEP 463 支持隐式类
void main() {
    System.out.println("Hello from JDK 21!");
}

带命令行参数的版本

public class Greeter {
    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("用法: java Greeter <名字>");
            return;
        }
        for (String arg : args) {
            System.out.println("你好, " + arg + "!");
        }
    }
}
javac Greeter.java
java Greeter 张三 李四

# 输出:
# 你好, 张三!
# 你好, 李四!

Java 编译运行流程

源码 .java  ──javac──▶  字节码 .class  ──java──▶  JVM 执行
                         │
                         ▼
                   可打包为 .jar
                   (Java Archive)

编译器 javac 常用选项

选项说明示例
-d <dir>指定输出目录javac -d out src/*.java
-sourcepath <path>源码搜索路径javac -sourcepath src -d out Main.java
-encoding UTF-8指定编码javac -encoding UTF-8 *.java
-version显示版本javac -version
-Xlint:all启用所有警告javac -Xlint:all *.java
@argfile从文件读取参数javac @sources.txt

java 运行时常用选项

选项说明示例
-cp <path>类路径java -cp out:lib/* Main
-D<key>=<value>系统属性java -Dapp.env=prod Main
-Xmx<mem>最大堆内存java -Xmx512m Main
-XX:+PrintFlagsFinal打印 JVM 参数诊断用
--enable-preview启用预览特性java --enable-preview Main

标准项目结构

Maven 标准目录布局

my-project/
├── pom.xml                          # Maven 项目配置
├── src/
│   ├── main/
│   │   ├── java/                    # 主代码
│   │   │   └── com/example/
│   │   │       └── App.java
│   │   ├── resources/               # 资源文件
│   │   │   └── application.properties
│   │   └── webapp/                  # Web 资源(WAR 项目)
│   │       └── WEB-INF/
│   └── test/
│       ├── java/                    # 测试代码
│       │   └── com/example/
│       │       └── AppTest.java
│       └── resources/               # 测试资源
├── target/                          # 构建输出(自动生成)
│   ├── classes/
│   ├── test-classes/
│   └── my-project-1.0.0.jar
├── .gitignore
└── README.md

包(Package)机制

// src/main/java/com/example/demo/HelloApp.java
package com.example.demo;  // 包声明,必须是第一条非注释语句

public class HelloApp {
    public static void main(String[] args) {
        System.out.println("包路径: " + HelloApp.class.getPackageName());
    }
}
# 带包名编译
javac -d out src/main/java/com/example/demo/HelloApp.java

# 带包名运行(使用全限定类名)
java -cp out com.example.demo.HelloApp

💡 包名全部小写,使用倒序域名:com.company.project.module

JAR 文件打包

什么是 JAR?

JAR(Java ARchive) 是基于 ZIP 格式的归档文件,包含:

  • 编译后的 .class 文件
  • 资源文件(配置、图片等)
  • META-INF/MANIFEST.MF(清单文件)

手动创建 JAR

# 1. 编译
javac -d out src/main/java/com/example/demo/HelloApp.java

# 2. 创建 MANIFEST.MF
echo 'Main-Class: com.example.demo.HelloApp
Class-Path: .
' > MANIFEST.MF

# 3. 打 JAR
jar cfm app.jar MANIFEST.MF -C out .

# 4. 运行
java -jar app.jar

使用 Maven 打包

pom.xml 中配置 maven-jar-plugin

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <archive>
                    <manifest>
                        <mainClass>com.example.demo.HelloApp</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>
mvn clean package
java -jar target/my-app-1.0.0.jar

Fat JAR / Uber JAR

普通 JAR 不包含依赖,运行时需要手动指定 classpath。Fat JAR 将所有依赖打包到一个 JAR 中。

使用 Maven Shade Plugin

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.5.1</version>
    <executions>
        <execution>
            <phase>package</phase>
            <goals><goal>shade</goal></goals>
            <configuration>
                <transformers>
                    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                        <mainClass>com.example.demo.HelloApp</mainClass>
                    </transformer>
                </transformers>
            </configuration>
        </execution>
    </executions>
</plugin>

使用 Spring Boot Maven Plugin(Spring Boot 项目推荐):

<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>
</plugin>

JAR 常用命令

# 查看 JAR 内容
jar tf app.jar

# 解压 JAR
jar xf app.jar

# 运行 JAR
java -jar app.jar

# 查看 MANIFEST.MF
unzip -p app.jar META-INF/MANIFEST.MF

# 运行指定 class(不带 Main-Class)
java -cp app.jar com.example.SomeClass

完整项目示例

创建一个带包结构和多类的项目:

greeting/
├── src/
│   └── com/example/greeting/
│       ├── Main.java
│       ├── Greeter.java
│       └── StringUtils.java
└── out/               # 编译输出
// src/com/example/greeting/StringUtils.java
package com.example.greeting;

public class StringUtils {
    public static boolean isEmpty(String s) {
        return s == null || s.isBlank();
    }

    public static String capitalize(String s) {
        if (isEmpty(s)) return s;
        return Character.toUpperCase(s.charAt(0)) + s.substring(1);
    }
}
// src/com/example/greeting/Greeter.java
package com.example.greeting;

public class Greeter {
    private final String prefix;

    public Greeter(String prefix) {
        this.prefix = prefix;
    }

    public String greet(String name) {
        if (StringUtils.isEmpty(name)) {
            return prefix + ", 匿名用户!";
        }
        return prefix + ", " + StringUtils.capitalize(name) + "!";
    }
}
// src/com/example/greeting/Main.java
package com.example.greeting;

public class Main {
    public static void main(String[] args) {
        Greeter greeter = new Greeter("你好");
        System.out.println(greeter.greet("java"));
        System.out.println(greeter.greet(""));
        System.out.println(greeter.greet(null));
    }
}
# 编译(编译器会自动处理同包内类的依赖)
javac -d out src/com/example/greeting/*.java

# 运行
java -cp out com.example.greeting.Main

# 输出:
# 你好, Java!
# 你好, 匿名用户!
# 你好, 匿名用户!

⚠️ 注意事项

  1. 一个 .java 文件只能有一个 public 类 — 文件名必须与 public 类名一致,其他类只能是包级私有。
  2. 编码问题 — Windows 默认 GBK,编译含中文的 .java 时加 -encoding UTF-8
  3. classpath 分隔符 — Linux/macOS 用 :,Windows 用 ;
  4. 不要在 classpath 中遗漏 .. 表示当前目录,否则可能找不到类。

💡 技巧

  1. 直接运行单文件源码(JDK 11+)—— 无需手动 javac:

    java HelloWorld.java          # 自动编译并运行
    java --source 21 HelloWorld.java
    
  2. JDK 21+ 多文件直接运行

    # 自动编译所有依赖的 .java 文件
    java src/com/example/greeting/Main.java
    
  3. 查看反编译字节码

    javap -c -p HelloWorld.class   # 反汇编
    javap -verbose HelloWorld.class # 详细信息
    

🏢 业务场景

  • 微服务部署: Spring Boot 应用打包为 Fat JAR,Docker 容器中直接 java -jar app.jar 启动。
  • CLI 工具: 使用 picocli 库构建命令行工具,打包为 JAR 后通过 java -jar tool.jar 执行。
  • 脚本化运行: JDK 21 支持单文件运行,可将简单 Java 程序当脚本使用,替代 Python 脚本。

📖 扩展阅读