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 · -

JDK 26 相对 JDK 25 的变化

JDK 26 于 2026 年 3 月 17 日 GA,是紧接 JDK 25 LTS 之后的短期特性版本(非 LTS)。OpenJDK 为其提供约六个月的支持,至 2026 年 9 月 JDK 27 发布为止。生产环境仍以 JDK 25 LTS 为主;JDK 26 适合尝鲜新特性或验证升级路径。 参考:JDK 26 项目页、JDK 25 项目页、JDK 26 Release Notes。 版本概览 项目 JDK 25 JDK 26 发布日期 2025-09-16 2026-03-17 LTS 是 否 JSR JSR 400 JSR 401 Class File 版本 69.0 70.0 Unicode 16.0.0 17.0.0 官方 JEP 数量 18 10 JDK 26 新增或正式交付的 JEP JEP 500:Prepare to Make Final Mean Final 对通过深层反射修改 final 字段的行为发出警告,为后续在 JVM 层面严格禁止此类修改做准备。JDK 26 尚未完全封死,但应开始排查依赖 setAccessible 篡改 final 的代码。 ...

Created 19196-06-19 · Updated 19196-06-19 · 3 min · 446 words · -

Java 泛型(Generics)

泛型是 JDK 5 引入的类型机制,本质是将类型参数化。可以把类型参数看作占位符:在定义类、接口或方法时声明 T、K、V 等类型变量,在使用时填入具体类型(如 String、Integer)。 泛型带来的主要好处: 可读性:从声明即可看出集合或容器中的元素类型 类型安全:非法类型在编译期报错,而非运行时才抛出 ClassCastException 消除强制转换:List<String> 的 get() 直接返回 String 为什么类型参数不能是 primitive 类型参数只能是引用类型(类、接口、枚举、数组、annotation),不能是 基本数据类型(primitive)。根本原因是:Java 泛型在语言规范和实现上,都只面向引用类型。 语言规范直接规定 JLS 要求类型参数的边界必须是引用类型。int、double、boolean 等 primitive 不是类,不能作为 T 填入: List<int> list; // ❌ 编译错误 List<Integer> list; // ✅ 用包装类代替 泛型靠类型擦除实现,擦除后是 Object Java 泛型在编译后会被擦除:List<String> 运行时其实就是 List,元素按 Object 处理。引用类型可以统一向上转型到 Object,而 primitive 不是 Object 的子类型,也不能直接赋给 Object 变量(必须先装箱): Object o = "hello"; // ✅ Object o = 42; // ❌ int 不能赋给 Object Object o = Integer.valueOf(42); // ✅ 装箱后才是引用类型 泛型容器在字节码层面按「对象引用」运作,primitive 放不进去这套模型。 ...

Created 26266-12-26 · Updated 19196-06-19 · 4 min · 713 words · -

Java 基本数据类型(Primitive Types)

Java 提供 8 种基本数据类型(primitive type):4 种整数型、2 种浮点型、1 种字符型、1 种布尔型。它们不是对象,直接存数值;与之相对的是引用类型(如 String、自定义类),详见 Java 引用类型与基本类型。 类型一览 类型 字节 位数 取值范围 / 说明 默认值 byte 1 8 -128 ~ 127 0 short 2 16 -32,768 ~ 32,767 0 int 4 32 -2,147,483,648 ~ 2,147,483,647 0 long 8 64 -2^63 ~ 2^63 - 1 0L float 4 32 约 ±3.4E38(7 位有效数字) 0.0f double 8 64 约 ±1.7E308(15 位有效数字) 0.0d char 2 16 \u0000 ~ \uffff(0 ~ 65,535) \u0000 boolean — — true / false(JVM 规范未规定精确字节数) false 未显式声明时,整型字面量默认为 int,浮点字面量默认为 double。 ...

Created 19196-09-19 · Updated 19196-06-19 · 2 min · 271 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 · -

物联网平台协议解析服务生产环境 OOM:MySQL 迁移 InfluxDB(2017)

背景 2017 年前后,我在大连 R 公司负责 R 平台协议解析层的开发和维护。R 平台是一套车联网平台,部署在 H 云上,用来接收和处理车载设备实时上报的数据。 协议解析层有十五个以上的服务,每个服务至少两个节点,负责解析车载设备上报的自定义 TCP 协议,网络通信与连接处理底层由 Netty 实现。平台共有 300 多台虚拟机,单个服务的规格通常不会拉很高,G4 所在的虚拟机配置是 2 CPU、4 GB 内存。 其中 G4 是协议解析层数据量最大的服务,负责解析行车数据,单条消息包含约两百个字段,涵盖车速、转速、门窗锁状态、发动机参数、各类液体温度、油量等实时车况信息。正常 QPS 约 700 条/秒,峰值超过 1000 条/秒,四个节点加起来总 QPS 在 5000 左右。 解析完成后,G4 把数据写入 Redis(实时数据)和 MySQL(行车历史数据,水平分片成 1024 个库),另外还有一些平台内部的 API 调用。MySQL 访问层是在 JDBI 基础上封装了一层。 协议解析层的数据来自上游接入层,接入层向协议解析层推送数据,同时用 RabbitMQ 做消息队列缓冲。 装车增长与早期告警 R 平台接入的设备数量持续增长,每个月有几千台新设备安装到实车上。随着在线行驶车辆增多,G4 的数据量也跟着涨。 大规模故障之前,G4 就开始在早高峰和晚高峰偶发内存告警,高峰过去后十几分钟会自行恢复。当时不清楚具体原因,尝试过把业务线程数从默认值增加到 16,改完之后等服务低峰期重启,观察下一次高峰是否有改善。效果不明确,可能有一定缓解,也可能影响有限,之后就转向其他方向排查,线程数就维持在 16 没有再改。 大规模 OOM 装车量继续增加,某天 G4 出现了大规模 OOM。现象是四个节点滚动离线:一个节点 OOM 重启,剩余节点压力上涨,接着下一个离线,重启后很快再 OOM,如此循环。 节点重启之后撑不了太久,因为上游 RabbitMQ 里已经积压了大量数据,积压时间估计有一两个小时。一个节点恢复,积压数据涌入,Netty 堆外内存里挤满了等待处理的业务数据,内存很快再次撑满。一个节点离线,剩余三个节点分摊的流量更大,内存涨得更快,形成连锁反应。节点之间的 OOM 间隔只有几分钟。 ...

Created 31316-05-31 · Updated 5056-06-05 · 2 min · 263 words · -

Android APK 安全评估项目回顾(2014)

2014 年,我在第一家公司接到了一个非常规的项目:对一款 Android APK 做安全评估。说"非常规",是因为那时部门的项目周期动辄两三个月到两三年,而这个只有一周。领导把任务交给我,大概是觉得我平时喜欢折腾各种新东西, 当时业余时间在研究树莓派。 背景 客户是一家制造电梯的公司,他们有一套电梯维保工单系统,安装在配发给维修工程师的 Android 平板上。工程师通过这个 APK 查看维护保养订单、处理故障报修。这个 APK 由另一家第三方公司(vendor-A)开发。 客户委托我所在的公司(vendor-H)对这个 APK 做安全评估,核心问题是:这个 APK 能不能被破解?里面内置的密钥等敏感数据能不能被提取出来? 这本质上是一次白盒逆向测试,只是用了"安全评估"这个更正式的说法。 第一次评估:一周内出报告 拿到 APK 之后,我先去查了 Android APK 逆向的相关资料。 APK 文件本质上是一个 zip 包,解压后可以看到 classes.dex——这是 Android Dalvik 虚拟机的字节码文件,而不是标准 Java 的 .class 文件。逆向的第一步是用 dex2jar 把 classes.dex 转换成普通的 jar 包,再用 jd-gui 等 Java 反编译工具将 jar 里的 .class 文件反编译成可读的 Java 源码。具体流程可以参考我当时整理的 Android APK 反编译。 第一版 APK 虽然有一些保护措施——把部分逻辑封装成单独的 jar 包作为依赖,密钥也不是直接明文写死,而是在运行时动态加载/生成——但通过反编译拿到全部源码之后,这些都可以被分析清楚。我新建了一个 Java 工程,把 APK 里的密钥加载逻辑复制进去单独执行,成功还原出了被保护的密钥。 此外,代码里也存在硬编码的敏感信息。 第一次评估报告在一周内交付给了客户,列出了几个可被利用的安全漏洞。 第二次评估:加壳技术 几个月后,客户再次找到我们,带来了 vendor-A 升级后的新版 APK。 ...

Created 5056-06-05 · Updated 5056-06-05 · 1 min · 148 words · -

Java 反编译工具:DJ Java Decompiler / JD-GUI

Java 反编译 Java Decompiler “Java Decompiler”, 由 Pavel Kouznetsov开发,目前最新版本为0.2.5. 它由 C++开发,并且官方可以下载windows、linux和苹果Mac Os三个平台的可执行程序。 http://jd.benow.ca/ http://www.oschina.net/p/java+decompiler DJ Java Decompiler(不免费) DJ Java Decompiler 是个反编译的工具,可以将编译过的 CLASS 文件编译还原成为 Java 原始文件,并且不需要额外安装 JVM (Java Virtual Machine) 或是JDK 的工具模组即可使用。不单如此,它也兼具有 Java 程序编辑工具的角色,提供一些辅助功能便于程序撰写与修改。 DJ Java Decompiler 在 Windows 95、Windows 98、Windows 2000、Windows XP、Windows 2003、Windows Vista、 Windows 7等多种平台上均可以运行的 Java 反编译器和汇编器。DJ Java Decompiler 能够反复编译复杂的 Java 小程序和二进制文件,生产出准确的源代码。它不仅是 Java 反编译器和汇编器,也是一个功能齐全的使用有着着色语法的图形用户界面的 Java 编辑器。 这款软件十分适合用来学习编程,而且它能够让用户保存、打印、编辑和编译生成的 Java 代码。 另外,DJ Java Decompiler 是一个独立的 windows 应用程序,它不要求安装Java,所以运行使用相当方便,大家不妨试一下。 个人觉得比较好的功能有: 可以直接打开jar 档, 查看jar 包里原文件 ...

Created 24246-08-24 · Updated 5056-06-05 · 1 min · 75 words · -

dex2jar

dex2jar https://code.google.com/p/dex2jar/ http://blog.csdn.net/kesenhoo/article/details/6544094 dex2jar contains following compment dex-reader is designed to read the Dalvik Executable (.dex/.odex) format. It has a light weight API similar with ASM. An example here dex-translator is designed to do the convert job. It reads the dex instruction to dex-ir format, after some optimize, convert to ASM format. dex-ir used by dex-translator, is designed to represent the dex instruction dex-tools tools to work with .class files. here are examples: Modify a apk DeObfuscate a jar d2j-smali [To be published] disassemble dex to smali files and assemble dex from smali files. different implementation to smali/baksmali, same syntax, but we support escape in type desc “Lcom/dex2jar\t\u1234;” dex-writer [To be published] write dex same way as dex-reader.

Created 10106-04-10 · Updated 5056-06-05 · 1 min · 118 words · -

Netty 堆外内存

为什么使用堆外内存 Java 堆内存(Heap)受 JVM GC 管理,分配和释放都有 GC 开销,且在 GC 发生时对象地址可能移动(Compacting GC)。进行 I/O 操作时,JVM 必须先把堆内数据拷贝到一块固定地址的堆外缓冲区,再交给内核。 堆外内存(Direct Memory / Off-Heap)直接由操作系统管理,有以下优势: 零拷贝:内核可以直接读写堆外内存,省掉堆内 → 堆外的一次拷贝 不受 GC 暂停影响:地址固定,内核持有指针不会因 GC 而失效 更可预测的延迟:不参与 Young/Old GC,减少 STW(Stop-The-World)概率 代价是分配/释放成本更高,且不会被 GC 自动回收,需要手动管理生命周期。 ByteBuf:Netty 的核心缓冲区抽象 Netty 用 ByteBuf 替代 JDK 的 ByteBuffer,消除了 flip()/rewind() 的使用负担,并同时支持堆内和堆外两种底层存储。 分类 ByteBuf ├── HeapByteBuf 堆内,byte[] 存储,GC 管理 ├── DirectByteBuf 堆外,java.nio.DirectByteBuffer 存储 └── CompositeByteBuf 零拷贝组合多个 ByteBuf(可混合堆内/堆外) 每种又分 Pooled(池化)和 Unpooled(非池化)两个版本: 类型 分配方式 适用场景 PooledDirectByteBuf 从内存池取 高频、短生命周期 I/O UnpooledDirectByteBuf 每次调用 malloc 低频、大块、长期持有 PooledHeapByteBuf 从内存池取 不需要 native I/O 的场景 UnpooledHeapByteBuf 每次 new byte[] 简单测试/工具代码 零拷贝(Zero-copy) OS 层面的 Zero-copy 指避免在用户态(User-space)与内核态(Kernel-space)之间拷贝数据,例如 Linux 的 mmap、sendfile。Netty 的 Zero-copy 完全在用户态(Java 层面),核心目标是减少 ByteBuf 之间不必要的内存拷贝。 ...

Created 23236-03-23 · Updated 3036-06-03 · 4 min · 678 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 · -