Skip to content

开发环境与第一个 Java 程序 ​

学习目标 ​

  • 区分 JDK、JRE、JVM,以及 javac、java、jar 各自负责的工作。
  • 能在命令行编译和运行带包名的程序,并解释字节码和 classpath 的作用。
  • 根据常见错误信息定位 Java 版本、类名、包名或入口方法问题。

核心知识点 ​

专业术语 ​

  • JVM(Java Virtual Machine):加载并执行 .class 字节码的虚拟机,负责类加载、执行和运行时内存管理。
  • JRE(Java Runtime Environment):运行 Java 程序所需的 JVM 与标准类库;现代 JDK 通常直接提供完整运行时,不再单独发布传统 JRE 安装包。
  • JDK(Java Development Kit):开发工具包,包含编译器、运行器、打包和诊断工具。
  • 字节码(bytecode):javac 生成的、与操作系统无关的中间指令,通常存放在 .class 文件中。
  • classpath:JVM 和编译器查找类文件与依赖的路径集合;包名对应 classpath 下的目录层级。
名称主要职责边界
JVM加载、校验并执行字节码不能把源码直接当作已编译项目管理
JRE提供 JVM 与运行所需的标准类库传统独立 JRE 安装包并不是现代 JDK 的必需分发形式
JDK提供编译、运行、打包和诊断工具开发者仍需正确配置版本和 classpath

白话解释与边界 ​

源码到字节码 ​

可以把源码到运行理解为“先翻译,再执行”:javac 把 .java 翻译成字节码,java 启动 JVM 找到入口类,JVM 再解释或即时编译字节码。JDK 20 是本课程基准;编译器版本和运行时版本不匹配时,可能出现 UnsupportedClassVersionError。classpath 必须指向包目录的根,而不是包目录本身;命令行能运行并不代表 IDE 的项目配置正确。

javac 编译 ​

javac 只负责把源码编译为字节码,并通过 -d 指定输出根目录;编译成功不代表程序已经运行。

java 运行 ​

java 负责启动 JVM,按全限定类名和 classpath 查找入口;JDK 11 起可以用 java Hello.java 直接运行单文件源码,但这不会取代正式项目对编译输出和依赖的管理。

jar 打包 ​

jar 负责创建、查看或更新归档;它不会自动执行程序,运行 JAR 仍需要正确的 classpath 或入口配置。

常见启动错误 ​

常见错误可以按阶段判断:找不到 javac 通常是 JDK 或 PATH 问题,Could not find or load main class 多半是包名或 classpath 根错误,UnsupportedClassVersionError 则说明编译目标高于运行时。先确认 java 与 javac 的实际路径,再检查输出目录层级,通常比反复修改源码更快。

常用用法 ​

javac 与 java:编译并运行类 ​

先用 javac CompileRunDemo.java 生成字节码,再用 java CompileRunDemo 启动;两步分开更容易定位编译错误和运行时错误。

java
public class CompileRunDemo {
// 作用:先用 javac CompileRunDemo.java 生成字节码,再用 java CompileRunDemo 启动;两步分开更容易定位编译错误和运行时错误。
// 初始状态:javac 编译源文件 CompileRunDemo.java,java 再以类名 CompileRunDemo 启动 main。
    public static void main(String[] args) {
        System.out.println("compiled and running");
        // 输出:compiled and running
    }
}

jar:把编译结果打成归档 ​

jar 只负责归档,真正运行仍由 java 启动;正式项目还要配置入口类和依赖 classpath。

java
// 语义:jar 只负责归档,真正运行仍由 java 启动。
public class JarEntryDemo {
// 作用:jar 只负责归档,真正运行仍由 java 启动;正式项目还要配置入口类和依赖 classpath。
    public static void main(String[] args) {
        System.out.println("jar entry");
// 输出:jar entry
        // 作用:通过 jar 把编译结果打成归档。
    }
}

不常用但需要知道 ​

java Source.java:直接运行单文件源码 ​

单文件启动适合教学和一次性脚本;多文件项目仍应显式编译并管理输出目录。

java
// 作用:通过 java Source.java 直接运行单文件源码。
public class SingleFileDemo {
// 作用:单文件启动适合教学和一次性脚本;多文件项目仍应显式编译并管理输出目录。
    public static void main(String[] args) {
        System.out.println("single file");
        // 输出:single file
    }
}

classpath:从包根查找类 ​

运行 demo.ClasspathDemo 时,classpath 要指向包含 demo 目录的根,而不是 demo 目录本身。

java
package demo;

public class ClasspathDemo {
// 作用:通过 classpath 从包根查找类。
    public static void main(String[] args) {
        System.out.println("package root");
        // 输出:package root
    }
}

简单案例 ​

先创建 src/com/example/HelloJava.java,再把编译产物放到 out:

java
package com.example;

public class HelloJava {
    public static void main(String[] args) {
        String name = args.length == 0 ? "Java" : args[0];
        System.out.println("Hello, " + name + "!");
        // 输出:Hello, 小明!
        System.out.println("class: " + HelloJava.class.getName());
        // 输出:class: com.example.HelloJava
    }
}
powershell
javac -d out src/com/example/HelloJava.java
java -cp out com.example.HelloJava 小明
jar --create --file hello.jar -C out .
java -cp hello.jar com.example.HelloJava 小明

out/com/example/HelloJava.class 就是编译生成的字节码。运行时写 com.example.HelloJava 而不是 HelloJava,因为 classpath 根是 out。

易混点 ​

  • JDK 是开发工具包,JVM 是运行字节码的程序;安装 JDK 不等于可以忽略运行时版本。
  • 包名不是文件夹的装饰,而是类的全限定名组成部分;-cp out 后才能从 out 找到 com.example.HelloJava。
  • javac 成功只表示源码编译成功,java 仍可能因入口签名、类路径或版本错误而启动失败。
  • .class 是字节码而不是某个平台的机器码;JVM 会在目标平台完成后续执行。

课后小问 ​

  1. 为什么运行带包名的 HelloJava 时要写 com.example.HelloJava? 答案:因为包名和类名共同组成全限定类名,JVM 按它在 classpath 根下定位 .class 文件。 解析:-cp out 指向的是目录根,实际文件位于 out/com/example/HelloJava.class;只写短类名会让 JVM 查找错误路径。

  2. javac、java 和 jar 之间有什么区别? 答案:javac 编译源码,java 启动 JVM 运行类,jar 创建或查看 JAR 归档。 解析:三者处于不同阶段;编译没有运行程序,打包也不会自动执行程序,仍需用 classpath 或 JAR 启动命令指定入口。

本节小结 ​

  • JDK 提供开发工具,JVM 负责加载和执行字节码,传统 JRE 只是运行时概念。
  • javac 生成 .class,java 按全限定类名和 classpath 找到入口并启动。
  • 包名决定类的路径关系,-d 和 -cp 分别控制输出根与查找根。
  • 版本、入口方法和 classpath 是命令行运行失败的三类常见边界。

快速回顾 ​

  • 能说明 JDK、JVM、字节码三者在运行链路中的位置。
  • 能用 javac -d 编译带包名的类并用 java -cp 启动。
  • 能解释 HelloJava.class 为什么位于包目录层级下。
  • 能区分找不到命令、找不到主类和版本不兼容错误。