java 反射, reflect

java 反射, reflect http://www.iteye.com/topic/137944 什么是反射 反射的概念是由 Smith 在 1982 年首次提出的,主要是指程序可以访问、检测和修改它本身状态或行为的一种能力。这一概念的提出很快引发了计算机科学领域关于应用反射性的研究。它首先被程序语言的设计领域所采用, 并在 Lisp 和面向对象方面取得了成绩。其中 LEAD/LEAD++ 、OpenC++ 、MetaXa 和 OpenJava 等就是基于反射机制的语言。最近,反射机制也被应用到了视窗系统、操作系统和文件系统中。 反射本身并不是一个新概念,尽管计算机科学赋予了反射概念新的含义。在计算机科学领域,反射是指一类应用,它们能够自描述和自控制。也就是说,这类应用通过采用某种机制来实现对自己行为的描述 (self-representation) 和监测 (examination) ,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。 什么是Java中的类反射 Reflection 是 Java 程序开发语言的特征之一,它允许运行中的 Java 程序对自身进行检查,或者说"自审",并能直接操作程序的内部属性和方法。Java 的这一能力在实际应用中用得不是很多,但是在其它的程序设计语言中根本就不存在这一特性。例如,Pascal、C 或者 C++ 中就没有办法在程序中获得函数定义相关的信息。 Reflection 是 Java 被视为动态 (或准动态) 语言的关键,允许程序于执行期 Reflection APIs 取得任何已知名称之 class 的內部信息,包括 package、type parameters、superclass、implemented interfaces、inner classes, outer class, fields、constructors、methods、modifiers,並可于执行期生成instances、变更 fields 內容或唤起 methods。 Java类反射中所必须的类 Java的类反射所需要的类并不多,它们分别是: Field、Constructor、Method、Class、Object,下面我将对这些类做一个简单的说明。 Field类: 提供有关类或接口的属性的信息,以及对它的动态访问权限。反射的字段可能是一个类 (静态) 属性或实例属性,简单的理解可以把它看成一个封装反射类的属性的类。 Constructor类: 提供关于类的单个构造方法的信息以及对它的访问权限。这个类和Field类不同,Field类封装了反射类的属性,而Constructor类则封装了反射类的构造方法。 Method类: 提供关于类或接口上单独某个方法的信息。所反映的方法可能是类方法或实例方法 (包括抽象方法) 。 这个类不难理解,它是用来封装反射类方法的一个类。 ...

Created 9096-09-09 · Updated 21216-06-21 · 5 min · 947 words · -

通过java反射机制获取该类的所有属性类型、值、

通过反射遍历对象字段的示例。object.getClass() 返回该实例类型的 Class 对象;反射 API 总览见 Java 反射机制。 通过java反射机制获取该类的所有属性类型、值 http://blog.csdn.net/sd4000784/article/details/7448221 方法使用了这俩个包下的 field 和method import Java.lang.reflect.Field; import java.lang.reflect.Method; public static void getObjectValue(Object object) throws Exception { //我们项目的所有实体类都继承BaseDomain (所有实体基类: 该类只是串行化一下) //不需要的自己去掉即可 if (object != null && object instanceof BaseDomain) {//if (object!=null ) --begin // 拿到该类 Class<?> clz = object.getClass(); // 获取实体类的所有属性,返回Field数组 Field[] fields = clz.getDeclaredFields(); for (Field field : fields) {// -for() begin System.out.println(field.getGenericType());//打印该类的所有属性类型 // 如果类型是String if (field.getGenericType().toString().equals( "class java.lang.String")) { // 如果type是类类型,则前面包含"class ",后面跟类名 // 拿到该属性的gettet方法 /** * 这里需要说明一下: 他是根据拼凑的字符来找你写的getter方法的 * 在Boolean值的时候是isXXX (默认使用ide生成getter的都是isXXX) * 如果出现NoSuchMethod异常 就说明它找不到那个gettet方法 需要做个规范 */ Method m = (Method) object.getClass().getMethod( "get" + getMethodName(field.getName())); String val = (String) m.invoke(object);// 调用getter方法获取属性值 if (val != null) { System.out.println("String type:" + val); } } // 如果类型是Integer if (field.getGenericType().toString().equals( "class java.lang.Integer")) { Method m = (Method) object.getClass().getMethod( "get" + getMethodName(field.getName())); Integer val = (Integer) m.invoke(object); if (val != null) { System.out.println("Integer type:" + val); } } // 如果类型是Double if (field.getGenericType().toString().equals( "class java.lang.Double")) { Method m = (Method) object.getClass().getMethod( "get" + getMethodName(field.getName())); Double val = (Double) m.invoke(object); if (val != null) { System.out.println("Double type:" + val); } } // 如果类型是Boolean 是封装类 if (field.getGenericType().toString().equals( "class java.lang.Boolean")) { Method m = (Method) object.getClass().getMethod( field.getName()); Boolean val = (Boolean) m.invoke(object); if (val != null) { System.out.println("Boolean type:" + val); } } // 如果类型是boolean 基本数据类型不一样 这里有点说名如果定义名是 isXXX的 那就全都是isXXX的 // 反射找不到getter的具体名 if (field.getGenericType().toString().equals("boolean")) { Method m = (Method) object.getClass().getMethod( field.getName()); Boolean val = (Boolean) m.invoke(object); if (val != null) { System.out.println("boolean type:" + val); } } // 如果类型是Date if (field.getGenericType().toString().equals( "class java.util.Date")) { Method m = (Method) object.getClass().getMethod( "get" + getMethodName(field.getName())); Date val = (Date) m.invoke(object); if (val != null) { System.out.println("Date type:" + val); } } // 如果类型是Short if (field.getGenericType().toString().equals( "class java.lang.Short")) { Method m = (Method) object.getClass().getMethod( "get" + getMethodName(field.getName())); Short val = (Short) m.invoke(object); if (val != null) { System.out.println("Short type:" + val); } } // 如果还需要其他的类型请自己做扩展 }//for() -end }//if (object!=null ) --end } // 把一个字符串的第一个字母大写、效率是最高的、 private static String getMethodName(String fildeName) throws Exception{ byte[] items = fildeName.getBytes(); items[0] = (byte) ((char) items[0] - 'a' + 'A'); return new String(items); }

Created 17176-09-17 · Updated 21216-06-21 · 2 min · 333 words · -

java.lang.Class类

说明 java.lang.Class 的说明已整理至专文:java.lang.Class。 本文保留 url java-class-object 以兼容旧链接。 维护记录 时间 修改内容 原因 2026-06-21 正文删去,改为指向 lang-class.md 与 Class 专文去重

Created 2026-01-02 · Updated 21216-06-21 · 1 min · 17 words · -

Java反射机制

Java反射机制 通过反射取对象的属性值 import java.lang.reflect.Field; import java.util.HashMap; import java.util.Map; public class ReflectTest { public static void main(String[] args) { Foo foo = new Foo(); foo.setBar("xxx"); Map map = new HashMap(); map.put("foo", "bar"); foo.setTags(map); Class fooClass = foo.getClass(); System.out.println("name:" + fooClass.getName()); try { Field[] fields = fooClass.getDeclaredFields(); for (Field field : fields) { field.setAccessible(true); System.out.println("field: " + field.getName()); Object obj = field.get(foo); System.out.println(obj); } Field field0 = fooClass.getDeclaredField("bar"); System.out.println(field0); } catch (Exception e) { e.printStackTrace(); } } } class Foo { private String bar; private Map<String, String> tags; public void setBar(String bar) { this.bar = bar; } public void setTags(Map tags) { this.tags = tags; } } Reflection 是Java被视为动态 (或准动态) 语言的一个关键性质。这个机制允许程序在运行时透过Reflection APIs取得任何一个已知名称的class的内部信息,包括其modifiers (诸如public, static 等等) 、superclass (例如Object) 、实现之interfaces (例如Cloneable) ,也包括fields和methods的所有信息,并可于运行时改变fields内容或唤起methods。本文借由实例,大面积示范Reflection APIs。 ...

Created 2026-01-02 · Updated 21216-06-21 · 5 min · 1008 words · -

Class.forName

Class.forName 是获取 java.lang.Class 对象的一种方式:用字符串形式的完全限定类名在运行时查找并加载类。类名来自配置、用户输入或 JDBC 驱动名等场景时,通常走这条路径。 API 形式 // 等价于 forName(name, true, 当前 ClassLoader) Class<?> clazz = Class.forName("com.example.Foo"); // 显式控制是否初始化、使用哪个 ClassLoader Class<?> clazz = Class.forName("com.example.Foo", false, classLoader); 第二个参数 initialize: true:加载后执行类初始化(静态代码块、静态字段赋值等) false:只加载并链接,暂不初始化 单参数 Class.forName(name) 相当于 initialize = true。 与 .class、getClass() 的区别 方式 类名来源 默认是否初始化 Foo.class 编译期写死 否 obj.getClass() 已有实例 否 Class.forName("Foo") 运行期字符串 是 Class 对象本身的含义、唯一性等见专文 java.lang.Class。 典型用途 JDBC 驱动加载(历史写法) 早期 JDBC 常用 Class.forName 触发驱动类静态块,向 DriverManager 注册: Class.forName("com.mysql.cj.jdbc.Driver"); Connection conn = DriverManager.getConnection(url, user, password); 驱动类静态块执行即可完成注册,一般不必再 newInstance()。现代 JDBC 4+ 驱动多通过 SPI 自动加载,此写法已较少见,但有助于理解 forName 与类初始化的关系。 ...

Created 21216-12-21 · Updated 21216-06-21 · 1 min · 127 words · -

java.lang.Class

java.lang.Class 是 JVM 为每一种 Java 类型在运行时维护的元信息对象。它不代表某个业务实例,而是代表类型本身——类、接口、枚举、注解类型、数组、基本类型乃至 void 都有对应的 Class 实例。 反射、注解 读取、框架扫描等能力,都依赖先拿到正确的 Class 对象。 Class 与实例的区别 实例对象 Class 对象 含义 某个类的具体对象 描述类型本身的元数据 示例 new SampleSubject() SampleSubject.class 数量 可创建多个实例 每种类型在 JVM 中唯一 SampleSubject a = new SampleSubject(); SampleSubject b = new SampleSubject(); a.getClass() == SampleSubject.class; // true a.getClass() == b.getClass(); // true:同一类型的 Class 只有一个 a == b; // false:实例不同 何时创建、由谁创建 Class 没有 public 构造方法,不能 new Class()。对象在类加载过程中由 JVM 通过类加载器的 defineClass 等机制创建。加载流程见 java classloader。 ...

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

Servlet 生命周期

Servlet 运行在 Servlet 容器中,生命周期由容器管理。规范通过 Servlet 接口的 init()、service()、destroy() 描述这一过程;Java EE 8 及以前包名为 javax.servlet.*,Spring Boot 3 起对应 Jakarta Servlet(jakarta.servlet.*),语义不变。 四个阶段 加载和实例化 Servlet 容器负责加载和实例化 Servlet。容器启动时,或在首次需要该 Servlet 处理请求时,创建实例。容器通过类加载器找到 Servlet 类,再用反射调用无参构造方法实例化,因此业务 Servlet 一般不应定义带参构造方法。 初始化 实例化后,容器调用 init() 做一次性初始化(如读取配置、建立连接)。每个 Servlet 实例的 init() 只执行一次。初始化期间可通过 ServletConfig 读取配置(传统项目多在 web.xml,Spring Boot 中常由框架代劳)。失败时可抛出 ServletException 或 UnavailableException 通知容器。 请求处理 请求到达后,容器调用 service();HttpServlet 会按 HTTP 方法分派到 doGet()、doPost() 等。service() 执行前 init() 必须已成功完成。处理中通过 ServletRequest / ServletResponse(或 HttpServletRequest / HttpServletResponse)读写请求与响应。若抛出表示永久不可用的 UnavailableException,容器会调用 destroy() 并返回 404;暂时不可用则返回 503。 服务终止 容器卸载 Servlet 或关闭时调用 destroy(),释放资源。之后实例被回收;若再次需要,容器会创建新实例。 ...

Created 30306-10-30 · Updated 21216-06-21 · 2 min · 227 words · -

Java 抽象类与接口

原文参考:abstract class 和 interface 的区别(天极网,约 2004)。下文在保留原意的基础上整理格式;Java 8 起接口支持 default/static 方法,见 JDK 8 与文末说明。 概述 在 Java 中,abstract class 与 interface 都用于抽象类型定义,语法上相似,甚至有时可以互换,但二者在继承模型、默认行为与设计理念上差异明显。选型往往反映对问题领域与设计意图的理解是否到位。 快速对比 维度 抽象类 接口 继承 / 实现 extends,单继承 implements,可多实现 构造函数 可以有 不能有 实例字段 可以有 传统上仅 public static final 常量(一般不定义字段) 方法 可有抽象与非抽象方法 传统上方法均为 public abstract;Java 8+ 可有 default/static 访问修饰 方法可为任意修饰符 方法默认 public 设计理念 常表示 is-a(本质同一类事物) 常表示 like-a / 契约(具备某种能力) 理解抽象类 abstract class 与 interface 都用于定义「抽象体」——注意此处「抽象类」是面向对象概念,不等同于关键字 abstract class。 在面向对象中,并非所有类都对应可实例化的具体对象。若类中信息不足以描述一个具体对象,即为抽象类。它往往表征问题领域中的抽象概念,例如图形编辑软件里的「形状」:圆、三角形是具体概念,「形状」在问题域中不存在对应实体,故抽象类不可实例化。 抽象类主要用于类型隐藏:固定一组行为的抽象描述,具体实现由派生类完成。模块依赖稳定的抽象体,符合开闭原则(OCP)。 语法层面的差异 以名为 Demo 的类型为例: ...

Created 3036-06-03 · Updated 20206-06-20 · 2 min · 371 words · -

Java 8 函数式接口 functional interface

原文参考:Secrets of Java 8 Functional Interface(colobu,2014) 概述 函数式接口不是一种新类型,而是 Java 8 对一类早已存在的接口给出的正式定义与命名:接口中恰好有一个抽象方法(SAM,Single Abstract Method),因此可以作为 Lambda 表达式或方法引用的目标类型。判定细节见下一节 SAM 定义。 可以这样理解二者关系: 概念 角色 Lambda / 方法引用 Java 8 新语法,表达「一段可传递的逻辑」 函数式接口 类型契约——Lambda 不能单独存在,必须赋给通过 SAM 判定的接口 Java 8 没有新增独立的函数类型(arrow type),而是继续用 interface 承载函数语义。JDK 8 之前已有不少接口在结构上符合 SAM(如 Runnable、Comparator),只是当时没有「函数式接口」这一术语,也不能写 Lambda 赋给它们;Java 8 把 SAM 规则写进语言规范,并新增 java.util.function 包中的标准函数式接口。 函数式接口代表一种契约:在需要它的上下文中,实际期望的是符合该契约的函数。Lambda 不能脱离上下文存在,必须有一个明确的目标类型,而这个目标类型必须是通过 SAM 判定的函数式接口。 SAM(Single Abstract Method) SAM 即 Single Abstract Method:接口中恰好有一个抽象方法(合并父接口继承来的抽象方法,并按规则排除 Object 的 public 方法)。 SAM 是函数式接口的判定标准 「函数式接口」是 Javadoc 和语言特性里的叫法;SAM 是判定它是否成立的结构规则。二者在 Java 8 里指同一类接口,只是视角不同: ...

Created 24246-12-24 · Updated 20206-06-20 · 6 min · 1066 words · -

Java 内部类

JDK 1.1 起支持在类或方法内部定义嵌套类。本文整理成员内部类、静态嵌套类、局部内部类与匿名内部类的用法与设计动机;版本背景见 JDK 1.1。 概览 静态与非静态 Java 把「定义在外部类内部的类」统称嵌套类(nested class)。按是否带 static,分为两大类: 静态嵌套类 非静态内部类 关键字 static class 无 static(成员 / 局部 / 匿名) 与外部类实例 不绑定,可 new Outer.Inner() 绑定,需 outer.new Inner() 访问 outer 的 private 仅 static 成员 实例成员(含 private) 典型用途 Builder、Holder 单例、Map.Entry 迭代器 helper、回调、访问 outer 私有状态 非静态内部类又常按定义位置细分为:成员内部类、局部内部类、匿名内部类。下文分别举例。 解决什么问题 封装与分组:把只服务于外部类的 helper 类型收进 Outer.Inner,缩小可见范围;外部 API 只暴露外层类或接口。 表达逻辑关联:其它代码也可以使用内部类(如 Map.Entry、Person.Home),嵌套表示「该类型从属于外部类的概念域」,而不一定是「只用一次」。 访问私有成员:非静态内部类可直接读写 outer 的 private 字段和方法,不必为此暴露 getter;这是相对顶层 helper 类的关键优势。 局部 / 匿名:封装只在一处使用、且与当前方法或外部类强相关的逻辑;JDK 8 起,SAM 回调场景优先用 Lambda(见下文「与现代 Java」)。 单继承下的行为组合:内部类可独立 extends / implements,与外部类已有继承关系互不干扰(现代代码中相对少见,更多用组合或接口 default 方法)。 成员内部类 public class OuterClass { private String name; private int age; class InnerClass { public InnerClass() { name = "mark"; age = 20; } public void echo() { System.out.println(name + " " + age); } } } 问题思考 上面这个很简单的例子中,也包含了很多应该思考的问题: ...

Created 4046-04-04 · Updated 20206-06-20 · 5 min · 921 words · -

Java 断言(assert)

assert 自 JDK 1.4 起成为 Java 语言关键字,在 JDK 21、25 等当前版本中依然存在,语法与语义未变。但它不是单元测试框架里的断言,也不适合承担业务代码对外契约校验。本文说明语法、运行时开关,以及在新版本 Java 里何时该用、何时不该用。 与现代 Java 的关系 assert 没有被废弃,也没有被新语法取代。JDK 5 之后的泛型、JDK 7 的 Objects.requireNonNull、JDK 14 的 switch 表达式等,解决的是类型安全、空值检查、控制流等问题,与 assert 的「内部不变量检查」定位不同。 现代代码里更常见的替代手段: 场景 更常见的做法 对外参数校验 Objects.requireNonNull、IllegalArgumentException、Bean Validation 不可达分支 switch 的 default 抛异常,或 sealed + 穷尽匹配 单元测试断言 JUnit assertEquals、AssertJ assertThat、TestNG assertTrue 开发期内部不变量 少数团队仍用 assert;更多团队用显式 if + 抛 IllegalStateException 结论:assert 在新版本 Java 里仍可用,但已不是主流推荐写法;日常业务与库 API 更倾向显式异常,测试用测试框架断言。 与单元测试断言的区别 这是两个完全不同的概念: Java assert JUnit / AssertJ 等 形式 语言关键字 测试框架 API 默认行为 运行时关闭,需 -ea 开启 测试运行时始终执行 失败时 抛出 AssertionError(Error 子类) 抛出 AssertionFailedError 等,标记测试失败 典型用途 源码里的内部不变量 验证方法返回值、异常、状态 单元测试几乎不会用 Java assert 关键字来写断言。Maven Surefire、Gradle 默认也不给测试进程加 -ea。测试里应使用框架提供的 assert* 方法。 ...

Created 21216-09-21 · Updated 20206-06-20 · 3 min · 440 words · -

JDK 17

概述 JDK 17 是 Java 的长期支持版本(LTS),于 2021 年 9 月发布。上一个 LTS 是 JDK 11(2018 年 9 月);中间 JDK 12~16 为半年一发的短期特性版本,每版引入少量 JEP,最终在 JDK 17 汇总为新的 LTS 基线。 从 JDK 11 直接跳到 JDK 17 时,需要关注的不只是 JDK 17 当版的 14 个 JEP,还包括 12~16 中已转正的语言特性(switch 表达式、文本块、record、模式匹配等)以及 JVM、工具链方面的累积变化。版本节奏与 LTS 策略见 Java 版本历史。 版本路线(JDK 11 → JDK 17) 版本 发布日期 类型 JEP 数 JDK 11 2018-09 LTS 17 JDK 12 2019-03 短期 8 JDK 13 2019-09 短期 5 JDK 14 2020-03 短期 16 JDK 15 2020-09 短期 14 JDK 16 2021-03 短期 17 JDK 17 2021-09 LTS 14 JDK 12(2019-03) JEP 特性 说明 189 Shenandoah GC(实验) 低延迟 GC,首次以实验特性引入 230 微基准测试套件 JMH 纳入 JDK,便于性能基准编写 325 switch 表达式(预览) switch 可作表达式返回值,支持 yield 334 JVM 常量 API java.lang.constant 包,描述 nominal 常量 340 统一 AArch64 端口 合并 32/64 位 ARM 端口实现 341 默认 CDS 归档 安装时生成默认 class 数据共享归档,缩短启动时间 344 G1 可中止混合回收 混合 GC 周期过长时可中止,降低停顿风险 346 G1 及时归还空闲内存 空闲堆内存更快归还给操作系统 JDK 13(2019-09) JEP 特性 说明 350 动态 CDS 归档 应用退出时生成定制 CDS 归档,进一步加速启动 351 ZGC 归还未用内存 ZGC 将未使用堆页归还给 OS 353 重写旧版 Socket API Socket/ServerSocket 底层实现现代化 354 switch 表达式(第二次预览) 语法与行为微调 355 文本块(预览) 多行字符串字面量,三引号 """ 语法 JDK 14(2020-03) 语言特性 JEP 特性 说明 305 instanceof 模式匹配(预览) 在 instanceof 中直接绑定变量 361 switch 表达式(正式) 预览两轮后转正,可替代传统 switch 语句 359 record(预览) 不可变数据载体,紧凑语法 368 文本块(第二次预览) 转义规则与缩进处理改进 switch 表达式示例: ...

Created 25256-04-25 · Updated 20206-06-20 · 6 min · 1076 words · -

Project Panama 与 Foreign Function & Memory API

Project Panama 是 OpenJDK 下的长期项目,目标是在 JVM 与 C 等本地代码之间建立更安全、更符合 Java 习惯互操作层。其交付物中最核心、与 JNI 直接对标的是 Foreign Function & Memory API(下称 FFM API,包名 java.lang.foreign),在 JDK 22 随 JEP 454 正式定稿。 发音:英语读作 /ˈpænəmɑː/,音节大致为「帕-nuh-马」,与中美洲国家 Panama(巴拿马)相同。OpenJDK 以地峡(isthmus)比喻 JVM 与本地代码之间的桥梁,故取此名。 本文说明 Panama 解决什么问题、核心概念怎么用,以及与 JNI 的取舍。JDK 版本层面的变更见 JDK 21 升级到 JDK 25 的变更汇总。 Panama 是什么 Project Panama 不只 FFM API,还包括: 组件 作用 Foreign Function & Memory API 调用 C 函数、读写堆外内存 Vector API SIMD 向量计算(另一 JEP 线,本文不展开) jextract 从 C 头文件生成 Java 绑定 日常说的「用 Panama 替代 JNI」,通常指 FFM API:用纯 Java + 标准库完成原本需要 native 方法、javah 头文件和 C/C++ 胶水代码的工作。 ...

Created 19196-06-19 · Updated 19196-06-19 · 4 min · 721 words · -

Java JNI

Java 运行在虚拟机中,若要调用操作系统动态库,需声明 native 方法,再由 C/C++ 实现。Java 调用这些 native 方法,等价于调用本地代码中的函数。这套机制称为 JNI(Java Native Interface,Java 本地编程接口)。 Android NDK 开发同样建立在 JNI 之上。下文在纯 Java 环境下编译 C 动态库并用 Java 调用,由浅入深给出四个示例。原文参考:CSDN - u_xtian。 相关:System.load 与 System.loadLibrary、JNI 类型签名 [B [C 等。JDK 22 起可用 Project Panama / FFM API 替代多数 JNI 场景;版本变更见 JDK 21 升级到 JDK 25 的变更汇总。 示例一:Hello World Java:HelloJni.java public class HelloJni { static { System.loadLibrary("hello"); } public static native void sayHello(); public static void main(String[] args) { HelloJni.sayHello(); } } 生成头文件 javac HelloJni.java javah -jni HelloJni # 旧版工具;JDK 8+ 可用 javac -h . HelloJni.java 生成的 HelloJni.h: ...

Created 14146-04-14 · Updated 19196-06-19 · 5 min · 953 words · -

JDK 21 升级到 JDK 25 的变更汇总

JDK 21(2023-09,LTS)到 JDK 25(2025-09,LTS)之间跨越了 JDK 22、23、24 三个非 LTS 版本。JDK 25 是 JDK 21 之后的下一个 LTS,Oracle 计划每两年发布一个 LTS(21 → 25 → 29)。 本文汇总这四个版本的重要变更,重点关注 breaking change 和需要主动迁移的部分。JDK 21 之前的变更见 JDK 17 升级到 JDK 21 的变更汇总;JDK 25 之后的变更见 JDK 26 相对 JDK 25 的变化。 JDK 22(2024 年 3 月) 正式特性 JEP 454: Foreign Function & Memory API(正式版) Project Panama 的核心 API 定稿,替代 JNI 处理本地代码互操作: // 调用 C 库函数 Linker linker = Linker.nativeLinker(); SymbolLookup stdlib = linker.defaultLookup(); MethodHandle strlen = linker.downcallHandle( stdlib.find("strlen").orElseThrow(), FunctionDescriptor.of(ValueLayout.JAVA_LONG, ValueLayout.ADDRESS) ); try (Arena arena = Arena.ofConfined()) { MemorySegment str = arena.allocateFrom("hello"); long len = (long) strlen.invoke(str); } JEP 456: Unnamed Variables & Patterns(正式版) ...

Created 19196-06-19 · Updated 19196-06-19 · 5 min · 937 words · -

工作流引擎

什么是工作流引擎 工作流引擎(Workflow Engine / Process Engine)是一种用于定义、执行和管理业务流程的中间件。它把业务逻辑从代码中剥离出来,用流程图(或规则配置)来描述"下一步该做什么、由谁来做、满足什么条件才能流转",从而让业务流程可配置、可追踪、可审计。 典型应用场景: 审批流:请假、报销、合同会签 订单流转:下单 → 支付 → 发货 → 收货 → 售后 任务编排:复杂的多步骤业务处理链路 规则驱动的路由决策 主流引擎一览 工作流引擎大致分两类: BPMN 流程引擎:遵循 BPMN 2.0 标准,配套可视化设计器,偏重人工审批、流程管理 轻量规则流引擎:不依赖 BPMN,以组件编排为核心,更适合纯代码驱动的业务链路 jBPM 官网:https://jbpm.org 仓库:https://github.com/kiegroup/jbpm jBPM 是 Java 工作流引擎的"鼻祖",由 Red Hat(JBoss)维护,历史最悠久。 特点: 与 Drools(规则引擎)深度集成,适合流程 + 规则组合的场景 提供 KIE Workbench 可视化管理平台 技术栈较重,学习曲线陡峭 社区活跃度不如 Flowable,国内使用较少 适用场景: 已有 Red Hat/JBoss 技术栈,或需要流程与 Drools 规则引擎联动。 Activiti 官网:https://www.activiti.org 仓库:https://github.com/Activiti/Activiti Activiti 是国内使用最广泛的开源 BPMN 流程引擎,诞生于 2010 年,由 Alfresco 公司主导,核心开发者来自 jBPM。 ...

Created 16166-06-16 · Updated 16166-06-16 · 5 min · 900 words · -

JDBI 使用笔记

JDBI 官网 JDBI 是一个非 ORM 的 Java 数据库访问库,特别适合数据库固定不变的场景——即不需要兼容多种数据库、也不打算更换数据库的项目。如果需要多数据库兼容,建议选择 Hibernate 等 ORM 框架。 JDBI 的优点: API 与 JDBC 接近,学习成本低 jdbi3 支持流式 / 函数式编程风格 源码实现清晰,封装出的数据库代码可读性强 两种 API 风格 Fluent API 链式调用风格,适合内联编写 SQL: handle.createUpdate("INSERT INTO user(id, name) VALUES (:id, :name)") .bind("id", 2) .bind("name", "Clarice") .execute(); Declarative API 通过注解声明 SQL,适合面向对象风格,代码更简洁易维护: public interface UserDao { @SqlUpdate("CREATE TABLE user (id INTEGER PRIMARY KEY, name VARCHAR)") void createTable(); @SqlUpdate("INSERT INTO user(id, name) VALUES (?, ?)") void insertPositional(int id, String name); @SqlUpdate("INSERT INTO user(id, name) VALUES (:id, :name)") void insertNamed(@Bind("id") int id, @Bind("name") String name); @SqlUpdate("INSERT INTO user(id, name) VALUES (:id, :name)") void insertBean(@BindBean User user); @SqlQuery("SELECT * FROM user ORDER BY name") @RegisterBeanMapper(User.class) List<User> listUsers(); } 参考 https://www.jianshu.com/p/1ee34c858cb9

Created 29296-10-29 · Updated 2026-06-02 · 1 min · 111 words · -

Java 诊断工具 BTrace

背景 本文记录的是 JDK 8 生产环境下的实践:线上出现问题时,在不改代码、不重启的前提下,用 BTrace attach 到运行中的 JVM,按条件过滤慢方法并持续输出。 BTrace 概念与相关文章的关系见 Java 领域知识关系图。 何时需要 BTrace(生产动机) 动机 说明 不能停机 滚动重启成本高或 SLA 不允许 保留故障现场 重启后偶现问题、特定请求参数、堆内对象态、当时流量下的慢调用往往 难以复现;需要在 故障仍在发生时 对当前 JVM 取样 观测而非热修 BTrace 用于 慢调用过滤、参数、耗时、调用路径 等可观测数据,不是用 agent 临时改业务逻辑修 Bug(机制上 agent 能改字节码,但 BTrace 产品侧用脚本校验限制为安全探针) 车联网协议解析 G4 因写 MySQL 偏慢导致 OOM、后迁移 InfluxDB 的完整案例见 物联网平台协议解析服务生产环境 OOM:MySQL 迁移 InfluxDB(职业叙事;本文偏工具用法)。 临时排查可配合 生产环境诊断工具选型(Arthas、JFR 等);BTrace 适合 脚本化、持续输出 的采集。 安装 BTrace 不是单个原生二进制,而是 Java 工具发行包(压缩包 + shell 脚本 + JAR),运行时依赖本机 JDK。 从 GitHub Releases 下载 btrace-bin.tar.gz 或 .zip,解压后大致结构: ...

Created 2026-06-02 · Updated 31316-05-31 · 6 min · 1146 words · -

Java Attach API

背景 JDK 8 生产排查里,BTrace、jcmd 等工具都需要在 不重启业务 JVM 的前提下与之交互。底层机制是 JDK 自带的 Attach API(com.sun.tools.attach,jdk.attach 模块):一个本地进程通过 PID 连上目标 HotSpot JVM,再在这条 attach 通道上发送不同种类的请求。 Attach 不是只有 loadAgent 一种用法。jcmd 的 Thread.print、JFR.start 等走 JVM 内置 Diagnostic Command,一般 不需要 再加载外部 agent JAR;BTrace 等才典型地 loadAgent。 本文说明 Attach 通道、公开 API、与 jcmd / BTrace 的关系。概念总图见 Java 领域知识关系图。容器、新版 JDK 注意点见文末。 是什么 Attach 让已启动的 HotSpot JVM 在运行中被另一进程「挂上」: 连接入口:VirtualMachine.attach(pid)(及 AttachProvider 发现 JVM) 通道上可发多种 attach 操作(见下节),不限于 loadAgent 其中一类操作会加载外部 Agent JAR,调用 agentmain(启动时 -javaagent 则走 premain) 不是远程 RPC:必须在本机(或共享进程命名空间,见文末),且一般要求与目标 JVM 相同的有效用户/组。 ...

Created 30306-05-30 · Updated 31316-05-31 · 3 min · 618 words · -

开发期热替换(HotSwap、DCEVM、HotSwapAgent)

背景 开发时希望改 Java 代码后 少重启 就能看到效果。这与生产上 BTrace attach 观测 不是同一条路。本文归纳 HotSwap 及相关工具,并说明各自边界。 字节码织入与 redefine 机制见 java-asm;调试协议见 JPDA、JVMTI。 HotSwap 是什么 HotSwap(热替换)泛指:不重启整个 JVM,把 已在内存中的类 换成新字节码。 HotSpot 自带 的热替换能力很窄(通过 调试器 触发,底层 JVMTI / redefine): 常见成功:改 已有方法的方法体 常见失败:新增方法/字段、改签名、改继承、新 import 等 因此 IDE 在 Debug 下保存后提示 “Hot swap” 成功或失败,依赖的是 JPDA → JDWP → JVMTI,不是 Attach API 加载 BTrace agent 那条栈。 开发期 HotSwap(典型) IDE 编译 .class → JDWP 命令 → JVM redefine 已加载类 生产期 BTrace(对比) loadAgent → Instrumentation + ASM retransform → 插探针(观测) 几种开发期方案 方案 怎么做 能力边界 IDE HotSwap Debug + JPDA 方法体为主,标准 HotSpot 最严 JRebel 商业 agent 类加载/字节码策略更激进,需授权 DCEVM 替换/并行使用 修改版 HotSpot(-XXaltjvm=dcevm) 放宽 redefine:可加删方法/字段等 HotSwapAgent -javaagent:hotswapagent.jar + 常配合 DCEVM 用 Javassist 改字节码;插件重载 Spring/Hibernate 等配置 Spring Boot DevTools 双 ClassLoader 快速 Restart 重建 Spring 上下文,不是 JVM 级 HotSwap Spring DevTools 细节见 Spring Boot DevTools。 ...

Created 17176-12-17 · Updated 31316-05-31 · 2 min · 224 words · -