JDK 8

JDK 8 相对于 JDK 7 引入的主要特性: Lambda 表达式 函数式接口(Functional Interface) Stream API 方法引用(Method Reference) 默认方法(Default Method) Optional 新的日期时间 API(java.time) Nashorn JavaScript 引擎 Base64 并发增强(CompletableFuture、StampedLock、LongAdder) 重复注解(Repeating Annotations) 类型注解(Type Annotations) PermGen 移除,改用 Metaspace HashMap 内部实现优化:链表转红黑树 GC 组合废弃(JEP 173) Lambda 表达式 Lambda 来自数学中的 λ 演算(Lambda Calculus),是函数式编程的理论基础。函数式编程的核心思想是:函数是"一等公民",可以像普通值一样被传递、赋值、返回。JDK 8 引入 Lambda,本质上是给 Java 这门面向对象语言加入了函数式编程的能力。 Lambda 表达式允许将函数(即 Lambda 表达式本身)作为参数传递,简化匿名内部类的写法。 语法结构 (参数列表) -> 方法体 (参数列表):传入的参数,无参数时写空括号 () ->:箭头操作符,读作 “goes to”,分隔参数列表和方法体 方法体:要执行的逻辑 Runnable 是一个接口(java.lang.Runnable),只有一个无参方法 void run(),因此参数列表为空 ()。 // JDK 7:匿名内部类写法 Runnable r = new Runnable() { @Override public void run() { System.out.println("Hello"); } }; // JDK 8:Lambda 写法,箭头右边等价于 run() 的方法体 Runnable r = () -> System.out.println("Hello"); 将函数作为参数传递 JDK 8 之前,Java 无法直接传递"一段逻辑",只能把逻辑包装成匿名对象再传递。Lambda 表达式让你可以直接把逻辑作为参数传入方法,这就是"将函数作为参数传递"的含义。 ...

Created 25256-04-25 · Updated 14146-07-14 · 3 min · 618 words · -

Java Random 随机数

Java 里获取随机数的几种方式 在 Java 中,获取随机数广义上有三种途径: 用 System.currentTimeMillis() 拿到当前时间的毫秒数,当作一个"看起来随机"的数字。 用 Math.random(),返回一个 [0, 1) 之间均匀分布的 double 值。 用 java.util.Random 类,功能更完整的专业随机数工具类。 Math.random() 内部其实也是委托给一个 java.util.Random 实例(每个类第一次调用时惰性初始化一个内部单例),所以第 2、3 种本质上是同一套实现。 java.util.Random 的用法 Random 有两种构造方法: Random():用当前时间 System.currentTimeMillis() 作为种子(seed)。 Random(long seed):用指定的 seed 初始化。 产生 Random 对象后,调用 nextInt()、nextLong()、nextFloat()、nextDouble()、nextBoolean()、nextGaussian()(返回符合高斯/正态分布的 double)等方法获得不同类型的随机数。 如果两个 Random 对象用相同的种子、并且以相同的顺序调用相同的方法,它们返回的值会完全相同——因为随机数序列完全由种子和内部算法决定: import java.util.Random; public class TestRandom { public static void main(String[] args) { Random random1 = new Random(100); System.out.println(random1.nextInt()); System.out.println(random1.nextFloat()); System.out.println(random1.nextBoolean()); Random random2 = new Random(100); System.out.println(random2.nextInt()); // 和 random1 完全一样 System.out.println(random2.nextFloat()); System.out.println(random2.nextBoolean()); } } 指定范围内的随机数,可以用取模运算符 %(配合 Math.abs 避免负数): ...

Created 25256-05-25 · Updated 13136-07-13 · 2 min · 244 words · -

Java hashCode 对象哈希值的计算方法

equals 和 hashCode 的约定 Java 规定了 hashCode() 和 equals() 之间的约定: 两个对象用 equals() 判断相等,则它们的 hashCode() 必须相等。 反过来不成立:hashCode() 相等的两个对象,equals() 未必相等(也就是哈希冲突)。 自定义 hashCode() 时必须遵守第 1 条,否则这个对象放进 HashMap、HashSet 之类基于哈希的容器后会出现"明明 equals 相等却查不到"的 bug。第 2 条则说明哈希冲突是被允许的,容器内部(开链法或开放地址法)本来就要处理冲突,具体见 开链法与开放地址法。 Object 默认的 hashCode 是怎么算的 没有重写 hashCode() 的类,用的是 Object 的默认实现,也叫 identity hash code(身份哈希码)。一个常见的误解是"默认哈希值就是对象的内存地址"——这在 HotSpot JVM 里并不准确: 对象在 GC(尤其是分代 GC 的 minor GC)过程中会被移动到不同的内存区域,如果哈希值直接绑定内存地址,对象移动后哈希值就变了,这会破坏"同一个对象的 hashCode 在生命周期内不能变"的规定。 所以 HotSpot 实际上是单独生成、缓存一个哈希值(存在对象头里),和地址无关。生成算法由 JVM 参数 -XX:hashCode=N 控制,N 有 0~6 几种取值,比如 0 是全局的 Park-Miller 随机数生成器,1 是基于地址计算,4 是直接用地址,其余是自增计数器等方案;默认值是 5,用的是 Marsaglia’s xorshift 算法,基于每个线程私有的状态做一次异或位移运算生成伪随机数,和地址、和对象内容都无关。 也就是说,默认的 hashCode() 本质上是一个和对象绑定的伪随机数,只是为了效率碰巧可能和地址相关的实现(-XX:hashCode=1/4)也存在,但不是默认策略。 ...

Created 21216-09-21 · Updated 13136-07-13 · 2 min · 249 words · -

Hashtable 哈希表原理与哈希算法对比

Hash 表也称散列表,也有直接译作哈希表,是一种特殊的数据结构,同数组、链表以及二叉排序树等相比较有很明显的区别:它能够快速定位到想要查找的记录,而不是与表中存在的记录的关键字进行比较来进行查找。这源于 Hash 表设计的特殊性,它采用函数映射的思想将记录的存储位置与记录的关键字关联起来,从而能够很快速地进行查找。 java.util.Hashtable 是 Java 中最早的哈希表实现,自 JDK 1.0(1996 年)就已存在。在现代 Java 中它依然保留,但仅用于向后兼容旧代码:非线程安全场景推荐使用 HashMap,需要线程安全时推荐使用 ConcurrentHashMap,两者性能和易用性都更好,Hashtable 已不建议在新代码中使用。 Hash 表的设计思想 对于一般的线性表,比如链表,如果要存储联系人信息: 张三 13980593357 李四 15828662334 王五 13409821234 张帅 13890583472 可能会设计一个结构体包含姓名、手机号码这些信息,然后把 4 个联系人的信息存到一张链表中。当要查找“李四 15828662334”这条记录是否在这张链表中,或者想要得到李四的手机号码时,可能会从链表的头结点开始遍历,依次将每个结点中的姓名同“李四”进行比较,直到查找成功或者失败为止,这种做法的时间复杂度为 O(n)。即使采用二叉排序树进行存储,也最多为 O(logn)。假设能够通过“李四”这个信息直接获取到该记录在表中的存储位置,就能省掉中间关键字比较的这个环节,复杂度直接降到 O(1)。Hash 表就能够达到这样的效果。 Hash 表采用一个映射函数 f : key -> address 将关键字映射到该记录在表中的存储位置,从而在想要查找该记录时,可以直接根据关键字和映射关系计算出该记录在表中的存储位置。通常情况下,这种映射关系称作 Hash 函数,而通过 Hash 函数和关键字计算出来的存储位置(注意这里的存储位置只是表中的存储位置,并不是实际的物理地址)称作 Hash 地址。比如上述例子中,假如联系人信息采用 Hash 表存储,则当想要找到“李四”的信息时,直接根据“李四”和 Hash 函数计算出 Hash 地址即可。 hashCode 与 equals 的关系 有人会说,可以直接根据 hashCode 值判断两个对象是否相等吗?肯定是不可以的,因为不同的对象可能会生成相同的 hashCode 值。虽然不能根据 hashCode 值判断两个对象是否相等,但是可以直接根据 hashCode 值判断两个对象不等——如果两个对象的 hashCode 值不等,则必定是两个不同的对象。如果要判断两个对象是否真正相等,必须通过 equals 方法。 ...

Created 15156-06-15 · Updated 4046-07-04 · 1 min · 184 words · -

Spring 事务 @Transactional

spring 事务 @Transactional 使用 @Transactional 的要点有: 在DAO 层使用 JdbcTemplate 实现DB操作, 在 Service 的实现类上加上 @Transactional 注解, 不推荐在 Service 接口上加 @Transactional 注解. 需要进行事务控制的方法, 必须是 public 方法, 同时要打上 @Transactional 注解. 也可以在Class上加上 @Transactional 注解, 这样相当于给每个 public 函数加上了 @Transactional 注解, 当然我们还可以在其中的函数上加该注解, 这时候将以函数上的设置为准. @Transactional 失效场景 只有 public 方法生效:private/protected/包级方法上加 @Transactional 不生效,因为 Spring 默认约定只处理 public 方法(CGLIB 虽然能代理 protected 方法,但 Spring 事务模块不支持)。 同类自调用(self-invocation):@Transactional 依赖 Spring AOP 生成的代理对象,只有通过代理调用方法才会走事务逻辑。同一个类内部用 this.xxx() 直接调用会绕过代理,导致事务失效。 @Service public class OrderService { public void placeOrder() { this.updateStock(); // this 调用不经过代理,事务失效 } @Transactional public void updateStock() { ... } } 解决办法:把需要事务的方法拆到另一个 Bean 里,或注入自身代理(AopContext.currentProxy(),需要 exposeProxy=true)。 ...

Created 2026-02-02 · Updated 1016-07-01 · 2 min · 219 words · -

Java ASM 与运行时字节码织入

背景 BTrace、Arthas、Spring LTW、部分 AOP 框架都会在不改变业务源码的前提下改写字节码。底层常依赖 ASM(或其它字节码库)配合 JVM 的 java.lang.instrument API。 本文说明 ASM 做什么、运行时织入怎么走,以及与 Attach API / Agent 的关系。BTrace 的 attach 与脚本下发见 BTrace。概念总图见 Java 领域知识关系图。ClassLoader 可见性见 java classloader。 JLS 与 java.lang.instrument 分别指什么 JLS(Java Language Specification):Java 语言规范,定义语法、类型、可见性等;不规定 Agent 怎么 attach。 java.lang.instrument:JDK 里的标准 API 包,定义 Instrumentation、ClassFileTransformer,以及 Agent 入口约定(premain / agentmain 的方法签名、manifest 的 Premain-Class / Agent-Class)。 日常说「走 instrument 机制」= 用这套 API + JVM 内置实现,在类加载或 retransform 时回调你的 transformer。 Agent 框架定义了什么、解决什么问题 Agent 不是一个限制能力的沙箱,而是一套标准钩子 + 能力接口: 约定 / 能力 作用 manifest Agent-Class / Premain-Class 告诉 JVM 加载 agent 后调用哪个类的哪个入口 agentmain / premain + Instrumentation 在 JVM 启动后或 attach 后执行你的初始化逻辑 ClassFileTransformer 在类字节码进入 JVM 前(或 retransform 时)允许你替换字节码 Attach API loadAgent 运行中的 JVM 也能挂上 agent 要解决的问题:在不重启、不改磁盘上已部署 jar 的前提下,对已在内存中的类做字节码级增强(监控、诊断、部分场景下的热修复)。 ...

Created 15156-12-15 · Updated 30306-06-30 · 3 min · 580 words · -

Java ClassLoader

概述 不同 JVM 实现细节各异,下文只讨论 HotSpot 在 JDK 9 及以后(含当前 JDK 26)的行为。JDK 8 及更早的 ExtClassLoader、jre/lib/ext、sun.misc.Launcher 等表述见文末「JDK 8 及更早」。 本文从四方面展开:内置 ClassLoader 层次、双亲委托、自定义 ClassLoader、打破双亲委托的常见场景。类加载器「去哪里找类」由 classpath / module path 决定,概念见 Java Classpath 与 Module Path。 内置 ClassLoader 层次 JDK 运行时提供三个内置加载器(ClassLoader 官方文档): 加载器 获取方式 职责 Bootstrap Java 代码中为 null 加载 java.base 等核心模块;C++ 实现,不是 ClassLoader 子类 Platform(原 Extension) ClassLoader.getPlatformClassLoader() 加载 Java SE 平台 API 及其模块(非应用 classpath) System / Application ClassLoader.getSystemClassLoader() 加载应用 classpath、module path 上的类 父子关系: Bootstrap (null) └── PlatformClassLoader └── AppClassLoader (System ClassLoader) 实现类位于 jdk.internal.loader.ClassLoaders(PlatformClassLoader、AppClassLoader),均为内部 BuiltinClassLoader 体系。不要依赖具体类名写业务逻辑。 ...

Created 9096-09-09 · Updated 28286-06-28 · 3 min · 550 words · -

java -cp / -classpath

作用 -cp 与 -classpath 等价,用来指定运行时查找类和 JAR 的 classpath(类路径)。classpath 的概念、与依赖管理工具和 module path 的关系,见 Java Classpath 与 Module Path。 语法 java -cp <类路径> <主类全限定名> [应用参数...] 条目之间的分隔符:Linux/macOS 用冒号 :,Windows 用分号 ; 条目可以是目录或 JAR/ZIP 文件 . 代表当前目录 支持通配符 lib/*(匹配目录下所有 JAR,不递归子目录),但不能写 lib/*.jar 一旦显式指定 -cp,默认的当前目录 . 不再自动加入,需要时要显式写上 示例 # 当前目录 + 一个 jar(Linux/macOS) java -cp .:lib/hsqldb.jar org.hsqldb.Server -database mydb # Windows 用分号分隔 java -cp .;lib\hsqldb.jar org.hsqldb.Server -database mydb # 用通配符引入一个目录下所有 jar java -cp "app.jar:lib/*" com.example.Main 现在大多用 Maven/Gradle 或 IDE 自动拼装 classpath,手写 -cp 多见于命令行手动编译运行、排查问题等场景。 ...

Created 17176-04-17 · Updated 28286-06-28 · 1 min · 87 words · -

Java ClassLoader (merged into classloader)

本文已合并 这篇 2012 年的转载内容(基于 JDK 8 的 ExtClassLoader、sun.misc.Launcher、jre/lib/ext 等)已过时,相关内容已并入并按 JDK 9+ / 当前 HotSpot 重写: 类加载器层次、双亲委托、自定义 ClassLoader、打破双亲委托:见 Java ClassLoader classpath / module path 概念:见 Java Classpath 与 Module Path 本页仅作旧链接(/p4316/、java-classloader-2)重定向占位保留。 维护记录 时间 修改内容 原因 2026-06-28 退役为重定向占位,正文并入 classloader.md;新增 classpath 概念链到 classpath.md;分类 Java→language,删除 reprint 与现行 HotSpot 不符、与 classloader.md 高度重复

Created 26266-09-26 · Updated 28286-06-28 · 1 min · 47 words · lcf

Java Classpath 与 Module Path

Classpath 是什么 Classpath(类路径) 是 JVM 在运行时查找 .class 文件和 JAR 包的搜索路径集合。当代码里出现 new Foo() 或 Foo.class 时,类加载器要把 com.example.Foo 这个名字解析成磁盘上的 com/example/Foo.class 字节码,去哪里找,就由 classpath 决定。 可以把它理解成「类的 PATH 环境变量」:shell 的 PATH 决定 ls、git 这些命令去哪些目录找可执行文件;JVM 的 classpath 决定加载类时去哪些目录和 JAR 里找字节码。 一条 classpath 由多个条目组成,每个条目是一个目录或一个 JAR/ZIP 文件: 目录条目:以该目录为根,按包名展开找 .class(如条目 target/classes + 类 com.foo.Bar → 找 target/classes/com/foo/Bar.class) JAR 条目:把 JAR 当成一个打包的目录,在其中按同样规则找 分隔符与操作系统有关:Linux/macOS 用冒号 :,Windows 用分号 ;。 如何设置 classpath 按优先级从高到低,常见几种方式: 方式 写法 说明 -cp / -classpath java -cp app.jar:lib/* com.foo.Main 命令行显式指定,最常用;详见 java -cp CLASSPATH 环境变量 export CLASSPATH=... 全局生效,易污染,现代项目基本不用 可执行 JAR 的 Class-Path MANIFEST.MF 里的 Class-Path: JAR 内清单声明依赖;见 MANIFEST.MF 默认值 不指定时为当前目录 . 仅在没有任何设置时 -cp 支持通配符 lib/*(匹配该目录下所有 JAR,但不递归子目录),但不能写成 lib/*.jar。指定了 -cp 后默认的当前目录 . 会失效,需要的话要显式写进去。 ...

Created 28286-06-28 · Updated 28286-06-28 · 2 min · 295 words · -

Java SPI 与 ServiceLoader

是什么 SPI(Service Provider Interface,服务提供者接口)是 Java 内置的插件机制:允许第三方在不修改调用方代码的前提下,提供接口的实现。 核心思路一句话: 调用方只依赖接口 → 实现由 ServiceLoader 在运行时发现 调用方编译期只认接口,不写死任何实现类;具体用哪个实现,由 classpath 上的实现库通过约定文件「自报家门」,由 java.util.ServiceLoader 在运行时加载并实例化。这与 IoC 的思想类似——把装配的控制权移到程序之外,在模块化设计里尤其重要。 三个组成部分 组成 说明 接口(Service) 由框架或标准库定义,如 java.sql.Driver 实现(Provider) 由第三方库提供,如 com.mysql.cj.jdbc.Driver 注册文件 META-INF/services/<接口全限定名>,内容是实现类的全限定名 注册文件规则: 文件名是服务接口的完全限定二进制名称 每行一个实现类的全限定名;空行、行首 # 注释会被忽略 文件必须使用 UTF-8 编码 提供者类必须有无参构造方法,以便被反射实例化 工作流程 ① 调用方:ServiceLoader.load(Driver.class) ↓ ② JVM 扫描 classpath 上所有 jar 里的 META-INF/services/java.sql.Driver ↓ ③ 读出文件中的实现类名,反射实例化 ↓ ④ 返回一个可迭代所有实现的迭代器 ServiceLoader 以延迟方式查找并实例化:维护已加载实现的缓存,iterator() 先返回缓存中的元素,再按需查找剩余实现并依次加入缓存,可用 reload() 清缓存。 一个完整例子 接口与两个实现: package com.example.api; public interface Greeting { void sayHi(); } public class GreetingBeijing implements Greeting { public void sayHi() { System.out.println("hi from beijing"); } } public class GreetingZhengzhou implements Greeting { public void sayHi() { System.out.println("hi from zhengzhou"); } } 注册文件 src/main/resources/META-INF/services/com.example.api.Greeting: ...

Created 7076-11-07 · Updated 27276-06-27 · 2 min · 237 words · -

GraalVM Native Image 简介

是什么 GraalVM Native Image 是 GraalVM 提供的 AOT(Ahead-Of-Time) 编译路径:在构建期把 Java 应用及其依赖静态分析,编译成本地可执行文件(Linux/macOS/Windows 上的单一二进制或小型目录),运行时不再依赖传统意义上的 java -jar + 完整 JRE/HotSpot 进程模型。 与日常默认的 HotSpot 路径对比: HotSpot: 源码 → javac → .class → java 启动 JVM → 解释/JIT → 运行 Native Image: 源码 → javac → .class → native-image 构建 → 本地二进制 → 直接运行 GraalVM 平台总览见 GraalVM 简介。作为运行时有两种常见用法,不要混为一谈: 作为 HotSpot 上的高性能 JIT 编译器(仍是 java 命令跑字节码) 作为 Native Image 工具链(产出独立可执行文件,本文主题) 为什么用它 维度 HotSpot + JAR Native Image 冷启动 秒级常见(JVM 启动、类加载、Spring 扫描) 毫秒级常见 内存占用 完整 JVM 堆与元空间 无 HotSpot 堆模型;通常更小 镜像/分发 需 JRE 或容器内 Java 运行时 单一二进制 + 可选少量资源文件 峰值吞吐 JIT 预热后通常更强 无 C2 长时间优化,长时间 CPU 密集型可能不如 HotSpot 构建 mvn package 较快 native-image 构建慢,CI 时间显著增加 典型场景:Serverless/函数计算、CLI 工具、Kubernetes 中短生命周期且副本多的 Pod、对启动延迟敏感的边缘服务。 ...

Created 21216-06-21 · Updated 27276-06-27 · 4 min · 667 words · -

Spring Boot Executable JAR: Fat JAR 与 JarLauncher

背景 Spring Boot 把应用打成单个可执行 JAR,java -jar app.jar 即可启动内嵌 Tomcat(或 Jetty/Undertow)与全部依赖。这与传统「WAR 部署到外部 Tomcat、classpath 由容器管理」不同,也不同于 Maven jar-with-dependencies 那种把依赖 class 解压合并进一个 jar 的做法。 本文说明 Spring Boot 可执行 JAR 的内部布局、JarLauncher 如何启动应用,以及 repackage / bootJar 如何生成该产物。容器镜像、分层 JAR、Buildpack 等部署话题见 Spring Boot Container Packaging;框架层面的「内嵌服务器 + 可执行 JAR」见 Spring Boot。 Thin jar 与 fat jar Fat jar、uber jar、executable jar 在 Spring Boot 语境下指同一件事:一个「全包」可执行 jar。Fat jar 与 uber jar 只是不同叫法(uber 来自德语 über,意为「之上 / 全部」);Spring 官方文档更常写 executable jar。 与 Maven/Gradle 默认打出的普通 jar(常称 thin jar / plain jar)对比如下: ...

Created 24246-06-24 · Updated 27276-06-27 · 5 min · 973 words · -

Spring Boot Container Packaging: Fat JAR、分层 JAR 与 Buildpacks

背景 Spring Boot REST API 打成 jar 之后,放进容器常见有三层递进:先有一个能跑的 fat jar,再用分层 JAR 优化 Docker 缓存,最后用 Cloud Native Buildpacks 一键出镜像;若还要缩小运行时,可在 Buildpack 上显式开启 jlink 裁剪 JRE。fat jar 布局与 JarLauncher 见专文 Spring Boot Executable JAR。 Spring Framework 本身几乎不讲容器镜像;相关说明集中在 Spring Boot Reference 的 Packaging → Container Images。本文把分层 JAR、bootBuildImage、Paketo jlink 串成一条阅读路径,并附上官方文档索引。GraalVM Native Image 是另一条路线,见 GraalVM Native Image 简介。 三条路径总览 flowchart TB subgraph build [构建] SRC[源码 mvn package / bootJar] FAT[Fat JAR 可执行 uber jar] LAY[分层 JAR 含 layers.idx] end subgraph deploy [容器化] D1[简单 DockerfileCOPY jar + java -jar] D2[分层 Dockerfilejarmode extract --layers] BP[bootBuildImage / build-imagePaketo Buildpacks] end subgraph runtime [运行时 JVM] JRE[完整 JRE] JLINK[jlink 精简 JREBP_JVM_JLINK_ENABLED] end SRC --> FAT FAT --> LAY FAT --> D1 LAY --> D2 LAY --> BP D1 --> JRE D2 --> JRE BP --> JRE BP -.->|可选| JLINK 路径 典型产物 主要收益 复杂度 Fat jar + 简单镜像 单层 app.jar + JRE 基础镜像 上手最快 低 分层 JAR + Dockerfile 多 COPY 层 + JRE 推送镜像时依赖层可缓存 中 Buildpack(bootBuildImage) OCI 镜像,自动检测 jar 零 Dockerfile、与 Paketo 生态一致 低~中 Buildpack + jlink 同上,运行时 JRE 更小 缩小镜像体积 中(需调 env) 分层 JAR 分层 JAR 在 fat jar 布局不变的前提下,额外写入 BOOT-INF/layers.idx,声明哪些路径属于哪一层、层的写入顺序。Spring Boot 2.3 起默认开启;Maven repackage / Gradle bootJar 的 layered 无需额外配置即可得到。 ...

Created 21216-06-21 · Updated 24246-06-24 · 5 min · 1027 words · -

Java version history, jdk history

Java 由 Sun Microsystems 于 1995 年发布,2010 年起由 Oracle 维护。讨论「版本」时容易混用 J2SE 1.4、Java 5、Java SE 8、JDK 17 等说法——它们分属平台品牌、对外版本号、运行时版本字符串、class 文件格式等不同层次。下文先列发布简史与各版本文档链接,再分节说明命名如何演变。 发布简史 时间 版本 备注 1995-05 Java 1.0 语言与平台首次发布 → JDK 1.0 1997-02 Java 1.1 内部类、JAR、RMI 等 → JDK 1.1 1998-12 Java 1.2 Java 2 Platform;Swing、集合框架 1999-04 HotSpot 首次发布 独立 JVM 产品,可配 JDK 1.2 → HotSpot 1999-06 J2SE / J2EE / J2ME 三版平台划分 2000-05 Java 1.3 HotSpot 成为 JDK 默认 VM(Classic 仍可选) 2002-02 J2SE 1.4 assert、NIO、regex、JUL;Classic VM 移除 → JDK 1.4 2004-02 J2SE 1.5 Beta Tiger 公开 Beta(泛型、注解等);早于 GA 2004-09 Java 5 泛型、enum、for-each、注解、JUC → JDK 5 2005-06 Java SE 6 公布 J2SE 更名为 Java SE 2006-12 Java SE 6 GA 脚本引擎、Web Service 增强 2011-07 Java SE 7 GA NIO.2、ForkJoin → JDK 7 2014-03 Java SE 8 GA Lambda、Stream → JDK 8;后被 Oracle 追溯认定为 LTS 2017-09 Java SE 9 GA 模块系统、半年发布模型 → JDK 9 2018-03 Java SE 10 短期版本 2018-09 Java SE 11 新发布策略下首个 LTS 2019~2021 Java 12~16 短期版本 2021-09 Java SE 17 LTS 间隔改为每 2 年 → JDK 17 2022~2023 Java 18~20 短期版本 2023-09 Java SE 21 → JDK 21 相对 JDK 17 的变化 2024~2025 Java 22~24 短期版本 2025-09 Java SE 25 → JDK 25 相对 JDK 21 的变化 2026-03 Java SE 26 → JDK 26 相对 JDK 25 的变化 2027-09 Java SE 29(计划) 下一 LTS 2010 年 Oracle 收购 Sun;2017 年起 OpenJDK 成为参考实现。发行版选型见 OpenJDK;企业版演变见 Java EE。 ...

Created 20206-06-20 · Updated 24246-06-24 · 3 min · 636 words · -

JDK 5

JDK 5(Java SE 5)于 2004 年 9 月正式发布,是 JDK 1.4 之后的一次重大更新。第一个对外以「Java 5」而非「Java 1.5」命名的版本;平台品牌 J2SE → Java SE 的过渡亦在此前后。详见 Java 版本历史。 Tiger 语言特性(含注解)自 2002 年起在 JCP 并行规划(JSR 175 等于 JDK 1.4 发布后不久立项);2003 年 12 月有 Early Access 构建,2004 年 2 月公开 Beta——均早于上述正式版。 发布历程 「2004 年 9 月」指 GA(正式版);Tiger 在 JCP 与 Sun 产品线上的里程碑更早: 阶段 大约时间 说明 JDK 1.4 GA 2002-02 1.4 落地后,Tiger 语言特性(含 JSR 175 注解)即在 JCP 并行推进 JSR 175 立项 2002-03~04 JCP 评审通过,专家组成立 JSR 175 公开评审 2003-11 注解规范草案公开 Tiger Early Access 2003-12 预发布 JDK(含注解等语言特性),面向早期测试 Tiger 公开 Beta 2004-02 可在 java.sun.com/j2se/1.5 下载试用 JDK 5 Final 2004-09-30 对外称 Java 5 / J2SE 5.0;java.version 仍为 1.5.0_xxx 同一 Tiger 版本还打包了泛型(JSR 14)、枚举与增强 for(JSR 201)、JUC(JSR 166)等;整体由 JSR 176(J2SE 5.0 Release Contents)归集交付。 ...

Created 28286-03-28 · Updated 24246-06-24 · 3 min · 560 words · -

JLS: Java Language Specification

什么是 JLS JLS(Java Language Specification,Java 语言规范)是 Oracle 官方发布的权威文档,完整定义了 Java 编程语言的语法、语义和行为。 可以把它理解为 Java 语言的"宪法"——编译器、JVM 实现者以及语言特性设计者,都必须以 JLS 为准。 官方地址:Java SE 规范 JLS 的主要内容 JLS 按章节组织,每章覆盖语言的一个核心方面: 章节 主题 第 2 章 词法结构(关键字、字面量、标识符) 第 3 章 类型、值与变量 第 6 章 名称(作用域、遮蔽、访问控制) 第 8 章 类声明 第 9 章 接口声明(含注解类型) 第 14 章 语句 第 15 章 表达式 第 17 章 线程与锁(Java 内存模型) 为什么要了解 JLS 大多数开发者日常不需要阅读 JLS,但以下场景中它非常有用: 理解边界行为:遇到语言"奇怪"行为时(如整数溢出、字符串 == 比较、自动装箱陷阱),JLS 给出权威解释。 注解与反射:JLS 第 9 章详细定义注解类型声明(@interface)的语法和语义,是理解注解底层的基础。 Java 内存模型:JLS 第 17 章定义了可见性、有序性保证,是理解 volatile、synchronized、happens-before 的权威来源。 框架/工具开发:编写编译器插件、注解处理器(APT)、字节码工具时,需要精确理解语言规范。 JLS 与 JVM 规范的区别 JLS 和 JVMS(Java Virtual Machine Specification,Java 虚拟机规范)经常被混淆: ...

Created 23236-06-23 · Updated 23236-06-23 · 1 min · 139 words · -

OpenJDK JDK 发行版:Temurin、Corretto、Dragonwell

OpenJDK 与「发行版」 OpenJDK 是 Java 平台的开源参考实现(源码项目)。日常 java -version 看到的并不是「裸 OpenJDK 源码」,而是各厂商/社区基于该源码(或与之同步的分支)构建、测试、打包后的 JDK 发行版(distribution)。 多数发行版默认捆绑 HotSpot 虚拟机,因此 Temurin、Corretto、Dragonwell 之间换的是供应商、构建渠道与支持策略,不是换一套 JVM 实现。JVM 实现层选型见 Java 虚拟机。 OpenJDK 源码(openjdk/jdk) │ ├── 各发行版构建 + TCK / 内部 QA │ ▼ Temurin / Corretto / Dragonwell / Oracle JDK … │ └── 默认内含 HotSpot(Semeru 等除外,见下文) 常见发行版对照 发行版 维护方 许可 / 费用 典型特点 Eclipse Temurin Eclipse Adoptium 免费(GPLv2 + Classpath) AdoptOpenJDK 继任;通过 Java SE TCK;无特殊理由时的默认推荐 Amazon Corretto Amazon 免费 AWS 生态常用;长期支持声明;Corretto 补丁分支 Dragonwell(含 Alibaba Dragonwell) 阿里云 免费 面向国内与大促场景优化;部分版本含 Wisp 协程、增强 GC 等 Alibaba 扩展 Oracle JDK Oracle 免费(个人/开发等,见 Oracle 许可 FAQ)/ 商业订阅 Oracle 官方构建;与 OpenJDK 功能同步策略见 Oracle 文档 Microsoft Build of OpenJDK Microsoft 免费 Azure / Windows / GitHub Actions 场景常见 Red Hat Build of OpenJDK Red Hat 免费 / 随 RHEL 订阅支持 OpenShift、RHEL 企业环境 Azul Platform Core(Zulu 等) Azul 免费构建 + 商业支持 多版本、多平台;企业 SLA IBM Semeru IBM 免费 / 商业 OpenJ9 虚拟机,不是 HotSpot;见 jvm.md Linux 发行版自带的 openjdk-*-jdk(Debian/Ubuntu/Arch 包)也是基于 OpenJDK 源码的构建,可视为「 distro 维护的发行版」,更新节奏跟随各发行版仓库。 ...

Created 21216-06-21 · Updated 21216-06-21 · 2 min · 346 words · -

GraalVM

是什么 GraalVM 是 Oracle 主导的 高性能 JDK 发行版与工具链,基于 OpenJDK,但不止「又一个 Temurin」:它在同一产品里组合了 Java 运行时、Graal JIT 编译器、Native Image AOT、Truffle 多语言运行时 等能力。 日常讨论里常把 GraalVM 缩略成「Native Image 用的那个 JDK」,实际上 Native Image 只是其组件之一;完整图景见下文与 Java 虚拟机 生态对照。 发行版 版本 说明 Community Edition (CE) 开源(GPLv2 + Classpath exception),GitHub 发布 Enterprise Edition (EE) 商业支持,额外性能与工具(需 Oracle 许可) 安装后通常提供 java、javac、native-image、gu(GraalVM Updater,用于安装/管理组件)等命令。具体组件集因版本与安装方式而异。 三种常见使用方式 GraalVM 发行版 │ ┌─────────────────────┼─────────────────────┐ ▼ ▼ ▼ JVM 模式(java) Native Image Truffle / Polyglot HotSpot + Graal JIT AOT 本地二进制 JS/Python/Ruby 等同 JVM 上互操作 │ │ │ └── 见专文 └── 仍跑 .class,可替代默认 C2 路径(视版本与 -XX 开关) 1. JVM 模式:Graal 作 JIT 用 GraalVM 自带的 java 跑字节码,底层仍是 HotSpot VM,但可用 Graal 编译器 作为 JIT 替代或补充传统 C1/C2(取决于 JDK 版本与 JVM 参数;OpenJDK 曾实验性集成 Graal JIT,JDK 17+ 默认路径以 C2 为主,Graal JIT 需显式启用或视发行版而定)。 ...

Created 21216-06-21 · Updated 21216-06-21 · 2 min · 341 words · -

Java 模块系统:JPMS 与 Jigsaw

Java 9 之前的版本 .class 文件是 JVM 看到的最小可执行文件,而一个大型程序需要编写很多 Class,并生成一堆 .class 文件,不便于管理,所以,jar 文件就是 class 文件的容器。 在 Java 9 之前,一个大型 Java 程序会生成自己的 jar 文件,同时引用依赖的第三方 jar 文件,而 JVM 自带的 Java 标准库,实际上也是以 jar 文件形式存放的,这个文件叫 rt.jar,一共有 60 多 M。 如果是自己开发的程序,除了一个自己的 app.jar 以外,还需要一堆第三方的 jar 包,运行一个 Java 程序,一般来说,命令行写这样: java -cp app.jar:a.jar:b.jar:c.jar com.liaoxuefeng.sample.Main jar 只是用于存放 class 的容器,它并不关心 class 之间的依赖。 从 Java 9 开始,原有的 Java 标准库已经由一个单一巨大的 rt.jar 分拆成了几十个模块,这些模块以 .jmod 扩展名标识,可以在 $JAVA_HOME/jmods 目录下找到它们: java.base.jmod java.compiler.jmod java.datatransfer.jmod java.desktop.jmod ... 这些 .jmod 文件每一个都是一个 JDK 模块,模块名就是文件名去掉 .jmod 后缀(例如 java.base.jmod → 模块 java.base)。 ...

Created 8086-03-08 · Updated 21216-06-21 · 5 min · 1003 words · -