Skip to content

Java语言基础与环境

Java 语言基础与环境(复习/面试笔记)

说明:本笔记聚焦「Java 语言基础与环境」,覆盖语言历史与特性、JDK/JRE/JVM 体系、程序执行机制、基础语法与数组,并结合版本升级、工具链和常见坑给出实战建议。适用于初学与面试/复盘阶段快速体系化梳理。


1. 概述

  • 一句话定义:Java 是一种面向对象、强静态类型、通过字节码与 JVM 实现平台无关的通用编程语言。
  • 解决问题:以安全、可维护、跨平台、高性能的方式构建大型企业级应用(后端服务、金融系统、大数据处理等)。
  • 适用场景:企业后端(Spring Boot/Cloud)、大数据(Spark/Flink)、Android(历史主语言)、高稳定性领域(金融/电信等)。

2. 核心概念 / 原理

2.1 JDK / JRE / JVM 体系结构

  • JDK(Development Kit):开发工具集,包含 JRE 与开发工具(javac、jdb、jar、jlink 等)。
  • JRE(Runtime Environment):运行时环境,包含 JVM 与核心类库(rt.jar 等),仅可运行程序。
  • JVM(Virtual Machine):执行字节码的虚拟机,含类加载、解释器、JIT 编译器、GC、线程调度等。

关系:JDK ⊃ JRE ⊃ JVM。理解三者的包含关系是掌握 Java 运行机制的基础。

2.2 Java 程序执行流程(编译期 + 运行期)

  • 编译期:javac 将 .java 源码编译为平台无关的 .class 字节码。
  • 运行期:JVM 类加载 → 解释执行 → 热点方法由 JIT 编译成本地机器码缓存,形成“解释+JIT”混合模式,兼顾冷启动与峰值性能。
  • 跨平台原理:字节码平台无关,JVM 屏蔽硬件与 OS 差异;JVM 本身则是平台相关的。
  • AOT:GraalVM Native Image 可将应用提前编译为平台原生可执行文件,提升启动速度(云原生/Serverless 友好),牺牲部分动态性与反射可达性(需配置反射元数据)。

流程示意(类加载到执行):

mermaid
flowchart TD
    A[.java 源文件] --> B[javac 编译]
    B --> C[.class 字节码]
    C --> D[类加载/链接/初始化]
    D --> E[解释器执行]
    E --> F{热点探测?}
    F -- 是 --> G[JIT 编译成本地码]
    G --> H[本地代码执行]
    F -- 否 --> E

2.3 JVM 规范与主流实现

  • 规范:定义字节码格式、指令集、内存模型、GC 接口等。
  • 实现:
    • HotSpot(Oracle/OpenJDK):最常用,JIT/GC 技术成熟。
    • GraalVM:多语言互操作、AOT(Native Image)。
    • OpenJ9(Eclipse/IBM):低内存占用、冷启动快,适配云场景。

2.4 Java 历史与 LTS

  • 发布节奏:自 2017 起半年一更;生产优先选用 LTS(Java 8、11、17、21)。
  • 关键版本(核对与补充):
    • Java 8:Lambda、Stream、Optional、新日期时间 API(java.time)。
    • Java 11:标准 HTTP Client(java.net.http),移除 Java EE 和 CORBA 模块;允许在 Lambda 参数上使用 var(JEP 323,属于“var 增强”,基础的 var 引入自 Java 10)。
    • Java 17:密封类(sealed)、Record 转正、模式匹配预览(instanceof pattern matching 更成熟)。
    • Java 21:虚拟线程(Project Loom)转正、结构化并发(java.util.concurrent.StructuredTaskScope,预览)。

3. 关键知识点详解(含代码/图示)

3.1 安装与多版本管理

  • 环境变量:配置 JAVA_HOME 与 PATH(Windows:系统属性;Mac/Linux:~/.zshrc 或 ~/.bashrc)。
  • 多版本管理工具:
    • Mac/Linux:jenv 或 SDKMAN!(推荐)。
    • 验证:java -version 与 javac -version 一致;IDEA 在 Project Structure 中设置 JDK。

示例(SDKMAN!):

bash
curl -s "https://get.sdkman.io" | bash
source "$HOME/.sdkman/bin/sdkman-init.sh"
sdk list java
sdk install java 21-tem
sdk use java 21-tem
java -version

3.2 升级路径与模块化兼容

  • 从 Java 8 → 11 → 17 的升级策略:
    • JPMS 模块化导致反射/内部 API 访问受限,需合理使用 --add-opens/--add-exports。
    • Java EE(javax.)模块已被移除(11),需迁移至 Jakarta EE(jakarta.)或 Spring 生态。
    • 默认 GC 变更:从 Parallel → G1(自 JDK 9),新 GC(ZGC、Shenandoah)可按需选择。
    • 借助工具:
      • 废弃 API 检测:jdeprscan。
      • 依赖治理:Maven Enforcer 插件约束 JDK/依赖版本。

示例(jdeprscan):

bash
$JAVA_HOME/bin/jdeprscan --release 17 target/classes

示例(Maven Enforcer):

xml
<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-enforcer-plugin</artifactId>
  <version>3.4.1</version>
  <executions>
    <execution>
      <goals><goal>enforce</goal></goals>
      <configuration>
        <rules>
          <requireJavaVersion>
            <version>[17,22)</version>
          </requireJavaVersion>
        </rules>
        <fail>true</fail>
      </configuration>
    </execution>
  </executions>
</plugin>

模块开放(示例):

bash
java --add-opens java.base/java.lang=ALL-UNNAMED -jar app.jar

3.3 OpenJDK 与商业 JDK 选择

  • 开源免费发行版:Eclipse Temurin(Adoptium, 推荐)、Amazon Corretto、Azul Zulu、Microsoft Build of OpenJDK。
  • 关注点:安全补丁频率、LTS 支持期限、容器/云原生优化、证书策略。
  • Oracle JDK 自 11 起商业用途需授权;多数企业选 OpenJDK 发行版。

3.4 字节码与 Class 文件结构(JDK 17 视角)

  • 结构:魔数 0xCAFEBABE、版本号、常量池、访问标志、类/超类/接口索引、字段表、方法表、属性表。
  • 常量池:存储符号引用(类名、方法描述符、字符串字面量等);反射、动态代理、invokedynamic 的基础。
  • 分析工具:javap。

示例(javap 分析):

java
public class BytecodeDemo {
    public static String foo(int x) {
        try {
            Integer a = x;           // 自动装箱 -> Integer.valueOf
            return "v=" + a;         // 字符串拼接 -> StringBuilder
        } finally {
            System.out.println("finally"); // try-finally 展开
        }
    }
}
bash
javac BytecodeDemo.java
javap -c -v BytecodeDemo
# 关注:
# - 常量池 (Constant pool)
# - 操作码:invokestatic/invokevirtual/monitorenter/monitorexit 等
# - 局部变量表/操作数栈/异常表

方法调用指令速览:

  • invokevirtual:实例方法(多态)
  • invokeinterface:接口方法
  • invokespecial:构造器/private/super(不走虚分派)
  • invokestatic:静态方法
  • invokedynamic:引导方法 + CallSite,Lambda/动态语言基础(Java 8 的 Lambdas)

3.5 Java 程序执行与 Loom

  • 虚拟线程(JDK 21 正式):轻量线程,百万级并发,API 简洁。
  • 结构化并发(JDK 21 预览):用 StructuredTaskScope 管理关联任务,简化并发错误处理与取消传播。

示例(虚拟线程 + 结构化并发):

java
// JDK 21, --enable-preview 可选(结构化并发是预览 API)
import java.util.concurrent.*;

public class LoomDemo {
    public static void main(String[] args) throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            Future<String> f1 = scope.fork(() -> fetch("A"));
            Future<String> f2 = scope.fork(() -> fetch("B"));
            scope.join();          // 等待所有任务
            scope.throwIfFailed(); // 若有异常则抛出并取消其他任务
            System.out.println(f1.resultNow() + " & " + f2.resultNow());
        }
    }

    static String fetch(String name) throws InterruptedException {
        Thread.sleep(100); // I/O 模拟
        return "ok-" + name;
    }
}

虚拟线程创建:

java
Thread.ofVirtual().start(() -> System.out.println(Thread.currentThread()));

3.6 基本语法:数据类型与变量

  • 基本类型(8 种)
    • 整型:byte(1B)、short(2B)、int(4B)、long(8B)
    • 浮点:float(4B, ~7 位有效)、double(8B, ~15 位)
    • 字符:char(2B, Unicode)
    • 布尔:boolean(JVM 层面按字节存储实现相关)
  • 引用类型:类、接口、数组、枚举、注解、记录(record)等。
  • 存储位置澄清:
    • “基本类型都在栈上”是误解。准确说:局部变量(含基本类型)的值在栈帧局部变量表;对象实例与数组元素(无论基本/引用)在堆上,对象字段在堆上。

取值范围与精度要点:

  • int 范围约 ±21 亿;long 用于大数计数与时间戳。
  • 金融/货币计算:严禁使用 float/double,使用 BigDecimal(字符串构造避免二进制小数误差)。
java
BigDecimal x = new BigDecimal("0.1");     // 正确
BigDecimal y = new BigDecimal(0.1);       // ⚠️ 精度问题

类型转换:

  • 自动提升:byte→short→int→long→float→double(算术中 byte/short/char 会先提升为 int)。
  • 强制转换:高精度转低精度需强转,可能溢出/截断。
java
byte b = (byte) 130; // 结果为 -126(仅保留低 8 位)
int i = 'A';         // 65,char 自动提升为 int

自动装箱/拆箱与缓存:

  • 编译器插入 Integer.valueOf/intValue。
  • Integer 缓存:[-128, 127],超出范围新建对象,== 比较可能出坑。
java
Integer a = 127, b = 127;     // true(缓存)
Integer c = 128, d = 128;     // false(不同对象)
System.out.println(a == b);
System.out.println(c == d);
// 规范:包装类型用 equals 比较

引用与 null:

  • 引用保存对象地址(引用别名);null 表示不指向任何对象。
  • NPE 定位:JDK 14+ 提供更详细的 NPE 信息。
  • 最佳实践:Optional、Objects.requireNonNull。
java
public String findName(User u) {
    Objects.requireNonNull(u, "user must not be null");
    return Optional.ofNullable(u.getName()).orElse("N/A");
}

3.7 运算符与表达式(含位运算)

  • 类别:算术、关系、逻辑、位运算、赋值、三目。
  • 优先级:一元 > 算术 > 移位 > 关系 > 按位 > 逻辑 > 三目 > 赋值。
  • 位运算高频实战:
    • 权限标志位(组合/检验)
    • HashMap 下标优化:n & (cap - 1)
    • 快速乘/除 2、交换两数
java
// 权限标志位
int READ = 1 << 0, WRITE = 1 << 1, EXEC = 1 << 2;
int perm = READ | WRITE;                  // 授权
boolean canWrite = (perm & WRITE) != 0;   // 检查

// 哈希取模(容量为 2 的幂)
int idx = hash & (capacity - 1);

// 异或交换(学习用,生产更可读的写法)
a ^= b; b ^= a; a ^= b;

3.8 流程控制与模式匹配

  • if-else、switch、for/while/do-while、break/continue/return。
  • 增强 for(for-each):
    • 遍历集合底层使用 Iterator;遍历数组使用下标。
    • 遍历集合删除元素:必须使用 Iterator.remove(),否则可能抛 ConcurrentModificationException。
java
Iterator<Integer> it = list.iterator();
while (it.hasNext()) {
    if (it.next() < 0) it.remove(); // 正确的删除方式
}
  • 标签跳转(不推荐,降低可读性):
java
outer:
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i * j > 2) break outer;
    }
}
  • switch 表达式(JDK 14 正式)与模式匹配(JDK 16 起 instanceof pattern;JDK 21 switch 上的模式匹配预览):
java
// switch 表达式
int score = 85;
String grade = switch (score / 10) {
    case 10, 9 -> "A";
    case 8 -> "B";
    case 7 -> "C";
    case 6 -> "D";
    default -> "F";
};

// instanceof 模式匹配
Object o = "hello";
if (o instanceof String s && s.length() > 3) {
    System.out.println(s.toUpperCase());
}

3.9 数组

  • 固定长度、同类型、索引从 0 开始,越界抛 ArrayIndexOutOfBoundsException。
  • 一维/多维(“数组的数组”,交错结构,每行长度可不同,内存不保证连续)。
  • 数组有 length 属性,数组名是引用;System.arraycopy() 原地高效拷贝。
java
int[] a = new int[5];          // 默认 0
int[] b = {1,2,3};
int[][] m = new int[2][];      // 交错数组
m[0] = new int[]{1,2};
m[1] = new int[]{3,4,5};

int[] dst = new int[3];
System.arraycopy(b, 0, dst, 0, 3);

Arrays 工具类(JDK 17):

  • 排序:Arrays.sort()
    • 基本类型:双轴快排(O(n log n))
    • 对象:TimSort(稳定,适合部分有序数据)
  • 二分查找:Arrays.binarySearch()(需先排序)
  • 复制/填充/比较:copyOf/copyOfRange/fill/equals/deepEquals
  • 注意:
    • Arrays.equals 比较一维数组元素是否相等;多维数组用 Arrays.deepEquals。
    • Arrays.asList 返回固定大小 List(不支持 add/remove),若需可变需 new ArrayList<>(...)。
    • 基本类型数组传入 Arrays.asList(int[]) 会被当作一个元素,需用 Integer[] 或流。
java
// asList 陷阱与正确用法
int[] arr = {1,2,3};
List<int[]> wrong = Arrays.asList(arr);       // 长度为 1,元素是 int[] 本身
Integer[] boxed = {1,2,3};
List<Integer> ok = new ArrayList<>(Arrays.asList(boxed));
// 或
List<Integer> ok2 = Arrays.stream(arr).boxed().toList();

4. 代码示例(可运行片段)

  1. 自动装箱/缓存陷阱与规范比较:
java
public class BoxingPitfall {
    public static void main(String[] args) {
        Integer a = 127, b = 127;
        Integer c = 128, d = 128;
        System.out.println(a == b);      // true
        System.out.println(c == d);      // false
        System.out.println(c.equals(d)); // true
    }
}
  1. 位运算在权限系统中的应用:
java
class Permission {
    static final int READ = 1 << 0;
    static final int WRITE = 1 << 1;
    static final int EXEC = 1 << 2;

    static int grant(int perm, int flag) { return perm | flag; }
    static int revoke(int perm, int flag) { return perm & ~flag; }
    static boolean has(int perm, int flag) { return (perm & flag) != 0; }

    public static void main(String[] args) {
        int p = 0;
        p = grant(p, READ);
        p = grant(p, WRITE);
        System.out.println(has(p, EXEC));  // false
        p = revoke(p, WRITE);
        System.out.println(has(p, WRITE)); // false
    }
}
  1. switch 表达式与 instanceof 模式匹配:
java
public class SwitchPatternDemo {
    static String typeOf(Object o) {
        return switch (o) {
            case null          -> "null";
            case Integer i     -> i > 0 ? "positive int" : "non-positive int";
            case String s      -> "String(" + s.length() + ")";
            default            -> "other";
        };
    }
}
  1. 虚拟线程并发请求(JDK 21):
java
import java.net.http.*;
import java.net.URI;

public class VirtualThreadHttp {
    public static void main(String[] args) throws Exception {
        var client = HttpClient.newHttpClient();
        Runnable task = () -> {
            try {
                var req = HttpRequest.newBuilder(URI.create("https://example.com")).build();
                var resp = client.send(req, HttpResponse.BodyHandlers.ofString());
                System.out.println(Thread.currentThread() + " -> " + resp.statusCode());
            } catch (Exception e) {
                e.printStackTrace();
            }
        };
        for (int i = 0; i < 1000; i++) Thread.ofVirtual().start(task);
        Thread.sleep(2000);
    }
}
  1. Arrays 工具与二分查找:
java
import java.util.Arrays;

public class ArraysDemo {
    public static void main(String[] args) {
        int[] arr = {5,1,4,3,2};
        Arrays.sort(arr);
        int idx = Arrays.binarySearch(arr, 3);
        System.out.println(Arrays.toString(arr)); // [1,2,3,4,5]
        System.out.println(idx);                  // 2
    }
}

5. 对比 / 易混淆点

概念JDKJREJVM
定义开发工具集运行时环境执行引擎
包含JRE + 开发工具JVM + 核心类库字节码执行、JIT、GC
适用开发/编译/打包/调试仅运行运行实现
概念基本类型包装类型
存储值语义(局部在栈帧,字段/数组元素在堆)引用语义(对象在堆)
默认值0/false/'\u0000'null
比较== 比较值equals 比较值,== 比较引用
其他无装箱成本自动装箱/拆箱有额外开销,存在缓存范围
Arrays.asList返回类型可变性常见坑
传对象数组固定大小 List不可 add/remove需 new ArrayList<>(...) 扩容
传基本类型数组List<int[]>(1 个元素)同上使用 Integer[] 或流 boxed
线程平台线程(OS 线程)虚拟线程(JDK 21)
数量级千级万到百万级
调度OS 调度JVM 调度,栈可挂起
适用CPU 密集/老代码I/O 密集/高并发服务

6. 最佳实践 / 常见坑

  • 版本与发行版
    • 生产优先 LTS(8/11/17/21);优选 Temurin、Corretto 等社区更新快的发行版。
    • 新特性引入循序渐进,升级路径建议 8 → 11 → 17 → 21,配合测试与工具(jdeprscan、Enforcer)。
  • 模块化兼容
    • 避免使用内部 API(com.sun.*),必要时显式 --add-opens,并规划长期替换。
  • 浮点精度
    • 金融计算使用 BigDecimal(字符串构造),避免二进制小数误差;谨慎使用 equals(需考虑精度/舍入)。
  • 装箱/拆箱
    • 性能敏感代码避免频繁装箱;Map/Stream 中注意自动装箱成本;比较用 equals。
  • 集合遍历删除
    • 使用 Iterator.remove();或使用 removeIf/过滤产出新集合。
  • Arrays 与集合互转
    • Arrays.asList 返回固定大小 List;基本类型数组陷阱需 boxed。
  • HashMap 容量
    • 预估容量并设置合适初始容量(2 的幂,或使用构造函数/负载因子);注意 hash & (n-1) 的位运算优化。
  • 异常信息与空值
    • JDK 14+ 的 NPE 定位更清晰;对外接口用 Optional 或清晰的非空契约;入参 Objects.requireNonNull。
  • 线程模型
    • I/O 密集使用虚拟线程可显著提升吞吐;CPU 密集仍需关注并行度与绑定线程池大小;使用结构化并发提升可控性。
  • AOT/Native Image
    • 启动时间敏感场景考虑 AOT;注意反射/动态代理/资源配置,配合 tracing agent/手工元数据描述。

7. 面试高频问题(简答)

  1. 为什么 Java 能做到“跨平台”?
  • 源码编译为平台无关的字节码;JVM 屏蔽 OS/硬件差异;不同平台提供各自的 JVM 实现。JVM 本身是平台相关的,字节码是平台无关的。
  1. JDK、JRE、JVM 的关系与区别?
  • JDK ⊃ JRE ⊃ JVM。JDK 包含开发工具;JRE 提供运行环境;JVM 是执行引擎(类加载、解释/JIT、GC)。
  1. 自动装箱/拆箱有哪些坑?
  • Integer 缓存范围 [-128,127] 导致 == 行为不一致;装箱/拆箱有性能与空指针风险(Integer 可能为 null 拆箱 NPE);比较统一用 equals。
  1. Java 8 → 17 升级的关键注意点?
  • Java EE 模块移除(11),迁移至 Jakarta 或 Spring;模块化导致反射与内部 API 受限(--add-opens);默认 GC 变更(G1),需复核性能与参数;使用 jdeprscan 检测弃用 API。
  1. 虚拟线程适合什么场景?
  • I/O 密集、高并发的服务端请求处理(百万级线程);与结构化并发结合可提升可观测性与错误处理易用性。CPU 密集不一定收益显著。

8. 总结(速记卡)

  • 体系:JDK(开发)/JRE(运行)/JVM(执行)层层包含;编译生成字节码,运行期解释+JIT 混合执行。
  • 版本:优选 LTS(8/11/17/21),关注模块化、移除的 Java EE、GC 默认变化与新并发(虚拟线程、结构化并发)。
  • 语法:强静态类型;基本类型与包装类差异明确;装箱/拆箱与缓存范围要牢记;null/Optional/requireNonNull 规范使用。
  • 运算:位运算在权限、哈希取模等高频出现;优先级和短路逻辑要清晰。
  • 数组:固定长度、交错多维;Arrays 工具常用;数组/集合互转坑点需避免。
  • 实战:版本管理(SDKMAN!/jenv)、升级工具(jdeprscan、Maven Enforcer)、并发模型(虚拟线程)、AOT(GraalVM)按需选型。

以上内容覆盖了「Java 语言基础与环境」的核心要点与实战建议,可作为复习与面试前的高效参考。