概述 Java 用途 Java 是用于开发应用程序的编程语言。 程序就是计算机执行某些操作或解决某个问题而编写的一系列有序指令的集合。 主要用于以下几个核心领域:
Android 应用开发: Java 是 Android 平台开发的主要语言之一。 许多我们日常使用的手机 App, 如 Spotify、 Signal 等, 都是使用 Java 构建的。
企业级后端开发: 这是 Java 最重要的应用场景。 凭借其稳定性、 安全性和高并发处理能力, Java 成为企业级服务端开发的首选。 大量的银行、 金融交易系统、 大型电商网站( 如 Twitter、 LinkedIn) 的后台都> 是由 Java 驱动的。
大数据处理: Java 是许多大数据技术生态的核心语言, 例如 Hadoop 和 Spark 等框架都是用 Java 编写的, 广泛用于处理和分析海量数据。
桌面应用程序: Java 可以开发跨平台的桌面软件。 著名的集成开发环境( IDE) 如 IntelliJ IDEA 和 Eclipse 本身就是用 Java 开发的桌面应用。
游戏开发: Java 也被用于游戏开发, 最著名的例子就是风靡全球的沙盒游戏《 我的世界》 ( Minecraft) 。
嵌入式系统与物联网 (IoT)得益于其跨平台和稳定性, Java 还被应用于智能电视、 车载系统、 工业控制设备等嵌入式和物联网领域。
Java 历史 🌱 起源: 从“ Oak” 到“ Java” (1991-1994) Java 的故事始于 1991 年, 当时 Sun Microsystems 公司启动了一个名为“ 绿色计划” ( Green Project) 的秘密项目。
最初目标: 由詹姆斯·高斯林( James Gosling) 等人领导的团队, 旨在为下一代智能消费电子产品( 如机顶盒、 交互式电视) 开发一种新的编程语言。
命名“ Oak” : Gosling 将这门新语言命名为“ Oak” ( 橡树) , 灵感来自他办公室窗外的一棵树。 然而, 由于硬件厂商接受度低, 该项目在消费电子市场遭遇失败。
关键转折: 1994 年, 互联网( Web) 开始兴起。 团队敏锐地意识到, Oak 语言所具备的安全性、 网络移动性和跨平台特性, 完美契合了互联网“ 网络即计算机” 的愿景。
正式更名: 由于“ Oak” 已被注册为商标, 团队选择了“ Java” 作为新名字, 灵感来自印度尼西亚的爪哇咖啡。
🚀 爆发: 互联网时代的宠儿 (1995-1999) 1995 年是 Java 历史上最重要的一年。
正式发布: 1995 年 5 月 23 日, Sun Microsystems 在 SunWorld 大会上正式发布 Java 1.0, 并提出了著名的口号 “ Write Once, Run Anywhere” (WORA), 即“ 一次编写, 到处运行” 。
引爆互联网: Java 的跨平台特性解决了当时软件开发的最大痛点。 Netscape Navigator 浏览器迅速集成 Java 技术, 使得 Java Applet( 小程序) 成为当时网页上实现动态交互的最热门技术。
随后的几年是 Java 标准化和壮大的关键期。
1996年: JDK 1.0 正式发布, 标志着 Java 进入主流开发视野。
1997年: JDK 1.1 引入了 JDBC( 数据库连接) 、 RMI( 远程方法调用) 和内部类等特性, 完善了语言基础设施。
1998年: JDK 1.2 是一个里程碑版本, 它被重命名为 “ Java 2” , 并首次将 Java 平台划分为三个版本, 这一战略奠定了 Java 随后二十年的发展格局:
J2SE (Java 2 Standard Edition): 面向桌面和工作站的通用开发。
J2EE (Java 2 Enterprise Edition): 面向服务器端企业级应用, 包含 EJB、 Servlet、 JSP 等规范。
J2ME (Java 2 Micro Edition): 面向移动设备( 如功能手机) 和嵌入式系统。
🏛️ 统治: 企业级开发的霸主 (2000-2009) 进入 21 世纪, 随着企业信息化的深入, Java 迅速确立了在企业后端开发的统治地位。
企业首选: 尽管早期的 EJB 因配置繁琐而备受诟病, 但 Java 凭借其稳定性、 安全性和庞大的类库, 成为了银行、 电信和电商等核心系统的首选语言。
语法革命 (Java 5): 2004 年发布的 JDK 5.0 是一次巨大的升级, 引入了泛型、 注解、 枚举、 自动装箱等特性, 极大地提升了代码的简洁性和类型安全。
开源化 (Java 6): 2006 年, Sun 公司宣布将 Java 技术作为免费软件对外发布, 此举极大地推动了 Java 社区的繁荣。
巨头更迭 (2009): 甲骨文公司( Oracle) 宣布收购 Sun 公司, Java 的归属权随之转入 Oracle 麾下。
🏎️ 进化: 云原生时代的重生 (2010-至今) 在 Oracle 的推动下, Java 进入了一个快速、 有序的现代化演进阶段。
里程碑式的 Java 8: 2014 年发布的 Java 8 至今仍是影响最深远的版本之一。 它引入的 Lambda 表达式和 Stream API 让 Java 拥有了函数式编程能力, 彻底改变了开发者的编码方式15。
模块化与新节奏 (Java 9): 2017 年发布的 Java 9 引入了模块化系统( Project Jigsaw) , 并开启了“ 每6个月发布一个新版本, 每2年发布一个长期支持版本( LTS) ” 的新节奏25。
拥抱云原生 (Java 21): 2023 年发布的 JDK 21 标志着 Java 迈入了协程时代。 核心特性虚拟线程( Virtual Threads) 让开发者能够轻松创建百万级线程, 极大地提升了高并发场景下的性能, 为云原生应用开发注入了强大动力5。
Java 特点 ⭐⭐ 核心优势
跨平台性 (Write Once, Run Anywhere) 这是 Java 最显著的特征。 Java 代码在编译后不会生成特定操作系统的机器码, 而是生成一种中间代码——字节码 。 这些字节码可以在任何安装了 Java 虚拟机 的硬件设备上运行。 JVM 负责将字节码解释或编译成对应平台的机器码, 从而实现了“ 一次编写, 到处运行” 的愿景, 极大地提升了软件的可移植性。
面向对象 (OOP) Java 是一门纯粹的面向对象编程语言。 它完全基于“ 类” 和“ 对象” 的概念来组织代码, 并支持封装、 继承、 多态等核心特性。 这种编程范式使得代码更易于理解、 复用和维护, 特别适合开发大型复杂系统。
简单易学 Java 的语法设计去除了 C++ 中复杂且容易出错的特性, 例如指针 、 多重继承 和 goto 语句。 同时, 它采用了自动垃圾回收机制来管理内存, 减轻了程序员的负担。 这使得 Java 语言相对简洁, 对于初学者来说非常友好。
⭐⭐ 稳健与安全
健壮性: Java 通过多种机制来保证程序的稳定运行:
强类型检查: 在编译和运行时都会进行严格的类型检查, 提前暴露错误。
异常处理: 提供了完善的 try-catch-finally 机制来捕获和处理运行时错误, 防止程序意外崩溃。
自动内存管理: 通过垃圾回收器自动回收不再使用的对象, 有效避免了内存泄漏和悬空指针等问题。
安全性: Java 被设计用于网络和分布式环境, 其安全性是内置的:
沙箱机制: JVM 为代码提供了一个受限的运行环境, 限制其对本地系统资源( 如文件、 网络) 的访问权限, 防止恶意代码破坏系统。
字节码验证: 在加载类文件时, 会进行严格的校验, 确保代码不会执行非法操作。
⭐⭐ 高性能与并发
高性能 虽然早期 Java 因解释执行而性能受限, 但现代 JVM 引入了 即时编译 技术。 JIT 会将频繁执行的“ 热点代码” 编译成本地机器码, 使其运行性能接近 C/C++ 等编译型语言。
多线程支持 Java 在语言层面原生支持多线程编程。 开发者可以方便地创建和管理多个线程, 以实现并发执行任务。 这对于开发高并发的服务器端应用和响应迅速的图形界面程序至关重要。
⭐⭐ 生态与扩展
分布式 Java 从诞生之初就具备网络基因, 内置了丰富的网络库( 如 java.net 和 java.rmi) , 使得开发分布式应用( 如 Web 服务、 远程调用) 变得非常简单。
动态性 Java 能够在运行时动态地加载类、 获取类信息并调用方法, 这一特性主要通过反射机制 实现。 它是许多框架( 如 Spring) 实现依赖注入、 面向切面编程等功能的基础, 极大地增强了程序的灵活性。
Java 运行机制 Java 的运行机制是其能够实现“ 一次编写, 到处运行” ( WORA) 的核心。 它并非直接由操作系统执行, 而是依赖于一个名为 Java 虚拟机 的中间层。
简单来说, Java 的运行机制可以分为三个核心阶段: 编译 、 加载 和 执行 。
⭐⭐ 核心组件: JDK、 JRE 与 JVM
JDK (Java Development Kit): 是 Java 的开发工具包, 包含了编译器 (javac)、 调试器等开发工具。 开发者使用 JDK 将源代码编译成字节码。 JRE (Java Runtime Environment): 是 Java 的运行时环境, 包含了 JVM 和核心类库。 普通用户只需安装 JRE 即可运行 Java 程序。 JVM (Java Virtual Machine): 是 Java 虚拟机, 是整个运行机制的核心。 它是一个虚拟的计算机, 负责加载字节码, 并将其转换为特定平台的机器码来执行。
⭐⭐ 阶段一: 编译( 从源码到字节码) 这是 Java 程序运行的第一步, 发生在开发阶段。
输入: 开发者编写的 .java 源代码文件。
工具: 使用 JDK 中的 javac 编译器。
过程: javac 编译器对源代码进行语法分析、 语义分析和字节码生成。
输出: 生成与平台无关的 .class 字节码文件。 此时的字节码并不是任何特定硬件的机器码, 而是一种 JVM 能够理解的中间指令集。
⭐⭐ 阶段二: 加载( 类加载器的职责) 当你在命令行执行 java HelloWorld 时, JVM 启动并进入类加载阶段。
加载: 类加载器 负责从文件系统或网络中找到 HelloWorld.class 文件, 并将其二进制字节流读入内存。
链接:
验证: 确保字节码是合法且安全的, 符合 JVM 规范, 防止恶意代码破坏系统。
准备: 为类的静态变量分配内存, 并设置其初始默认值( 如 0、 null) 。
解析: 将常量池内的符号引用( 如类名、 方法名) 替换为直接引用( 内存地址) 。
初始化: 执行类构造器 <clinit>() 方法, 为类的静态变量赋予代码中指定的初始值, 并执行静态代码块。
⭐⭐ 阶段三: 执行( 执行引擎的魔法) 类加载完成后, JVM 的执行引擎 开始接管, 执行 main 方法中的字节码。 现代 JVM 采用了一种混合模式 来平衡启动速度和运行效率。
混合执行模式
解释执行 (Interpreter)
方式: 像同声传译一样, 逐条读取字节码, 将其翻译成当前平台的机器码并执行。
优点: 启动速度快, 无需等待编译。
缺点: 运行效率较低, 因为同一段代码每次执行都需要翻译。
即时编译 (JIT - Just-In-Time Compiler)
方式: JVM 会监控程序运行, 当发现某个方法或代码块被频繁调用( 称为“ 热点代码” ) 时, JIT 编译器会将其整个编译成本地机器码, 并缓存起来。
优点: 后续调用该热点代码时, 直接执行高效的机器码, 运行速度极快, 接近 C/C++。
缺点: 编译过程会消耗 CPU 和内存资源, 且首次执行时有延迟。
总结: Java 程序启动时, 解释器优先执行, 保证快速响应; 在运行过程中, JIT 编译器不断优化热点代码, 提升系统吞吐量。 这种“ 解释器与编译器并存” 的策略, 是 Java 性能卓越的关键。
⭐⭐ 内存管理: 自动垃圾回收
在整个执行过程中, JVM 的运行时数据区 负责管理内存。 其中最核心的机制之一是垃圾回收 。 JVM 会自动追踪对象的引用情况, 当发现某些对象不再被任何引用指向时, 垃圾回收器会自动回收其占用的内存, 极大地减轻了开发者手动管理内存的负担和出错风险。
Java 开发的类别
Java SE 是Java平台标准版的简称(Java platform, standard edition), Java SE 以前称为 J2SE。 适用于标准的应用开发, 用于开发和部署桌面、 服务器以及嵌入设备和实时环境中的Java应用程序。 Java SE包括用于开发Java web服务的类库, 同时, Java SE为Java EE提供了基础。
Java EE 是Java平台企业版的简称(Java platform, enterprise edition), 以前称为 J2EE。 适用于企业级的应用服务开发, 用于简化企业解决方案的开发、 部署和管理相关的复杂问题的体系结构。 Java EE建立于Java SE之上, 具有web服务、 组件模型、 以及通信API等特性, 可以用来实现企业级的面向服务体系结构(SOA)和 Web 2.0 应用程序。
Java ME 是Java微版的简称(Java platform, enterprise edition), 以前称为 J2ME, 常用于手机上的开发。 是一个技术和规范的集合, Java ME为在移动设备和嵌入式设备(比如手机、 PDA、 电视机顶盒和打印机)上运行的应用程序提供一个健壮且灵活的环境。 Java ME 包括灵活的用户界面、 健壮的安全模型、 许多内置的网络协议以及对可以动态下载的连网和离线应用程序的丰富支持。 基于 Java ME 规范的应用程序只需编写一次, 就可以用于许多设备, 而且可以利用每个设备的本机功能。
基础部分 安装运行 安装配置 JAVA 8 安装详解: https://blog.csdn.net/suzengxin/article/details/115401549
运行程序 ⭐⭐ 第一步: 开发程序 1 2 3 4 5 public class Hello { public static void main (String[] args) { System.out.println("Hello World" ); } }
⭐⭐ 第二步: 编译程序
⭐⭐ 第三步: 运行程序
转义字符
转义字符 描述
\t
制表符
\n
换行符
\r
回车
\"
双引号
\'
单引号
\\
反斜杠
代码注释 单行注释
多行注释
文档注释
⭐⭐ 生成 Java 文档 1 javadoc -d D:\\javadoc -author -version Hello.java
⭐⭐ 标准标签
标签名 用途说明 适用对象
@author
指明类或接口的作者
包、 类、 接口
@version
指定类或接口的版本信息
包、 类、 接口
@param
描述方法或构造函数的参数名称和含义
方法、 构造函数
@return
描述方法的返回值
方法
@throws
描述方法可能抛出的异常
方法、 构造函数
@exception
与 @throws 功能相同, 用于描述异常。
方法、 构造函数
@see
提供指向其他类、 方法或外部资源的“ 参见” 链接
全局(类、 方法、 包等)
@since
指明该特性从哪个版本( 或日期) 开始引入
全局
@deprecated
标记该 API 已过时, 不建议使用
全局
@serial
用于序列化字段的文档说明
字段 (Field)
@serialData
描述通过 `writeObject` 或 `writeExternal` 写入的数据
方法 (通常为 writeObject)
@serialField
描述 `ObjectStreamField` 组件
字段
⭐⭐ 内联标签 这些标签用 {} 包裹, 可以嵌入到注释的任意文本中, 用于内联显示特殊内容或链接。
标签名 用途说明
{@link}
内联插入一个指向其他类或成员的链接( 类似于 @see 但可嵌入文本)
{@linkplain}
与 {@link} 类似, 但链接文本以纯文本( 非代码样式) 显示
{@value}
显示静态字段( 常量) 的值
{@code}
将文本显示为代码样式( 通常用于表示关键字或代码片段) , 并避免被解析为 HTML
{@literal}
显示文本而不进行任何转义( 显示原样文本) , 不解析为 HTML 或其他标签
{@inheritDoc}
从直接父类或实现的接口中继承文档注释
{@docRoot}
指明生成的文档根目录的路径( 用于构建相对路径链接)
⭐⭐ 不常用标签
标签名 用途说明
@category
(非标准, 某些 Doclet 可能支持) 用于分类
@index
(Java 8+) 强制将标识符添加到索引中
@apiNote
(Java 8+) 提供关于 API 使用的额外说明( 非规范性)
@implSpec
(Java 8+) 提供实现者需要遵守的规范说明
@implNote
(Java 8+) 提供关于实现的非规范性说明
数据类型 在 Java 中, 数据类型、 变量、 常量 是编程的三大基石, 它们共同决定了程序如何存储和处理数据。 简单来说, 数据类型是“ 容器的规格” , 变量是“ 可变的容器” , 而常量是“ 不可变的容器” 。
数据类型: 用于定义变量或常量可以存储的数据种类以及占用内存的大小。 Java 是强类型语言, 每一个变量都必须先声明类型才能使用。
变量: 变量是程序运行过程中值可以发生改变的数据存储单元。 你可以把它想象成一个贴了标签的盒子, 盒子的大小由数据类型决定, 里面的内容( 值) 可以根据需要更换。
常量: 常量是程序运行过程中值不能发生改变的数据。 使用 final 关键字修饰的变量。 一旦赋值, 就不能再修改。
类型描述
类型
关键字
字节/位数
取值范围
默认值
描述
整数型
byte
1字节 / 8位
-128 到 127
0
最小的整数类型, 常用于节省内存的场景( 如处理二进制数据)
short
2字节 / 16位
-32768 到 32767
0
较少使用, 通常用于特定的内存优化场景
int
4字节 / 32位
-2³¹ 到 2³¹-1
0
最常用的整数类型, 也是整数字面量的默认类型
long
8字节 / 64位
-2⁶³ 到 2⁶³-1
0L
用于存储非常大的数值( 如时间戳、 文件大小) , 定义时需在数值后加 L
浮点型
float
4字节 / 32位
约 ±3.4028235E+38
0.0F
单精度浮点数, 定义时需在数值后加 F
double
8字节 / 64位
约 ±1.7976931348623157E+308
0.0D
双精度浮点数, 精度更高, 是浮点数字面量的默认类型
字符型
char
2字节 (16位)
0 到 65535 (\u0000 到 \uffff)
\u0000
使用单引号 '' 表示, 例如: '中'、 '国'、 'A'
布尔型
boolean
1 位 | 1 字节
true (真) 或 false (假)
false
常用于条件判断和循环控制
类型转换 ⭐⭐ 自动类型转换( 隐式转换) 这是 Java 编译器自动完成的转换, 也叫拓宽转换( Widening Conversion) 。 当你把一个“ 小杯子” 里的水倒进一个“ 大杯子” 时, 不会溢出, 因此是安全的。
转换规则: 容量小的数据类型可以直接赋值给容量大的数据类型。
byte → short → int → long → float → double
char → int → long → float → double
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int a = 100 ;long b = a; double c = a; char ch = 'A' ; int ascii = ch; byte x = 10 ;short y = 20 ;int result = x + y; float f = 1.5f ;double d = 2.5 ;double sum = f + d;
⭐⭐ 强制类型转换( 显式转换) 这是程序员明确指示编译器进行的转换, 也叫窄化转换( Narrowing Conversion) 。 当你把一个“ 大杯子” 里的水倒进一个“ 小杯子” 时, 可能会溢出或洒掉( 数据丢失) , 因此需要你“ 手动” 负责。
精度丢失: 浮点数转整数时, 直接截断小数部分( 不是四舍五入) 。
数据溢出: 数值超过了目标类型的取值范围, 会发生“ 截断” 或“ 溢出” 。
1 2 3 4 5 6 double pi = 3.1415926 ;int intPi = (int ) pi; long bigNum = 3000000000L ;int num = (int ) bigNum;
二进制 计算机中的数据存储: 学习笔记
各个进制之间相互转换: 学习笔记
原码、 反码和补码: 学习笔记
运算符 Java运算符是用于对变量和值进行操作的符号。
算术运算符 算术运算符用于执行基本的数学运算。
运算符
名称
示例
描述
+
加法
a + b
相加两个操作数
-
减法
a - b
从第一个操作数中减去第二个操作数
*
乘法
a * b
相乘两个操作数
/
除法
a / b
分子除以分母
%
取模
++a 或 a++
返回除法的余数
++
自增
++a 或 a++
操作数的值增加1
--
自减
--a 或 a--
操作数的值减少1
⭐⭐ 示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class ArithmeticOperators { public static void main (String[] args) { int a = 10 , b = 3 ; System.out.println("a + b = " + (a + b)); System.out.println("a - b = " + (a - b)); System.out.println("a * b = " + (a * b)); System.out.println("a / b = " + (a / b)); System.out.println("a % b = " + (a % b)); int c = 5 ; System.out.println("c++ = " + (c++)); System.out.println("++c = " + (++c)); } }
关系运算符 关系运算符用于比较两个值, 返回布尔值( true或false) 。
运算符
名称
示例
描述
==
等于
a == b
检查两个操作数的值是否相等
!=
不等于
a != b
检查两个操作数的值是否不相等
>
大于
a > b
检查左操作数的值是否大于右操作数的值
<
小于
a < b
检查左操作数的值是否小于右操作数的值
>=
大于等于
a >= b
检查左操作数的值是否大于或等于右操作数的值
<=
小于等于
a <= b
检查左操作数的值是否小于或等于右操作数的值
⭐⭐ 示例代码 1 2 3 4 5 6 7 8 9 10 11 public class RelationalOperators { public static void main (String[] args) { int a = 10 , b = 20 ; System.out.println("a == b: " + (a == b)); System.out.println("a != b: " + (a != b)); System.out.println("a > b: " + (a > b)); System.out.println("a < b: " + (a < b)); System.out.println("a >= b: " + (a >= b)); System.out.println("a <= b: " + (a <= b)); } }
逻辑运算符 逻辑运算符用于组合多个布尔表达式。
运算符
名称
示例
描述
&&
短路与
a && b
如果两个操作数都为真, 则返回结果为真
||
短路或
a || b
如果两个操作数有一个为真, 则返回结果为真
&
逻辑与
a & b
如果两个操作数都为真, 则返回结果为真
|
逻辑或
a | b
如果两个操作数有一个为真, 则返回结果为真
^
逻辑异或
a ^ b
两个操作数一个为真一个为假, 则返回结果为真
!
非( 取反)
!a
如果操作数为真, 则返回结果为假( 反转逻辑状态)
⭐⭐ 示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class LogicalOperators { public static void main (String[] args) { boolean x = true , y = false ; System.out.println("x && y: " + (x && y)); System.out.println("x || y: " + (x || y)); System.out.println("!x: " + (!x)); int a = 5 , b = 10 ; if (a > 10 && b++ > 5 ) { System.out.println("条件成立" ); } System.out.println("b = " + b); } }
位运算符 位运算符对整数类型的位进行操作。
运算符
名称
示例
描述
&
按位与
a & b
只有当两个对应的二进制位都为 1 时, 结果的该位才为 1, 否则为 0
|
按位或
a | b
只要有一个对应的二进制位为 1, 结果位就为 1, 否则为 0
^
按位异或
a ^ b
当两个对应的二进制位不相同时, 结果位为 1; 否则( 相同时) 为 0
~
按位取反
~a
当原二进制位为 0 时, 结果位为 1; 当原位为 1 时, 结果位为 0
<<
左移
a << 1
将a的二进制位向左移动1位, 右边空出的位补入 0 左移几位, 相当于乘以 2 的几次方
>>
右移
a >> 1
将a的二进制位向右移动1位, 左边空出的位补入符号位 右移几位, 相当于除以 2 的几次方
>>>
无符号右移
a >>> 1
将a的二进制位向右移动1位, 左边空出的位补入 0 处理的是二进制位的移动, 而不是数学计算
⭐⭐ 示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class BitwiseOperators { public static void main (String[] args) { int a = 6 ; int b = 3 ; System.out.println("a & b = " + (a & b)); System.out.println("a | b = " + (a | b)); System.out.println("a ^ b = " + (a ^ b)); System.out.println("~a = " + (~a)); System.out.println("a << 1 = " + (a << 1 )); System.out.println("a >> 1 = " + (a >> 1 )); } }
赋值运算符 赋值运算符用于给变量赋值。
运算符
示例
等价于
=
a = 5
简单赋值
+=
a += 5
a = a + 5
-=
a -= 5
a = a - 5
*=
a *= 5
a = a * 5
/=
a /= 5
a = a / 5
%=
a %= 5
a = a % 5
&=
a &= 5
a = a & 5
|=
a |= 5
a = a | 5
^=
a ^= 5
a = a ^ 5
<<=
a <<= 5
a = a << 5
>>=
a >>= 5
a = a >> 5
>>>=
a >>>= 5
a = a >>> 5
⭐⭐ 示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class AssignmentOperators { public static void main (String[] args) { int a = 10 ; a += 5 ; System.out.println("a += 5: " + a); a -= 3 ; System.out.println("a -= 3: " + a); a *= 2 ; System.out.println("a *= 2: " + a); } }
三元运算符 条件运算符是Java中唯一的三元运算符。
语法: 条件表达式 ? 表达式1 : 表达式2, 条件表达式为真, 返回表达式1, 为假返回表达式2。
⭐⭐ 示例代码 1 2 3 4 5 6 7 8 9 10 11 12 13 public class TernaryOperator { public static void main (String[] args) { int a = 10 , b = 20 ; int max = (a > b) ? a : b; System.out.println("最大值: " + max); String result = (a % 2 == 0 ) ? "偶数" : "奇数" ; System.out.println(a + "是" + result); } }
特殊运算符 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 String str = "Hello" + " " + "World" ; String result = "The answer is " + 42 ; String str = "Hello" ;System.out.println("str instanceof String: " + (str instanceof String)); System.out.println("str instanceof Integer: " + (str instanceof Integer)); System.out::println;
运算符优先级 当表达式包含多个运算符时, Java按照优先级顺序执行。
优先级
运算符
运行顺序
1
`()` `[]` `.`
从左到右
2
`++` `--` `!` `~` `+` `-` `(type)` `new`
从右到左
3
`*` `/` `%`
从左到右
4
`+` `-`
从左到右
5
`<<` `>>` `>>>`
从左到右
6
`<` `<=` `>` `>=` `instanceof`
从左到右
7
`==` `!=`
从左到右
8
`&`
从左到右
9
`^`
从左到右
10
`|`
从左到右
11
`&&`
从左到右
12
`||`
从左到右
13
`? :`
从右到左
14
赋值运算符
从右到左
⭐⭐ 示例代码 1 2 int result = 10 + 5 * 2 ; int result2 = (10 + 5 ) * 2 ;
控制结构 Java 的控制结构决定了代码的执行顺序和逻辑走向。 简单来说, 它让程序不再是“ 一条路走到黑” 的顺序执行, 而是具备了判断( 分支) 和重复( 循环) 的能力。
顺序结构 这是最简单的结构, 程序默认从上到下逐行执行, 中间没有任何判断和跳转。
1 2 3 4 5 6 public static void main (String[] args) { int a = 10 ; int b = a + 2 ; }
分支控制 ⭐⭐ 单分支结构
// 基本语法
if (条件表达式) {
// 代码块( 当条件表达式为 true 时执行)
}
1.程序首先计算 条件表达式 的值。
2.如果结果为 true, 则执行大括号 {} 内的代码块。
3.如果结果为 false, 则跳过整个 if 结构, 执行紧跟在 if 后面的下一行代码。
4.如果代码块中只有一行语句, 大括号 {} 可以省略( 但强烈不建议省略, 为了代码可读性和避免错误) 。
1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) { double balance = 500.0 ; double withdraw = 800.0 ; if (withdraw > balance) { System.out.println("余额不足, 取款失败! " ); System.out.println("当前余额: " + balance); } }
⭐⭐ 双分支结构
// 基本语法
if (条件表达式) {
// 代码块1( 当条件为 true 时执行)
} else {
// 代码块2( 当条件为 false 时执行)
}
1.程序首先计算 条件表达式 的值。
2.如果结果为 true, 执行 代码块1。
3.如果结果为 false, 执行 代码块2。
4.执行完毕后, 执行紧跟在 if 后面的下一行代码。
1 2 3 4 5 6 7 8 public static void main (String[] args) { int number = 7 ; if (number % 2 == 0 ) { System.out.println(number + " 是偶数" ); } else { System.out.println(number + " 是奇数" ); } }
⭐⭐ 多分支结构
// 基本语法
if (条件表达式1) {
// 代码块1
} else if (条件表达式2) {
// 代码块2
} else {
// 默认代码块( 以上条件都不满足时执行)
}
1.自上而下依次判断条件表达式。
2.一旦遇到某个 条件表达式 为 true, 就执行对应的代码块, 执行完毕后立即跳出整个结构, 不再判断后续的 else if。
3.如果所有的 if 和 else if 条件都不成立, 且存在 else 分支, 则执行 else 中的代码块。
4.else 分支如果没有要处理的代码块, 可以省略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void main (String[] args) { int score = 85 ; String grade; if (score >= 90 ) { grade = "A (优秀)" ; } else if (score >= 80 ) { grade = "B (良好)" ; } else if (score >= 70 ) { grade = "C (中等)" ; } else if (score >= 60 ) { grade = "D (及格)" ; } else { grade = "E (不及格)" ; } System.out.println("分数: " + score + ", 等级: " + grade); }
⭐⭐ 嵌套分支结构
// 基本语法
if (外层条件表达式) {
// 外层代码块
if (内层条件表达式) {
// 内层代码块 A
} else {
// 内层代码块 B
}
} else {
// 外层代码块
}
1.程序首先判断外层条件。 只有在外层条件成立的情况下, 才会去执行内部的内层条件判断。
2.内层的 if-else 完全属于外层 if 或 else 的代码块范围。
3.逻辑关系: 通常用于表达“ 并且” 的关系。 例如: “ 如果是男性 并且 年龄大于 18” -> 外层判断性别, 内层判断年龄。
4.嵌套分支结构其实就是将上述的单分支、 双分支或多分支结构互相“ 包裹” 起来, 形成里外两层或多层的判断逻辑。
5.理论上 Java 支持无限层嵌套, 但在实际开发中, 为了代码的可读性和维护性, 建议嵌套层次不要超过 3 层。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void main (String[] args) { String inputPassword = "888888" ; String realPassword = "888888" ; double balance = 1000.0 ; double withdraw = 1500.0 ; if (inputPassword.equals(realPassword)) { System.out.println("密码正确, 正在验证金额..." ); if (withdraw <= balance) { System.out.println("取款成功, 请取走现金! " ); } else { System.out.println("余额不足, 取款失败! " ); } } else { System.out.println("密码错误, 卡已锁定, 请联系银行! " ); } }
⭐⭐ 多值匹配结构
// 基本语法
switch (表达式) {
case 值1:
// 执行代码块1
break;
case 值2:
// 执行代码块2
break;
case 值3:
case 值4: // 多个case共用代码
// 执行代码块3或4
break;
default:
// 当所有case都不匹配时执行
}
1.表达式求值: switch 后面括号中的表达式只会被计算一次。
2.顺序匹配: 从上到下依次将表达式的值与 case 后面的值进行比较。 一旦找到匹配的 case, 程序会从该处开始执行代码。
3.case 的值必须是字面量或 final 常量, 不能是变量或表达式。
4.break 语句用于终止 switch 结构, 跳出整个代码块。
5.穿透现象: 如果省略 break, 程序不会停止, 而是会继续执行下一个 case 或 default 的代码, 直到遇到 break 或 switch 结束。
6.除非刻意利用穿透, 否则每个 case 后都应该写 break, 否则会导致逻辑错误。
7.default 分支: 当所有 case 都不匹配时执行。 它是可选的, 位置可以放在 switch 块的任何地方, 但为了代码可读性, 强烈建议放在末尾。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main (String[] args) { int day = 3 ; switch (day) { case 1 : System.out.println("星期一" ); break ; case 2 : System.out.println("星期二" ); break ; case 3 : System.out.println("星期三" ); break ; case 4 : System.out.println("星期四" ); break ; default : System.out.println("周末或输入错误" ); } }
循环控制 ⭐⭐ for 循环
// 基本语法
for (初始化语句; 循环条件; 迭代语句) {
// 循环体( 重复执行的代码)
}
1.初始化语句: 最先执行, 且只执行一次。 通常用于定义循环变量( 如 int i = 0) 。
2.循环条件: 必须是一个布尔表达式。 在每次循环开始前进行判断。
3.如果结果为 true, 则执行循环体。
4.如果结果为 false, 则终止循环, 跳出 for 结构。
5.循环体: 条件为 true 时执行的具体代码。
6.迭代语句: 在循环体执行完毕后执行, 通常用于更新循环变量( 如 i++) 。
7.流程顺序: 初始化 -> 判断条件 -> 执行循环体 -> 执行迭代语句 -> 再次判断条件……直到条件为 false。
1 2 3 4 5 6 7 8 public static void main (String[] args) { int sum = 0 ; for (int i = 1 ; i <= 100 ; i++) { sum += i; } System.out.println("1 到 100 的和是: " + sum); }
⭐⭐ while 循环
// 基本语法
while (循环条件) {
// 循环体
// 迭代语句( 通常放在循环体末尾)
}
1.如果条件为 true, 则执行循环体中的代码。
2.如果条件为 false, 则直接跳过整个 while 循环, 执行循环后面的代码。
3.注意: 由于是先判断, 如果一开始条件就是 false, 循环体一次都不会执行。
1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) { int sum = 0 ; int i = 1 ; while (i <= 100 ) { sum += i; i++; } System.out.println("1 到 100 的和是: " + sum); }
⭐⭐ do-while 循环
// 基本语法
do {
// 循环体
// 迭代语句
} while (循环条件); // 注意这里的分号不能丢
1.先执行: 无论条件是否成立, 循环体至少会执行一次。
2.后判断: 执行完循环体后, 再判断 循环条件。
3.如果条件为 true, 则继续执行循环体。
4.如果条件为 false, 则终止循环。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static void main (String[] args) { Scanner scanner = new Scanner (System.in); int secret = 8 ; int guess; do { System.out.print("请猜一个数字 (1-10): " ); guess = scanner.nextInt(); if (guess > secret) { System.out.println("太大了, 再试一次! " ); } else if (guess < secret) { System.out.println("太小了, 再试一次! " ); } } while (guess != secret); System.out.println("恭喜你, 猜对了! " ); scanner.close(); }
⭐⭐ 循环嵌套
// 基本语法
for (初始化语句1; 循环条件1; 迭代语句1) {
// 外层循环体
for (初始化语句2; 循环条件2; 迭代语句2) {
// 内层循环体( 核心逻辑通常在这里)
}
}
1.循环嵌套可以是任意循环( for、 while、 do-while) 的互相组合。
2.最常见的是 for 循环嵌套 for 循环。
1 2 3 4 5 6 7 8 9 10 11 public static void main (String[] args) { System.out.println("九九乘法表" ); for (int i = 1 ; i <= 9 ; i++) { for (int j = 1 ; j <= i; j++) { System.out.print(j + "*" + i + "=" + (i*j) + "\t" ); } System.out.println(); } }
流程控制 ⭐⭐ break 终止循环 1 2 3 4 5 6 7 8 9 10 11 12 13 public static void main (String[] args) { for (int i = 1 ; i <= 10 ; i++) { if (i == 4 ) { System.out.println("找到数字 4, 停止寻找! " ); break ; } System.out.println("正在检查数字: " + i); } System.out.println("循环结束后的后续代码" ); }
⭐⭐ continue 跳过本次循环 1 2 3 4 5 6 7 8 9 10 11 12 public static void main (String[] args) { for (int i = 1 ; i <= 5 ; i++) { if (i == 3 ) { System.out.println("跳过数字 3" ); continue ; } System.out.println("当前数字: " + i); } }
⭐⭐ return 结束方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class ReturnDemo { public static int findNumber () { for (int i = 1 ; i <= 10 ; i++) { if (i == 5 ) { System.out.println("找到目标数字 5, 直接结束方法并返回结果" ); return i; } System.out.println("检查数字: " + i); } return -1 ; } public static void main (String[] args) { int result = findNumber(); System.out.println("方法返回的结果是: " + result); } }
面向对象 Java 是一门纯面向对象的编程语言, 这意味着在 Java 中, “ 万物皆对象” 。 面向对象编程( Object-Oriented Programming, 简称 OOP) 是一种编程思想, 它将现实世界中的事物抽象为程序中的“ 对象” , 通过对象之间的交互来完成任务。
数组 数组是 Java 中一种特殊的对象, 它是存储相同类型数据的固定长度的容器, 通过索引进行访问。
核心概念
类型统一: 数组只能存储相同数据类型的元素。 比如 int[] 只能存整数, String[] 只能存字符串。
长度固定: 数组一旦创建, 其长度( 能存放多少个元素) 不可改变。
索引访问: 数组中的元素通过索引( 下标) 访问。 索引从 0 开始, 最大索引是 数组长度 - 1。
越界风险: 如果访问 arr[-1] 或 arr[长度], 程序会抛出 ArrayIndexOutOfBoundsException。
内存连续: 数组在内存中占据一块连续的空间, 这使得通过索引查找元素的速度极快( 随机访问) 。
定义与初始化
方式
语法示例
说明
适用场景
静态初始化
int[] arr = {1, 2, 3};
声明时直接赋值。 长度由系统自动推断。
元素值已知、 固定的场景( 如配置项) 。
动态初始化
int[] arr = new int[5];
先指定长度, 由系统赋予默认值( 0/false/null)
长度已知, 但元素值需要后续计算或用户输入。
完整写法
int[] arr = new int[]{1, 2, 3};
静态初始化的完整形式, new 关键字不能省。
声明和赋值分开写, 或者作为方法参数传递时。
基本操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static void main (String[] args) { String[] names = {"张三" , "李四" , "王五" }; int [] scores = new int [5 ]; scores[0 ] = 95 ; scores[1 ] = 87 ; double [] prices; prices = new double []{19.9 , 29.5 , 8.8 }; int [] nums = {10 , 20 , 30 }; System.out.println(nums[0 ]); nums[1 ] = 99 ; System.out.println(nums[1 ]); int len = nums.length; for (int i = 0 ; i < nums.length; i++) { System.out.println("索引 " + i + " 的值是: " + nums[i]); } }
二维数组 如果一维数组是一行格子, 二维数组就是一个表格( 行和列) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int [][] matrix1 = { {1 , 2 , 3 }, {4 , 5 , 6 } }; int [][] matrix2 = new int [2 ][]; matrix2[0 ] = new int [3 ]; matrix2[1 ] = new int [5 ]; for (int i = 0 ; i < matrix1.length; i++) { for (int j = 0 ; j < matrix1[i].length; j++) { System.out.print(matrix1[i][j] + " " ); } System.out.println(); }
类与对象 类是一个抽象的概念, 它描述了一类事物的共同特征和行为。 对象是类的具体实例, 是内存中真实存在的实体。
类是对象的模板( 蓝图) , 对象是类的实例( 具体产品) 。
⭐⭐ 定义一个类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Phone { String brand; String model; double price; public void call (String name) { System.out.println("正在给" + name + "打电话..." ); } public void sendMessage (String content) { System.out.println("发送短信: " + content); } }
⭐⭐ 创建并使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class TestPhone { public static void main (String[] args) { Phone phone1 = new Phone (); phone1.brand = "苹果" ; phone1.model = "iPhone 15" ; phone1.price = 5999.0 ; System.out.println(phone1.brand + "的价格是: " + phone1.price); phone1.call("张三" ); phone1.sendMessage("晚上吃饭" ); Phone phone2 = new Phone (); phone2.brand = "华为" ; phone2.model = "Mate 60" ; phone2.call("李四" ); } }
属性 属性用于描述对象的状态或特征。 它定义在类中、 方法之外。
⭐⭐ 属性的构成
一个完整的属性声明通常包含以下几部分:
访问修饰符: 控制谁可以访问( private, public, protected) 。
非访问修饰符: 改变属性的特性( static, final, transient) 。
数据类型: 可以是基本类型( int, boolean) 或引用类型( String, List) 。
属性名: 变量名。
初始值: 可选的默认值。
⭐⭐ 属性的定义 1 2 3 4 5 6 7 8 9 10 public class Car { private String color = "红色" ; public static int wheelCount = 4 ; public final String BRAND = "丰田" ; }
方法 方法用于描述对象的行为或功能。 它是一段封装好的、 可以被重复调用的代码块。
⭐⭐ 方法的组成 1 2 3 4 5 6 7 8 9 10 [访问修饰符] [非访问修饰符] 返回值类型 方法名([参数列表]) { // 方法体 [return 返回值;] } 返回值类型: 方法执行完后返回的结果类型。 如果不需要返回值, 写 void。 方法名: 动词开头, 小驼峰命名法( 如 calculateSum) 。 参数列表: 方法的输入。 可以没有参数, 也可以有多个参数( 用逗号分隔) 。 方法体: 具体的逻辑代码。 return 语句: 结束方法执行, 并返回结果。
⭐⭐ 实例方法
定义: 没有 static 修饰的方法。 特点: 必须先创建对象( new) , 然后通过对象名调用。 用途: 通常用于操作对象的状态( 属性) 。
1 2 3 4 5 6 7 8 public class MethodConcepts { public void instanceMethod () { System.out.println("这是实例方法, 属于具体的对象" ); } } MethodConcepts mc = new MethodConcepts ();mc.instanceMethod();
⭐⭐ 静态方法
定义: 用 static 修饰的方法。 特点: 属于类本身, 不属于任何对象。 可以直接通过类名调用( 如 Math.sqrt()) 。 限制: 静态方法中不能直接访问实例变量或实例方法, 只能访问静态成员。
1 2 3 4 5 6 7 8 public class MethodConcepts { public static void staticMethod () { System.out.println("这是静态方法, 属于类本身" ); } } MethodConcepts.staticMethod();
⭐⭐ 传参机制 Java 中只有值传递, 没有引用传递。
基本类型: 传递的是值的副本。 在方法内修改参数, 不会影响原始变量。 引用类型: 传递的是对象地址的副本。 在方法内可以通过这个地址修改对象的内容( 属性) , 但无法改变外部引用变量的指向。 对于 String 等不可变类, 看似“ 传值” , 其实是对象本身不允许修改, 不是传递方式变了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class MethodConcepts { public void changePrimitive (int x) { x = 100 ; } public void changeReference (int [] array) { array[0 ] = 99 ; } } MethodConcepts mc = new MethodConcepts ();int num = 10 ;mc.changePrimitive(num); System.out.println("基本类型传参后: " + num); int [] arr = {1 , 2 , 3 };mc.changeReference(arr); System.out.println("引用类型传参后 arr[0]: " + arr[0 ]);
⭐⭐ 可变参数 可变参数( Varargs, Variable Arguments 的缩写) 是一种非常实用的语法特性。 它允许你编写能够接收不定数量参数的方法, 极大地提高了代码的灵活性和简洁性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class VarargsExample { public static int sum (int ... numbers) { int total = 0 ; for (int num : numbers) { total += num; } return total; } } VarargsExample ve = new VarargsExample ();System.out.println(sum(1 , 2 )); System.out.println(sum(1 , 2 , 3 , 4 , 5 )); System.out.println(sum());
⭐⭐ 方法的重载
定义: 在同一个类中, 允许存在多个同名方法, 但参数列表必须不同( 参数个数、 类型或顺序不同) 。 用途: 实现“ 同一种行为, 多种输入方式” 。 例如: print(int i) 和 print(String s)。 注意: 仅返回值不同是不能构成重载的, 编译器会报错。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class MethodConcepts { public int add (int a, int b) { return a + b; } public int add (int a, int b, int c) { return a + b + c; } public double add (double a, double b) { return a + b; } } MethodConcepts mc = new MethodConcepts ();System.out.println("两个整数相加: " + mc.add(1 , 2 )); System.out.println("三个整数相加: " + mc.add(1 , 2 , 3 )); System.out.println("两个小数相加: " + mc.add(1.5 , 2.5 ));
构造器 构造器( 也称为构造方法或构造函数) 是一种特殊的方法, 它的主要任务是在创建对象时初始化对象。
⭐⭐ 构造器的特征
构造器虽然看起来像方法, 但它和普通方法有本质区别:
名字必须与类名完全相同: 包括大小写。
没有返回值类型: 连 void 都不能写。 如果写了返回值类型, 它就变成了普通方法。
不能被 static、 final、 abstract、 synchronized、 native 修饰: 构造器只能由 new 关键字调用, 不能被继承, 也不能被静态修饰。
自动调用: 当你使用 new 创建对象时, 构造器会自动执行, 无需你手动调用。
⭐⭐ 构造器的分类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Person { public Person () { System.out.println("无参构造器被调用了" ); } } public class Person { private String name; private int age; public Person (String name, int age) { this .name = name; this .age = age; } }
⭐⭐ this 关键字与构造器 在构造器中, this 代表当前正在创建的对象。 它主要用于解决参数名与属性名冲突的问题。
1 2 3 4 public Person (String name, int age) { this .name = name; this .age = age; }
⭐⭐ 构造器重载 一个类可以有多个构造器, 只要它们的参数列表不同( 参数个数、 类型或顺序不同) 。 这和方法重载的原理一样。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Person { private String name; private int age; public Person () { this .name = "未知" ; this .age = 0 ; } public Person (String name) { this .name = name; this .age = 0 ; } public Person (String name, int age) { this .name = name; this .age = age; } }
⭐⭐ 构造器链 在一个构造器中, 你可以通过 this() 调用本类的另一个构造器, 或者通过 super() 调用父类的构造器。
this() 或 super() 必须是构造器中的第一行语句。 两者不能同时出现在同一个构造器中( 因为都必须是第一行) 。
如果你在子类构造器中没有显式写 super(), Java 会自动帮你插入 super(), 即调用父类的无参构造器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Person { private String name; private int age; private String gender; public Person (String name, int age, String gender) { this .name = name; this .age = age; this .gender = gender; } public Person (String name, int age) { this (name, age, "未知" ); } }
核心特性 封装、 继承、 多态是 Java 面向对象编程( OOP) 的三大核心特性。 它们就像盖房子的三大法则: 封装负责把砖石砌成独立的房间( 安全) , 继承负责让子楼房复用地基的结构( 复用) , 多态则让同一个开关能控制不同房间的灯( 灵活) 。
⭐⭐ 封装 (Encapsulation): 隐藏细节, 保证安全 对外只暴露必要的接口, 将对象的内部实现细节( 数据和逻辑) 隐藏起来, 防止外部随意篡改。
安全: 防止数据被非法设置。
易用: 调用者无需关心内部怎么实现, 只要会用接口就行。
解耦: 内部实现逻辑变了, 只要接口不变, 外部调用代码就不需要改。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class Person { private int age; public void setAge (int age) { if (age < 0 || age > 150 ) { System.out.println("年龄不合法, 已设为默认值18" ); this .age = 18 ; } else { this .age = age; } } public int getAge () { return age; } } Person p = new Person ();p.setAge(-5 ); System.out.println(p.getAge());
⭐⭐ 继承 (Inheritance): 代码复用, 建立关系 子类可以继承父类的属性和方法, 避免代码重复。
代码复用: 公共的代码写在父类里, 子类直接拥有。
可扩展性: 子类可以在父类基础上增加自己特有的功能。
多态的基础: 没有继承, 就没有多态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class Animal { protected String name; public void eat () { System.out.println(name + " 在吃东西" ); } } public class Dog extends Animal { public void bark () { System.out.println(name + " 在汪汪叫" ); } @Override public void eat () { System.out.println(name + " 在啃骨头" ); } } Dog dog = new Dog ();dog.name = "旺财" ; dog.eat(); dog.bark();
在 Java 语言中, java.lang.Object 类是所有类的“ 始祖” 。 如果一个类在定义时没有使用 extends 关键字明确指定其父类, 那么它会自动继承 Object 类。 这意味着, 你编写的任何一个类, 都默认直接或间接地继承自 Object, 所有对象( 包括数组) 都拥有 Object 类定义的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 class Person { String name; int age; public Person (String name, int age) { this .name = name; this .age = age; } @Override public boolean equals (Object obj) { if (this == obj) return true ; if (obj == null || getClass() != obj.getClass()) return false ; Person person = (Person) obj; return age == person.age && Objects.equals(name, person.name); } } Person p1 = new Person ("张三" , 20 );Person p2 = new Person ("张三" , 20 );System.out.println(p1.equals(p2)); class Person { String name; int age; public Person (String name, int age) { this .name = name; this .age = age; } @Override public int hashCode () { return Objects.hash(name, age); } } Person p1 = new Person ("张三" , 20 );Person p2 = new Person ("张三" , 20 );System.out.println(p1.hashCode() == p2.hashCode());
⭐⭐ 多态 (Polymorphism): 同一操作, 多种形态 同一个父类类型的引用, 指向不同的子类对象, 从而在执行时产生不同的行为。 它是建立在继承( 或实现接口) 和方法重写之上的。
灵活性: 接口通用, 实现各异。
可扩展性: 新增功能只需添加新子类, 无需修改已有调用逻辑。
1 2 3 4 5 6 7 Animal animal1 = new Dog (); Animal animal2 = new Cat (); animal1.eat(); animal2.eat();
⭐⭐ 编译类型和运行类型
编译时类型: 是变量声明时的类型( 身份证名字) , 决定了你能调用哪些方法。
运行时类型: 是对象实际创建时的类型( 真实面目) , 决定了方法具体执行哪段代码。
这种“ 编译时看父类, 运行时看子类” 的机制, 就是 多态。
1 2 3 4 5 6 7 8 9 10 if (myPet instanceof Dog) { System.out.println("它其实是一只狗" ); } if (myPet instanceof Dog) { System.out.println("它其实是一只狗" ); }
⭐⭐ 动态绑定 动态绑定也叫运行时绑定, 指的是: 在程序运行期间, JVM 根据对象的实际类型( 运行时类型) , 来决定到底调用哪个具体的方法实现。
静态绑定( 反义词) : 在编译阶段就决定了调用哪个方法( 比如 static 方法、 private 方法) 。
动态绑定: 在编译阶段不做决定, 只在运行阶段才做决定。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 class Animal { public void makeSound () { System.out.println("动物发出声音" ); } public static void sleep () { System.out.println("动物在睡觉 (静态方法)" ); } } class Dog extends Animal { @Override public void makeSound () { System.out.println("汪汪汪! (Dog重写了)" ); } public static void sleep () { System.out.println("狗在睡觉 (静态方法)" ); } } public class DynamicBindingDemo { public static void main (String[] args) { Animal animal = new Dog (); animal.makeSound(); animal.sleep(); } }
动态绑定只针对方法, 不针对属性( 成员变量) 。
1 2 3 4 5 6 7 8 9 10 11 12 class Animal { String name = "动物" ; } class Dog extends Animal { String name = "狗" ; } Animal animal = new Dog ();System.out.println(animal.name);
包的应用 包是用于组织类和接口的命名空间机制。 你可以把它想象成电脑中的文件夹, 而类就是文件夹里的文件25。 包的核心作用是解决命名冲突、 控制访问权限以及让项目结构更清晰。
⭐⭐ 为什么要用包 如果没有包, 所有的类都挤在一起, 就像桌面上堆满了成千上万个文件, 不仅难以管理, 还容易重名。
解决命名冲突: 不同公司或开发者编写的类可能重名( 比如都写了一个 UserService) 。 只要将它们放在不同的包中( 如 com.companyA.service.UserService 和 com.companyB.service.UserService) , 就可以共存。
访问控制: 包提供了一种访问级别( 默认/default 和 protected) , 允许类之间在包内自由访问, 而对外隐藏细节。
模块化管理: 将功能相关的类组织在一起( 如数据库相关的放 dao 包, 业务逻辑放 service 包) , 项目结构一目了然。
⭐⭐ 包的命名规范 包名通常全部使用小写字母, 以确保与类名( 通常大写开头) 区分开。
反向域名: 这是最标准、 最推荐的命名方式。 因为域名是全球唯一的, 用域名倒过来作为包名前缀, 可以保证包名的唯一性。
格式: 域名后缀.公司名.项目名.模块名
示例: 如果你的公司域名是 example.com, 项目叫 shop, 用户模块就可以命名为 com.example.shop.user。
层级分隔: 包的层级之间用英文句点 . 分隔。 这对应着文件系统中的目录结构。
com.example.demo 对应的文件夹路径是 com/example/demo。
⭐⭐ 创建和使用包 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package com.example.animal; public class Dog { public void bark () { System.out.println("汪汪" ); } } package com.example.demo;import com.example.animal.Dog; public class Main { public static void main (String[] args) { Dog dog = new Dog (); } } import com.example.animal.*; com.example.animal.Dog dog = new com .example.animal.Dog();
访问控制 为了保证数据安全, Java 提供了访问修饰符来控制属性的可见性。 核心原则是“ 高内聚, 低耦合” , 尽量隐藏内部数据。
修饰符
权限
描述
用途
private
私有
只有在本类内部可以访问
最常用, 用于实现封装, 防止外部随意修改数据
public
公有
任何地方都可以访问
慎用, 直接暴露属性风险很大, 别人可以随意修改数据
protected
受保护
本包内和子类中可以访问
当你设计一个父类, 希望子类能够重写某些方法或直接调用某些父类逻辑, 但又不想让这些方法被无关的外部类随意调用时可以使用。
不写修饰符
默认( 包私有)
只有在同一个包内可以访问
当你有一组紧密相关的类放在同一个包内时, 它们之间需要共享一些数据或方法, 但又不希望暴露给整个项目。 这时用默认修饰符最合适。
⭐⭐ 私有 规则: 只能在本类内部访问, 外部完全不可见。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class ClassA { private void privateMethod () { System.out.println("我是私有的, 只有本类能访问" ); } public void callFromInside () { privateMethod(); } } class ClassB { public void test () { ClassA a = new ClassA (); } }
⭐⭐ 公有 规则: 任何地方都可以访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package com.example.utils;public class PublicClass { public void doSomething () { System.out.println("我是公开的, 谁都能调用" ); } } package com.example.main;import com.example.utils.PublicClass;public class Main { public static void main (String[] args) { PublicClass pc = new PublicClass (); pc.doSomething(); } }
⭐⭐ 受保护 规则: 同一个包内的类可以访问; 不同包的子类也可以访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package com.example.parent;public class Parent { protected void protectedMethod () { System.out.println("我是受保护的" ); } } package com.example.child;import com.example.parent.Parent;public class Child extends Parent { public void test () { protectedMethod(); } } package com.example.other;import com.example.parent.Parent;public class Other { public void test () { Parent p = new Parent (); } }
⭐⭐ 默认 规则: 不写任何修饰符。 只有同一个包内的类可以访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package com.example.package1;class DefaultClass { void defaultMethod () { System.out.println("我是默认权限, 只有同包能访问" ); } } class SamePackageClass { public void test () { DefaultClass dc = new DefaultClass (); dc.defaultMethod(); } } package com.example.package2;import com.example.package1.DefaultClass;public class DifferentPackageClass { public void test () { DefaultClass dc = new DefaultClass (); } }
代码块 ⭐⭐ 局部代码块 在方法中定义的代码块, 用于限定变量的作用域。
1 2 3 4 5 6 7 8 9 10 11 public class LocalBlockDemo { public void method () { { int x = 10 ; System.out.println("代码块内: x = " + x); } } }
⭐⭐ 构造代码块 在类中直接定义的代码块, 每次创建对象时都会执行, 且在构造方法之前执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class ConstructorBlockDemo { private String name; private int age; { System.out.println("构造代码块执行" ); this .name = "默认姓名" ; this .age = 18 ; } public ConstructorBlockDemo () { System.out.println("无参构造方法执行" ); System.out.println("name: " + name + ", age: " + age); } public ConstructorBlockDemo (String name, int age) { System.out.println("有参构造方法执行" ); this .name = name; this .age = age; System.out.println("name: " + name + ", age: " + age); } public static void main (String[] args) { new ConstructorBlockDemo (); System.out.println("---" ); new ConstructorBlockDemo ("张三" , 25 ); } }
⭐⭐ 静态代码块 使用 static 关键字修饰的代码块, 在类加载时执行, 只执行一次。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class StaticBlockDemo { private static String databaseUrl; private static int connectionCount; static { System.out.println("静态代码块执行" ); databaseUrl = "jdbc:mysql://localhost:3306/mydb" ; connectionCount = 10 ; System.out.println("数据库连接池初始化完成" ); } { System.out.println("构造代码块执行" ); } public StaticBlockDemo () { System.out.println("构造方法执行" ); } public static void main (String[] args) { System.out.println("main方法开始执行" ); new StaticBlockDemo (); new StaticBlockDemo (); } }
⭐⭐ 同步代码块 用于多线程同步, 保证同一时刻只有一个线程可以执行该代码块。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class SynchronizedBlockDemo { private static int count = 0 ; private static Object lock = new Object (); public void increment () { synchronized (lock) { count++; System.out.println(Thread.currentThread().getName() + ": count = " + count); } } public static void main (String[] args) { SynchronizedBlockDemo demo = new SynchronizedBlockDemo (); for (int i = 0 ; i < 5 ; i++) { new Thread (() -> { for (int j = 0 ; j < 3 ; j++) { demo.increment(); try { Thread.sleep(10 ); } catch (InterruptedException e) { e.printStackTrace(); } } }, "线程-" + i).start(); } } }
⭐⭐ 代码块执行顺序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 class Parent { static { System.out.println("1. 父类静态代码块" ); } { System.out.println("3. 父类构造代码块" ); } public Parent () { System.out.println("4. 父类构造方法" ); } } public class CodeBlockOrderDemo extends Parent { static { System.out.println("2. 子类静态代码块" ); } { System.out.println("5. 子类构造代码块" ); } public CodeBlockOrderDemo () { System.out.println("6. 子类构造方法" ); } public static void main (String[] args) { System.out.println("开始创建对象..." ); new CodeBlockOrderDemo (); System.out.println("创建第二个对象..." ); new CodeBlockOrderDemo (); } }
内部类 内部类是指定义在另一个类内部的类。 内部类的核心价值在于实现更好的封装、 解决多重继承的局限以及让代码结构更紧密。 可以把外部类看作一个“ 整体” , 而内部类则是这个整体中的“ 组成部分” 或“ 辅助工具” 。
实现多重继承的效果: Java 不支持类的多重继承( 即一个类不能同时继承两个类) , 但通过内部类, 可以让外部类继承一个类, 同时让内部类继承另一个类, 从而在逻辑上实现多重继承。
更好的封装性: 将一个类隐藏在另一个类内部, 不让外部其他类直接访问, 只有外部类可以控制它的行为, 提高了代码的安全性和内聚性。
方便访问外部类成员: 内部类可以自由访问外部类的成员( 包括 private 私有成员) , 而外部类访问内部类时需要通过内部类的对象。
成员内部类 成员内部类是最普通的内部类, 定义在外部类的成员位置( 与成员变量和方法同级) 。
依附于外部类实例: 必须先创建外部类对象, 才能创建内部类对象。
访问权限: 可以直接访问外部类的所有成员( 包括私有成员) 。
不能定义静态成员: 除非是 static final 修饰的常量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Outer { private String msg = "Hello" ; public class Inner { public void print () { System.out.println(msg); } } } Outer outer = new Outer ();Outer.Inner inner = outer.new Inner (); inner.print();
静态内部类 静态内部类也称为嵌套类, 定义在外部类的成员位置, 但使用 static 修饰。
不依附外部类实例: 可以直接创建对象, 不需要外部类对象。
访问限制: 只能访问外部类的静态成员, 不能直接访问外部类的非静态成员。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Outer { private static String staticMsg = "Static Hello" ; public static class StaticInner { public void print () { System.out.println(staticMsg); } } } Outer.StaticInner inner = new Outer .StaticInner(); inner.print();
局部内部类 局部内部类定义在外部类的方法或代码块内部。
作用域局限: 只能在定义它的方法内部使用, 方法执行完后即失效。
访问局部变量: 只能访问方法中被 final 修饰或“ 实际上为 final” ( Java 8+, 即赋值后未改变) 的局部变量。
不能有访问修饰符: 不能用 public、 private 等修饰。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class Outer { public void method () { final int localVar = 10 ; class LocalInner { public void print () { System.out.println(localVar); } } new LocalInner ().print(); } }
匿名内部类 没有名字的内部类, 通常用于实现接口或继承类, 并且只使用一次。
没有类名: 通常在创建对象时直接定义类体。
必须继承或实现: 必须继承一个类或实现一个接口。
常用于回调/监听: 比如按钮点击事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 interface OnClickListener { void onClick () ; } OnClickListener listener = new OnClickListener () { @Override public void onClick () { System.out.println("按钮被点击了" ); } }; listener.onClick();
多重继承 Java 不支持多重继承, 但内部类可以完美解决这个问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 class A { public void a () { System.out.println("A" ); } } class B { public void b () { System.out.println("B" ); } } class C extends A { public void callA () { a(); } private class InnerB extends B { public void callB () { b(); } } public void useB () { new InnerB ().callB(); } } C c = new C ();c.callA(); c.useB();
抽象类 抽象类是一种不能被直接实例化的特殊类。 它就像是一个“ 半成品” 或“ 模板” , 主要用于为子类提供统一的结构和规范, 强制子类实现特定的方法, 同时也可以提供可复用的公共代码, 抽象类通过 abstract 关键字来定义。
抽象类的核心价值在于代码复用和定义规范。
提取共性: 将多个子类共有的属性和方法提取到抽象父类中, 减少代码重复。
强制约束: 通过定义抽象方法, 强制要求所有子类必须提供自己的具体实现, 确保子类具备某些特定行为。
提供默认实现: 抽象类中可以包含普通方法( 具体方法) , 为子类提供通用的、 可选的默认行为。
不能直接实例化: 抽象类不能使用 new 关键字直接创建对象。
核心语法 使用 abstract 修饰的类就是抽象类, 修饰的方法就是抽象方法。 抽象方法是一种没有方法体( 只有声明) 的方法, 它存在的意义就是被子类重写。
抽象方法不能是 private: private 修饰的方法对子类不可见。
抽象方法不能是 final: final 修饰的方法不能被重写。
抽象方法不能是 static: static 修饰的方法属于类本身。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 abstract class Animal { protected String name; public Animal (String name) { this .name = name; } public void sleep () { System.out.println(name + " 正在睡觉" ); } public abstract void makeSound () ; }
子类继承 当一个普通类( 非抽象类) 继承一个抽象类时, 它必须重写父类中的所有抽象方法。 如果子类不想实现, 那么子类也必须声明为抽象类。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 abstract class Animal { protected String name; public abstract void makeSound () ; } class Dog extends Animal { public Dog (String name) { super (name); } @Override public void makeSound () { System.out.println(name + " 汪汪叫" ); } }
构造方法 虽然不能直接实例化, 但抽象类可以定义构造方法。 它的构造方法主要用于被子类的构造方法调用, 通过 super 关键字, 以初始化抽象类中定义的成员变量。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 abstract class Employee { protected String name; protected String employeeId; public Employee (String name, String employeeId) { this .name = name; this .employeeId = employeeId; System.out.println("【 抽象父类构造方法】 正在创建员工: " + name + ", 工号: " + employeeId); } public abstract void doWork () ; public void clockIn () { System.out.println(name + " (" + employeeId + ") 打卡上班" ); } } class Engineer extends Employee { private String programmingLanguage; public Engineer (String name, String employeeId, String language) { super (name, employeeId); this .programmingLanguage = language; System.out.println("【 子类构造方法】 工程师 " + name + " 擅长 " + language); } @Override public void doWork () { System.out.println(name + " 正在编写 " + programmingLanguage + " 代码" ); } } class Manager extends Employee { private int teamSize; public Manager (String name, String employeeId, int size) { super (name, employeeId); this .teamSize = size; System.out.println("【 子类构造方法】 经理 " + name + " 管理 " + teamSize + " 人团队" ); } @Override public void doWork () { System.out.println(name + " 正在开会并制定团队计划" ); } } public class PolymorphismDemo { public static void main (String[] args) { System.out.println("=== 创建工程师对象 ===" ); Employee emp1 = new Engineer ("张工" , "E001" , "Java" ); System.out.println("\n=== 创建经理对象 ===" ); Employee emp2 = new Manager ("李经理" , "M001" , 10 ); System.out.println("\n=== 通过多态调用方法 ===" ); emp1.clockIn(); emp2.clockIn(); emp1.doWork(); emp2.doWork(); } }
接口 接口是一种极其重要的引用数据类型, 它是一种比抽象类更加纯粹的抽象。 如果说抽象类定义了“ 是什么” 的基础结构, 那么接口则定义了“ 能做什么” 的行为规范或契约。 接口使用 interface 关键字来定义。
实现多态: 接口是实现多态的重要手段。 通过接口类型的引用指向具体的实现类对象, 可以让程序在运行时动态决定调用哪个类的方法。
解决单继承局限: Java 类只能单继承, 但一个类可以实现多个接口。 这使得对象可以具备多种类型的行为能力, 变相实现了“ 多重继承” 。
实现解耦: 接口将“ 定义” 与“ 实现” 分离。 调用者只依赖于接口, 不依赖于具体的实现类, 降低了模块间的耦合度, 提高了系统的可扩展性和可维护性。
定义标准与规范: 接口就像一份“ 契约” , 规定了所有实现类必须提供哪些方法, 确保了不同类之间的行为一致性。
接口常量 接口中的变量会被隐式地指定为 public static final, 即公共的、 静态的、 不可变的常量。
1 2 3 4 public interface Constants { int VALUE = 100 ; }
抽象方法 接口中的方法默认是 public abstract 的, 不需要手动添加修饰符。
1 2 3 4 public interface Flyable { void fly () ; }
默认方法 默认方法使用 default 修饰, 可以包含方法体。 这允许在不破坏现有实现类的情况下为接口新增方法。 如果一个类实现了两个包含同名 default 方法的接口, 必须在实现类中重写该方法, 并使用 接口名.super.方法名() 来指定调用哪一个。
1 2 3 4 5 public interface Flyable { default void land () { System.out.println("默认的降落方式" ); } }
静态方法 静态方法使用 static 修饰, 可以直接通过接口名调用, 主要用于提供工具方法。
1 2 3 4 5 6 7 public interface Flyable { static void showInfo () { System.out.println("这是一个飞行接口" ); } } Flyable.showInfo();
私有方法 私有方法使用 private 修饰, 用于在接口内部封装 default 方法或 static 方法的公共逻辑, 不能被实现类调用。
1 2 3 4 5 6 7 8 9 10 11 public interface Flyable { private void helper () { } default void methodA () { helper(); } default void methodB () { helper(); } }
多重继承 一个类可以实现多个接口, 从而具备多种能力。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface Flyable { void fly () ; } interface Swimmable { void swim () ; } class Duck implements Flyable , Swimmable { @Override public void fly () { System.out.println("鸭子扇动翅膀飞行" ); } @Override public void swim () { System.out.println("鸭子用脚蹼游泳" ); } }
接口继承 接口之间可以使用 extends 关键字进行继承, 并且支持多继承。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 interface A { void methodA () ; } interface B { void methodB () ; } interface C extends A , B { void methodC () ; } class Impl implements C { @Override public void methodA () { } @Override public void methodB () { } @Override public void methodC () { } }
应用示例 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 interface Calculator { int calculate (int a, int b) ; } class AddCalculator implements Calculator { @Override public int calculate (int a, int b) { return a + b; } } class SubCalculator implements Calculator { @Override public int calculate (int a, int b) { return a - b; } } public class Test { public static void main (String[] args) { Calculator calc; calc = new AddCalculator (); System.out.println(calc.calculate(5 , 3 )); calc = new SubCalculator (); System.out.println(calc.calculate(5 , 3 )); } }
枚举 枚举是一种特殊的类, 用于定义一组固定的、 命名的常量集合。 它不仅仅是一个常量列表, 更是一种强大的类型安全工具, 能够有效替代传统的 public static final 常量定义方式。 枚举使用 enum 关键字定义。
类型安全: 编译器会检查枚举类型, 杜绝非法值。
可读性强: 语义清晰, 直接使用 Season.SPRING。
功能强大: 可以像普通类一样拥有成员变量、 方法, 甚至实现接口。
核心语法 1 2 3 public enum Season { SPRING, SUMMER, AUTUMN, WINTER; }
构造函数 枚举可以包含成员变量和构造函数, 但构造函数必须是私有的 private 可省略, 以防止外部创建实例。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public enum Season { SPRING("春天" , 1 ), SUMMER("夏天" , 2 ); private final String name; private final int value; Season(String name, int value) { this .name = name; this .value = value; } public String getName () { return name; } public int getValue () { return value; } }
抽象方法 枚举可以定义抽象方法, 让每个枚举常量提供自己的具体实现, 这非常适合实现策略模式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public enum Operation { ADD { public int apply (int a, int b) { return a + b; } }, SUBTRACT { public int apply (int a, int b) { return a - b; } }; public abstract int apply (int a, int b) ; } int result = Operation.ADD.apply(5 , 3 );
常用方法 Java 编译器会自动为枚举类生成一些静态方法。
方法
说明
示例
values()
返回枚举类型的所有常量数组( 按定义顺序)
Season[] seasons = Season.values();
valueOf(String)
根据名称返回枚举实例( 名称必须完全匹配, 否则抛异常)
Season s = Season.valueOf("SPRING");
name()
返回枚举常量的名称( 字符串)
String name = Season.SPRING.name();
ordinal()
返回枚举常量的序号( 从 0 开始)
int index = Season.SPRING.ordinal();
应用示例 ⭐⭐ 状态机与状态管理 这是枚举最常见的场景, 如订单状态、 审核状态。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public enum OrderStatus { PENDING("待支付" ), PAID("已支付" ), SHIPPED("已发货" ), COMPLETED("已完成" ); private final String desc; OrderStatus(String desc) { this .desc = desc; } public String getDesc () { return desc; } }
⭐⭐ 策略模式 当代码中出现大量的 if-else 或 switch 判断类型时, 可以用枚举策略替代。
1 2 3 4 5 6 7 8 9 10 11 public enum PayStrategy { ALI_PAY { public void pay () { } }, WECHAT_PAY { public void pay () { } }; public abstract void pay () ; }
⭐⭐ 线程安全的单例模式 枚举是实现单例模式的最佳方式之一, 不仅代码简洁, 而且天然防止反射和反序列化攻击, 绝对线程安全。
1 2 3 4 5 6 7 8 9 10 public enum Singleton { INSTANCE; public void doSomething () { System.out.println("执行业务逻辑" ); } } Singleton.INSTANCE.doSomething();
泛型 Java 泛型是 Java SE 5.0 引入的一项重大特性, 它提供了编译时类型安全检测机制, 允许程序员在编译时检测到非法的类型操作。 泛型的本质是参数化类型, 即所操作的数据类型被指定为一个参数。
标识符 在 Java 泛型中, 虽然可以使用任何合法的标识符作为类型参数名( 例如 MyType) , 但为了代码的可读性和社区共识, 惯例是使用单个大写字母。
字母
全称
含义
示例
T
Type
最常用。 代表一个未知的具体类型。 适用于: 泛型类、 泛型方法、 泛型接口。
public <T> T getValue()
E
Element
集合专用。 代表集合中的元素类型。 适用于: 实现 `Collection`, `List`, `Set` 等集合类时。
class MyList<E> implements List<E>
K
Key
键专用。 代表映射( Map) 中的键类型。 适用于: `Map`, `Dictionary` 等相关类。
class MyMap<K, V>
V
Value
值专用。 代表映射( Map) 中的值类型。 通常与 `K` 成对出现。
class MyMap<K, V>
N
Number
数字专用。 代表数值类型。 适用于: 需要限制类型为数字及其子类( Integer, Double 等) 的场景。
class Stats<N extends Number>
R
Return / Result
返回值。 常用于函数式接口或回调中, 表示返回结果的类型。
Function<T, R> (输入 T, 返回 R)
S, U, V...
序列
第二个、 第三个类型参数。 当 `T` 被占用, 或者需要多个不同类型的参数时, 按字母表顺序向后推演。
TriFunction<T, U, V>
泛型类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class Box <T> { private T content; public void set (T content) { this .content = content; } public T get () { return content; } } Box<String> stringBox = new Box <>(); stringBox.set("Hello" ); String s = stringBox.get();
泛型方法 1 2 3 4 5 6 7 8 9 10 11 12 public class Util { public static <T> void printArray (T[] array) { for (T element : array) { System.out.println(element); } } String[] strArr = {"A" , "B" , "C" }; Util.printArray(strArr); }
泛型接口 1 2 3 4 5 6 7 8 9 10 public interface Generator <T> { T next () ; } public class StringGenerator implements Generator <String> { @Override public String next () { return "随机字符串" ; } }
通配符 ⭐⭐ 无限定通配符 (?) 表示未知类型。 通常用于读取操作, 但不能向其中添加元素( 除了 null) 。
1 2 3 4 5 6 public void printList (List<?> list) { for (Object elem : list) { System.out.println(elem); } }
⭐⭐ 上界通配符 (? extends T) 表示类型必须是 T 或 T 的子类。
原则: PECS (Producer Extends, Consumer Super)。 如果参数化类型作为一个生产者( 只读/输出数据) , 使用 extends。
限制: 不能向集合中添加元素( 因为不知道具体是哪个子类) , 只能读取为 T 类型。
1 2 3 4 5 6 7 8 9 10 public double sumOfFruits (List<? extends Fruit> fruits) { double sum = 0 ; for (Fruit f : fruits) { sum += f.getPrice(); } return sum; }
⭐⭐ 下界通配符 (? super T) 表示类型必须是 T 或 T 的父类。
原则: 如果参数化类型作为一个消费者( 写入/输入数据) , 使用 super。
限制: 读取时只能得到 Object 类型( 因为可能是很远的父类) , 但可以安全地写入 T 及其子类。
1 2 3 4 5 6 7 public void addFruits (List<? super Fruit> fruits) { fruits.add(new Apple ()); fruits.add(new Fruit ()); }
⭐⭐ 多重边界 类型参数可以同时被多个接口限定, 但只能有一个类( 且必须在第一位) 。
1 2 3 public <T extends Comparable <T> & Serializable> void sortAndSave (T item) { }
类型擦除 Java 的泛型是伪泛型。 泛型信息只存在于代码编译阶段, 一旦编译成 .class 文件, 泛型类型参数就会被擦除, 替换为其限定类型( 如果没有指定限定, 则替换为 Object) 。
为了保持多态性, 编译器会在子类中生成桥接方法。 例如, 泛型类重写父类方法时, 编译器会生成一个签名包含 Object 的桥接方法来调用具体的泛型方法。
List<String> 和 List<Integer> 在运行时是同一个类 (List.class)。
不能使用 new T() 实例化泛型类型。
不能创建泛型数组 new T[]。
不能使用 instanceof 检查泛型类型( 如 obj instanceof List<String> 是非法的, 只能 obj instanceof List) 。
⭐⭐ 常见误区与限制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 List<Integer> list = new ArrayList <>(); public class GenericTest <T> { private static T t; }
反射 Java 反射是 Java 语言中一种非常强大但需要谨慎使用的机制。 它允许程序在运行时动态地获取类的信息, 并操作类或对象的属性和方法, 打破了 Java 作为静态语言在编译期就必须确定所有类型的限制。
获取类的结构信息: 获取类的构造器、 方法、 字段、 注解、 父类、 接口等。
动态创建对象: 不仅限于无参构造, 甚至可以调用私有构造器。
动态调用方法: 调用任意对象的任意方法( 包括私有方法) 。
动态访问和修改属性: 读写任意对象的任意字段( 包括私有字段) 。
破坏封装性: 通过 setAccessible(true) 突破 private 等访问修饰符的限制。
核心类
作用
获取方式
Class
类的元数据, 是反射的入口
Class.forName("全类名")
Field
描述类的成员变量
clazz.getDeclaredField("name")
Method
描述类的方法
clazz.getDeclaredMethod("methodName")
Constructor
描述类的构造器
clazz.getDeclaredConstructor(String.class)
核心语法 ⭐⭐ 第一种: 类名.class 1 2 Class<User> clazz = User.class;
⭐⭐ 第二种: 对象.getClass() 1 2 3 User user = new User ();Class<?> clazz = user.getClass();
⭐⭐ 第三种: Class.forName("全限定类名") 1 2 Class<?> clazz = Class.forName("com.example.User" );
创建对象 1 2 3 4 5 6 7 8 9 Class<?> clazz = Class.forName("com.example.User" ); Object obj = clazz.getDeclaredConstructor().newInstance();Constructor<?> ctor = clazz.getConstructor(String.class, int .class); Object obj2 = ctor.newInstance("张三" , 25 );
操作属性 1 2 3 4 5 6 7 8 9 10 11 Field field = clazz.getDeclaredField("name" );field.setAccessible(true ); field.set(obj, "李四" ); String name = (String) field.get(obj);
调用方法 1 2 3 4 5 6 7 8 Method method = clazz.getDeclaredMethod("sayHello" );method.setAccessible(true ); method.invoke(obj);
注解 注解是一种特殊的接口, 用于为代码添加元数据信息, 修饰解释 包、 类、 方法、 属性、 构造器、 局部变量等数据。 它不会直接影响程序的运行逻辑, 但可以被编译器、 开发工具或框架所使用。
元注解 ⭐⭐ @Retention 指定注解的保留策略。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public enum RetentionPolicy { SOURCE, CLASS, RUNTIME } @Retention(RetentionPolicy.SOURCE) @interface SourceAnnotation {} @Retention(RetentionPolicy.CLASS) @interface ClassAnnotation {} @Retention(RetentionPolicy.RUNTIME) @interface RuntimeAnnotation {}
⭐⭐ @Target 指定注解可以使用的目标位置。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public enum ElementType { TYPE, FIELD, METHOD, PARAMETER, CONSTRUCTOR, LOCAL_VARIABLE, ANNOTATION_TYPE, PACKAGE, TYPE_PARAMETER, TYPE_USE } @Target({ElementType.TYPE, ElementType.METHOD}) @interface MyTargetAnnotation {}@MyTargetAnnotation public class TargetDemo { @MyTargetAnnotation public void method () {} private String field; }
⭐⭐ @Documented 使注解信息出现在Javadoc中。
1 2 3 4 5 6 7 8 9 10 11 @Documented @interface DocumentedAnnotation {}@DocumentedAnnotation public class DocumentedDemo { }
⭐⭐ @Inherited 允许子类继承父类的注解。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Inherited @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface InheritedAnnotation {}@InheritedAnnotation class Parent {}class Child extends Parent {} public class InheritedDemo { public static void main (String[] args) { boolean hasAnnotation = Child.class.isAnnotationPresent(InheritedAnnotation.class); System.out.println("Child是否有注解: " + hasAnnotation); } }
内置注解 ⭐⭐ @Override 表示方法重写父类方法, 编译器会检查该方法是否真正重写了父类方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 class Parent { public void method () {} } class Child extends Parent { @Override public void method () { System.out.println("子类方法" ); } }
⭐⭐ @Deprecated 标记已过时的方法或类, 使用时会显示警告。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class DeprecatedDemo { @Deprecated public void oldMethod () { System.out.println("这是一个过时的方法" ); } @Deprecated(since = "2.0", forRemoval = true) public void oldMethodForRemoval () { System.out.println("即将被移除的方法" ); } public void newMethod () { System.out.println("推荐使用的新方法" ); } public static void main (String[] args) { DeprecatedDemo demo = new DeprecatedDemo (); demo.oldMethod(); demo.oldMethodForRemoval(); } }
⭐⭐ @SuppressWarnings 抑制编译警告。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public class SuppressWarningsDemo { @SuppressWarnings("unchecked") public void useRawType () { List list = new ArrayList (); list.add("string" ); } @SuppressWarnings({"unchecked", "deprecation"}) public void suppressMultipleWarnings () { DeprecatedDemo demo = new DeprecatedDemo (); demo.oldMethod(); } @SuppressWarnings("all") public void suppressAllWarnings () { } }
⭐⭐ @SafeVarargs 抑制可变参数类型的安全警告。
1 2 3 4 5 6 7 8 9 10 11 12 13 public class SafeVarargsDemo { @SafeVarargs public static <T> void printElements (T... elements) { for (T element : elements) { System.out.println(element); } } @SafeVarargs public final <T> List<T> createList (T... elements) { return Arrays.asList(elements); } }
⭐⭐ @FunctionalInterface 标记函数式接口。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @FunctionalInterface interface Calculator { int calculate (int a, int b) ; default void print () { System.out.println("计算结果" ); } static void info () { System.out.println("这是一个函数式接口" ); } }
自定义注解 ⭐⭐ 基本语法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 import java.lang.annotation.*;@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented @Inherited public @interface MyAnnotation { String value () default "" ; int priority () default 1 ; String[] tags() default {}; Class<?> type() default Object.class; }
⭐⭐ 注解属性类型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public @interface CompleteAnnotation { int intValue () default 0 ; boolean booleanValue () default true ; char charValue () default 'A' ; String stringValue () default "" ; Class<?> classValue() default Object.class; enum Priority { LOW, MEDIUM, HIGH } Priority priority () default Priority.MEDIUM; AnotherAnnotation anotherAnnotation () default @AnotherAnnotation ; String[] tags() default {}; String value () default "" ; }
⭐⭐ 自定义重复注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Roles { Role[] value(); } @Repeatable(Roles.class) @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @interface Role { String value () ; } @Role("ADMIN") @Role("USER") @Role("MANAGER") class MultiRoleUser { } public class RepeatableAnnotationDemo { public static void main (String[] args) { Role[] roles = MultiRoleUser.class.getAnnotationsByType(Role.class); System.out.println("用户拥有的角色:" ); for (Role role : roles) { System.out.println(" - " + role.value()); } } }
注解处理 ⭐⭐ 运行时注解处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class RuntimeProcessor { public static void processAnnotation (Object obj) { Class<?> clazz = obj.getClass(); for (Annotation annotation : clazz.getAnnotations()) { System.out.println("类注解: " + annotation); } for (Method method : clazz.getDeclaredMethods()) { for (Annotation annotation : method.getAnnotations()) { System.out.println("方法 " + method.getName() + " 的注解: " + annotation); } } for (Field field : clazz.getDeclaredFields()) { for (Annotation annotation : field.getAnnotations()) { System.out.println("字段 " + field.getName() + " 的注解: " + annotation); } } } }
⭐⭐ 编译时注解处理 1 2 3 4 5 6 7 8 9 10 @SupportedAnnotationTypes("com.example.MyAnnotation") @SupportedSourceVersion(SourceVersion.RELEASE_8) public class MyAnnotationProcessor extends AbstractProcessor { @Override public boolean process (Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { return true ; } }
实际应用 ⭐⭐ 单元测试框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface Test { String description () default "" ; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface BeforeEach {}@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface AfterEach {}class TestRunner { public static void runTests (Class<?> testClass) throws Exception { Object instance = testClass.getDeclaredConstructor().newInstance(); for (Method method : testClass.getDeclaredMethods()) { if (method.isAnnotationPresent(BeforeEach.class)) { method.invoke(instance); } } for (Method method : testClass.getDeclaredMethods()) { if (method.isAnnotationPresent(Test.class)) { try { method.invoke(instance); System.out.println("✓ " + method.getName() + " 测试通过" ); } catch (Exception e) { System.out.println("✗ " + method.getName() + " 测试失败: " + e.getCause().getMessage()); } } } for (Method method : testClass.getDeclaredMethods()) { if (method.isAnnotationPresent(AfterEach.class)) { method.invoke(instance); } } } } class MyTest { @BeforeEach public void setup () { System.out.println("执行测试前准备" ); } @AfterEach public void cleanup () { System.out.println("执行测试后清理" ); } @Test(description = "测试加法") public void testAdd () { int result = 2 + 3 ; assert result == 5 ; } @Test(description = "测试减法") public void testSubtract () { int result = 5 - 3 ; assert result == 2 ; } }
⭐⭐ 字段验证框架 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface NotNull { String message () default "字段不能为空" ; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Min { int value () ; String message () default "值太小" ; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Max { int value () ; String message () default "值太大" ; } @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface Email { String message () default "邮箱格式不正确" ; } class Validator { public static List<String> validate (Object obj) throws IllegalAccessException { List<String> errors = new ArrayList <>(); Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { field.setAccessible(true ); Object value = field.get(obj); if (field.isAnnotationPresent(NotNull.class)) { if (value == null ) { NotNull notNull = field.getAnnotation(NotNull.class); errors.add(field.getName() + ": " + notNull.message()); } } if (value instanceof Integer) { int intValue = (int ) value; if (field.isAnnotationPresent(Min.class)) { Min min = field.getAnnotation(Min.class); if (intValue < min.value()) { errors.add(field.getName() + ": " + min.message() + " (最小值: " + min.value() + ")" ); } } if (field.isAnnotationPresent(Max.class)) { Max max = field.getAnnotation(Max.class); if (intValue > max.value()) { errors.add(field.getName() + ": " + max.message() + " (最大值: " + max.value() + ")" ); } } } if (field.isAnnotationPresent(Email.class) && value instanceof String) { String email = (String) value; if (!email.matches("^[A-Za-z0-9+_.-]+@(.+)$" )) { Email emailAnno = field.getAnnotation(Email.class); errors.add(field.getName() + ": " + emailAnno.message()); } } } return errors; } } class User { @NotNull(message = "用户名不能为空") private String username; @Min(value = 18, message = "年龄必须大于等于18岁") @Max(value = 120, message = "年龄必须小于等于120岁") private int age; @Email(message = "邮箱格式不正确") @NotNull(message = "邮箱不能为空") private String email; public User (String username, int age, String email) { this .username = username; this .age = age; this .email = email; } } public class ValidationDemo { public static void main (String[] args) throws IllegalAccessException { User user = new User ("张三" , 15 , "invalid-email" ); List<String> errors = Validator.validate(user); if (errors.isEmpty()) { System.out.println("验证通过" ); } else { System.out.println("验证失败:" ); errors.forEach(error -> System.out.println(" - " + error)); } } }
⭐⭐ 权限控制注解 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @interface RequiresRole { String[] value(); Logical logical () default Logical.AND; } enum Logical { AND, OR } class SecurityManager { private Set<String> currentUserRoles = new HashSet <>(); public void login (String... roles) { currentUserRoles.clear(); currentUserRoles.addAll(Arrays.asList(roles)); System.out.println("用户登录, 角色: " + currentUserRoles); } public boolean checkPermission (Method method) { if (!method.isAnnotationPresent(RequiresRole.class)) { return true ; } RequiresRole requiresRole = method.getAnnotation(RequiresRole.class); String[] requiredRoles = requiresRole.value(); Logical logical = requiresRole.logical(); if (logical == Logical.AND) { return currentUserRoles.containsAll(Arrays.asList(requiredRoles)); } else { for (String role : requiredRoles) { if (currentUserRoles.contains(role)) { return true ; } } return false ; } } } class Service { @RequiresRole("ADMIN") public void adminOnly () { System.out.println("执行管理员操作" ); } @RequiresRole(value = {"ADMIN", "MANAGER"}, logical = Logical.OR) public void adminOrManager () { System.out.println("执行管理员或经理操作" ); } @RequiresRole(value = {"ADMIN", "USER"}, logical = Logical.AND) public void bothRoles () { System.out.println("执行需要同时拥有ADMIN和USER角色的操作" ); } public void publicMethod () { System.out.println("执行公开方法" ); } }
常用类库 Java 自带的类构成了其强大的标准库( 也称为 Java API 或 JDK 类库) 。 这些类按照功能被组织在不同的包( Package) 中。 掌握核心包的结构和常用类, 是进行 Java 开发的基础。
异常 异常是程序运行过程中的“ 意外状况” 。 它会打断代码原本的正常执行流程。 Java 的异常处理机制非常强大, 它不仅能让程序在遇到错误时优雅降级( 比如给出友好提示、 回滚事务) , 还能帮助开发者快速定位问题根源。
异常体系 Java 中所有的异常都继承自 java.lang.Throwable 类。 它是整个异常体系的顶层父类。 Throwable 有两个重要的子类: Error 和 Exception。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 java.lang.Object │ └── java.lang.Throwable (所有异常和错误的基类) │ ├── java.lang.Error (错误 - 不可恢复的系统级问题) │ │ │ ├── 虚拟机错误 │ │ ├── VirtualMachineError │ │ │ ├── StackOverflowError (栈溢出) │ │ │ ├── OutOfMemoryError (内存溢出) │ │ │ ├── InternalError (内部错误) │ │ │ └── UnknownError (未知错误) │ │ │ │ │ └── ThreadDeath (线程终止) │ │ │ ├── 类加载错误 │ │ ├── NoClassDefFoundError (类定义未找到) │ │ ├── ClassFormatError (类格式错误) │ │ ├── UnsupportedClassVersionError (不支持的类版本) │ │ └── IncompatibleClassChangeError (不兼容的类变更) │ │ │ ├── 链接错误 │ │ ├── LinkageError (链接错误) │ │ │ ├── NoClassDefFoundError │ │ │ ├── ClassCircularityError (类循环依赖) │ │ │ └── UnsatisfiedLinkError (不满足的链接) │ │ │ │ │ └── ExceptionInInitializerError (初始化器异常) │ │ │ ├── 断言错误 │ │ └── AssertionError (断言失败) │ │ │ ├── AWT 错误 │ │ └── AWTError (AWT 错误) │ │ │ └── 其他错误 │ ├── IOError (I/O 错误) │ └── ServiceConfigurationError (服务配置错误) │ └── java.lang.Exception (异常 - 可处理的程序问题) │ ├── 运行时异常 (RuntimeException - 未检查异常) │ │ │ ├── 类型转换异常 │ │ └── ClassCastException (类型转换异常) │ │ │ ├── 空指针异常 │ │ └── NullPointerException (空指针异常) │ │ │ ├── 数组相关异常 │ │ ├── ArrayIndexOutOfBoundsException (数组越界) │ │ ├── NegativeArraySizeException (负数组大小) │ │ └── ArrayStoreException (数组存储类型不匹配) │ │ │ ├── 字符串相关异常 │ │ ├── StringIndexOutOfBoundsException (字符串越界) │ │ └── IndexOutOfBoundsException (索引越界) │ │ │ ├── 数字相关异常 │ │ ├── ArithmeticException (算术异常, 如除零) │ │ ├── NumberFormatException (数字格式异常) │ │ └── IllegalArgumentException (非法参数) │ │ ├── IllegalThreadStateException (非法线程状态) │ │ ├── NumberFormatException │ │ └── PatternSyntaxException (正则表达式语法) │ │ │ ├── 安全相关异常 │ │ ├── SecurityException (安全异常) │ │ └── AccessControlException (访问控制异常) │ │ │ ├── 状态相关异常 │ │ ├── IllegalStateException (非法状态) │ │ ├── UnsupportedOperationException (不支持的操作) │ │ └── ConcurrentModificationException (并发修改异常) │ │ │ ├── 枚举相关异常 │ │ └── EnumConstantNotPresentException (枚举常量不存在) │ │ │ └── 其他运行时异常 │ ├── MissingResourceException (资源缺失) │ └── EmptyStackException (空栈异常) │ └── 检查异常 (Checked Exception - 必须处理) │ ├── I/O 异常 │ ├── IOException (I/O 异常基类) │ │ ├── FileNotFoundException (文件未找到) │ │ ├── EOFException (文件结束异常) │ │ ├── CharConversionException (字符转换异常) │ │ ├── ObjectStreamException (对象流异常) │ │ │ ├── InvalidClassException (无效类) │ │ │ ├── NotSerializableException (不可序列化) │ │ │ └── WriteAbortedException (写入中断) │ │ ├── InterruptedIOException (I/O 中断) │ │ │ └── SocketTimeoutException (Socket 超时) │ │ ├── UnsupportedEncodingException (不支持的编码) │ │ ├── UTFDataFormatException (UTF 格式错误) │ │ └── MalformedURLException (URL 格式错误) │ │ │ └── SyncFailedException (同步失败) │ ├── SQL 异常 │ └── SQLException (SQL 异常) │ ├── SQLWarning (SQL 警告) │ ├── SQLDataException (SQL 数据异常) │ ├── SQLIntegrityConstraintViolationException (完整性约束) │ ├── SQLSyntaxErrorException (SQL 语法错误) │ └── SQLTimeoutException (SQL 超时) │ ├── 类加载异常 │ ├── ClassNotFoundException (类未找到) │ └── InstantiationException (实例化异常) │ ├── 反射异常 │ ├── IllegalAccessException (非法访问) │ ├── NoSuchFieldException (字段不存在) │ ├── NoSuchMethodException (方法不存在) │ └── InvocationTargetException (调用目标异常) │ ├── 线程相关异常 │ └── InterruptedException (中断异常) │ ├── 图形界面异常 │ ├── AWTException (AWT 异常) │ └── HeadlessException (无头模式异常) │ ├── 解析异常 │ ├── ParseException (解析异常) │ ├── java.text.ParseException (文本解析异常) │ └── java.util.prefs.InvalidPreferencesFormatException │ ├── 安全异常 │ └── GeneralSecurityException (安全异常基类) │ ├── NoSuchAlgorithmException (算法不存在) │ ├── KeyManagementException (密钥管理异常) │ ├── SignatureException (签名异常) │ └── CertificateException (证书异常) │ ├── 其他检查异常 │ ├── CloneNotSupportedException (克隆不支持) │ ├── javax.naming.NamingException (命名异常) │ ├── javax.xml.parsers.ParserConfigurationException │ ├── org.xml.sax.SAXException (XML 解析异常) │ └── javax.management.JMException │ └── 自定义异常 └── UserDefinedException (用户自定义异常)
异常区分
类
含义
特点
常见类型
Error
表示程序无法处理的严重问题, 通常由 JVM 系统引起
不推荐捕获。 一旦发生, 程序通常无法恢复, 只能尽力记录日志或优雅退出
OutOfMemoryError: 内存溢出, JVM 内存耗尽
StackOverflowError: 栈溢出, 通常由无限递归导致。
Exception
表示程序运行时可能发生的、 可以处理的意外情况
分为受检异常和非受检异常
IOException: 文件读写或网络通信异常
SQLException: 数据库操作异常
维度
受检异常
非受检异常
继承关系
继承自 Exception
继承自 RuntimeException 或 Error
编译器要求
强制处理。 不捕获或声明, 编译直接报错
不强制处理。 编译器不管, 但运行时可能崩溃
产生原因
外部环境因素。 如文件被删了、 网络断了、 数据库挂了
代码逻辑错误。 如空指针、 数组越界、 除以零
处理策略
必须处理( 重试、 降级、 提示用户)
应该通过修正代码逻辑来避免( 如判空)
典型代表
IOException | SQLException
NullPointerException | ArrayIndexOutOfBoundsException
自定义异常 在项目中, 通常会定义自己的异常类, 继承自 RuntimeException。 这样可以携带业务错误码和提示信息, 方便前后端交互。
1 2 3 4 5 6 7 8 public class BizException extends RuntimeException { private int code; public BizException (int code, String message) { super (message); this .code = code; } }
异常处理 ⭐⭐ try-catch-finally
try: 包裹可能出错的代码。
catch: 捕获并处理特定类型的异常。 注意顺序要从具体到宽泛( 子类在前, 父类在后) 。
finally: 无论是否出错, 都会执行的代码。 通常用于释放资源( 如关闭文件流、 数据库连接) 。
1 2 3 4 5 6 7 8 9 10 11 try { int a = 10 / 0 ; } catch (ArithmeticException e) { System.err.println("算术异常: " + e.getMessage()); } catch (Exception e) { e.printStackTrace(); } finally { System.out.println("清理工作" ); }
⭐⭐ throw、 throws
throw: 写在方法体内, 用来抛出一个具体的异常对象。
throws: 写在方法签名后面, 用来声明该方法可能会抛出某种异常, 让调用者处理。
1 2 3 4 5 6 7 public void readFile (String path) throws IOException { if (path == null ) { throw new IOException ("文件路径不能为空" ); } }
⭐⭐ try-with-resources 这是处理资源( 流、 连接) 的最佳实践。 它能自动关闭实现了 AutoCloseable 接口的资源, 无需手动写 finally 块来关闭, 代码更简洁且不易出错。
1 2 3 4 5 6 7 try (FileInputStream fis = new FileInputStream ("test.txt" )) { int data = fis.read(); } catch (IOException e) { e.printStackTrace(); }
大数 在 Java 中, 处理大数( 超出基本数据类型 int、 long、 double 等范围的数值) 主要依赖 java.math 包中的两个核心类: BigInteger 整数、 BigDecimal 浮点数。
BigIntegerBigInteger 类是专门用于处理任意精度的整数。 当计算结果超出了基本数据类型( 如 long, 最大值约为 9×10^18) 的范围时可以使用该类。
理论上 BigInteger 只受限于计算机内存大小, 可以表示极大或极小的整数( 例如几百位的质数) 。
对象一旦创建, 其值就不能改变。 所有的运算方法都不会修改原对象, 而是返回一个新的 BigInteger 对象。
⭐⭐ 初始化对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 BigInteger a = new BigInteger ("123456789012345678901234567890" );BigInteger b = new BigInteger ("-98765432109876543210" );BigInteger c = BigInteger.valueOf(100L );BigInteger d = BigInteger.valueOf(Integer.MAX_VALUE).add(BigInteger.ONE);byte [] bytes = {1 , 2 , 3 , 4 };BigInteger e = new BigInteger (bytes);BigInteger randomNum = new BigInteger (128 , new java .util.Random());
⭐⭐ 常用运算方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 BigInteger a = new BigInteger ("123456789012345678901234567890" );BigInteger b = new BigInteger ("98765432109876543210" );BigInteger sum = a.add(b);System.out.println("1. 加法 (a + b): " + sum); BigInteger diff = a.subtract(b);System.out.println("2. 减法 (a - b): " + diff); BigInteger product = a.multiply(b);System.out.println("3. 乘法 (a * b): " + product); BigInteger quotient = a.divide(b);System.out.println("4. 除法 (a / b): " + quotient); BigInteger remainder = a.remainder(b);System.out.println("5. 取余 (a % b): " + remainder); BigInteger[] divRem = a.divideAndRemainder(b); System.out.println("6. 商和余: 商=" + divRem[0 ] + ", 余=" + divRem[1 ]); BigInteger power = b.pow(3 );System.out.println("7. 幂运算 (b^3): " + power); BigInteger modPow = b.modPow(BigInteger.valueOf(3 ), a);System.out.println("8. 模幂 ((b^3) % a): " + modPow); BigInteger negative = new BigInteger ("-999999999999999999999" );BigInteger absVal = negative.abs();System.out.println("9. 绝对值 (|-...|): " + absVal); BigInteger gcdVal = a.gcd(b);System.out.println("10. 最大公约数 (gcd): " + gcdVal); BigInteger minVal = a.min(b);BigInteger maxVal = a.max(b);System.out.println("11. 最小值 (min): " + minVal); System.out.println(" 最大值 (max): " + maxVal); int comparison = a.compareTo(b);String resultStr = (comparison > 0 ) ? "a > b" : (comparison < 0 ) ? "a < b" : "a == b" ;System.out.println("12. 比较大小 (compareTo): " + resultStr + " (返回值:" + comparison + ")" ); BigInteger shifted = b.shiftLeft(2 );System.out.println("13. 位左移 (b << 2): " + shifted); boolean isPrime = a.isProbablePrime(20 );System.out.println("14. 素数判定 (a is prime?): " + isPrime); BigInteger randomBig = new BigInteger (64 , new Random ());System.out.println("15. 随机大数 (64位): " + randomBig); int i = a.intValue(); long l = a.longValue(); double d = a.doubleValue(); String s = a.toString(); try { long safeLong = a.longValueExact(); System.out.println("16. 安全转换: " + safeLong); } catch (ArithmeticException e) { System.out.println("16. 安全转换失败: 数值超出 long 范围 (" + e.getMessage() + ")" ); } BigInteger shifted = a.shiftLeft(2 );
BigDecimalBigDecimal 类是专门用于高精度浮点数计算的类。 它是金融、 商业计算中处理货币金额的标准解决方案。
在计算机中, float 和 double 类型是基于二进制浮点数标准( IEEE 754) 存储的。 这导致许多十进制小数无法被精确表示。
BigDecimal 通过存储任意精度的整数非标度值( Unscaled Value) 和32位的整数标度( Scale) 来精确表示十进制数。
例如: 3.14 存储为 非标度值 314, 标度 2。
⭐⭐ 初始化对象 1 2 3 4 5 6 7 8 9 10 11 12 BigDecimal a = new BigDecimal ("0.1" );BigDecimal b = new BigDecimal ("-0.2" );BigDecimal c = BigDecimal.valueOf(0.1 ); BigDecimal bad = new BigDecimal (0.1 );System.out.println(bad);
⭐⭐ 舍入模式
枚举常量
别名 (旧版)
描述
逻辑
示例
UP
ROUND_UP
远离零方向舍入
只要丢弃的部分非零, 就向绝对值增大的方向进位
1.1 → 2
-1.1 → -2
1.0 → 1
DOWN
ROUND_DOWN
向零方向舍入
直接截断丢弃部分, 永不进位
1.9 → 1
-1.9 → -1
1.0 → 1
CEILING
ROUND_CEILING
向正无穷方向舍入
向数轴右侧( 更大值) 取整。 正数同 `UP`, 负数同 `DOWN`
1.1 → 2
-1.1 → -1
1.0 → 1
FLOOR
ROUND_FLOOR
向负无穷方向舍入
向数轴左侧( 更小值) 取整。 正数同 `DOWN`, 负数同 `UP`
1.1 → 1
-1.1 → -2
1.0 → 1
HALF_UP
ROUND_HALF_UP
四舍五入
丢弃部分 > 0.5 则进位; = 0.5 则进位
1.5 → 2
1.4 → 1
-1.5 → -2
HALF_DOWN
ROUND_HALF_DOWN
五舍六入
丢弃部分 > 0.5 则进位; = 0.5 则舍去
1.5 → 1
1.6 → 2
-1.5 → -1
HALF_EVEN
ROUND_HALF_EVEN
银行家舍入法
丢弃部分 > 0.5 进位; = 0.5 时, 看前一位: 奇数进位, 偶数舍去
1.5 → 2
2.5 → 2
UNNECESSARY
ROUND_UNNECESSARY
禁止舍入
如果运算结果需要舍入( 即除不尽或精度不够) , 直接抛出 ArithmeticException
1.5 → 抛异常
1.0 → 1
⭐⭐ 常用运算方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 BigDecimal a = new BigDecimal ("10.55" );BigDecimal b = new BigDecimal ("3.2" );BigDecimal c = new BigDecimal ("10.550" ); BigDecimal sum = a.add(b);System.out.println("1. 加法 (a + b): " + sum); BigDecimal diff = a.subtract(b);System.out.println("2. 减法 (a - b): " + diff); BigDecimal product = a.multiply(b);System.out.println("3. 乘法 (a * b): " + product); BigDecimal x = new BigDecimal ("10.0" );BigDecimal y = new BigDecimal ("2.0" );BigDecimal divExact = x.divide(y);System.out.println("4A. 整除 (10.0 / 2.0): " + divExact); BigDecimal ten = new BigDecimal ("10" );BigDecimal three = new BigDecimal ("3" );BigDecimal divRounded = ten.divide(three, 4 , RoundingMode.HALF_UP);System.out.println("4B. 非整除 (10 / 3, 保留4位, 四舍五入): " + divRounded); BigDecimal rem = a.remainder(b);System.out.println("5. 取余 (a % b): " + rem); BigDecimal negative = new BigDecimal ("-99.88" );BigDecimal absVal = negative.abs();System.out.println("6. 绝对值 (|-99.88|): " + absVal); BigDecimal rawResult = new BigDecimal ("12.34567" );BigDecimal scaled = rawResult.setScale(2 , RoundingMode.HALF_UP);System.out.println("7. 设置精度 (12.34567 -> 2位): " + scaled); BigDecimal maxVal = a.max(b);BigDecimal minVal = a.min(b);System.out.println("8. 最大值 (max): " + maxVal); System.out.println(" 最小值 (min): " + minVal); int cmp1 = a.compareTo(c); System.out.println("9. 比较大小 (10.55 vs 10.550): " + cmp1); boolean eq1 = a.equals(c);System.out.println("10. 对象相等 (equals 10.55 vs 10.550): " + eq1); BigDecimal shiftedLeft = a.movePointLeft(2 );System.out.println("11. 小数点左移2位 (10.55 -> 0.1055): " + shiftedLeft); BigDecimal shiftedRight = a.movePointRight(1 );System.out.println(" 小数点右移1位 (10.55 -> 105.5): " + shiftedRight); BigDecimal power = b.pow(2 );System.out.println("12. 幂运算 (3.2^2): " + power); try { long safeLong = a.longValueExact(); System.out.println("13. 安全转 long: " + safeLong); } catch (ArithmeticException e) { System.out.println("13. 安全转 long 失败: " + e.getMessage()); } long truncatedLong = a.setScale(0 , RoundingMode.DOWN).longValueExact();System.out.println(" 去小数后转 long: " + truncatedLong);
包装类 Java 中的包装类是将基本数据类型封装成对象的类。 Java 为每个基本数据类型都提供了对应的包装类。
基本数据类型 相对应的包装类 使用示例
byte
Byte
Byte b = 10;
short
Short
Short s = 100;
int
Integer
Integer i = 100;
long
Long
Long l = 1000L;
float
Float
Float f = 3.14f;
double
Double
Double d = 3.14159;
char
Character
Character c = 'A';
boolean
Boolean
Boolean bool = true;
装箱与拆箱 ⭐⭐ 自动装箱 自动将基本类型转换为包装类对象
1 2 3 4 5 Integer num = 100 ;Integer num = Integer.valueOf(100 );
⭐⭐ 自动拆箱 自动将包装类对象转换为基本类型
1 2 3 4 5 6 7 Integer num = 100 ;int value = num;Integer num = Integer.valueOf(100 );int value = num.intValue();
缓存机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 Integer i1 = 100 ;Integer i2 = 100 ;System.out.println(i1 == i2); Integer i3 = 200 ;Integer i4 = 200 ;System.out.println(i3 == i4); Integer i5 = new Integer (100 );Integer i6 = new Integer (100 );System.out.println(i5 == i6); Short s1 = 100 ;Short s2 = 100 ;System.out.println(s1 == s2); Character c1 = 'a' ;Character c2 = 'a' ;System.out.println(c1 == c2); Boolean b1 = true ;Boolean b2 = true ;System.out.println(b1 == b2);
类型转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 int i = Integer.parseInt("123" );double d = Double.parseDouble("3.14" );boolean b = Boolean.parseBoolean("true" );String s1 = Integer.toString(100 );String s2 = String.valueOf(100 );String s3 = "" + 100 ;String bin = Integer.toBinaryString(10 ); String hex = Integer.toHexString(255 ); String oct = Integer.toOctalString(8 );
比较方法 1 2 3 4 5 6 7 8 9 10 Integer num1 = 100 ;Integer num2 = 200 ;int result = num1.compareTo(num2); Integer a = 1000 ;Integer b = 1000 ;System.out.println(a.equals(b)); System.out.println(a == b);
数值操作 1 2 3 4 5 6 7 8 int max = Integer.max(10 , 20 ); int min = Integer.min(10 , 20 ); int sum = Integer.sum(10 , 20 ); int maxValue = Integer.MAX_VALUE; int minValue = Integer.MIN_VALUE;
字符串 String 类用于保存字符串, 也就是一组字符序列。
类的特性 ⭐⭐ 不可变性 String 类的对象一旦创建, 其内容就不能被改变。
1 2 3 4 5 6 7 8 9 10 String str = "Hello" ;str = str + " World" ; System.out.println(str); String s1 = "Java" ;String s2 = s1;s1 = s1 + " Programming" ; System.out.println(s1); System.out.println(s2);
⭐⭐ 字符串常量池 JVM 维护一个字符串常量池, 用于存储字符串字面量, 实现字符串重用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 String str1 = "Hello" ;String str2 = "Hello" ;System.out.println(str1 == str2); String str3 = new String ("Hello" );String str4 = new String ("Hello" );System.out.println(str3 == str4); System.out.println(str3.equals(str4)); String str5 = new String ("Hello" ).intern();System.out.println(str1 == str5);
创建方式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 String s1 = "Hello" ;String s2 = "Hello" ; String s3 = new String ("Hello" ); char [] chars = {'H' , 'e' , 'l' , 'l' , 'o' };String s4 = new String (chars);byte [] bytes = {72 , 101 , 108 , 108 , 111 };String s5 = new String (bytes);
去除空白 1 2 3 4 5 6 7 8 9 10 String str = " Hello World \n" ;String trimmed = str.trim(); String stripped = str.strip();
字符串比较 1 2 3 4 5 6 7 8 9 10 11 12 13 14 String s1 = "Hello" ;String s2 = "hello" ;System.out.println(s1.equals(s2)); System.out.println(s1.equalsIgnoreCase(s2)); System.out.println(s1.compareTo(s2)); System.out.println(s1.compareToIgnoreCase(s2));
查找与判断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 String str = "Hello World, Hello Java" ;boolean contains = str.contains("World" ); int index1 = str.indexOf('o' ); int index2 = str.lastIndexOf('o' ); int index3 = str.indexOf("Hello" , 1 ); boolean startsWith = str.startsWith("Hello" ); boolean endsWith = str.endsWith("Java" ); String empty = "" ;String nullStr = null ;boolean isEmpty = empty.isEmpty(); boolean isBlank = " " .isBlank();
截取与分割 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 String str = "Hello World" ;String sub1 = str.substring(6 ); String sub2 = str.substring(0 , 5 ); String csv = "apple,banana,orange" ;String[] fruits = csv.split("," ); String text = "Java|Python|C++" ;String[] langs = text.split("\\|" ); String[] parts = "a,b,c,d" .split("," , 2 );
替换与转换 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 String str = "Hello World" ;String replaced1 = str.replace('o' , 'a' ); String replaced2 = str.replace("World" , "Java" ); String numbers = "a1b2c3" ;String result = numbers.replaceAll("\\d" , "*" ); String text = "apple apple apple" ;String newText = text.replaceFirst("apple" , "orange" ); String upper = str.toUpperCase(); String lower = str.toLowerCase();
拼接与格式化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 String s1 = "Hello" .concat(" World" ); String joined = String.join("-" , "2023" , "01" , "15" ); List<String> list = Arrays.asList("Java" , "Python" , "C++" ); String result = String.join(", " , list); String name = "Alice" ;int age = 25 ;String formatted = String.format("Name: %s, Age: %d" , name, age);String price = String.format("%.2f" , 19.999 );
可变字符串 频繁修改字符串时使用可变字符串类。
StringBuilder: 非线程安全, 性能高
StringBuffer: 线程安全, 性能稍低
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 StringBuilder sb = new StringBuilder ("Hello" );sb.append(" World" ); sb.append(123 ); sb.insert(5 , " Java" ); sb.delete(5 , 10 ); sb.deleteCharAt(5 ); sb.replace(5 , 10 , "Java" ); sb.reverse(); int capacity = sb.capacity(); sb.ensureCapacity(100 ); sb.setLength(10 );
集合类 Java 集合框架是 Java 语言中用于存储和操作一组对象的核心工具, 它提供了一套统一、 高效且易于使用的数据结构和算法。 整个 Java 集合框架主要由两大顶级接口构成: Collection( 单列集合) 和 Map( 双列集合) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 java.lang.Object │ ├── Collection 体系 (单列集合) │ │ │ └── java.util.Collection (集合根接口) │ │ │ ├── List (有序、 可重复、 有索引) │ │ │ │ │ ├── ArrayList (数组实现, 随机访问快, 增删慢) │ │ ├── LinkedList (双向链表实现, 增删快, 随机访问慢) │ │ ├── Vector (线程安全的动态数组, 已过时) │ │ │ └── Stack (栈, 继承自Vector) │ │ └── CopyOnWriteArrayList (并发包, 写时复制) │ │ │ ├── Set (无序、 不可重复) │ │ │ │ │ ├── HashSet (哈希表实现, 无序) │ │ │ └── LinkedHashSet (哈希表+链表, 有序) │ │ │ │ │ ├── TreeSet (红黑树实现, 可排序) │ │ │ │ │ ├── EnumSet (枚举专用, 位向量实现) │ │ │ │ │ └── CopyOnWriteArraySet (并发包, 写时复制) │ │ │ ├── Queue (队列) │ │ │ │ │ ├── PriorityQueue (优先队列, 堆实现) │ │ │ │ │ ├── ArrayDeque (双端队列, 数组实现) │ │ │ │ │ ├── LinkedList (也实现了Deque) │ │ │ │ │ └── 并发队列 │ │ ├── ConcurrentLinkedQueue (非阻塞队列) │ │ └── BlockingQueue (阻塞队列接口) │ │ ├── ArrayBlockingQueue (有界数组队列) │ │ ├── LinkedBlockingQueue (链表队列) │ │ ├── PriorityBlockingQueue (优先阻塞队列) │ │ ├── DelayQueue (延迟队列) │ │ ├── SynchronousQueue (同步队列) │ │ └── LinkedTransferQueue (传输队列) │ │ │ └── 遗留集合类 │ ├── Vector (JDK 1.0) │ ├── Stack (JDK 1.0) │ └── Hashtable (JDK 1.0) │ ├── Map 体系 (双列集合) │ │ │ └── java.util.Map (映射根接口) │ │ │ ├── 哈希表实现 │ │ ├── HashMap (哈希表, 无序, 允许null键值) │ │ │ └── LinkedHashMap (哈希表+链表, 有序) │ │ │ │ │ ├── Hashtable (线程安全, 不允许null, 已过时) │ │ │ │ │ └── ConcurrentHashMap (并发包, 分段锁/CAS) │ │ │ ├── 树实现 │ │ └── TreeMap (红黑树, 可排序) │ │ │ ├── 专用Map │ │ ├── EnumMap (枚举专用) │ │ ├── WeakHashMap (弱引用键) │ │ ├── IdentityHashMap (引用相等) │ │ └── Properties (属性文件) │ │ │ └── 遗留Map │ └── Dictionary (抽象类, 已废弃) │ └── Hashtable │ └── 辅助工具类 │ ├── java.util.Collections (集合工具类) ├── java.util.Arrays (数组工具类) ├── java.util.Objects (对象工具类) │ └── 迭代器 ├── Iterator (迭代器接口) │ └── ListIterator (双向迭代器) └── Enumeration (遗留枚举接口)
ListList 是一种有序、 可重复的集合, 元素按照插入顺序排列, 并且可以通过整数索引( 下标) 来精确访问。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 List<String> list = new ArrayList <>(); list.add("Apple" ); list.add("Banana" ); list.add(1 , "Orange" ); list.addAll(Arrays.asList("Grape" , "Melon" )); String fruit = list.get(0 ); int index = list.indexOf("Banana" ); boolean hasMelon = list.contains("Melon" ); list.set(1 , "Mango" ); list.remove(2 ); list.remove("Grape" ); System.out.println("列表大小: " + list.size()); List<String> subList = list.subList(0 , 2 ); System.out.println("--- 遍历 ---" ); for (int i = 0 ; i < list.size(); i++) { System.out.println("索引:" + i + ", 值:" + list.get(i)); } for (String item : list) { System.out.println(item); }
SetSet 是一个不允许包含重复元素的集合, 其核心用途是数据去重。 元素的唯一性通过 hashCode() 和 equals() 方法来保证。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 Set<Integer> set = new HashSet <>(); set.add(10 ); set.add(20 ); set.add(10 ); set.addAll(Arrays.asList(30 , 40 , 50 )); System.out.println("Set内容: " + set); boolean exists = set.contains(20 ); set.remove(30 ); set.clear(); Set<String> linkedSet = new LinkedHashSet <>(); linkedSet.add("C" ); linkedSet.add("A" ); linkedSet.add("B" ); System.out.println("LinkedHashSet顺序: " + linkedSet); Set<Integer> treeSet = new TreeSet <>(); treeSet.add(5 ); treeSet.add(1 ); treeSet.add(9 ); System.out.println("TreeSet排序: " + treeSet); Set<Integer> s1 = new HashSet <>(Arrays.asList(1 , 2 , 3 )); Set<Integer> s2 = new HashSet <>(Arrays.asList(2 , 3 , 4 )); s1.retainAll(s2); System.out.println("交集: " + s1);
QueueQueue 是一种特殊的线性表, 通常遵循先进先出( FIFO) 的原则。 它主要用于在数据处理前暂存元素。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 Queue<String> queue = new ArrayDeque <>(); queue.offer("Task 1" ); queue.offer("Task 2" ); String head = queue.peek(); System.out.println("队头元素: " + head); String task = queue.poll(); System.out.println("处理任务: " + task); System.out.println("剩余队列: " + queue); System.out.println("\n--- 优先级队列 ---" ); Queue<Integer> pQueue = new PriorityQueue <>(); pQueue.offer(10 ); pQueue.offer(5 ); pQueue.offer(20 ); while (!pQueue.isEmpty()) { System.out.print(pQueue.poll() + " " ); } System.out.println("\n--- 双端队列作栈 ---" ); Deque<String> stack = new ArrayDeque <>(); stack.push("A" ); stack.push("B" ); System.out.println("弹栈: " + stack.pop());
MapMap 用于存储键值对映射, 其中键( Key) 是唯一的, 但值( Value) 可以重复。 一个键最多只能映射一个值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 Map<String, Integer> map = new HashMap <>(); map.put("Apple" , 10 ); map.put("Banana" , 20 ); map.put("Apple" , 15 ); map.putIfAbsent("Orange" , 30 ); Integer count = map.get("Banana" ); Integer unknown = map.getOrDefault("Grape" , 0 ); boolean hasKey = map.containsKey("Apple" ); boolean hasValue = map.containsValue(20 ); map.remove("Apple" ); map.remove("Banana" , 20 ); map.put("A" , 1 ); map.put("B" , 2 ); map.put("C" , 3 ); System.out.println("--- 遍历 KeySet ---" ); for (String key : map.keySet()) { System.out.println(key + " = " + map.get(key)); } System.out.println("--- 遍历 EntrySet (推荐, 最高效) ---" ); for (Map.Entry<String, Integer> entry : map.entrySet()) { System.out.println(entry.getKey() + " = " + entry.getValue()); } System.out.println("--- Java 8 forEach ---" ); map.forEach((k, v) -> System.out.println(k + " -> " + v)); Map<String, Integer> treeMap = new TreeMap <>(); treeMap.put("C" , 3 ); treeMap.put("A" , 1 ); treeMap.put("B" , 2 ); System.out.println("TreeMap顺序: " + treeMap);
CollectionsCollections 是 Java 集合框架中的一个核心工具类。 它包含了一系列操作集合( 如 List、 Set、 Map 等) 的静态方法。
⭐⭐ 排序与顺序操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 List<Integer> numbers = new ArrayList <>(Arrays.asList(5 , 2 , 9 , 1 , 5 , 6 )); Collections.sort(numbers); System.out.println("排序后: " + numbers); List<String> cards = new ArrayList <>(Arrays.asList("A" , "K" , "Q" , "J" )); Collections.shuffle(cards); System.out.println("洗牌后: " + cards); List<String> list = new ArrayList <>(Arrays.asList("One" , "Two" , "Three" )); Collections.reverse(list); System.out.println("反转后: " + list); List<String> list = new ArrayList <>(Arrays.asList("A" , "B" , "C" , "D" )); Collections.swap(list, 0 , 3 ); System.out.println("交换后: " + list); List<String> list = new ArrayList <>(Arrays.asList("A" , "B" , "C" )); Collections.fill(list, "X" ); System.out.println("填充后: " + list); List<Integer> list = new ArrayList <>(Arrays.asList(1 , 2 , 3 , 4 , 5 )); Collections.rotate(list, 2 ); System.out.println("旋转后: " + list);
⭐⭐ 搜索与查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 List<Integer> list = new ArrayList <>(Arrays.asList(1 , 3 , 5 , 7 , 9 )); int index = Collections.binarySearch(list, 5 );System.out.println("索引位置: " + index); List<String> source = new ArrayList <>(Arrays.asList("A" , "B" , "C" , "D" , "E" )); List<String> target = new ArrayList <>(Arrays.asList("C" , "D" )); int index = Collections.indexOfSubList(source, target);System.out.println("子列表起始索引: " + index);
⭐⭐ 最值与统计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 List<Integer> numbers = Arrays.asList(10 , 5 , 20 , 8 ); Integer maxVal = Collections.max(numbers);System.out.println("最大值: " + maxVal); List<Integer> numbers = Arrays.asList(10 , 5 , 20 , 8 ); Integer minVal = Collections.min(numbers);System.out.println("最小值: " + minVal); List<String> list = Arrays.asList("A" , "B" , "A" , "C" , "A" ); int count = Collections.frequency(list, "A" );System.out.println("'A' 出现的次数: " + count); List<String> list1 = Arrays.asList("A" , "B" ); List<String> list2 = Arrays.asList("C" , "D" ); boolean isDisjoint = Collections.disjoint(list1, list2);System.out.println("是否无交集: " + isDisjoint);
⭐⭐ 不可变集合包装 1 2 3 4 5 6 7 8 9 10 11 List<String> original = new ArrayList <>(Arrays.asList("A" , "B" )); List<String> readOnly = Collections.unmodifiableList(original); try { readOnly.add("C" ); } catch (UnsupportedOperationException e) { System.out.println("无法修改只读列表: " + e.getMessage()); }
⭐⭐ 同步集合包装 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 List<Integer> safeList = Collections.synchronizedList(new ArrayList <>()); int threadCount = 10 ;CountDownLatch latch = new CountDownLatch (threadCount);for (int i = 0 ; i < threadCount; i++) { final int taskId = i; new Thread (() -> { for (int j = 0 ; j < 1000 ; j++) { safeList.add(taskId * 1000 + j); } latch.countDown(); }).start(); } latch.await(); System.out.println("最终列表大小: " + safeList.size()); System.out.println("开始遍历..." ); synchronized (safeList) { int count = 0 ; for (Integer val : safeList) { count++; if (count % 5000 == 0 ) System.out.println("处理中..." ); } System.out.println("遍历完成, 共处理: " + count + " 个元素" ); }
⭐⭐ 空集合与单元素集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 List<String> empty = Collections.emptyList(); System.out.println("空列表大小: " + empty.size()); Set<String> singleSet = Collections.singleton("OnlyOne" ); System.out.println("单元素集合: " + singleSet); Map<String, Integer> singleMap = Collections.singletonMap("Key" , 100 ); System.out.println("单元素Map: " + singleMap);
⭐⭐ 其他方法 1 2 3 4 5 6 7 8 9 10 11 12 13 List<String> list = new ArrayList <>(); Collections.addAll(list, "A" , "B" , "C" ); System.out.println("添加后: " + list); List rawList = new ArrayList ();List<String> checkedList = Collections.checkedList(rawList, String.class); checkedList.add("Safe String" );
正则验证 Java 中正则相关的主要类位于 java.util.regex 包中: Pattern 编译后的正则表达式, Matcher 匹配器, 用于执行匹配操作。
正则字符
类别
表达式
匹配数据
基本字符类
[abc]
a、 b、 c
[^abc]
除 a、 b、 c 外的任意字符
[a-zA-Z]
任意大小写字母
[0-9]
任意数字
[a-d[m-p]]
a-d或m-p( 并集)
[a-z&&[def]]
d、 e或f( 交集)
预定义字符类
.
任意字符
\d
数字: [0-9]
\D
非数字: [^0-9]
\s
空白字符
\S
非空白字符
\w
单词字符: [a-zA-Z_0-9]
\W
非单词字符
边界匹配器
^
行的开头
$
行的结尾
\b
单词边界
\B
非单词边界
\A
输入的开头
\z
输入的结尾
\Z
除最后终止符外的结尾
量词
X?
X出现0次或1次
X*
X出现0次或多次
X+
X出现1次或多次
X{n}
X恰好出现n次
X{n,}
X至少出现n次
X{n,m}
X出现n到m次
Pattern 类1 2 3 4 5 6 7 8 Pattern pattern = Pattern.compile("\\d+" );pattern.pattern(); pattern.flags(); pattern.split("a1b2c3" ); pattern.matcher("input" );
Matcher 类1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Pattern pattern = Pattern.compile("\\d+" );Matcher matcher = pattern.matcher("abc123def456" );matcher.matches(); matcher.lookingAt(); matcher.find(); matcher.find(int start); if (matcher.find()) { matcher.start(); matcher.end(); matcher.group(); matcher.groupCount(); } matcher.replaceAll("replacement" ); matcher.replaceFirst("replacement" ); matcher.appendReplacement(StringBuffer sb, String replacement); matcher.appendTail(StringBuffer sb);
基本匹配 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public class RegexDemo { public static void main (String[] args) { String email = "user@example.com" ; String emailRegex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" ; System.out.println(email.matches(emailRegex)); String phone = "13812345678" ; String phoneRegex = "^1[3-9]\\d{9}$" ; System.out.println(phone.matches(phoneRegex)); String text = "价格: 100元, 折扣: 20%" ; Pattern pattern = Pattern.compile("\\d+" ); Matcher matcher = pattern.matcher(text); while (matcher.find()) { System.out.println("找到数字: " + matcher.group()); } } }
分组捕获 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public class GroupDemo { public static void main (String[] args) { String text = "姓名: 张三, 年龄: 25, 电话: 13812345678" ; Pattern pattern = Pattern.compile("姓名: (\\S+), 年龄: (\\d+), 电话: (1[3-9]\\d{9})" ); Matcher matcher = pattern.matcher(text); if (matcher.find()) { System.out.println("完整匹配: " + matcher.group(0 )); System.out.println("姓名: " + matcher.group(1 )); System.out.println("年龄: " + matcher.group(2 )); System.out.println("电话: " + matcher.group(3 )); } } }
替换操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public class ReplaceDemo { public static void main (String[] args) { String text = "Hello 123 World 456" ; String result = text.replaceAll("\\d+" , "数字" ); System.out.println(result); String date = "2024-01-15" ; String newDate = date.replaceAll("(\\d{4})-(\\d{2})-(\\d{2})" , "$2/$3/$1" ); System.out.println(newDate); Pattern pattern = Pattern.compile("\\d+" ); Matcher matcher = pattern.matcher(text); StringBuffer sb = new StringBuffer (); while (matcher.find()) { int num = Integer.parseInt(matcher.group()); matcher.appendReplacement(sb, String.valueOf(num * 2 )); } matcher.appendTail(sb); System.out.println(sb.toString()); } }
匹配标志 1 2 3 4 5 6 7 8 9 10 Pattern.CASE_INSENSITIVE Pattern.MULTILINE Pattern.DOTALL Pattern.UNICODE_CASE Pattern pattern = Pattern.compile("java" , Pattern.CASE_INSENSITIVE);Pattern pattern2 = Pattern.compile("(?i)java" );
常用表达式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 String regex = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$" ; "^1[3-9]\\d{9}$" ; "^(https?://)?([\\da-z.-]+)\\.([a-z.]{2,6})([/\\w .-]*)*/?$" ; "^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$" ; "^\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])$" ; "^[\\u4e00-\\u9fa5]+$" ; "^[1-9]\\d{5}$" ;
数学运算 Math 类是 Java 提供的一个工具类, 位于 java.lang 包中, 包含执行基本数学运算的方法, 如指数、 对数、 平方根、 三角函数等。
数学常量 1 2 3 4 5 System.out.println(Math.PI); System.out.println(Math.E);
基本运算 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Math.abs(-10 ); Math.abs(-3.14 ); Math.max(10 , 20 ); Math.min(10 , 20 ); Math.round(3.4 ); Math.round(3.6 ); Math.round(3.5 ); Math.ceil(3.1 ); Math.floor(3.9 ); Math.floorDiv(7 , 2 ); Math.floorMod(7 , 2 );
平方运算 1 2 3 4 5 6 7 8 9 10 11 12 13 Math.pow(2 , 3 ); Math.pow(4 , 0.5 ); Math.sqrt(16 ); Math.sqrt(2 ); Math.cbrt(27 ); Math.hypot(3 , 4 );
指数运算 1 2 3 4 5 6 7 8 9 10 11 Math.exp(1 ); Math.log(Math.E); Math.log10(100 ); Math.log(8 ) / Math.log(2 );
三角函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Math.toRadians(180 ); Math.toDegrees(Math.PI); Math.sin(Math.PI / 2 ); Math.cos(0 ); Math.tan(Math.PI / 4 ); Math.asin(1 ); Math.acos(0 ); Math.atan(1 ); Math.atan2(1 , 1 );
随机数 1 2 3 4 5 6 7 8 9 double random = Math.random(); int randomInt = (int )(Math.random() * 100 );int min = 10 , max = 20 ;int rangeRandom = min + (int )(Math.random() * (max - min + 1 ));
其他方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 Math.signum(5.5 ); Math.signum(-3.2 ); Math.signum(0 ); Math.copySign(5 , -3 ); Math.nextUp(1.0 ); Math.nextDown(1.0 ); Math.addExact(10 , 20 ); Math.subtractExact(100 , 30 ); Math.multiplyExact(5 , 6 ); Math.incrementExact(9 ); Math.decrementExact(10 ); Math.negateExact(5 );
数组操作 Arrays 类是 Java 提供的数组工具类, 位于 java.util 包中, 包含用于操作数组的各种静态方法, 如排序、 搜索、 比较、 填充、 转换为字符串等。
数组转字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 import java.util.Arrays;int [] arr1 = {1 , 2 , 3 , 4 , 5 };System.out.println(Arrays.toString(arr1)); int [][] arr2 = {{1 , 2 }, {3 , 4 }, {5 , 6 }};System.out.println(Arrays.deepToString(arr2)); System.out.println(Arrays.toString(arr2));
数组排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 int [] numbers = {5 , 2 , 8 , 1 , 9 };Arrays.sort(numbers); System.out.println(Arrays.toString(numbers)); int [] arr = {5 , 2 , 8 , 1 , 9 , 3 , 7 };Arrays.sort(arr, 1 , 5 ); System.out.println(Arrays.toString(arr)); String[] names = {"Bob" , "Alice" , "Charlie" }; Arrays.sort(names); System.out.println(Arrays.toString(names)); Integer[] nums = {5 , 2 , 8 , 1 , 9 }; Arrays.sort(nums, (a, b) -> b - a); System.out.println(Arrays.toString(nums)); class Person { String name; int age; Person(String name, int age) { this .name = name; this .age = age; } public String toString () { return name + ":" + age; } } Person[] persons = { new Person ("Bob" , 25 ), new Person ("Alice" , 30 ), new Person ("Charlie" , 20 ) }; Arrays.sort(persons, (p1, p2) -> p1.age - p2.age); System.out.println(Arrays.toString(persons));
二分查找 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 int [] arr = {1 , 3 , 5 , 7 , 9 , 11 };int index = Arrays.binarySearch(arr, 7 );System.out.println(index); int notFound = Arrays.binarySearch(arr, 6 );System.out.println(notFound); int rangeSearch = Arrays.binarySearch(arr, 1 , 5 , 9 );System.out.println(rangeSearch);
数组填充 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int [] arr = new int [5 ];Arrays.fill(arr, 10 ); System.out.println(Arrays.toString(arr)); int [] arr2 = new int [10 ];Arrays.fill(arr2, 2 , 6 , 99 ); System.out.println(Arrays.toString(arr2)); int [][] matrix = new int [3 ][3 ];for (int [] row : matrix) { Arrays.fill(row, 1 ); } System.out.println(Arrays.deepToString(matrix));
数组复制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 int [] original = {1 , 2 , 3 , 4 , 5 };int [] copy1 = Arrays.copyOf(original, original.length);System.out.println(Arrays.toString(copy1)); int [] copy2 = Arrays.copyOf(original, 3 );System.out.println(Arrays.toString(copy2)); int [] copy3 = Arrays.copyOf(original, 7 );System.out.println(Arrays.toString(copy3)); int [] copy4 = Arrays.copyOfRange(original, 1 , 4 );System.out.println(Arrays.toString(copy4)); int [][] matrix = {{1 , 2 }, {3 , 4 }};int [][] matrixCopy = Arrays.copyOf(matrix, matrix.length);matrixCopy[0 ][0 ] = 99 ; System.out.println(matrix[0 ][0 ]);
数组比较 1 2 3 4 5 6 7 8 9 10 11 12 13 int [] arr1 = {1 , 2 , 3 };int [] arr2 = {1 , 2 , 3 };int [] arr3 = {1 , 2 , 4 };System.out.println(Arrays.equals(arr1, arr2)); System.out.println(Arrays.equals(arr1, arr3)); int [][] deep1 = {{1 , 2 }, {3 , 4 }};int [][] deep2 = {{1 , 2 }, {3 , 4 }};System.out.println(Arrays.equals(deep1, deep2)); System.out.println(Arrays.deepEquals(deep1, deep2));
并行操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 int [] largeArray = new int [1000000 ];Arrays.parallelSort(largeArray); int [] arr = new int [100 ];Arrays.parallelSetAll(arr, i -> i * 2 ); System.out.println(Arrays.toString(Arrays.copyOf(arr, 10 ))); int [] prefix = {1 , 2 , 3 , 4 , 5 };Arrays.parallelPrefix(prefix, (x, y) -> x + y); System.out.println(Arrays.toString(prefix));
数组转集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 int [] intArray = {1 , 2 , 3 , 4 , 5 };List<Integer> list1 = Arrays.stream(intArray) .boxed() .collect(Collectors.toList()); Integer[] integerArray = {1 , 2 , 3 , 4 , 5 }; List<Integer> list2 = Arrays.asList(integerArray); list2.set(0 , 100 ); Set<Integer> set = new HashSet <>(Arrays.asList(integerArray));
时间处理 相关类 1 2 3 4 5 6 7 8 9 10 Date date = new Date ();Calendar calendar = Calendar.getInstance();LocalDate localDate = LocalDate.now();LocalTime localTime = LocalTime.now();LocalDateTime dateTime = LocalDateTime.now();
新版时间 API( java.time, JDK 8+)
主要类 描述
LocalDate
日期( 年-月-日)
LocalTime
时间( 时-分-秒-纳秒)
LocalDateTime
日期时间
ZonedDateTime
带时区的日期时间
Instant
时间戳( 机器时间)
Duration
时间段( 秒、 纳秒)
Period
时间段( 年、 月、 日)
ZoneId
时区
DateTimeFormatter
格式化( 线程安全)
Date1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 Date now = new Date (); Date date1 = new Date (0 ); Date date2 = new Date (System.currentTimeMillis()); System.out.println(now); long time = now.getTime(); System.out.println("时间戳: " + time); boolean after = date1.after(date2); boolean before = date1.before(date2); int compare = date1.compareTo(date2); boolean equals = date1.equals(date2); SimpleDateFormat sdf = new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" );String formatted = sdf.format(now);System.out.println("格式化: " + formatted); try { Date parsed = sdf.parse("2024-03-24 15:30:45" ); System.out.println("解析结果: " + parsed); } catch (Exception e) { e.printStackTrace(); } Date newDate = new Date ();newDate.setTime(1000000000000L ); System.out.println("设置后: " + newDate); public class DateUtils { private static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat ("yyyy-MM-dd HH:mm:ss" )); public static String format (Date date) { return dateFormat.get().format(date); } }
Calendar1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 Calendar calendar = Calendar.getInstance();Calendar gregorian = new GregorianCalendar ();int year = calendar.get(Calendar.YEAR);int month = calendar.get(Calendar.MONTH); int day = calendar.get(Calendar.DAY_OF_MONTH);int hour = calendar.get(Calendar.HOUR_OF_DAY); int minute = calendar.get(Calendar.MINUTE);int second = calendar.get(Calendar.SECOND);int millisecond = calendar.get(Calendar.MILLISECOND);int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); System.out.printf("%d-%02d-%02d %02d:%02d:%02d%n" , year, month + 1 , day, hour, minute, second); calendar.set(2024 , Calendar.MARCH, 24 , 15 , 30 , 45 ); calendar.set(Calendar.YEAR, 2025 ); calendar.set(Calendar.MONTH, Calendar.DECEMBER); calendar.add(Calendar.DAY_OF_MONTH, 10 ); calendar.add(Calendar.MONTH, -1 ); calendar.roll(Calendar.DAY_OF_MONTH, 5 ); int maxDay = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);int minDay = calendar.getActualMinimum(Calendar.DAY_OF_MONTH);System.out.println("当月最大天数: " + maxDay); calendar.setTimeZone(TimeZone.getTimeZone("America/New_York" )); TimeZone tz = calendar.getTimeZone();System.out.println("时区: " + tz.getID()); GregorianCalendar gc = new GregorianCalendar ();boolean isLeap = gc.isLeapYear(2024 );System.out.println("2024是闰年: " + isLeap); Date date = calendar.getTime();
LocalDate1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 LocalDate today = LocalDate.now(); LocalDate specific = LocalDate.of(2024 , 3 , 24 ); LocalDate parsed = LocalDate.parse("2024-03-24" ); System.out.println("今天: " + today); System.out.println("指定: " + specific); int year = today.getYear();int month = today.getMonthValue(); Month monthEnum = today.getMonth(); int day = today.getDayOfMonth();int dayOfYear = today.getDayOfYear();DayOfWeek dayOfWeek = today.getDayOfWeek();System.out.printf("%d年%d月%d日 %s %d年中的第%d天%n" , year, month, day, dayOfWeek, year, dayOfYear); LocalDate tomorrow = today.plusDays(1 );LocalDate nextWeek = today.plusWeeks(1 );LocalDate nextMonth = today.plusMonths(1 );LocalDate nextYear = today.plusYears(1 );LocalDate yesterday = today.minusDays(1 );LocalDate lastWeek = today.minusWeeks(1 );System.out.println("明天: " + tomorrow); System.out.println("下周: " + nextWeek); boolean isAfter = tomorrow.isAfter(today);boolean isBefore = yesterday.isBefore(today);boolean isEqual = tomorrow.isEqual(today);boolean isLeap = today.isLeapYear(); LocalDate date1 = LocalDate.of(2024 , 1 , 1 );LocalDate date2 = LocalDate.of(2024 , 12 , 31 );long daysBetween = ChronoUnit.DAYS.between(date1, date2);long monthsBetween = ChronoUnit.MONTHS.between(date1, date2);System.out.println("相差天数: " + daysBetween); LocalDate firstDayOfMonth = today.withDayOfMonth(1 );LocalDate lastDayOfMonth = today.withDayOfMonth(today.lengthOfMonth());LocalDate firstDayOfYear = today.withDayOfYear(1 );LocalDate nextMonday = today.with(DayOfWeek.MONDAY);boolean isWeekend = today.getDayOfWeek() == DayOfWeek.SATURDAY || today.getDayOfWeek() == DayOfWeek.SUNDAY;int daysInMonth = today.lengthOfMonth();int daysInYear = today.lengthOfYear();System.out.println("本月天数: " + daysInMonth);
LocalTime1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 LocalTime now = LocalTime.now(); LocalTime specific = LocalTime.of(15 , 30 , 45 ); LocalTime specificWithNano = LocalTime.of(15 , 30 , 45 , 123456789 );LocalTime parsed = LocalTime.parse("15:30:45" );System.out.println("当前时间: " + now); int hour = now.getHour();int minute = now.getMinute();int second = now.getSecond();int nano = now.getNano();LocalTime plusHours = now.plusHours(2 );LocalTime plusMinutes = now.plusMinutes(30 );LocalTime plusSeconds = now.plusSeconds(45 );LocalTime minusHours = now.minusHours(1 );LocalTime morning = LocalTime.of(9 , 0 );LocalTime evening = LocalTime.of(18 , 0 );boolean isBefore = morning.isBefore(evening);boolean isAfter = evening.isAfter(morning);long hoursBetween = ChronoUnit.HOURS.between(morning, evening);long minutesBetween = ChronoUnit.MINUTES.between(morning, evening);System.out.println("工作小时数: " + hoursBetween); LocalTime midnight = LocalTime.MIDNIGHT; LocalTime noon = LocalTime.NOON; LocalTime min = LocalTime.MIN; LocalTime max = LocalTime.MAX; boolean isMorning = now.isAfter(morning) && now.isBefore(noon);LocalTime truncatedToMinutes = now.truncatedTo(ChronoUnit.MINUTES);LocalTime truncatedToHours = now.truncatedTo(ChronoUnit.HOURS);
LocalDateTime1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 LocalDateTime now = LocalDateTime.now();LocalDateTime specific = LocalDateTime.of(2024 , 3 , 24 , 15 , 30 , 45 );LocalDateTime fromDateAndTime = LocalDateTime.of( LocalDate.of(2024 , 3 , 24 ), LocalTime.of(15 , 30 , 45 ) ); LocalDateTime parsed = LocalDateTime.parse("2024-03-24T15:30:45" );LocalDate date = now.toLocalDate();LocalTime time = now.toLocalTime();LocalDateTime fromDate = date.atTime(15 , 30 ); LocalDateTime fromTime = time.atDate(date); LocalDateTime nextHour = now.plusHours(1 );LocalDateTime nextDay = now.plusDays(1 );LocalDateTime nextMonth = now.plusMonths(1 );int year = now.getYear();int month = now.getMonthValue();int day = now.getDayOfMonth();int hour = now.getHour();int minute = now.getMinute();LocalDateTime withYear = now.withYear(2025 );LocalDateTime withMonth = now.withMonth(12 );LocalDateTime withHour = now.withHour(23 );
ZonedDateTime1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 Set<String> zoneIds = ZoneId.getAvailableZoneIds(); System.out.println("可用时区数量: " + zoneIds.size()); ZoneId shanghai = ZoneId.of("Asia/Shanghai" );ZoneId tokyo = ZoneId.of("Asia/Tokyo" );ZoneId ny = ZoneId.of("America/New_York" );ZoneId systemDefault = ZoneId.systemDefault();ZonedDateTime nowInShanghai = ZonedDateTime.now(shanghai);ZonedDateTime nowInTokyo = ZonedDateTime.now(tokyo);ZonedDateTime nowInNY = ZonedDateTime.now(ny);ZonedDateTime nowDefault = ZonedDateTime.now();System.out.println("上海时间: " + nowInShanghai); System.out.println("东京时间: " + nowInTokyo); System.out.println("纽约时间: " + nowInNY); ZonedDateTime tokyoTime = nowInShanghai.withZoneSameInstant(tokyo);ZonedDateTime nyTime = nowInShanghai.withZoneSameInstant(ny);int offsetHours = shanghai.getRules().getOffset(Instant.now()).getTotalSeconds() / 3600 ;System.out.println("上海时区偏移: UTC+" + offsetHours); LocalDateTime localDateTime = LocalDateTime.now();ZonedDateTime zoned = localDateTime.atZone(shanghai);Instant instant = nowInShanghai.toInstant();ZoneId losAngeles = ZoneId.of("America/Los_Angeles" );ZonedDateTime summerTime = ZonedDateTime.of(2024 , 3 , 10 , 2 , 0 , 0 , 0 , losAngeles);System.out.println("夏令时: " + summerTime); ZoneOffset offset = nowInShanghai.getOffset();System.out.println("当前偏移: " + offset);
Instant1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Instant now = Instant.now(); Instant epoch = Instant.EPOCH; Instant fromMillis = Instant.ofEpochMilli(System.currentTimeMillis());Instant fromSeconds = Instant.ofEpochSecond(1000000000 );System.out.println("当前时间戳: " + now); System.out.println("时间戳毫秒数: " + now.toEpochMilli()); System.out.println("时间戳秒数: " + now.getEpochSecond()); Instant later = now.plusSeconds(3600 ); Instant earlier = now.minus(1 , ChronoUnit.DAYS); boolean isAfter = later.isAfter(now);boolean isBefore = earlier.isBefore(now);ZonedDateTime zonedDateTime = now.atZone(ZoneId.systemDefault());LocalDateTime localDateTime = LocalDateTime.ofInstant(now, ZoneId.systemDefault());Instant start = Instant.now();Thread.sleep(1000 ); Instant end = Instant.now();Duration duration = Duration.between(start, end);System.out.println("耗时: " + duration.toMillis() + "ms" ); System.out.println("耗时: " + duration.getNano() + "ns" );
Duration1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 Duration d1 = Duration.ofDays(1 ); Duration d2 = Duration.ofHours(2 ); Duration d3 = Duration.ofMinutes(30 ); Duration d4 = Duration.ofSeconds(45 ); Duration d5 = Duration.ofMillis(500 ); Duration d6 = Duration.ofNanos(1000000 ); LocalTime start = LocalTime.of(9 , 0 );LocalTime end = LocalTime.of(17 , 30 );Duration workDuration = Duration.between(start, end);System.out.println("工作时长: " + workDuration.toHours() + "小时" ); System.out.println("工作时长: " + workDuration.toMinutes() + "分钟" ); Duration total = d1.plus(d2).minus(d3);Duration multiplied = d1.multipliedBy(2 );Duration divided = d1.dividedBy(2 );
Period1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 Period p1 = Period.ofYears(1 ); Period p2 = Period.ofMonths(3 ); Period p3 = Period.ofDays(10 ); Period p4 = Period.of(1 , 2 , 15 ); LocalDate date1 = LocalDate.of(2024 , 1 , 1 );LocalDate date2 = LocalDate.of(2025 , 3 , 24 );Period period = Period.between(date1, date2);System.out.printf("相差: %d年 %d个月 %d天%n" , period.getYears(), period.getMonths(), period.getDays()); Period totalPeriod = p1.plus(p2).minus(p3);Period normalized = p1.normalized();
常用模式字母
字母模版 描述
yyyy
年份
MM
月份( 01-12)
dd
日期( 01-31)
HH
小时( 00-23)
hh
小时( 01-12)
mm
分钟( 00-59)
ss
秒( 00-59)
SSS
毫秒
EEEE
星期几( 完整)
E
星期几( 缩写)
a
上午/下午
z
时区
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 LocalDateTime now = LocalDateTime.now(); DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_LOCAL_DATE_TIME;DateTimeFormatter isoDate = DateTimeFormatter.ISO_LOCAL_DATE;DateTimeFormatter isoTime = DateTimeFormatter.ISO_LOCAL_TIME;System.out.println("ISO日期时间: " + now.format(isoDateTime)); System.out.println("ISO日期: " + now.format(isoDate)); DateTimeFormatter longFormat = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);DateTimeFormatter mediumFormat = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.MEDIUM);DateTimeFormatter shortFormat = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);System.out.println("长格式: " + now.format(longFormat)); System.out.println("中格式: " + now.format(mediumFormat)); System.out.println("短格式: " + now.format(shortFormat)); DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss" );DateTimeFormatter formatter2 = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒" );DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS" );DateTimeFormatter formatter4 = DateTimeFormatter.ofPattern("yyyy/MM/dd EEEE HH:mm" , Locale.CHINA);System.out.println("自定义1: " + now.format(formatter1)); System.out.println("自定义2: " + now.format(formatter2)); System.out.println("自定义3: " + now.format(formatter3)); System.out.println("自定义4: " + now.format(formatter4)); String dateStr = "2024-03-24 15:30:45" ;LocalDateTime parsed = LocalDateTime.parse(dateStr, formatter1);System.out.println("解析结果: " + parsed); String result = now.format( DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm" ) .withLocale(Locale.CHINA) );
文件操作 基本概念 File 类是 Java早期( JDK 1.0) 提供的文件操作类, 用于表示文件或目录的路径名。
Files类是 JDK 1.7 引入的NIO.2( New I/O) 的一部分, 提供了更强大、 更便捷的文件操作API。
特性
File类
Files类
引入版本
JDK 1.0
JDK 1.7 (NIO.2)
核心接口
File 对象
Path 接口
异常处理
返回 boolean
抛出 IOException
符号链接
有限支持
完整支持
批量操作
不支持
支持流式操作
文件监控
不支持
支持WatchService
性能
较慢
更快( 利用OS特性)
异步 IO
不支持
支持 AsynchronousFileChannel
内存映射
不支持
支持 FileChannel
路径分隔符 1 2 3 4 5 6 String separator = File.separator; String pathSeparator = File.pathSeparator; File file = new File ("C:" + File.separator + "test" + File.separator + "file.txt" );
创建对象 ⭐⭐ File 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 File file1 = new File ("C:/test.txt" );File file2 = new File ("test.txt" ); File file3 = new File ("C:/test" , "file.txt" );File parent = new File ("C:/test" );File file4 = new File (parent, "file.txt" );File file5 = new File (URI.create("file:/C:/test.txt" ));
⭐⭐ Files 对象 1 Path path = Paths.get("example.txt" );
文件属性 ⭐⭐ File 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 File file = new File ("example.txt" );boolean exists = file.exists();boolean isFile = file.isFile();boolean isDirectory = file.isDirectory();boolean canRead = file.canRead();boolean canWrite = file.canWrite();boolean canExecute = file.canExecute();boolean isHidden = file.isHidden();boolean isAbsolute = file.isAbsolute();String name = file.getName(); String path = file.getPath(); String absolutePath = file.getAbsolutePath(); String canonicalPath = file.getCanonicalPath(); String parent = file.getParent(); File parentFile = file.getParentFile();long length = file.length();long lastModified = file.lastModified();File[] roots = File.listRoots();
⭐⭐ Files 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 Path path = Paths.get("test.txt" );boolean exists = Files.exists(path);boolean notExists = Files.notExists(path);boolean isDir = Files.isDirectory(path);boolean isRegFile = Files.isRegularFile(path);boolean isSymLink = Files.isSymbolicLink(path);boolean isReadable = Files.isReadable(path);boolean isWritable = Files.isWritable(path);boolean isExecutable = Files.isExecutable(path);long size = Files.size(path); FileTime lastModified = Files.getLastModifiedTime(path);UserPrincipal owner = Files.getOwner(path);Path path = Paths.get("." );FileStore store = Files.getFileStore(path);System.out.println("文件系统: " + store.name()); System.out.println("类型: " + store.type()); System.out.println("总空间: " + store.getTotalSpace() / (1024 *1024 *1024 ) + " GB" ); System.out.println("可用空间: " + store.getUsableSpace() / (1024 *1024 *1024 ) + " GB" ); System.out.println("未分配空间: " + store.getUnallocatedSpace() / (1024 *1024 *1024 ) + " GB" ); BasicFileAttributes basic = Files.readAttributes(path, BasicFileAttributes.class);System.out.println("创建时间: " + basic.creationTime()); System.out.println("最后访问: " + basic.lastAccessTime()); System.out.println("最后修改: " + basic.lastModifiedTime()); System.out.println("文件大小: " + basic.size()); System.out.println("是否为目录: " + basic.isDirectory()); System.out.println("是否为普通文件: " + basic.isRegularFile()); System.out.println("是否为符号链接: " + basic.isSymbolicLink()); if (FileSystems.getDefault().supportedFileAttributeViews().contains("posix" )) { PosixFileAttributes posix = Files.readAttributes(path, PosixFileAttributes.class); System.out.println("所有者: " + posix.owner()); System.out.println("权限: " + PosixFilePermissions.toString(posix.permissions())); System.out.println("组: " + posix.group()); } DosFileAttributes dos = Files.readAttributes(path, DosFileAttributes.class);System.out.println("只读: " + dos.isReadOnly()); System.out.println("隐藏: " + dos.isHidden()); System.out.println("系统: " + dos.isSystem()); Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis())); UserPrincipal user = FileSystems.getDefault() .getUserPrincipalLookupService() .lookupPrincipalByName("username" ); Files.setOwner(path, user); Set<PosixFilePermission> perms = PosixFilePermissions.fromString("rw-r--r--" ); Files.setPosixFilePermissions(path, perms); Files.setAttribute(path, "dos:hidden" , true ); Files.setAttribute(path, "dos:readonly" , false );
创建操作 ⭐⭐ File 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 File file = new File ("newfile.txt" );boolean created = file.createNewFile();File dir = new File ("newdir" );boolean mkdir = dir.mkdir(); File dirs = new File ("parent/child/grandchild" );boolean mkdirs = dirs.mkdirs(); File tempFile; try { tempFile = File.createTempFile("prefix" , ".tmp" ); tempFile.deleteOnExit(); } catch (IOException e) { e.printStackTrace(); }
⭐⭐ Files 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 Path path = Paths.get("example.txt" );try { Path file = Files.createFile(path); System.out.println("创建成功: " + file); } catch (FileAlreadyExistsException e) { System.out.println("文件已存在" ); } catch (IOException e) { e.printStackTrace(); } Path dir = Paths.get("mydir" );Path newDir = Files.createDirectory(dir);Path multiDir = Paths.get("parent/child/grandchild" );Path newMultiDir = Files.createDirectories(multiDir);Path tempFile = Files.createTempFile("prefix" , ".tmp" );Path tempDir = Files.createTempDirectory("tempdir" );Path tempFile2 = Files.createTempFile(Paths.get("/tmp" ), "prefix" , ".tmp" );Path tempFile = Files.createTempFile("temp" , ".tmp" );Files.write(tempFile, data); Files.move(tempFile, target, StandardCopyOption.ATOMIC_MOVE);
删除操作 ⭐⭐ File 对象 1 2 3 4 5 boolean deleted = file.delete();file.deleteOnExit();
⭐⭐ Files 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 Path path = Paths.get("test.txt" );try { Files.delete(path); } catch (NoSuchFileException e) { System.out.println("文件不存在" ); } catch (DirectoryNotEmptyException e) { System.out.println("目录非空" ); } boolean deleted = Files.deleteIfExists(path);if (deleted) { System.out.println("删除成功" ); }
移动复制 ⭐⭐ File 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 File dest = new File ("renamed.txt" );boolean renamed = file.renameTo(dest);public static void copyFile (File source, File dest) throws IOException { if (!source.exists() || !source.isFile()) { throw new IllegalArgumentException ("源文件不存在" ); } try (FileInputStream fis = new FileInputStream (source); FileOutputStream fos = new FileOutputStream (dest)) { byte [] buffer = new byte [8192 ]; int length; while ((length = fis.read(buffer)) > 0 ) { fos.write(buffer, 0 , length); } System.out.println("文件复制成功" ); } }
⭐⭐ Files 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 Path source = Paths.get("source.txt" );Path target = Paths.get("target.txt" );Path copy = Files.copy(source, target);Path copy2 = Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.COPY_ATTRIBUTES); Path moved = Files.move(source, target, StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE); try (InputStream in = new URL ("https://example.com" ).openStream()) { Files.copy(in, Paths.get("downloaded.html" ), StandardCopyOption.REPLACE_EXISTING); } try (OutputStream out = new FileOutputStream ("copy.txt" )) { Files.copy(Paths.get("original.txt" ), out); } public static void copyWithProgress (Path source, Path target) throws IOException { long total = Files.size(source); long [] copied = {0 }; try (InputStream in = Files.newInputStream(source); OutputStream out = Files.newOutputStream(target)) { byte [] buffer = new byte [8192 ]; int length; while ((length = in.read(buffer)) > 0 ) { out.write(buffer, 0 , length); copied[0 ] += length; int percent = (int ) (copied[0 ] * 100 / total); System.out.printf("\r进度: %d%%" , percent); } } System.out.println("\n复制完成! " ); }
遍历目录 ⭐⭐ File 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void listAllFiles (File dir, int level) { if (dir == null || !dir.exists()) { return ; } String indent = " " .repeat(level); if (dir.isFile()) { System.out.println(indent + "📄 " + dir.getName() + " (" + dir.length() + " bytes)" ); } else if (dir.isDirectory()) { System.out.println(indent + "📁 " + dir.getName()); File[] files = dir.listFiles(); if (files != null ) { for (File file : files) { listAllFiles(file, level + 1 ); } } } }
⭐⭐ Files 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 Path dir = Paths.get("." );try (Stream<Path> stream = Files.list(dir)) { stream.forEach(System.out::println); } try (Stream<Path> stream = Files.list(dir)) { stream.filter(Files::isRegularFile) .filter(p -> p.toString().endsWith(".txt" )) .forEach(System.out::println); } Path start = Paths.get("src" );try (Stream<Path> stream = Files.walk(start)) { stream.filter(Files::isRegularFile) .forEach(System.out::println); } try (Stream<Path> stream = Files.walk(start, 2 )) { stream.forEach(System.out::println); } public static void printTree (Path path, int level) throws IOException { String indent = " " .repeat(level); if (Files.isDirectory(path)) { System.out.println(indent + "📁 " + path.getFileName()); try (Stream<Path> children = Files.list(path)) { List<Path> sorted = children.sorted().collect(Collectors.toList()); for (Path child : sorted) { printTree(child, level + 1 ); } } } else { long size = Files.size(path); System.out.println(indent + "📄 " + path.getFileName() + " (" + size + " bytes)" ); } }
文件搜索 ⭐⭐ File 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 File dir = new File ("." );File[] txtFiles = dir.listFiles(new FileFilter () { @Override public boolean accept (File pathname) { return pathname.getName().endsWith(".txt" ); } }); File[] txtFiles2 = dir.listFiles((d, name) -> name.endsWith(".txt" )); public static List<File> searchFiles (File dir, String keyword) { List<File> result = new ArrayList <>(); if (dir == null || !dir.exists()) { return result; } if (dir.isDirectory()) { File[] files = dir.listFiles(); if (files != null ) { for (File file : files) { if (file.isDirectory()) { result.addAll(searchFiles(file, keyword)); } else if (file.getName().contains(keyword)) { result.add(file); } } } } return result; }
⭐⭐ Files 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Path dir = Paths.get("." );try (Stream<Path> stream = Files.list(dir)) { stream.filter(p -> p.toString().endsWith(".txt" )) .forEach(System.out::println); } try (DirectoryStream<Path> stream = Files.newDirectoryStream(dir, "*.txt" )) { for (Path entry : stream) { System.out.println(entry); } } try (Stream<Path> stream = Files.find(start, Integer.MAX_VALUE, (path, attrs) -> attrs.isRegularFile() && path.toString().endsWith(".java" ))) { stream.forEach(System.out::println); }
文件比较 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static boolean compareFiles (Path file1, Path file2) throws IOException { if (Files.size(file1) != Files.size(file2)) { return false ; } try (InputStream in1 = Files.newInputStream(file1); InputStream in2 = Files.newInputStream(file2)) { byte [] buf1 = new byte [8192 ]; byte [] buf2 = new byte [8192 ]; int len; while ((len = in1.read(buf1)) != -1 ) { in2.read(buf2); if (!Arrays.equals(buf1, buf2)) { return false ; } } return true ; } }
读写内容 ⭐⭐ 读取内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 Path path = Paths.get("data.txt" );byte [] bytes = Files.readAllBytes(path);String content = new String (bytes, StandardCharsets.UTF_8);List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8); lines.forEach(System.out::println); try (Stream<String> lines = Files.lines(path, StandardCharsets.UTF_8)) { lines.filter(line -> line.contains("keyword" )) .forEach(System.out::println); }
⭐⭐ 写入内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 Path path = Paths.get("output.txt" );byte [] data = "Hello World" .getBytes(StandardCharsets.UTF_8);Files.write(path, data); List<String> lines = Arrays.asList("Line 1" , "Line 2" , "Line 3" ); Files.write(path, lines, StandardCharsets.UTF_8); Files.write(path, "追加的内容" .getBytes(), StandardOpenOption.APPEND); Files.write(path, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
⭐⭐ 批量文件处理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static void batchProcess (Path dir, String extension) throws IOException { try (Stream<Path> stream = Files.walk(dir)) { stream.filter(Files::isRegularFile) .filter(p -> p.toString().endsWith(extension)) .forEach(file -> { try { System.out.println("处理: " + file); List<String> lines = Files.readAllLines(file); List<String> numbered = new ArrayList <>(); for (int i = 0 ; i < lines.size(); i++) { numbered.add((i + 1 ) + ": " + lines.get(i)); } Files.write(file, numbered); } catch (IOException e) { System.err.println("处理文件失败: " + file); } }); } }
符号链接 ⭐⭐ File 对象 1 2 3 4 File link = new File ("link" );File target = new File ("target" );boolean isLink = Files.isSymbolicLink(link.toPath());
⭐⭐ Files 对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 Path link = Paths.get("link" );Path target = Paths.get("target.txt" );Files.createSymbolicLink(link, target); Files.createLink(link, target); Path realPath = Files.readSymbolicLink(link);Path real = target.toRealPath(LinkOption.NOFOLLOW_LINKS);
目录监控 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 Path dir = Paths.get("watchdir" );WatchService watchService = FileSystems.getDefault().newWatchService();dir.register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_DELETE, StandardWatchEventKinds.ENTRY_MODIFY); while (true ) { WatchKey key = watchService.take(); for (WatchEvent<?> event : key.pollEvents()) { WatchEvent.Kind<?> kind = event.kind(); if (kind == StandardWatchEventKinds.OVERFLOW) { continue ; } Path filename = (Path) event.context(); System.out.println(kind.name() + ": " + filename); } boolean valid = key.reset(); if (!valid) { break ; } }
综合其他 ⭐⭐ OpenOptions 选项 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 StandardOpenOption.READ StandardOpenOption.WRITE StandardOpenOption.APPEND StandardOpenOption.CREATE StandardOpenOption.CREATE_NEW StandardOpenOption.TRUNCATE_EXISTING StandardOpenOption.DELETE_ON_CLOSE StandardOpenOption.SPARSE StandardOpenOption.SYNC StandardOpenOption.DSYNC try (SeekableByteChannel channel = Files.newByteChannel(path, EnumSet.of(StandardOpenOption.CREATE, StandardOpenOption.WRITE, StandardOpenOption.APPEND))) { }
系统相关 System 类是 Java 提供的系统相关工具类, 位于 java.lang 包中, 提供了访问系统属性、 环境变量、 加载类库、 数组复制等功能。
属性与变量 ⭐⭐ 系统属性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 Properties props = System.getProperties();props.list(System.out); String javaVersion = System.getProperty("java.version" );String osName = System.getProperty("os.name" );String userName = System.getProperty("user.name" );String userDir = System.getProperty("user.dir" );String fileSeparator = System.getProperty("file.separator" );String lineSeparator = System.getProperty("line.separator" );System.out.println("Java版本: " + javaVersion); System.out.println("操作系统: " + osName); System.out.println("用户名: " + userName); System.out.println("当前目录: " + userDir); System.out.println("文件分隔符: " + fileSeparator); String[] commonProps = { "java.version" , "java.home" , "java.io.tmpdir" , "os.arch" , "os.version" , "user.home" , "user.dir" , "file.encoding" , "path.separator" , }; for (String prop : commonProps) { System.out.println(prop + " = " + System.getProperty(prop)); } System.setProperty("my.custom.property" , "自定义值" ); System.out.println(System.getProperty("my.custom.property" ));
⭐⭐ 环境变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 Map<String, String> env = System.getenv(); for (Map.Entry<String, String> entry : env.entrySet()) { System.out.println(entry.getKey() + " = " + entry.getValue()); } String path = System.getenv("PATH" );String javaHome = System.getenv("JAVA_HOME" );String os = System.getenv("OS" );System.out.println("PATH: " + path); System.out.println("JAVA_HOME: " + javaHome); System.out.println("操作系统: " + os); String programFiles = System.getenv("ProgramFiles" );String appData = System.getenv("APPDATA" );System.out.println("Program Files: " + programFiles); System.out.println("AppData: " + appData);
系统时间 1 2 3 4 5 6 7 8 9 10 11 12 13 long currentTime = System.currentTimeMillis();System.out.println("当前时间戳: " + currentTime); System.out.println("当前时间: " + new Date (currentTime)); long startTime = System.nanoTime();Thread.sleep(1000 ); long endTime = System.nanoTime();long duration = endTime - startTime;System.out.println("耗时: " + duration + " 纳秒" ); System.out.println("耗时: " + duration / 1_000_000 + " 毫秒" );
垃圾回收 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 System.gc(); System.runFinalization(); Runtime runtime = Runtime.getRuntime();long maxMemory = runtime.maxMemory();long totalMemory = runtime.totalMemory();long freeMemory = runtime.freeMemory();System.out.printf("最大内存: %.2f MB%n" , maxMemory / 1024.0 / 1024 ); System.out.printf("已分配内存: %.2f MB%n" , totalMemory / 1024.0 / 1024 ); System.out.printf("空闲内存: %.2f MB%n" , freeMemory / 1024.0 / 1024 ); System.out.printf("已使用内存: %.2f MB%n" , (totalMemory - freeMemory) / 1024.0 / 1024 ); List<byte []> list = new ArrayList <>(); for (int i = 0 ; i < 1000 ; i++) { list.add(new byte [1024 * 1024 ]); if (i % 100 == 0 ) { System.out.println("创建 " + (i + 1 ) + " MB 对象" ); System.out.println("空闲内存: " + runtime.freeMemory() / 1024 / 1024 + " MB" ); } } list.clear(); System.gc(); Thread.sleep(1000 ); System.out.println("GC后空闲内存: " + runtime.freeMemory() / 1024 / 1024 + " MB" );
安全管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 SecurityManager securityManager = System.getSecurityManager();Runtime.getRuntime().addShutdownHook(new Thread (() -> { System.out.println("JVM正在关闭, 执行清理工作..." ); })); public class ShutdownDemo { public static void main (String[] args) { Runtime.getRuntime().addShutdownHook(new Thread (() -> { System.out.println("钩子1: 保存配置文件" ); })); Runtime.getRuntime().addShutdownHook(new Thread (() -> { System.out.println("钩子2: 关闭数据库连接" ); })); System.out.println("应用运行中..." ); try { Thread.sleep(3000 ); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("准备退出" ); System.exit(0 ); } }
输入输出 I/O 流( Input/Output Stream) 是 Java 中用于处理输入输出操作的核心机制。 它提供了一种统一的方式来读取数据源( 如文件、 网络连接、 内存等) 和写入数据到目标位置。
流( Stream) 是一组有序的数据序列, 可以理解为数据在程序和数据源/目标之间的“ 管道” 。 程序可以从流中读取数据( 输入流) , 也可以向流中写入数据( 输出流) 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 java.io │ ├── 字节流 (Byte Streams) - 处理二进制数据 │ │ │ ├── 字节输入流 │ │ └── java.io.InputStream (抽象基类) │ │ ├── FileInputStream (文件输入流) │ │ ├── ByteArrayInputStream (字节数组输入流) │ │ ├── StringBufferInputStream (已废弃, 字符串输入流) │ │ ├── PipedInputStream (管道输入流) │ │ ├── SequenceInputStream (合并输入流) │ │ ├── ObjectInputStream (对象输入流) │ │ ├── FilterInputStream (过滤输入流基类) │ │ │ ├── BufferedInputStream (缓冲输入流) │ │ │ ├── DataInputStream (数据输入流) │ │ │ ├── PushbackInputStream (回推输入流) │ │ │ ├── LineNumberInputStream (已废弃, 行号输入流) │ │ │ └── CipherInputStream (加密输入流, javax.crypto) │ │ ├── AudioInputStream (音频输入流, javax.sound) │ │ └── ZipInputStream (ZIP压缩输入流, java.util.zip) │ │ │ └── 字节输出流 │ └── java.io.OutputStream (抽象基类) │ ├── FileOutputStream (文件输出流) │ ├── ByteArrayOutputStream (字节数组输出流) │ ├── PipedOutputStream (管道输出流) │ ├── ObjectOutputStream (对象输出流) │ ├── FilterOutputStream (过滤输出流基类) │ │ ├── BufferedOutputStream (缓冲输出流) │ │ ├── DataOutputStream (数据输出流) │ │ ├── PrintStream (打印流, System.out/err) │ │ └── CipherOutputStream (加密输出流, javax.crypto) │ └── ZipOutputStream (ZIP压缩输出流, java.util.zip) │ ├── 字符流 (Character Streams) - 处理文本数据 │ │ │ ├── 字符输入流 │ │ └── java.io.Reader (抽象基类) │ │ ├── InputStreamReader (字节→字符转换流) │ │ │ └── FileReader (文件字符输入流) │ │ ├── BufferedReader (缓冲字符输入流) │ │ │ └── LineNumberReader (行号字符输入流) │ │ ├── CharArrayReader (字符数组输入流) │ │ ├── StringReader (字符串输入流) │ │ ├── PipedReader (管道字符输入流) │ │ └── FilterReader (过滤字符输入流) │ │ └── PushbackReader (回推字符输入流) │ │ │ └── 字符输出流 │ └── java.io.Writer (抽象基类) │ ├── OutputStreamWriter (字符→字节转换流) │ │ └── FileWriter (文件字符输出流) │ ├── BufferedWriter (缓冲字符输出流) │ ├── CharArrayWriter (字符数组输出流) │ ├── StringWriter (字符串输出流) │ ├── PipedWriter (管道字符输出流) │ ├── FilterWriter (过滤字符输出流, 抽象类) │ └── PrintWriter (打印字符输出流) │ ├── 压缩流 (Compression Streams) - 处理ZIP/JAR格式 │ │ │ ├── DeflaterInputStream (压缩输入流) │ ├── InflaterInputStream (解压输入流) │ │ ├── ZipEntry (ZIP条目, 表示压缩文件中的单个文件) │ │ ├── ZipFile (ZIP文件直接访问类) │ │ ├── ZipInputStream (ZIP解压输入流) │ │ ├── ZipOutputStream (ZIP压缩输出流) │ │ ├── GZIPInputStream (GZIP解压输入流) │ │ ├── GZIPOutputStream (GZIP压缩输出流) │ │ ├── CheckedInputStream (校验和输入流) │ │ └── CheckedOutputStream (校验和输出流) │ │ │ └── JAR扩展 (java.util.jar) │ ├── JarEntry (JAR条目, 继承自ZipEntry) │ ├── JarFile (JAR文件访问类, 继承自ZipFile) │ ├── JarInputStream (JAR输入流, 继承自ZipInputStream) │ └── JarOutputStream (JAR输出流, 继承自ZipOutputStream) │ └── 标准流 ├── System.in (InputStream) - 标准输入 ├── System.out (PrintStream) - 标准输出 └── System.err (PrintStream) - 标准错误
标准流 Java 程序与控制台( 终端) 之间的预定义流, 由 JVM 自动打开。 可直接使用, 无需手动创建流对象。
标准输入流 System.in 标准输入流( InputStream 类型) , 通常从键盘读取。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 Scanner scanner = new Scanner (System.in);System.out.print("请输入姓名: " ); String name = scanner.nextLine();System.out.print("请输入年龄: " ); int age = scanner.nextInt();System.out.printf("姓名: %s, 年龄: %d%n" , name, age); try { System.setIn(new FileInputStream ("input.txt" )); Scanner fileScanner = new Scanner (System.in); while (fileScanner.hasNextLine()) { System.out.println(fileScanner.nextLine()); } fileScanner.close(); } catch (FileNotFoundException e) { e.printStackTrace(); }
标准输出流 System.out 标准输出流( PrintStream 类型) , 向控制台输出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 System.out.print("不换行输出" ); System.out.println("换行输出" ); System.out.printf("格式化输出: %d, %.2f, %s%n" , 10 , 3.14159 , "Java" ); int age = 25 ;String name = "张三" ;double salary = 12345.6789 ;System.out.printf("姓名: %s, 年龄: %d, 工资: %.2f%n" , name, age, salary); System.out.printf("十六进制: %x%n" , 255 ); System.out.printf("八进制: %o%n" , 255 ); System.out.printf("科学计数法: %e%n" , 12345.67 ); System.out.printf("布尔值: %b%n" , true ); System.out.printf("%-10s %5d %8.2f%n" , "张三" , 25 , 12345.67 ); System.out.printf("%-10s %5d %8.2f%n" , "李四" , 30 , 9876.54 ); System.out.printf("当前时间: %tF %<tT%n" , System.currentTimeMillis());
标准错误流 System.err 标准错误流( PrintStream 类型) , 输出错误信息。
1 2 3 4 5 6 7 8 9 10 11 System.err.println("错误信息" ); System.err.printf("错误码: %d%n" , 404 ); try { int result = 10 / 0 ; } catch (ArithmeticException e) { System.err.println("计算错误: " + e.getMessage()); e.printStackTrace(System.err); }
字节流 以字节( 8 位) 为单位进行数据读写的流。 InputStream( 输入) 和 OutputStream( 输出) 。
处理二进制数据( 如图片、 音频、 视频、 任意文件) 。
无缓冲, 通常需要手动包装为缓冲流提升性能。
InputStream 是所有字节输入流的抽象基类, 定义了读取字节数据的基本方法。 它本身不能直接实例化, 但提供了统一的接口规范。
⭐⭐ 核心方法 1 2 3 4 5 6 7 8 9 10 11 int read(): 读取一个字节, 返回 0-255, 返回 -1 表示结束 int read(byte[] b): 读取字节到数组, 返回实际读取的字节数 int read(byte[] b, int off, int len): 读取 len 个字节到数组指定位置 long skip(long n): 跳过 n 个字节 int available(): 返回可读取的字节数 void close(): 关闭流, 释放系统资源
OutputStream OutputStream 是所有字节输出流的抽象基类, 定义了向目的地写入字节数据的基本方法。 它本身不能直接实例化, 但提供了统一的接口规范。
⭐⭐ 核心方法 1 2 3 4 5 6 7 8 9 void write(int b): 写入一个字节 void write(byte[] b): 写入字节数组 void write(byte[] b, int off, int len): 写入字节数组的指定部分 void flush(): 刷新缓冲区, 强制写出所有缓冲的数据 void close(): 关闭流, 释放系统资源
文件输入输出流 FileInputStream 用于从文件系统中的文件读取原始字节数据。 它适合读取图像、 音频、 视频等二进制文件, 也适合读取文本文件( 但推荐用字符流处理文本) 。
从文件系统读取字节数据
支持顺序访问
可以读取任何类型的文件
必须处理FileNotFoundException和IOException
FileOutputStream 用于向文件系统写入原始字节数据。 它可以将数据写入文件, 支持覆盖和追加两种模式。
写入数据到文件
支持覆盖模式( 默认) 和追加模式
可以写入任何类型的字节数据
自动创建文件( 如果不存在)
必须处理IOException
⭐⭐ 读取数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 import java.io.*;public class FileInputStreamDemo { public static void main (String[] args) { FileInputStream fis = null ; try { fis = new FileInputStream ("example.txt" ); int byteData; while ((byteData = fis.read()) != -1 ) { System.out.print((char ) byteData); } } catch (FileNotFoundException e) { System.out.println("文件未找到: " + e.getMessage()); e.printStackTrace(); } catch (IOException e) { System.out.println("读取文件时发生错误: " + e.getMessage()); e.printStackTrace(); } finally { if (fis != null ) { try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
⭐⭐ 批量读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import java.io.*;public class FileInputStreamBatchDemo { public static void main (String[] args) { try (FileInputStream fis = new FileInputStream ("example.txt" )) { byte [] buffer = new byte [1024 ]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1 ) { String content = new String (buffer, 0 , bytesRead); System.out.print(content); } } catch (FileNotFoundException e) { System.out.println("文件未找到: " + e.getMessage()); } catch (IOException e) { System.out.println("读取文件时发生错误: " + e.getMessage()); } } }
⭐⭐ 覆盖模式: 每次写入都会清空原有内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void writeWithOverwrite () { try (FileOutputStream fos = new FileOutputStream ("output.txt" )) { fos.write(65 ); fos.write(66 ); fos.write(67 ); byte [] bytes = "Hello, World!" .getBytes(); fos.write(bytes); byte [] partBytes = "Java Programming" .getBytes(); fos.write(partBytes, 0 , 4 ); System.out.println("覆盖模式写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 追加模式: 在文件末尾添加内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void writeWithAppend () { try (FileOutputStream fos = new FileOutputStream ("output.txt" , true )) { fos.write('\n' ); String appendContent = "This is appended content.\n" ; fos.write(appendContent.getBytes()); String moreContent = "Another line of text." ; fos.write(moreContent.getBytes()); System.out.println("追加模式写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 使用缓冲区批量写入( 提高性能) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static void writeWithBuffer () { try (FileOutputStream fos = new FileOutputStream ("largefile.txt" )) { byte [] buffer = new byte [8192 ]; for (int i = 0 ; i < 100 ; i++) { String line = "这是第 " + (i + 1 ) + " 行数据\n" ; byte [] lineBytes = line.getBytes(); if (lineBytes.length > buffer.length) { fos.write(lineBytes); } else { System.arraycopy(lineBytes, 0 , buffer, 0 , lineBytes.length); fos.write(buffer, 0 , lineBytes.length); } } System.out.println("批量写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 写入不同类型的数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static void writeDifferentTypes () { try (FileOutputStream fos = new FileOutputStream ("mixed.dat" )) { int number = 12345 ; fos.write(number >> 24 ); fos.write(number >> 16 ); fos.write(number >> 8 ); fos.write(number); boolean flag = true ; fos.write(flag ? 1 : 0 ); char ch = 'X' ; fos.write(ch); String text = "Hello" ; fos.write(text.getBytes()); } catch (IOException e) { e.printStackTrace(); } }
字节数组输入输出流 ByteArrayInputStream () 从内存中的字节数组读取数据。 它将字节数组包装成输入流, 可以在不创建临时文件的情况下处理内存中的数据。
数据源是内存中的字节数组
不需要关闭流( 关闭无效, 但习惯上还是会调用)
适合处理小型数据或测试场景
支持标记和重置操作
ByteArrayOutputStream 将数据写入内存中的字节数组。 它自动管理内部缓冲区, 随着数据写入而增长, 最终可以通过 toByteArray() 获取数据。
数据写入内存, 不涉及磁盘
自动扩容的缓冲区
可以获取字节数组或字符串
不需要关闭流( 关闭无效)
适合临时存储或数据传输
⭐⭐ 读取数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 import java.io.*;public class ByteArrayInputStreamDemo { public static void main (String[] args) { byte [] data = "Hello, ByteArrayInputStream!" .getBytes(); ByteArrayInputStream bais = new ByteArrayInputStream (data); try { int byteData; System.out.println("方法1: 逐个字节读取" ); while ((byteData = bais.read()) != -1 ) { System.out.print((char ) byteData); } bais.reset(); System.out.println("\n\n方法2: 批量读取" ); byte [] buffer = new byte [10 ]; int bytesRead; while ((bytesRead = bais.read(buffer)) != -1 ) { System.out.print(new String (buffer, 0 , bytesRead)); } bais.reset(); System.out.println("\n\n可读取的字节数: " + bais.available()); bais.skip(7 ); System.out.print("跳过7个字节后的内容: " ); while ((byteData = bais.read()) != -1 ) { System.out.print((char ) byteData); } } catch (IOException e) { e.printStackTrace(); } try { bais.close(); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 写入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public static void basicDemo () { ByteArrayOutputStream baos = new ByteArrayOutputStream (); try { baos.write(65 ); baos.write(66 ); baos.write(67 ); byte [] bytes = "Hello" .getBytes(); baos.write(bytes); byte [] more = " World!" .getBytes(); baos.write(more, 0 , 6 ); byte [] result = baos.toByteArray(); System.out.println("写入的数据: " + new String (result)); System.out.println("当前大小: " + baos.size() + " 字节" ); baos.reset(); System.out.println("重置后大小: " + baos.size()); baos.write("New Data" .getBytes()); System.out.println("新数据: " + baos.toString()); } finally { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 指定初始容量( 减少扩容次数, 提高性能) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void specifyCapacityDemo () { ByteArrayOutputStream baos = new ByteArrayOutputStream (1024 ); try { for (int i = 0 ; i < 100 ; i++) { String data = "Line " + i + "\n" ; baos.write(data.getBytes()); } System.out.println("最终大小: " + baos.size() + " 字节" ); System.out.println("缓冲区容量: " + baos.toByteArray().length + " 字节" ); } finally { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 写入不同类型数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public static void writeDifferentTypes () { ByteArrayOutputStream baos = new ByteArrayOutputStream (); try { int num = 123456789 ; baos.write((num >> 24 ) & 0xFF ); baos.write((num >> 16 ) & 0xFF ); baos.write((num >> 8 ) & 0xFF ); baos.write(num & 0xFF ); baos.write("Hello" .getBytes()); baos.write(1 ); byte [] data = baos.toByteArray(); System.out.println("总数据大小: " + data.length + " 字节" ); } finally { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 转换为不同格式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static void convertToDifferentFormats () { ByteArrayOutputStream baos = new ByteArrayOutputStream (); try { baos.write("Java I/O Tutorial" .getBytes()); byte [] bytes = baos.toByteArray(); System.out.println("字节数组: " + bytes.length + " 字节" ); String str = baos.toString(); System.out.println("字符串: " + str); String utf8Str = baos.toString("UTF-8" ); System.out.println("UTF-8字符串: " + utf8Str); try (FileOutputStream fos = new FileOutputStream ("output.txt" )) { baos.writeTo(fos); System.out.println("已写入文件" ); } } catch (IOException e) { e.printStackTrace(); } finally { try { baos.close(); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 合并多个流数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void mergeMultipleStreams () { ByteArrayOutputStream baos1 = new ByteArrayOutputStream (); ByteArrayOutputStream baos2 = new ByteArrayOutputStream (); ByteArrayOutputStream merged = new ByteArrayOutputStream (); try { baos1.write("First stream data. " .getBytes()); baos2.write("Second stream data." .getBytes()); baos1.writeTo(merged); baos2.writeTo(merged); System.out.println("合并后: " + merged.toString()); } catch (IOException e) { e.printStackTrace(); } finally { try { baos1.close(); baos2.close(); merged.close(); } catch (IOException e) { e.printStackTrace(); } } }
管道输入输出流 PipedInputStream 与 PipedOutputStream 配合使用, 实现线程间的通信。 一个线程通过 PipedOutputStream 写入数据, 另一个线程通过 PipedInputStream 读取数据。
用于线程间通信
必须成对使用( 输入流和输出流)
数据是先进先出( FIFO) 的
线程安全
连接后自动建立管道
⭐⭐ 生产者线程: 向管道写入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 class Producer extends Thread { private PipedOutputStream pos; public Producer (PipedOutputStream pos) { this .pos = pos; } @Override public void run () { try { String message = "Hello from Producer!" ; byte [] data = message.getBytes(); System.out.println("生产者: 开始发送数据..." ); pos.write(data); pos.flush(); System.out.println("生产者: 数据发送完成" ); for (int i = 1 ; i <= 5 ; i++) { String msg = "消息 " + i; pos.write(msg.getBytes()); Thread.sleep(500 ); } pos.close(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
⭐⭐ 消费者线程: 从管道读取数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 class Consumer extends Thread { private PipedInputStream pis; public Consumer (PipedInputStream pis) { this .pis = pis; } @Override public void run () { try { byte [] buffer = new byte [1024 ]; int bytesRead; System.out.println("消费者: 等待接收数据..." ); while ((bytesRead = pis.read(buffer)) != -1 ) { String message = new String (buffer, 0 , bytesRead); System.out.println("消费者接收到: " + message); } pis.close(); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public class PipedInputStreamDemo { public static void main (String[] args) { try { PipedInputStream pis = new PipedInputStream (); PipedOutputStream pos = new PipedOutputStream (); pis.connect(pos); Producer producer = new Producer (pos); Consumer consumer = new Consumer (pis); consumer.start(); Thread.sleep(1000 ); producer.start(); producer.join(); consumer.join(); } catch (IOException e) { e.printStackTrace(); } catch (InterruptedException e) { e.printStackTrace(); } } }
对象输入输出流 ObjectInputStream 用于反序列化对象, 即从输入流中读取之前通过 ObjectOutputStream 序列化的 Java 对象。 它可以将字节流恢复为 Java 对象。
反序列化Java对象
要求类实现 Serializable 接口
支持基本类型和对象
必须处理 ClassNotFoundException
可以控制序列化过程( transient关键字、 writeObject/readObject方法)
ObjectOutputStream 用于序列化 Java 对象, 将对象转换为字节流, 以便存储到文件或通过网络传输。 只有实现了 Serializable 接口的对象才能被序列化。
序列化Java对象
支持基本类型和对象
处理对象引用( 避免重复序列化)
可以控制序列化过程( transient、 writeObject/readObject)
支持版本控制( serialVersionUID)
⭐⭐ 定义可序列化的类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 class Student implements Serializable { private static final long serialVersionUID = 1L ; private String name; private int age; private transient String password; private List<String> courses; public Student (String name, int age, String password) { this .name = name; this .age = age; this .password = password; this .courses = new ArrayList <>(); } public void addCourse (String course) { courses.add(course); } @Override public String toString () { return "Student{name='" + name + "', age=" + age + ", password='" + password + "', courses=" + courses + "}" ; } } class CustomSerialization implements Serializable { private static final long serialVersionUID = 1L ; private String data; private transient String sensitiveData; public CustomSerialization (String data, String sensitiveData) { this .data = data; this .sensitiveData = sensitiveData; } private void writeObject (ObjectOutputStream out) throws IOException { out.defaultWriteObject(); out.writeUTF(encrypt(sensitiveData)); } private void readObject (ObjectInputStream in) throws IOException, ClassNotFoundException { in.defaultReadObject(); this .sensitiveData = decrypt(in.readUTF()); } private String encrypt (String s) { return new StringBuilder (s).reverse().toString(); } private String decrypt (String s) { return new StringBuilder (s).reverse().toString(); } @Override public String toString () { return "CustomSerialization{data='" + data + "', sensitiveData='" + sensitiveData + "'}" ; } }
⭐⭐ 序列化单个对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static void serializeSingleObject () { Student student = new Student ("张三" , 20 , "secret123" ); student.addCourse("Java" ); student.addCourse("Python" ); student.addCourse("Database" ); try (ObjectOutputStream oos = new ObjectOutputStream ( new FileOutputStream ("student.dat" ))) { System.out.println("开始序列化对象: " + student); oos.writeObject(student); System.out.println("序列化完成" ); } catch (IOException e) { e.printStackTrace(); } } public static void deserializeObject () { try (ObjectInputStream ois = new ObjectInputStream ( new FileInputStream ("student.dat" ))) { System.out.println("\n开始反序列化对象..." ); Student student = (Student) ois.readObject(); System.out.println("反序列化结果: " + student); } catch (FileNotFoundException e) { System.out.println("文件未找到: " + e.getMessage()); } catch (IOException e) { System.out.println("I/O错误: " + e.getMessage()); } catch (ClassNotFoundException e) { System.out.println("类未找到: " + e.getMessage()); } }
⭐⭐ 序列化多个对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public static void serializeMultipleObjects () { try (ObjectOutputStream oos = new ObjectOutputStream ( new FileOutputStream ("multiple.dat" ))) { System.out.println("\n序列化多个对象..." ); oos.writeInt(100 ); oos.writeUTF("Hello World" ); oos.writeObject(new Student ("小明" , 18 , "xiao123" )); oos.writeObject(new Student ("小红" , 19 , "hong456" )); oos.writeBoolean(true ); System.out.println("多个对象序列化完成" ); } catch (IOException e) { e.printStackTrace(); } try (ObjectInputStream ois = new ObjectInputStream ( new FileInputStream ("multiple.dat" ))) { System.out.println("\n反序列化多个对象..." ); int num = ois.readInt(); String str = ois.readUTF(); Student s1 = (Student) ois.readObject(); Student s2 = (Student) ois.readObject(); boolean flag = ois.readBoolean(); System.out.println("整数: " + num); System.out.println("字符串: " + str); System.out.println("学生1: " + s1); System.out.println("学生2: " + s2); System.out.println("布尔值: " + flag); } catch (Exception e) { e.printStackTrace(); } }
⭐⭐ 序列化集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public static void serializeCollection () { List<Student> studentList = new ArrayList <>(); studentList.add(new Student ("李四" , 21 , "pass1" )); studentList.add(new Student ("王五" , 22 , "pass2" )); studentList.add(new Student ("赵六" , 20 , "pass3" )); studentList.get(0 ).addCourse("Math" ); studentList.get(1 ).addCourse("Physics" ); studentList.get(2 ).addCourse("Chemistry" ); try (ObjectOutputStream oos = new ObjectOutputStream ( new FileOutputStream ("students.dat" ))) { System.out.println("\n序列化学生列表..." ); oos.writeObject(studentList); System.out.println("序列化完成, 共 " + studentList.size() + " 个学生" ); } catch (IOException e) { e.printStackTrace(); } try (ObjectInputStream ois = new ObjectInputStream ( new FileInputStream ("students.dat" ))) { @SuppressWarnings("unchecked") List<Student> deserializedList = (List<Student>) ois.readObject(); System.out.println("反序列化学生列表, 共 " + deserializedList.size() + " 个学生" ); for (Student s : deserializedList) { System.out.println(" " + s); } } catch (Exception e) { e.printStackTrace(); } }
⭐⭐ 反序列化基本类型数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void deserializePrimitives () { try (ObjectInputStream ois = new ObjectInputStream ( new FileInputStream ("data.dat" ))) { int num = ois.readInt(); String str = ois.readUTF(); double d = ois.readDouble(); System.out.println("读取整数: " + num); System.out.println("读取字符串: " + str); System.out.println("读取浮点数: " + d); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 自定义序列化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void customSerializationDemo () { CustomSerialization cs = new CustomSerialization ("Public Data" , "Sensitive Password" ); try (ObjectOutputStream oos = new ObjectOutputStream ( new FileOutputStream ("custom.dat" ))) { System.out.println("\n自定义序列化..." ); oos.writeObject(cs); System.out.println("原始对象: " + cs); } catch (IOException e) { e.printStackTrace(); } try (ObjectInputStream ois = new ObjectInputStream ( new FileInputStream ("custom.dat" ))) { CustomSerialization deserialized = (CustomSerialization) ois.readObject(); System.out.println("反序列化对象: " + deserialized); } catch (Exception e) { e.printStackTrace(); } }
合并输入流 SequenceInputStream 将多个输入流合并成一个逻辑流。 当读取时, 它会依次从第一个流读取, 直到第一个流结束, 然后自动切换到第二个流, 以此类推。
合并多个输入流
可以处理任意数量的流
对调用者透明( 看起来像一个流)
适合合并多个文件
⭐⭐ 合并两个文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void mergeTwoFiles () { try { FileInputStream fis1 = new FileInputStream ("file1.txt" ); FileInputStream fis2 = new FileInputStream ("file2.txt" ); SequenceInputStream sis = new SequenceInputStream (fis1, fis2); System.out.println("=== 合并文件1和文件2 ===" ); byte [] buffer = new byte [1024 ]; int bytesRead; while ((bytesRead = sis.read(buffer)) != -1 ) { System.out.print(new String (buffer, 0 , bytesRead)); } sis.close(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 合并多个文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public static void mergeMultipleFiles () { try { Vector<FileInputStream> streams = new Vector <>(); streams.add(new FileInputStream ("file1.txt" )); streams.add(new FileInputStream ("file2.txt" )); streams.add(new FileInputStream ("file3.txt" )); java.util.Enumeration<FileInputStream> enumeration = streams.elements(); SequenceInputStream sis = new SequenceInputStream (enumeration); System.out.println("\n=== 合并多个文件 ===" ); byte [] buffer = new byte [1024 ]; int bytesRead; while ((bytesRead = sis.read(buffer)) != -1 ) { System.out.print(new String (buffer, 0 , bytesRead)); } sis.close(); } catch (IOException e) { e.printStackTrace(); } }
音频输入流 AudioInputStream 位于 javax.sound.sampled 包中, 专门用于处理音频数据。 它提供了音频格式信息( 采样率、 位深度、 声道数等) , 并支持从各种来源读取音频数据。
处理音频格式信息 支持多种音频格式( WAV、 AIFF、 AU等) 可以获取音频格式信息 支持音频格式转换 可以处理音频文件流
⭐⭐ 读取音频文件信息 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static void readAudioInfo () throws Exception { try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream( new File ("music.wav" ))) { AudioFormat format = audioInputStream.getFormat(); System.out.println("=== 音频文件信息 ===" ); System.out.println("文件: " + audioFile); System.out.println("编码格式: " + format.getEncoding()); System.out.println("采样率: " + format.getSampleRate() + " Hz" ); System.out.println("采样位数: " + format.getSampleSizeInBits() + " bits" ); System.out.println("声道数: " + format.getChannels()); System.out.println("帧率: " + format.getFrameRate() + " fps" ); System.out.println("帧大小: " + format.getFrameSize() + " bytes" ); System.out.println("是否大端序: " + format.isBigEndian()); long frameLength = audioInputStream.getFrameLength(); System.out.println("总帧数: " + frameLength); double duration = frameLength / format.getFrameRate(); System.out.println("时长: " + duration + " 秒" ); int available = audioInputStream.available(); System.out.println("可读取字节数: " + available); } }
⭐⭐ 播放音频 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public static void playAudio () throws Exception { try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream( new File ("music.wav" ))) { AudioFormat format = audioInputStream.getFormat(); DataLine.Info info = new DataLine .Info(SourceDataLine.class, format); if (!AudioSystem.isLineSupported(info)) { System.out.println("不支持该音频格式播放" ); return ; } try (SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info)) { line.open(format); line.start(); System.out.println("开始播放: " + audioFile); int bufferSize = 4096 ; byte [] buffer = new byte [bufferSize]; int bytesRead; while ((bytesRead = audioInputStream.read(buffer, 0 , bufferSize)) != -1 ) { line.write(buffer, 0 , bytesRead); } line.drain(); System.out.println("播放完成" ); } } }
⭐⭐ 转换音频格式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static void convertAudioFormat () throws Exception { try (AudioInputStream originalStream = AudioSystem.getAudioInputStream( new File ("music.wav" ))) { AudioFormat originalFormat = originalStream.getFormat(); AudioFormat targetFormat = new AudioFormat ( AudioFormat.Encoding.PCM_SIGNED, 44100.0f , 16 , 2 , 4 , 44100.0f , false ); AudioFormat[] formats = {targetFormat}; AudioInputStream convertedStream = AudioSystem.getAudioInputStream( targetFormat, originalStream); AudioSystem.write(convertedStream, AudioFileFormat.Type.WAVE, new File ("converted.wav" )); System.out.println("音频格式转换完成" ); System.out.println("原始格式: " + originalFormat); System.out.println("目标格式: " + targetFormat); convertedStream.close(); } }
⭐⭐ 读取音频数据到字节数组 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public static byte [] readAudioData() throws Exception { try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream( new File ("music.wav" ))) { int frameLength = (int ) audioInputStream.getFrameLength(); int frameSize = audioInputStream.getFormat().getFrameSize(); int dataSize = frameLength * frameSize; byte [] audioData = new byte [dataSize]; int bytesRead = audioInputStream.read(audioData); System.out.println("读取音频数据: " + bytesRead + " 字节" ); return audioData; } }
⭐⭐ 从 URL 读取音频 1 2 3 4 5 6 7 8 public static void playFromURL (String audioUrl) throws Exception { java.net.URL url = new java .net.URL(audioUrl); try (AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(url)) { System.out.println("从URL播放: " + audioUrl); } }
⭐⭐ 录制音频 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public static void recordAudio () throws Exception { AudioFormat format = new AudioFormat ( AudioFormat.Encoding.PCM_SIGNED, 44100.0f , 16 , 2 , 4 , 44100.0f , false ); DataLine.Info info = new DataLine .Info(TargetDataLine.class, format); if (!AudioSystem.isLineSupported(info)) { System.out.println("不支持录制" ); return ; } TargetDataLine line = (TargetDataLine) AudioSystem.getLine(info); line.open(format); line.start(); System.out.println("开始录制..." ); AudioInputStream recordingStream = new AudioInputStream (line); Thread.sleep(5000 ); line.stop(); line.close(); AudioSystem.write(recordingStream, AudioFileFormat.Type.WAVE, new File ("recording.wav" )); System.out.println("录制完成, 已保存到 recording.wav" ); recordingStream.close(); }
FilterInputStream 是所有过滤输入流的基类。 它包装了另一个输入流, 可以在读取数据时添加额外的功能。 它本身是一个抽象类, 提供了装饰器模式的基础。
包装其他输入流, 增强功能
可以嵌套使用多个过滤器
子类实现具体功能
FilterOutputStream FilterOutputStream 是所有过滤输出流的基类。 它包装了另一个输出流, 可以在写入数据时添加额外的功能。 它本身是一个抽象类, 提供了装饰器模式的基础。
包装其他输出流, 增强功能
可以嵌套使用多个过滤器
子类实现具体功能
缓冲输入输出流 BufferedInputStream 为输入流提供缓冲功能。 它内部维护一个字节数组作为缓冲区, 减少底层系统调用的次数, 从而提高I/O性能。
提高读取性能( 减少系统调用)
支持标记和重置( mark/reset)
可以设置缓冲区大小
BufferedOutputStream 为输出流提供缓冲功能。 它内部维护一个字节数组作为缓冲区, 减少底层系统调用的次数, 从而提高I/O性能。
提高写入性能( 减少系统调用)
可以设置缓冲区大小
需要调用flush()确保数据写入
⭐⭐ 使用缓冲的读取数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void readWithBuffer () { long startTime = System.currentTimeMillis(); try (BufferedInputStream bis = new BufferedInputStream ( new FileInputStream ("largefile.txt" ), 8192 )) { int data; while ((data = bis.read()) != -1 ) { } long endTime = System.currentTimeMillis(); System.out.println("使用缓冲耗时: " + (endTime - startTime) + "ms" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 标记和重置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public static void markAndResetDemo () { try (BufferedInputStream bis = new BufferedInputStream ( new FileInputStream ("example.txt" ))) { if (bis.markSupported()) { System.out.println("支持mark/reset操作" ); byte [] buffer = new byte [10 ]; bis.read(buffer); System.out.println("前10个字节: " + new String (buffer)); bis.mark(100 ); byte [] next20 = new byte [20 ]; bis.read(next20); System.out.println("接下来的20个字节: " + new String (next20)); bis.reset(); System.out.println("已重置到标记位置" ); byte [] afterReset = new byte [20 ]; bis.read(afterReset); System.out.println("重置后读取的内容: " + new String (afterReset)); } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 写入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void basicUsage () { try (BufferedOutputStream bos = new BufferedOutputStream ( new FileOutputStream ("basic.txt" ))) { bos.write("Hello" .getBytes()); bos.write(" World" .getBytes()); bos.flush(); System.out.println("基本缓冲写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 设置自定义缓冲区大小 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void setBufferSize () { try (BufferedOutputStream bos = new BufferedOutputStream ( new FileOutputStream ("customBuffer.txt" ), 4096 )) { byte [] data = new byte [1024 ]; for (int i = 0 ; i < data.length; i++) { data[i] = (byte ) ('A' + (i % 26 )); } for (int i = 0 ; i < 10 ; i++) { bos.write(data); } bos.flush(); System.out.println("使用4KB缓冲区写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
数据输入输出流 DataInputStream 允许应用程序以与机器无关的方式从底层输入流中读取基本Java数据类型。 它提供了读取各种类型数据的方法。
读取基本数据类型和字符串
与 DataOutputStream 配合使用
平台无关( 使用固定的字节格式)
可以读取 UTF-8 编码的字符串
DataOutputStream 允许应用程序以与机器无关的方式将基本 Java 数据类型写入底层输出流。 它提供了写入各种类型数据的方法。
写入基本数据类型和字符串
与 DataInputStream 配合使用
平台无关( 使用固定的字节格式)
可以写入UTF-8编码的字符串
⭐⭐ 写入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void writeData () { try (DataOutputStream dos = new DataOutputStream ( new FileOutputStream ("data.bin" ))) { dos.writeInt(100 ); dos.writeDouble(3.14159 ); dos.writeBoolean(true ); dos.writeChar('A' ); dos.writeUTF("Hello, DataInputStream!" ); System.out.println("数据写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void readData () { try (DataInputStream dis = new DataInputStream ( new FileInputStream ("data.bin" ))) { int intValue = dis.readInt(); double doubleValue = dis.readDouble(); boolean boolValue = dis.readBoolean(); char charValue = dis.readChar(); String strValue = dis.readUTF(); System.out.println("读取整数: " + intValue); System.out.println("读取浮点数: " + doubleValue); System.out.println("读取布尔值: " + boolValue); System.out.println("读取字符: " + charValue); System.out.println("读取字符串: " + strValue); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 写入数组和集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public static void writeArrayAndCollection () { try (DataOutputStream dos = new DataOutputStream ( new FileOutputStream ("array.dat" ))) { int [] numbers = {10 , 20 , 30 , 40 , 50 }; dos.writeInt(numbers.length); for (int num : numbers) { dos.writeInt(num); } String[] names = {"Apple" , "Banana" , "Cherry" }; dos.writeInt(names.length); for (String name : names) { dos.writeUTF(name); } System.out.println("\n数组数据写入完成" ); } catch (IOException e) { e.printStackTrace(); } try (DataInputStream dis = new DataInputStream ( new FileInputStream ("array.dat" ))) { int length = dis.readInt(); int [] numbers = new int [length]; for (int i = 0 ; i < length; i++) { numbers[i] = dis.readInt(); } System.out.print("整数数组: " ); for (int num : numbers) { System.out.print(num + " " ); } System.out.println(); length = dis.readInt(); String[] names = new String [length]; for (int i = 0 ; i < length; i++) { names[i] = dis.readUTF(); } System.out.print("字符串数组: " ); for (String name : names) { System.out.print(name + " " ); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 大小端序( Java使用大端序) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public static void endiannessDemo () { try (DataOutputStream dos = new DataOutputStream ( new FileOutputStream ("endian.dat" ))) { int value = 0x12345678 ; System.out.println("\n大小端序演示:" ); System.out.printf("原始值: 0x%08X\n" , value); dos.writeInt(value); try (FileInputStream fis = new FileInputStream ("endian.dat" )) { System.out.print("存储的字节顺序: " ); for (int i = 0 ; i < 4 ; i++) { int b = fis.read(); System.out.printf("0x%02X " , b); } System.out.println("\nJava使用大端序( 高位字节存储在低地址) " ); } } catch (IOException e) { e.printStackTrace(); } }
加密输入输出流 CipherInputStream 位于 javax.crypto 包中, 用于对输入流进行加密或解密。 它包装了一个 Cipher 对象, 在读取数据时自动进行加解密处理。
透明地进行加解密操作
支持多种加密算法( AES、 DES、 RSA等)
需要配合 Cipher 对象使用
适合处理加密文件或网络传输
CipherOutputStream 位于 javax.crypto 包中, 用于对输出流进行加密或解密。 它包装了一个 Cipher 对象, 在写入数据时自动进行加解密处理。
透明地进行加解密操作
支持多种加密算法( AES、 DES、 RSA等)
需要配合 Cipher 对象使用
适合加密文件或网络传输
⭐⭐ 加密解密文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 public static void main (String[] args) { try { SecretKey secretKey = generateKey(); encryptFile("plaintext.txt" , "encrypted.dat" , secretKey); decryptFile("encrypted.dat" , "decrypted.txt" , secretKey); } catch (Exception e) { e.printStackTrace(); } } public static SecretKey generateKey () throws NoSuchAlgorithmException { KeyGenerator keyGen = KeyGenerator.getInstance("AES" ); keyGen.init(128 ); SecretKey secretKey = keyGen.generateKey(); return secretKey; } public static void encryptFile (String inputFile, String outputFile, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding" ); byte [] iv = new byte [16 ]; SecureRandom random = new SecureRandom (); random.nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec (iv); cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); try (FileInputStream fis = new FileInputStream (inputFile); FileOutputStream fos = new FileOutputStream (outputFile); CipherInputStream cis = new CipherInputStream (fis, cipher)) { fos.write(iv); byte [] buffer = new byte [1024 ]; int bytesRead; while ((bytesRead = cis.read(buffer)) != -1 ) { fos.write(buffer, 0 , bytesRead); } System.out.println("文件加密完成: " + outputFile); } } public static void decryptFile (String inputFile, String outputFile, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding" ); try (FileInputStream fis = new FileInputStream (inputFile); FileOutputStream fos = new FileOutputStream (outputFile)) { byte [] iv = new byte [16 ]; fis.read(iv); IvParameterSpec ivSpec = new IvParameterSpec (iv); cipher.init(Cipher.DECRYPT_MODE, key, ivSpec); try (CipherInputStream cis = new CipherInputStream (fis, cipher)) { byte [] buffer = new byte [1024 ]; int bytesRead; while ((bytesRead = cis.read(buffer)) != -1 ) { fos.write(buffer, 0 , bytesRead); } System.out.println("文件解密完成: " + outputFile); } } }
⭐⭐ 加密解密内存中的数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public static byte [] encryptData(byte [] data, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding" ); byte [] iv = new byte [12 ]; SecureRandom random = new SecureRandom (); random.nextBytes(iv); GCMParameterSpec spec = new GCMParameterSpec (128 , iv); cipher.init(Cipher.ENCRYPT_MODE, key, spec); ByteArrayOutputStream baos = new ByteArrayOutputStream (); try (CipherOutputStream cos = new CipherOutputStream (baos, cipher)) { cos.write(data); } byte [] encryptedData = baos.toByteArray(); byte [] result = new byte [iv.length + encryptedData.length]; System.arraycopy(iv, 0 , result, 0 , iv.length); System.arraycopy(encryptedData, 0 , result, iv.length, encryptedData.length); return result; } public static byte [] decryptData(byte [] encryptedData, SecretKey key) throws Exception { byte [] iv = new byte [12 ]; System.arraycopy(encryptedData, 0 , iv, 0 , iv.length); byte [] cipherText = new byte [encryptedData.length - iv.length]; System.arraycopy(encryptedData, iv.length, cipherText, 0 , cipherText.length); Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding" ); GCMParameterSpec spec = new GCMParameterSpec (128 , iv); cipher.init(Cipher.DECRYPT_MODE, key, spec); ByteArrayInputStream bais = new ByteArrayInputStream (cipherText); ByteArrayOutputStream baos = new ByteArrayOutputStream (); try (CipherInputStream cis = new CipherInputStream (bais, cipher)) { byte [] buffer = new byte [8192 ]; int bytesRead; while ((bytesRead = cis.read(buffer)) != -1 ) { baos.write(buffer, 0 , bytesRead); } } return baos.toByteArray(); }
⭐⭐ 使用自定义密码的加密文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static void encryptWithPassword (String inputFile, String outputFile, String password) throws Exception { byte [] keyBytes = password.getBytes("UTF-8" ); MessageDigest md = MessageDigest.getInstance("SHA-256" ); byte [] hash = md.digest(keyBytes); SecretKeySpec keySpec = new SecretKeySpec (hash, 0 , 16 , "AES" ); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding" ); byte [] iv = new byte [16 ]; SecureRandom random = new SecureRandom (); random.nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec (iv); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); try (FileInputStream fis = new FileInputStream (inputFile); FileOutputStream fos = new FileOutputStream (outputFile); CipherInputStream cis = new CipherInputStream (fis, cipher)) { fos.write(iv); byte [] buffer = new byte [1024 ]; int bytesRead; while ((bytesRead = cis.read(buffer)) != -1 ) { fos.write(buffer, 0 , bytesRead); } System.out.println("使用密码加密完成" ); } }
⭐⭐ 使用密码加密( 从密码派生密钥) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static void encryptWithPassword (String inputFile, String outputFile, String password) throws Exception { byte [] salt = new byte [8 ]; SecureRandom random = new SecureRandom (); random.nextBytes(salt); SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256" ); KeySpec spec = new PBEKeySpec (password.toCharArray(), salt, 65536 , 256 ); SecretKey tmp = factory.generateSecret(spec); SecretKeySpec keySpec = new SecretKeySpec (tmp.getEncoded(), "AES" ); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding" ); byte [] iv = new byte [16 ]; random.nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec (iv); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); try (FileInputStream fis = new FileInputStream (inputFile); FileOutputStream fos = new FileOutputStream (outputFile); CipherOutputStream cos = new CipherOutputStream (fos, cipher)) { fos.write(salt); fos.write(iv); byte [] buffer = new byte [8192 ]; int bytesRead; while ((bytesRead = fis.read(buffer)) != -1 ) { cos.write(buffer, 0 , bytesRead); } System.out.println("使用密码加密完成: " + outputFile); } }
⭐⭐ 加密大文件( 流式处理, 内存友好) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 public static void encryptLargeFile (String inputFile, String outputFile, SecretKey key) throws Exception { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding" ); byte [] iv = new byte [16 ]; SecureRandom random = new SecureRandom (); random.nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec (iv); cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); long startTime = System.currentTimeMillis(); try (FileInputStream fis = new FileInputStream (inputFile); FileOutputStream fos = new FileOutputStream (outputFile); CipherOutputStream cos = new CipherOutputStream (fos, cipher)) { fos.write(iv); byte [] buffer = new byte [65536 ]; int bytesRead; long totalBytes = 0 ; while ((bytesRead = fis.read(buffer)) != -1 ) { cos.write(buffer, 0 , bytesRead); totalBytes += bytesRead; if (totalBytes % (10 * 1024 * 1024 ) < bytesRead) { System.out.printf("已加密: %.2f MB%n" , totalBytes / (1024.0 * 1024.0 )); } } long endTime = System.currentTimeMillis(); System.out.printf("大文件加密完成: %s (%.2f MB, 耗时: %.2f秒)%n" , outputFile, totalBytes / (1024.0 * 1024.0 ), (endTime - startTime) / 1000.0 ); } catch (IOException e) { e.printStackTrace(); } }
回推输入流 PushbackInputStream 允许将读取的字节推回到流中, 以便后续再次读取。 这在解析数据时非常有用, 当需要”预览”下一个字节而不实际消费它时。
支持回推已读取的字节
可以设置回推缓冲区大小
适合解析器、 编译器等场景
回推的字节会被后续的read方法重新读取
⭐⭐ 回推数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public static void basicPushbackDemo () { try (PushbackInputStream pbis = new PushbackInputStream ( new FileInputStream ("example.txt" ), 10 )) { System.out.println("=== 基本回推演示 ===" ); int firstByte = pbis.read(); System.out.println("读取到: " + (char ) firstByte); pbis.unread(firstByte); System.out.println("已将字节推回流中" ); int reReadByte = pbis.read(); System.out.println("重新读取到: " + (char ) reReadByte); byte [] bytesToPush = "ABC" .getBytes(); pbis.unread(bytesToPush); System.out.println("已回推3个字节: A, B, C" ); System.out.print("读取回推的字节: " ); for (int i = 0 ; i < 3 ; i++) { int b = pbis.read(); System.out.print((char ) b); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 查找并处理特殊标记 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public static void parseWithPushback () { try (PushbackInputStream pbis = new PushbackInputStream ( new FileInputStream ("config.txt" ))) { System.out.println("\n=== 解析配置文件 ===" ); int data; while ((data = pbis.read()) != -1 ) { if (data == '#' ) { System.out.println("发现注释, 跳过整行..." ); while ((data = pbis.read()) != -1 && data != '\n' ) { } } else { StringBuilder line = new StringBuilder (); while (data != -1 && data != '\n' ) { line.append((char ) data); data = pbis.read(); } if (line.length() > 0 ) { System.out.println("有效内容: " + line.toString()); } } } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取数字, 向前查看 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void peekDemo () { try (PushbackInputStream pbis = new PushbackInputStream ( new FileInputStream ("numbers.txt" ))) { int data; while ((data = pbis.read()) != -1 ) { int nextByte = pbis.read(); if (nextByte != -1 ) { pbis.unread(nextByte); if (Character.isDigit((char ) data) && Character.isDigit((char ) nextByte)) { int twoDigit = (data - '0' ) * 10 + (nextByte - '0' ); System.out.println("读取到两位数: " + twoDigit); pbis.read(); } else { System.out.println("读取到单数字: " + (data - '0' )); } } else { System.out.println("读取到单数字: " + (data - '0' )); } } } catch (IOException e) { e.printStackTrace(); } }
打印流 PrintStream 提供了方便的打印方法( print、 println) , 可以将各种数据类型格式化为文本输出。 它是 System.out 和 System.err 的类型, 也是日志输出的常用类。
提供 print、 println、 printf 等便捷方法
自动刷新( 可选)
不会抛出 IOException
支持格式化输出
可以自动刷新
⭐⭐ 打印数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static void basicPrinting () { try (PrintStream ps = new PrintStream ("print.txt" )) { ps.print("Hello" ); ps.print(" " ); ps.print("World" ); ps.println(); ps.println("This is a new line" ); ps.println("String: " + "Java" ); ps.println("Integer: " + 100 ); ps.println("Double: " + 3.14159 ); ps.println("Boolean: " + true ); ps.println("Character: " + 'A' ); ps.println("Object: " + new Date ()); int [] numbers = {1 , 2 , 3 , 4 , 5 }; ps.println("Array: " + java.util.Arrays.toString(numbers)); System.out.println("数据已写入print.txt" ); } catch (FileNotFoundException e) { e.printStackTrace(); } }
⭐⭐ 格式化输出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void formattedOutput () { try (PrintStream ps = new PrintStream ("format.txt" )) { ps.printf("Hello, %s!%n" , "World" ); ps.printf("整数: %d, 浮点数: %.2f%n" , 100 , 3.14159 ); ps.println("\n学生成绩表:" ); ps.printf("%-10s %-10s %-10s%n" , "姓名" , "年龄" , "成绩" ); ps.printf("%-10s %-10d %-10.2f%n" , "张三" , 20 , 85.5 ); ps.printf("%-10s %-10d %-10.2f%n" , "李四" , 21 , 92.0 ); ps.printf("%-10s %-10d %-10.2f%n" , "王五" , 19 , 78.5 ); Date now = new Date (); ps.printf("当前时间: %tF %tT%n" , now, now); ps.printf("千分位: %,d%n" , 1234567 ); ps.printf("科学计数法: %e%n" , 12345.6789 ); System.out.println("格式化数据已写入format.txt" ); } catch (FileNotFoundException e) { e.printStackTrace(); } }
⭐⭐ 自动刷新 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static void autoFlushDemo () { try (PrintStream ps = new PrintStream ( new FileOutputStream ("autoflush.txt" ), true )) { ps.println("这行会自动刷新到文件" ); ps.printf("自动刷新: %d%n" , 100 ); ps.print("这行不会自动刷新" ); System.out.println("自动刷新模式已启用" ); } catch (IOException e) { e.printStackTrace(); } try (PrintStream ps = new PrintStream ( new FileOutputStream ("noauto.txt" ))) { ps.print("这行不会自动刷新" ); ps.flush(); System.out.println("手动刷新模式" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 重定向 System.out 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void redirectSystemOut () { PrintStream originalOut = System.out; try { PrintStream fileOut = new PrintStream ("redirect.txt" ); System.setOut(fileOut); System.out.println("这条消息写入文件" ); System.out.println("重定向System.out到文件" ); System.out.printf("当前时间: %tF %<tT%n" , new Date ()); System.setOut(originalOut); System.out.println("已恢复控制台输出" ); System.out.println("消息已重定向到redirect.txt" ); } catch (FileNotFoundException e) { e.printStackTrace(); System.setOut(originalOut); } }
⭐⭐ 自定义 PrintStream 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public static void customPrintStream () { try (PrintStream ps = new PrintStream ("custom.txt" ) { @Override public void println (String x) { String timestamp = new java .text.SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format(new Date ()); super .println("[" + timestamp + "] " + x); } @Override public void println (int x) { String timestamp = new java .text.SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format(new Date ()); super .println("[" + timestamp + "] " + x); } }) { ps.println("第一条日志消息" ); Thread.sleep(1000 ); ps.println("第二条日志消息" ); Thread.sleep(1000 ); ps.println(12345 ); System.out.println("带时间戳的日志已写入custom.txt" ); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }
⭐⭐ 日志记录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public static void loggingExample () { try (PrintStream log = new PrintStream ( new BufferedOutputStream (new FileOutputStream ("app.log" )), true )) { String[] levels = {"INFO" , "WARNING" , "ERROR" , "DEBUG" }; String[] messages = { "Application started" , "Configuration loaded successfully" , "Database connection established" , "User logged in: admin" , "Warning: Low disk space" , "Error: File not found" , "Processing request #12345" , "Application shutdown" }; for (int i = 0 ; i < messages.length; i++) { String level = levels[i % levels.length]; String timestamp = new java .text.SimpleDateFormat( "yyyy-MM-dd HH:mm:ss.SSS" ).format(new Date ()); log.printf("[%s] [%s] %s%n" , timestamp, level, messages[i]); Thread.sleep(100 ); } System.out.println("日志已写入app.log" ); } catch (IOException | InterruptedException e) { e.printStackTrace(); } System.out.println("\n日志内容预览:" ); try (BufferedReader br = new BufferedReader (new FileReader ("app.log" ))) { String line; int count = 0 ; while ((line = br.readLine()) != null && count < 5 ) { System.out.println(line); count++; } System.out.println("..." ); } catch (IOException e) { e.printStackTrace(); } }
字符流 以字符( 16 位 Unicode) 为单位进行数据读写的流, 自动处理字符编码( 如 UTF-8、 GBK) 。 Reader( 输入) 和 Writer( 输出) 。
专门处理文本数据。
内部使用字符编码集( Charset) 在字节与字符之间转换。
Reader Reader 是所有字符输入流的抽象基类, 定义了读取字符数据的基本方法。 它处理的是16位Unicode字符, 而不是字节。
⭐⭐ 核心方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public abstract class Reader implements Readable , Closeable { public int read () throws IOException public int read (char [] cbuf) throws IOException public abstract int read (char [] cbuf, int off, int len) public int read (CharBuffer target) throws IOException public long skip (long n) throws IOException public boolean ready () throws IOException public boolean markSupported () public void mark (int readAheadLimit) throws IOException public void reset () throws IOException public abstract void close () throws IOException }
Writer Writer 是所有字符输出流的抽象基类, 定义了向目的地写入字符数据的基本方法。 它处理的是16位Unicode字符, 而不是字节。
⭐⭐ 核心方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public abstract class Writer implements Appendable , Closeable, Flushable { public void write (int c) throws IOException public void write (char [] cbuf) throws IOException public abstract void write (char [] cbuf, int off, int len) throws IOException public void write (String str) throws IOException public void write (String str, int off, int len) throws IOException public Writer append (char c) throws IOException public Writer append (CharSequence csq) throws IOException public Writer append (CharSequence csq, int start, int end) throws IOException public abstract void flush () throws IOException public abstract void close () throws IOException }
字节字符转换流 InputStreamReader 是字节流到字符流的桥梁。 它读取字节并使用指定的字符集解码为字符。
OutputStreamWriter 是字符流到字节流的桥梁。 它将写入的字符使用指定的字符集编码为字节, 然后写入底层字节输出流。
处理字符编码转换
可以指定字符集( UTF-8、 GBK、 ISO-8859-1等)
缓冲字节流以提高性能
⭐⭐ 创建不同编码的文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 private static void createTestFiles () { String content = "你好, 世界! Hello, World! こんにちは" ; try (FileOutputStream fos = new FileOutputStream ("test_utf8.txt" ); OutputStreamWriter osw = new OutputStreamWriter (fos, "UTF-8" )) { osw.write(content); } catch (IOException e) { e.printStackTrace(); } try (FileOutputStream fos = new FileOutputStream ("test_gbk.txt" ); OutputStreamWriter osw = new OutputStreamWriter (fos, "GBK" )) { osw.write(content); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取不同字符集的文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static void basicUsage () { System.out.println("=== UTF-8 文件读取 ===" ); try (FileInputStream fis = new FileInputStream ("test_utf8.txt" ); InputStreamReader isr = new InputStreamReader (fis, "UTF-8" )) { char [] buffer = new char [1024 ]; int length = isr.read(buffer); System.out.println("内容: " + new String (buffer, 0 , length)); } catch (IOException e) { e.printStackTrace(); } System.out.println("\n=== GBK 文件读取 ===" ); try (FileInputStream fis = new FileInputStream ("test_gbk.txt" ); InputStreamReader isr = new InputStreamReader (fis, "GBK" )) { char [] buffer = new char [1024 ]; int length = isr.read(buffer); System.out.println("内容: " + new String (buffer, 0 , length)); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 自动检测并处理BOM 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public static void detectBOM () { try (FileOutputStream fos = new FileOutputStream ("utf8_bom.txt" )) { fos.write(new byte []{(byte ) 0xEF , (byte ) 0xBB , (byte ) 0xBF }); fos.write("带BOM的UTF-8文件" .getBytes("UTF-8" )); } catch (IOException e) { e.printStackTrace(); } try (FileInputStream fis = new FileInputStream ("utf8_bom.txt" )) { PushbackInputStream pbis = new PushbackInputStream (fis, 3 ); byte [] bom = new byte [3 ]; int bytesRead = pbis.read(bom); String encoding = "UTF-8" ; int skipBytes = 0 ; if (bytesRead >= 3 && bom[0 ] == (byte ) 0xEF && bom[1 ] == (byte ) 0xBB && bom[2 ] == (byte ) 0xBF ) { skipBytes = 3 ; System.out.println("检测到UTF-8 BOM" ); } else { pbis.unread(bom, 0 , bytesRead); } if (skipBytes > 0 ) { pbis.skip(skipBytes); } try (InputStreamReader isr = new InputStreamReader (pbis, encoding)) { char [] buffer = new char [1024 ]; int length = isr.read(buffer); System.out.println("内容: " + new String (buffer, 0 , length)); } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取多种编码的文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public static void readMultipleEncodings () { Map<String, String> files = new HashMap <>(); files.put("utf8.txt" , "UTF-8" ); files.put("gbk.txt" , "GBK" ); files.put("utf16be.txt" , "UTF-16BE" ); files.put("utf16le.txt" , "UTF-16LE" ); for (Map.Entry<String, String> entry : files.entrySet()) { try (FileOutputStream fos = new FileOutputStream (entry.getKey()); OutputStreamWriter osw = new OutputStreamWriter (fos, entry.getValue())) { osw.write("测试文本 Test Text" ); } catch (IOException e) { e.printStackTrace(); } } for (Map.Entry<String, String> entry : files.entrySet()) { System.out.println("\n读取 " + entry.getKey() + " (" + entry.getValue() + "):" ); try (FileInputStream fis = new FileInputStream (entry.getKey()); InputStreamReader isr = new InputStreamReader (fis, entry.getValue())) { char [] buffer = new char [1024 ]; int length = isr.read(buffer); System.out.println(" " + new String (buffer, 0 , length)); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 编码转换: 将一种编码的文件转换为另一种 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public static void encodingConversion () { String sourceFile = "source_gbk.txt" ; String targetFile = "target_utf8.txt" ; try (FileOutputStream fos = new FileOutputStream (sourceFile); OutputStreamWriter osw = new OutputStreamWriter (fos, "GBK" )) { osw.write("编码转换示例: 将GBK转换为UTF-8" ); } catch (IOException e) { e.printStackTrace(); } try (FileInputStream fis = new FileInputStream (sourceFile); InputStreamReader isr = new InputStreamReader (fis, "GBK" ); FileOutputStream fos = new FileOutputStream (targetFile); OutputStreamWriter osw = new OutputStreamWriter (fos, "UTF-8" )) { char [] buffer = new char [4096 ]; int bytesRead; while ((bytesRead = isr.read(buffer)) != -1 ) { osw.write(buffer, 0 , bytesRead); } System.out.println("编码转换完成: GBK -> UTF-8" ); } catch (IOException e) { e.printStackTrace(); } try (FileInputStream fis = new FileInputStream (targetFile); InputStreamReader isr = new InputStreamReader (fis, "UTF-8" )) { char [] buffer = new char [1024 ]; int length = isr.read(buffer); System.out.println("转换后内容: " + new String (buffer, 0 , length)); } catch (IOException e) { e.printStackTrace(); } new File (sourceFile).delete(); new File (targetFile).delete(); }
文件字符输入输出流 FileReader 是 InputStreamReader 的便捷子类, 专门用于读取文本文件。 它使用平台默认字符集, 简化了文件读取操作。
直接读取文本文件
使用平台默认字符集( 注意: 可能导致编码问题)
适合读取系统默认编码的文本文件
FileWriter 是 OutputStreamWriter 的便捷子类, 专门用于向文本文件写入字符数据。 它使用平台默认字符集, 简化了文件写入操作。
直接写入文本文件
使用平台默认字符集( 注意: 可能导致编码问题)
支持覆盖和追加两种模式
⭐⭐ 写入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void basicWrite () { try (FileWriter fw = new FileWriter ("basic.txt" )) { fw.write("Hello, FileWriter!\n" ); fw.write("这是第一行\n" ); fw.write("这是第二行\n" ); fw.write("这是第三行" ); System.out.println("文件写入完成: basic.txt" ); } catch (IOException e) { e.printStackTrace(); } displayFileContent("basic.txt" ); }
⭐⭐ 追加模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void appendMode () { try (FileWriter fw = new FileWriter ("append.txt" )) { fw.write("初始内容\n" ); System.out.println("第一次写入完成" ); } catch (IOException e) { e.printStackTrace(); } try (FileWriter fw = new FileWriter ("append.txt" , true )) { fw.write("追加的内容\n" ); fw.write("第二行追加内容\n" ); System.out.println("追加写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 批量写入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void batchWrite () { String[] lines = { "批量写入第1行" , "批量写入第2行" , "批量写入第3行" , "批量写入第4行" , "批量写入第5行" }; try (FileWriter fw = new FileWriter ("batch.txt" )) { for (String line : lines) { fw.write(line + "\n" ); } System.out.println("批量写入 " + lines.length + " 行数据" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 写入不同类型数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static void writeDifferentTypes () { try (FileWriter fw = new FileWriter ("types.txt" )) { fw.write("字符串: Hello\n" ); fw.write("字符: " ); fw.write('A' ); fw.write('\n' ); int number = 12345 ; fw.write("整数: " + number + "\n" ); double pi = 3.14159 ; fw.write("浮点数: " + pi + "\n" ); boolean flag = true ; fw.write("布尔值: " + flag + "\n" ); char [] chars = {'数' , '组' , '测' , '试' }; fw.write("字符数组: " ); fw.write(chars); fw.write('\n' ); System.out.println("多种类型数据写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取数据 1 2 3 4 5 6 7 8 9 10 11 12 13 public static void basicFileRead () { try (FileReader fr = new FileReader ("test.txt" )) { int character; System.out.print("读取内容: " ); while ((character = fr.read()) != -1 ) { System.out.print((char ) character); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 批量读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void batchRead () { try (FileReader fr = new FileReader ("test.txt" )) { char [] buffer = new char [1024 ]; int bytesRead; while ((bytesRead = fr.read(buffer)) != -1 ) { System.out.print(new String (buffer, 0 , bytesRead)); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 逐字符读取并统计 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static void characterByCharacter () { try (FileReader fr = new FileReader ("test.txt" )) { int ch; int letterCount = 0 ; int digitCount = 0 ; int chineseCount = 0 ; int spaceCount = 0 ; int otherCount = 0 ; while ((ch = fr.read()) != -1 ) { char c = (char ) ch; if (Character.isLetter(c)) { if (c >= 0x4E00 && c <= 0x9FFF ) { chineseCount++; } else { letterCount++; } } else if (Character.isDigit(c)) { digitCount++; } else if (Character.isSpaceChar(c)) { spaceCount++; } else { otherCount++; } } System.out.println("英文字母: " + letterCount); System.out.println("中文字符: " + chineseCount); System.out.println("数字: " + digitCount); System.out.println("空格: " + spaceCount); System.out.println("其他字符: " + otherCount); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 指定编码读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 public static void recommendedApproach () { String content = "这是使用OutputStreamWriter明确指定UTF-8编码写入的内容" ; try (FileOutputStream fos = new FileOutputStream ("recommended.txt" ); OutputStreamWriter osw = new OutputStreamWriter (fos, "UTF-8" )) { osw.write(content); System.out.println("使用OutputStreamWriter指定UTF-8编码写入完成" ); } catch (IOException e) { e.printStackTrace(); } try (FileInputStream fis = new FileInputStream ("recommended.txt" ); InputStreamReader isr = new InputStreamReader (fis, "UTF-8" ); BufferedReader br = new BufferedReader (isr)) { String line = br.readLine(); System.out.println("读取验证: " + line); System.out.println("内容匹配: " + content.equals(line)); } catch (IOException e) { e.printStackTrace(); } new File ("recommended.txt" ).delete(); }
缓冲字符输入输出流 BufferedReader 为字符输入流提供缓冲功能, 并提供了 readLine() 方法, 可以高效地按行读取文本。
提供缓冲功能, 减少I/O操作次数
提供 readLine() 方法, 方便按行读取
支持标记和重置
可以包装任何Reader
BufferedWriter 为字符输出流提供缓冲功能, 并提供 newLine() 方法用于写入平台独立的换行符。
提供缓冲功能, 减少I/O操作次数
提供 newLine() 方法, 写入平台独立的换行符
支持自定义缓冲区大小
⭐⭐ 写入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static void basicUsage () { try (BufferedWriter bw = new BufferedWriter (new FileWriter ("basic_bw.txt" ))) { bw.write("第一行内容" ); bw.newLine(); bw.write("第二行内容" ); bw.newLine(); bw.write("第三行内容" ); System.out.println("写入完成" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 缓冲区大小设置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void bufferSizeDemo () { try (BufferedWriter bw = new BufferedWriter (new FileWriter ("default_buffer.txt" ))) { bw.write("使用默认缓冲区大小" ); System.out.println("默认缓冲区: 8192字节" ); } catch (IOException e) { e.printStackTrace(); } try (BufferedWriter bw = new BufferedWriter (new FileWriter ("custom_buffer.txt" ), 32768 )) { bw.write("使用32KB自定义缓冲区" ); System.out.println("自定义缓冲区: 32768字节" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 写入 CSV 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void writeCSVFile () { String[][] data = { {"姓名" , "年龄" , "城市" , "职业" }, {"张三" , "25" , "北京" , "工程师" }, {"李四" , "30" , "上海" , "设计师" }, {"王五" , "28" , "广州" , "产品经理" }, {"赵六" , "35" , "深圳" , "架构师" } }; try (BufferedWriter bw = new BufferedWriter (new FileWriter ("output.csv" ))) { for (String[] row : data) { String line = String.join("," , row); bw.write(line); bw.newLine(); } System.out.println("CSV文件写入完成, 共 " + data.length + " 行" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 写入 HTML 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 public static void writeHTMLFile () { try (BufferedWriter bw = new BufferedWriter (new FileWriter ("output.html" ))) { bw.write("<!DOCTYPE html>" ); bw.newLine(); bw.write("<html>" ); bw.newLine(); bw.write("<head>" ); bw.newLine(); bw.write(" <title>BufferedWriter示例</title>" ); bw.newLine(); bw.write(" <style>" ); bw.newLine(); bw.write(" body { font-family: Arial; margin: 40px; }" ); bw.newLine(); bw.write(" h1 { color: #333; }" ); bw.newLine(); bw.write(" </style>" ); bw.newLine(); bw.write("</head>" ); bw.newLine(); bw.write("<body>" ); bw.newLine(); bw.write(" <h1>BufferedWriter生成的HTML</h1>" ); bw.newLine(); bw.write(" <p>这是使用BufferedWriter动态生成的HTML文件。 </p>" ); bw.newLine(); bw.write(" <ul>" ); bw.newLine(); bw.write(" <li>支持newLine()方法</li>" ); bw.newLine(); bw.write(" <li>自动处理换行符</li>" ); bw.newLine(); bw.write(" <li>提高写入性能</li>" ); bw.newLine(); bw.write(" </ul>" ); bw.newLine(); bw.write("</body>" ); bw.newLine(); bw.write("</html>" ); System.out.println("HTML文件写入完成: output.html" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 写入配置文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static void writeCSVFile () { try (BufferedWriter bw = new BufferedWriter (new FileWriter ("config.properties" ))) { bw.write("# 数据库配置\n" ); bw.write("db.url=jdbc:mysql://localhost:3306/test\n" ); bw.write("db.username=root\n" ); bw.write("db.password=123456\n" ); bw.write("\n" ); bw.write("# 应用配置\n" ); bw.write("app.name=MyApplication\n" ); bw.write("app.version=1.0.0\n" ); bw.write("app.debug=true\n" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 按行读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public static void basicReadLine () { try (FileReader fr = new FileReader ("lines.txt" ); BufferedReader br = new BufferedReader (fr)) { String line; int lineNum = 1 ; while ((line = br.readLine()) != null ) { System.out.println(lineNum + ": " + line); lineNum++; } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取 CSV 文件并解析 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static void readCSVFile () { List<Map<String, String>> records = new ArrayList <>(); try (FileReader fr = new FileReader ("data.csv" ); BufferedReader br = new BufferedReader (fr)) { String headerLine = br.readLine(); if (headerLine == null ) return ; String[] headers = headerLine.split("," ); String line; while ((line = br.readLine()) != null ) { String[] values = line.split("," ); Map<String, String> record = new LinkedHashMap <>(); for (int i = 0 ; i < headers.length && i < values.length; i++) { record.put(headers[i], values[i]); } records.add(record); } for (Map<String, String> record : records) { System.out.println(record); } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取配置文件( 类似.properties格式) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public static void readPropertiesFile () { Properties props = new Properties (); try (FileReader fr = new FileReader ("config.properties" ); BufferedReader br = new BufferedReader (fr)) { String line; while ((line = br.readLine()) != null ) { line = line.trim(); if (line.isEmpty() || line.startsWith("#" )) { continue ; } int separatorIndex = line.indexOf('=' ); if (separatorIndex > 0 ) { String key = line.substring(0 , separatorIndex).trim(); String value = line.substring(separatorIndex + 1 ).trim(); props.setProperty(key, value); } } System.out.println("数据库URL: " + props.getProperty("db.url" )); System.out.println("用户名: " + props.getProperty("db.username" )); System.out.println("应用名称: " + props.getProperty("app.name" )); System.out.println("调试模式: " + props.getProperty("app.debug" )); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 标记和重置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 public static void markAndReset () { String content = "Line 1\nLine 2\nLine 3\nLine 4\nLine 5" ; try (StringReader sr = new StringReader (content); BufferedReader br = new BufferedReader (sr)) { if (br.markSupported()) { System.out.println("支持 mark/reset 操作" ); String line1 = br.readLine(); System.out.println("第一行: " + line1); br.mark(1000 ); String line2 = br.readLine(); String line3 = br.readLine(); System.out.println("第二行: " + line2); System.out.println("第三行: " + line3); br.reset(); System.out.println("\n重置后重新读取:" ); System.out.println("第二行: " + br.readLine()); System.out.println("第三行: " + br.readLine()); } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取多行用户输入 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void readMultiLineInput () { try (InputStreamReader isr = new InputStreamReader (System.in); BufferedReader br = new BufferedReader (isr)) { List<String> lines = new ArrayList <>(); String line; while ((line = br.readLine()) != null && !line.isEmpty()) { lines.add(line); } System.out.println("\n您输入了 " + lines.size() + " 行:" ); for (String l : lines) { System.out.println(" " + l); } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 处理大日志文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public static void processLogFile () { long startTime = System.currentTimeMillis(); int totalLines = 0 ; int errorCount = 0 ; int warningCount = 0 ; int infoCount = 0 ; try (FileReader fr = new FileReader ("app.log" ); BufferedReader br = new BufferedReader (fr)) { String line; while ((line = br.readLine()) != null ) { totalLines++; if (line.contains("[ERROR]" )) { errorCount++; } else if (line.contains("[WARN]" )) { warningCount++; } else if (line.contains("[INFO]" )) { infoCount++; } if (totalLines % 1000 == 0 ) { System.out.println("已处理 " + totalLines + " 行..." ); } } long endTime = System.currentTimeMillis(); System.out.println("\n=== 统计结果 ===" ); System.out.println("总行数: " + totalLines); System.out.println("ERROR: " + errorCount); System.out.println("WARN: " + warningCount); System.out.println("INFO: " + infoCount); System.out.println("处理时间: " + (endTime - startTime) + "ms" ); } catch (IOException e) { e.printStackTrace(); } new File ("app.log" ).delete(); }
行号字符输入流 LineNumberReader 是 BufferedReader 的子类, 它在读取文本时自动跟踪行号, 并提供获取和设置行号的方法。
自动维护行号计数器
继承 BufferedReader 的所有功能
提供 getLineNumber() 和 setLineNumber() 方法
适合需要行号信息的场景( 如编译器、 代码分析工具)
⭐⭐ 显示行号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void basicLineNumber () { try (FileReader fr = new FileReader ("sample.txt" ); LineNumberReader lnr = new LineNumberReader (fr)) { String line; while ((line = lnr.readLine()) != null ) { System.out.println(lnr.getLineNumber() + ": " + line); } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 统计代码行数( 忽略空行和注释) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 public static void codeAnalyzer () { int totalLines = 0 ; int codeLines = 0 ; int commentLines = 0 ; int blankLines = 0 ; try (FileReader fr = new FileReader ("SampleCode.java" ); LineNumberReader lnr = new LineNumberReader (fr)) { String line; boolean inBlockComment = false ; while ((line = lnr.readLine()) != null ) { totalLines++; String trimmed = line.trim(); if (trimmed.isEmpty()) { blankLines++; continue ; } if (inBlockComment) { commentLines++; if (trimmed.contains("*/" )) { inBlockComment = false ; } continue ; } if (trimmed.startsWith("/*" )) { commentLines++; if (!trimmed.contains("*/" )) { inBlockComment = true ; } continue ; } if (trimmed.startsWith("//" )) { commentLines++; continue ; } codeLines++; } System.out.println("=== 统计结果 ===" ); System.out.println("总行数: " + totalLines); System.out.println("代码行: " + codeLines); System.out.println("注释行: " + commentLines); System.out.println("空行: " + blankLines); System.out.println("代码率: " + (codeLines * 100.0 / totalLines) + "%" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 错误定位: 在文件中查找并定位错误 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public static void errorLocator () { try (FileReader fr = new FileReader ("config_with_error.properties" ); LineNumberReader lnr = new LineNumberReader (fr)) { String line; List<String> errors = new java .util.ArrayList<>(); while ((line = lnr.readLine()) != null ) { int lineNum = lnr.getLineNumber(); String trimmed = line.trim(); if (trimmed.isEmpty() || trimmed.startsWith("#" )) { continue ; } if (!trimmed.contains("=" )) { errors.add("行 " + lineNum + ": 缺少等号分隔符 - " + trimmed); continue ; } String[] parts = trimmed.split("=" , 2 ); String key = parts[0 ].trim(); String value = parts[1 ].trim(); if (key.isEmpty()) { errors.add("行 " + lineNum + ": 键名为空" ); } if (value.isEmpty()) { errors.add("行 " + lineNum + ": 值为空" ); } if (key.equals("port" )) { try { int port = Integer.parseInt(value); if (port < 1 || port > 65535 ) { errors.add("行 " + lineNum + ": 端口号超出范围 (1-65535) - " + port); } } catch (NumberFormatException e) { errors.add("行 " + lineNum + ": 端口号格式错误 - " + value); } } } if (errors.isEmpty()) { System.out.println("配置文件格式正确" ); } else { System.out.println("发现 " + errors.size() + " 个错误:" ); for (String error : errors) { System.out.println(" " + error); } } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 语法高亮 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public static void syntaxHighlighter () { String[] keywords = {"public" , "class" , "static" , "void" , "int" , "String" , "if" , "else" , "for" , "while" , "return" }; try (FileReader fr = new FileReader ("SampleCode.java" ); LineNumberReader lnr = new LineNumberReader (fr)) { String line; while ((line = lnr.readLine()) != null ) { int lineNum = lnr.getLineNumber(); String highlighted = line; for (String keyword : keywords) { highlighted = highlighted.replaceAll( "\\b" + keyword + "\\b" , "\033[34m" + keyword + "\033[0m" ); } highlighted = highlighted.replaceAll( "\"([^\"]*)\"" , "\033[32m\"$1\"\033[0m" ); highlighted = highlighted.replaceAll( "//.*$" , "\033[90m$0\033[0m" ); System.out.printf("%4d | %s%n" , lineNum, highlighted); } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 搜索并显示行号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void searchWithLineNumbers () { String searchTerm = "error" ; try (FileReader fr = new FileReader ("search.log" ); LineNumberReader lnr = new LineNumberReader (fr)) { String line; int matchCount = 0 ; System.out.println("匹配结果:" ); while ((line = lnr.readLine()) != null ) { if (line.toLowerCase().contains(searchTerm.toLowerCase())) { matchCount++; System.out.printf("行 %4d: %s%n" , lnr.getLineNumber(), line.substring(0 , Math.min(80 , line.length()))); } } System.out.println("\n共找到 " + matchCount + " 处匹配" ); } catch (IOException e) { e.printStackTrace(); } }
字符数组输入输出流 CharArrayReader 从字符数组中读取数据, 将内存中的字符数组包装成字符输入流。
数据源是内存中的字符数组
不需要关闭流( close()无效)
支持标记和重置
适合处理临时字符数据
CharArrayWriter 将字符数据写入内存中的字符数组缓冲区。 它会自动管理缓冲区大小, 可以根据需要增长。
数据写入内存, 不涉及磁盘
自动扩容的缓冲区
可以获取字符数组或字符串
不需要关闭流
可以写入到其他Writer
⭐⭐ 写入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void basicUsage () { try (CharArrayWriter caw = new CharArrayWriter ()) { caw.write('H' ); caw.write('e' ); caw.write('l' ); caw.write('l' ); caw.write('o' ); caw.write(", " ); caw.write("World!" ); caw.write(" 你好, 世界! " ); char [] chars = caw.toCharArray(); String str = caw.toString(); System.out.println("字符数组长度: " + chars.length); System.out.println("字符串内容: " + str); System.out.println("当前大小: " + caw.size() + " 字符" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 写入到其他流 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void writeToOtherWriter () { try (CharArrayWriter caw = new CharArrayWriter (); FileWriter fw = new FileWriter ("caw_output.txt" ); StringWriter sw = new StringWriter ()) { caw.write("这是要写入文件的内容\n" ); caw.write("这是要写入StringWriter的内容\n" ); caw.write("可以同时写入多个目标\n" ); caw.writeTo(fw); System.out.println("已写入文件: caw_output.txt" ); caw.writeTo(sw); System.out.println("StringWriter内容: " + sw.toString()); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 重置和重用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void resetAndReuse () { try (CharArrayWriter caw = new CharArrayWriter ()) { caw.write("第一批数据" ); System.out.println("第一批数据: " + caw.toString()); System.out.println("大小: " + caw.size()); caw.reset(); System.out.println("\n重置后大小: " + caw.size()); caw.write("第二批数据" ); System.out.println("第二批数据: " + caw.toString()); System.out.println("大小: " + caw.size()); caw.reset(); caw.write("第三批数据" ); System.out.println("\n第三批数据: " + caw.toString()); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 构建字符串 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public static void buildString () { try (CharArrayWriter caw = new CharArrayWriter ()) { caw.write("{" ); caw.write("\"name\": \"张三\"," ); caw.write("\"age\": 25," ); caw.write("\"city\": \"北京\"," ); caw.write("\"skills\": [\"Java\", \"Python\", \"JavaScript\"]" ); caw.write("}" ); String json = caw.toString(); System.out.println("生成的JSON:" ); System.out.println(json); caw.reset(); caw.write("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); caw.write("<user>\n" ); caw.write(" <name>李四</name>\n" ); caw.write(" <age>30</age>\n" ); caw.write(" <city>上海</city>\n" ); caw.write("</user>" ); String xml = caw.toString(); System.out.println("\n生成的XML:" ); System.out.println(xml); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取数据 1 2 3 4 5 6 7 8 9 10 11 12 13 public static void basicUsage () { char [] data = "Hello, CharArrayReader! 这是测试数据" .toCharArray(); try (CharArrayReader car = new CharArrayReader (data)) { int ch; System.out.print("读取内容: " ); while ((ch = car.read()) != -1 ) { System.out.print((char ) ch); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 标记和重置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public static void markAndReset () { char [] data = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" .toCharArray(); try (CharArrayReader car = new CharArrayReader (data)) { char [] buffer = new char [5 ]; car.read(buffer); System.out.println("前5个字符: " + new String (buffer)); car.mark(100 ); car.read(buffer); System.out.println("接下来5个字符: " + new String (buffer)); car.reset(); car.read(buffer); System.out.println("重置后读取: " + new String (buffer)); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 部分读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 public static void partialRead () { char [] data = "Java Programming Language" .toCharArray(); try (CharArrayReader car = new CharArrayReader (data, 5 , 11 )) { int ch; System.out.print("部分读取: " ); while ((ch = car.read()) != -1 ) { System.out.print((char ) ch); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 字符串解析器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static void stringParser () { String expression = "10 + 20 * 30 - 40 / 5" ; try (CharArrayReader car = new CharArrayReader (expression.toCharArray())) { StringBuilder token = new StringBuilder (); int ch; System.out.println("=== 解析表达式 ===" ); System.out.print("Tokens: " ); while ((ch = car.read()) != -1 ) { char c = (char ) ch; if (Character.isWhitespace(c)) { if (token.length() > 0 ) { System.out.print(token + " " ); token.setLength(0 ); } } else if (c == '+' || c == '-' || c == '*' || c == '/' ) { if (token.length() > 0 ) { System.out.print(token + " " ); token.setLength(0 ); } System.out.print(c + " " ); } else { token.append(c); } } if (token.length() > 0 ) { System.out.print(token); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 字符数组操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 public static void charArrayOperations () { String[] lines = { "Line 1: First line" , "Line 2: Second line" , "Line 3: Third line" }; StringBuilder sb = new StringBuilder (); for (String line : lines) { sb.append(line).append('\n' ); } char [] data = sb.toString().toCharArray(); try (CharArrayReader car = new CharArrayReader (data); BufferedReader br = new BufferedReader (car)) { System.out.println("\n=== 逐行处理 ===" ); String line; int lineNum = 1 ; while ((line = br.readLine()) != null ) { System.out.println(lineNum + ": " + line); lineNum++; } } catch (IOException e) { e.printStackTrace(); } }
字符串输入输出流 StringReader 从字符串中读取字符数据, 将字符串包装成字符输入流。
数据源是字符串
简单高效, 适合处理字符串数据
支持标记和重置
不需要关闭流
StringWriter 将字符数据写入内存中的字符串缓冲区。 它内部维护一个 StringBuilder, 可以方便地获取最终字符串。
数据写入内存字符串
内部使用StringBuilder
不需要关闭流
适合构建字符串
线程安全( 方法同步)
⭐⭐ 写入数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void basicUsage () { try (StringWriter sw = new StringWriter ()) { sw.write("Hello" ); sw.write(", " ); sw.write("StringWriter" ); sw.write("!" ); sw.append(' ' ); sw.append("This is appended." ); String result = sw.toString(); StringBuffer buffer = sw.getBuffer(); System.out.println("结果: " + result); System.out.println("StringBuffer内容: " + buffer.toString()); System.out.println("长度: " + sw.getBuffer().length()); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 读取数据 1 2 3 4 5 6 7 8 9 10 11 public static void basicUsage () { String text = "Hello, StringReader! 这是字符串读取测试" ; try (StringReader sr = new StringReader (text)) { char [] buffer = new char [1024 ]; int length = sr.read(buffer); System.out.println("读取内容: " + new String (buffer, 0 , length)); } catch (IOException e) { e.printStackTrace(); } }
管道字符输入输出流 PipedReader 与 PipedWriter 配合使用, 实现线程间的字符数据通信。
⭐⭐ 基本通信 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public static void basicCommunication () { try { PipedReader reader = new PipedReader (); PipedWriter writer = new PipedWriter (); reader.connect(writer); Thread sender = new Thread (() -> { try { String[] messages = {"Hello" , "World" , "Piped" , "Reader" , "Writer" }; for (String msg : messages) { writer.write(msg); writer.write('\n' ); writer.flush(); Thread.sleep(500 ); } writer.close(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); Thread receiver = new Thread (() -> { try (BufferedReader br = new BufferedReader (reader)) { String line; System.out.println("接收线程开始读取..." ); while ((line = br.readLine()) != null ) { System.out.println("收到: " + line); } System.out.println("通信结束" ); } catch (IOException e) { e.printStackTrace(); } }); receiver.start(); Thread.sleep(100 ); sender.start(); sender.join(); receiver.join(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }
⭐⭐ 生产者-消费者模式 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 public static void producerConsumer () { try { PipedReader reader = new PipedReader (1024 ); PipedWriter writer = new PipedWriter (); reader.connect(writer); Thread producer = new Thread (() -> { try { for (int i = 1 ; i <= 20 ; i++) { String data = "数据-" + i; writer.write(data); writer.write('\n' ); writer.flush(); System.out.println("生产: " + data); Thread.sleep(200 ); } writer.close(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); Thread consumer = new Thread (() -> { try (BufferedReader br = new BufferedReader (reader)) { String line; int count = 0 ; while ((line = br.readLine()) != null ) { System.out.println("消费: " + line + " -> 处理中..." ); Thread.sleep(300 ); count++; } System.out.println("共处理 " + count + " 条数据" ); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); consumer.start(); Thread.sleep(100 ); producer.start(); producer.join(); consumer.join(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }
⭐⭐ 多管道通信 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 public static void multiChannelCommunication () { try { PipedReader readerA = new PipedReader (); PipedWriter writerA = new PipedWriter (); PipedReader readerB = new PipedReader (); PipedWriter writerB = new PipedWriter (); readerA.connect(writerA); readerB.connect(writerB); Thread thread1 = new Thread (() -> { try (BufferedReader br = new BufferedReader (readerA); PrintWriter pw = new PrintWriter (writerB)) { String[] messages = {"你好" , "今天天气不错" , "吃了吗" , "再见" }; for (String msg : messages) { System.out.println("线程1发送: " + msg); pw.println(msg); pw.flush(); String response = br.readLine(); System.out.println("线程1收到: " + response); Thread.sleep(1000 ); } } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); Thread thread2 = new Thread (() -> { try (BufferedReader br = new BufferedReader (readerB); PrintWriter pw = new PrintWriter (writerA)) { String line; while ((line = br.readLine()) != null ) { System.out.println("线程2收到: " + line); String response = "响应: " + line; System.out.println("线程2发送: " + response); pw.println(response); pw.flush(); } } catch (IOException e) { e.printStackTrace(); } }); thread2.start(); Thread.sleep(100 ); thread1.start(); thread1.join(); thread2.interrupt(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }
⭐⭐ 带超时的通信 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 public static void timeoutCommunication () { try { PipedReader reader = new PipedReader (); PipedWriter writer = new PipedWriter (); reader.connect(writer); ExecutorService executor = Executors.newSingleThreadExecutor(); Thread sender = new Thread (() -> { try { String[] messages = {"消息1" , "消息2" , "消息3" }; for (String msg : messages) { writer.write(msg); writer.write('\n' ); writer.flush(); System.out.println("发送: " + msg); Thread.sleep(2000 ); } writer.close(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); Future<?> future = executor.submit(() -> { try (BufferedReader br = new BufferedReader (reader)) { String line; while ((line = br.readLine()) != null ) { System.out.println("接收: " + line); } } catch (IOException e) { e.printStackTrace(); } }); sender.start(); try { future.get(3 , TimeUnit.SECONDS); } catch (TimeoutException e) { System.out.println("接收超时, 取消操作" ); future.cancel(true ); } catch (Exception e) { e.printStackTrace(); } executor.shutdown(); sender.interrupt(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 数据处理流水线 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 public static void processingPipeline () { try { PipedReader reader1 = new PipedReader (); PipedWriter writer1 = new PipedWriter (); PipedReader reader2 = new PipedReader (); PipedWriter writer2 = new PipedWriter (); reader1.connect(writer1); reader2.connect(writer2); Thread stage1 = new Thread (() -> { try { String[] rawData = {"apple" , "BANANA" , "cherry" , "DATE" , "elderberry" }; for (String data : rawData) { writer1.write(data); writer1.write('\n' ); writer1.flush(); System.out.println("阶段1-生成: " + data); Thread.sleep(100 ); } writer1.close(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); Thread stage2 = new Thread (() -> { try (BufferedReader br = new BufferedReader (reader1); PrintWriter pw = new PrintWriter (writer2)) { String line; while ((line = br.readLine()) != null ) { String transformed = line.toLowerCase(); pw.println(transformed); pw.flush(); System.out.println("阶段2-转换: " + line + " -> " + transformed); Thread.sleep(100 ); } writer2.close(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }); Thread stage3 = new Thread (() -> { try (BufferedReader br = new BufferedReader (reader2)) { String line; int count = 0 ; while ((line = br.readLine()) != null ) { System.out.println("阶段3-输出: " + line); count++; } System.out.println("总计处理: " + count + " 条数据" ); } catch (IOException e) { e.printStackTrace(); } }); stage3.start(); stage2.start(); stage1.start(); stage1.join(); stage2.join(); stage3.join(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } }
过滤字符输入输出流 FilterReader 是所有过滤字符输入流的抽象基类。 它包装了另一个 Reader, 可以在读取数据时添加额外的功能。
FilterWriter 是所有过滤字符输出流的抽象基类。 它包装了另一个Writer, 可以在写入数据时添加额外的功能。
⭐⭐ 输入流过滤: 转换为大写的过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 static class UppercaseFilterReader extends FilterReader { protected UppercaseFilterReader (Reader in) { super (in); } @Override public int read () throws IOException { int ch = super .read(); if (ch != -1 ) { return Character.toUpperCase(ch); } return ch; } @Override public int read (char [] cbuf, int off, int len) throws IOException { int count = super .read(cbuf, off, len); if (count > 0 ) { for (int i = off; i < off + count; i++) { cbuf[i] = Character.toUpperCase(cbuf[i]); } } return count; } } public static void uppercaseFilter () { String text = "Hello, World! 你好, 世界! " ; try (StringReader sr = new StringReader (text); UppercaseFilterReader ufr = new UppercaseFilterReader (sr)) { int ch; System.out.print("大写转换: " ); while ((ch = ufr.read()) != -1 ) { System.out.print((char ) ch); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 输入流过滤: 移除多余空白的过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 static class RemoveWhitespaceFilterReader extends FilterReader { private boolean lastWasSpace = false ; protected RemoveWhitespaceFilterReader (Reader in) { super (in); } @Override public int read () throws IOException { int ch = super .read(); if (ch == -1 ) return -1 ; if (Character.isWhitespace(ch)) { if (lastWasSpace) { return read(); } lastWasSpace = true ; return ' ' ; } lastWasSpace = false ; return ch; } } public static void removeWhitespaceFilter () { String text = "Hello World! This is a test." ; try (StringReader sr = new StringReader (text); RemoveWhitespaceFilterReader rwf = new RemoveWhitespaceFilterReader (sr)) { int ch; System.out.print("移除多余空格: " ); while ((ch = rwf.read()) != -1 ) { System.out.print((char ) ch); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 输入流过滤: XOR加密/解密过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 static class XorFilterReader extends FilterReader { private final byte key; protected XorFilterReader (Reader in, byte key) { super (in); this .key = key; } @Override public int read () throws IOException { int ch = super .read(); if (ch != -1 ) { return ch ^ key; } return ch; } @Override public int read (char [] cbuf, int off, int len) throws IOException { int count = super .read(cbuf, off, len); if (count > 0 ) { for (int i = off; i < off + count; i++) { cbuf[i] = (char ) (cbuf[i] ^ key); } } return count; } } public static void encryptionFilter () { String text = "这是需要加密的文本" ; byte key = 0x55 ; try (StringReader sr = new StringReader (text); XorFilterReader xorReader = new XorFilterReader (sr, key); StringWriter sw = new StringWriter ()) { int ch; while ((ch = xorReader.read()) != -1 ) { sw.write(ch); } String encrypted = sw.toString(); System.out.println("原始文本: " + text); System.out.println("加密文本: " + encrypted); try (StringReader sr2 = new StringReader (encrypted); XorFilterReader xorReader2 = new XorFilterReader (sr2, key); StringWriter sw2 = new StringWriter ()) { while ((ch = xorReader2.read()) != -1 ) { sw2.write(ch); } System.out.println("解密文本: " + sw2.toString()); } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 输入流过滤: 链式过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void chainedFilters () { String text = " Hello, World! This is a test. " ; try (StringReader sr = new StringReader (text); RemoveWhitespaceFilterReader rwf = new RemoveWhitespaceFilterReader (sr); UppercaseFilterReader ufr = new UppercaseFilterReader (rwf)) { int ch; System.out.print("\n链式过滤( 移除空格+大写) : " ); while ((ch = ufr.read()) != -1 ) { System.out.print((char ) ch); } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 输出流过滤: 大写过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 static class UppercaseFilterWriter extends FilterWriter { public UppercaseFilterWriter (Writer out) { super (out); } @Override public void write (int c) throws IOException { out.write(Character.toUpperCase(c)); } @Override public void write (char [] cbuf, int off, int len) throws IOException { char [] upper = new char [len]; for (int i = 0 ; i < len; i++) { upper[i] = Character.toUpperCase(cbuf[off + i]); } out.write(upper, 0 , len); } @Override public void write (String str, int off, int len) throws IOException { String upper = str.substring(off, off + len).toUpperCase(); out.write(upper); } } public static void uppercaseFilter () { try (StringWriter sw = new StringWriter (); UppercaseFilterWriter ufw = new UppercaseFilterWriter (sw)) { ufw.write("Hello, World!" ); ufw.write(" 你好, 世界! " ); System.out.println("原始输出应该包含小写" ); System.out.println("实际输出: " + sw.toString()); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 输出流过滤: 行号过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 static class LineNumberFilterWriter extends FilterWriter { private int lineNumber = 1 ; private boolean atStartOfLine = true ; public LineNumberFilterWriter (Writer out) { super (out); } @Override public void write (int c) throws IOException { if (atStartOfLine) { String lineNum = String.format("%4d: " , lineNumber++); out.write(lineNum); atStartOfLine = false ; } out.write(c); if (c == '\n' ) { atStartOfLine = true ; } } @Override public void write (char [] cbuf, int off, int len) throws IOException { for (int i = 0 ; i < len; i++) { write(cbuf[off + i]); } } @Override public void write (String str, int off, int len) throws IOException { for (int i = 0 ; i < len; i++) { write(str.charAt(off + i)); } } } public static void lineNumberFilter () { System.out.println("\n=== 行号过滤器 ===" ); try (StringWriter sw = new StringWriter (); LineNumberFilterWriter lnfw = new LineNumberFilterWriter (sw)) { lnfw.write("第一行内容\n" ); lnfw.write("第二行内容\n" ); lnfw.write("第三行内容\n" ); lnfw.write("第四行内容" ); System.out.println("带行号的输出:" ); System.out.println(sw.toString()); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 输出流过滤: HTML转义过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 static class HtmlEscapeFilterWriter extends FilterWriter { public HtmlEscapeFilterWriter (Writer out) { super (out); } @Override public void write (int c) throws IOException { char ch = (char ) c; switch (ch) { case '<' : out.write("<" ); break ; case '>' : out.write(">" ); break ; case '&' : out.write("&" ); break ; case '"' : out.write(""" ); break ; case '\'' : out.write("'" ); break ; default : out.write(c); } } @Override public void write (char [] cbuf, int off, int len) throws IOException { for (int i = 0 ; i < len; i++) { write(cbuf[off + i]); } } @Override public void write (String str, int off, int len) throws IOException { for (int i = 0 ; i < len; i++) { write(str.charAt(off + i)); } } } public static void htmlEscapeFilter () { System.out.println("\n=== HTML转义过滤器 ===" ); try (StringWriter sw = new StringWriter (); HtmlEscapeFilterWriter hefw = new HtmlEscapeFilterWriter (sw)) { hefw.write("<html>" ); hefw.write(" <body>" ); hefw.write(" <h1>Hello & Welcome</h1>" ); hefw.write(" <p>This is a test.</p>" ); hefw.write(" </body>" ); hefw.write("</html>" ); System.out.println("转义后的HTML:" ); System.out.println(sw.toString()); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 输出流过滤: 压缩空格过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 static class CompressWhitespaceFilterWriter extends FilterWriter { private boolean lastWasSpace = false ; public CompressWhitespaceFilterWriter (Writer out) { super (out); } @Override public void write (int c) throws IOException { char ch = (char ) c; if (Character.isWhitespace(ch)) { if (!lastWasSpace) { out.write(' ' ); lastWasSpace = true ; } } else { out.write(c); lastWasSpace = false ; } } @Override public void write (char [] cbuf, int off, int len) throws IOException { for (int i = 0 ; i < len; i++) { write(cbuf[off + i]); } } @Override public void write (String str, int off, int len) throws IOException { for (int i = 0 ; i < len; i++) { write(str.charAt(off + i)); } } } public static void compressWhitespaceFilter () { System.out.println("\n=== 压缩空格过滤器 ===" ); try (StringWriter sw = new StringWriter (); CompressWhitespaceFilterWriter cwf = new CompressWhitespaceFilterWriter (sw)) { cwf.write("This is a test with multiple spaces." ); cwf.write("\n\n" ); cwf.write(" Multiple lines with spaces . " ); System.out.println("压缩空格后的输出:" ); System.out.println(sw.toString()); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 输出流过滤: 链式过滤器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void chainedFilters () { System.out.println("\n=== 链式过滤器 ===" ); try (StringWriter sw = new StringWriter (); UppercaseFilterWriter ufw = new UppercaseFilterWriter (sw); HtmlEscapeFilterWriter hefw = new HtmlEscapeFilterWriter (ufw)) { hefw.write("Hello & Goodbye <test>" ); System.out.println("链式过滤结果:" ); System.out.println(sw.toString()); } catch (IOException e) { e.printStackTrace(); } }
回推字符输入流 PushbackReader 允许将读取的字符推回到流中, 以便后续再次读取。 它是 FilterReader 的子类。
支持回推已读取的字符
可以设置回推缓冲区大小
适合解析器、 编译器场景
⭐⭐ 回推数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public static void basicPushback () { String text = "123ABC" ; try (StringReader sr = new StringReader (text); PushbackReader pr = new PushbackReader (sr, 10 )) { StringBuilder numbers = new StringBuilder (); int ch; while ((ch = pr.read()) != -1 && Character.isDigit(ch)) { numbers.append((char ) ch); } System.out.println("读取的数字: " + numbers); if (ch != -1 ) { pr.unread(ch); System.out.println("回推字符: " + (char ) ch); } StringBuilder rest = new StringBuilder (); while ((ch = pr.read()) != -1 ) { rest.append((char ) ch); } System.out.println("剩余内容: " + rest); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 解析数字 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 public static void parseNumbers () { String text = "100 -200 3.14 -5.67 999" ; try (StringReader sr = new StringReader (text); PushbackReader pr = new PushbackReader (sr, 5 )) { System.out.println("\n=== 数字解析 ===" ); System.out.println("原文: " + text); System.out.print("解析结果: " ); int ch; while ((ch = pr.read()) != -1 ) { if (ch == '-' || Character.isDigit(ch)) { StringBuilder num = new StringBuilder (); boolean isNegative = (ch == '-' ); boolean hasDecimal = false ; if (isNegative) { num.append((char ) ch); ch = pr.read(); } while (ch != -1 && Character.isDigit(ch)) { num.append((char ) ch); ch = pr.read(); } if (ch == '.' ) { num.append('.' ); ch = pr.read(); hasDecimal = true ; while (ch != -1 && Character.isDigit(ch)) { num.append((char ) ch); ch = pr.read(); } } System.out.print(num + " " ); if (ch != -1 && !Character.isWhitespace(ch)) { pr.unread(ch); } } } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 解析 HTML 标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public static void parseHTMLTags () { String html = "<html><body><h1>标题</h1><p>段落</p></body></html>" ; try (StringReader sr = new StringReader (html); PushbackReader pr = new PushbackReader (sr, 100 )) { int ch; while ((ch = pr.read()) != -1 ) { if (ch == '<' ) { StringBuilder tag = new StringBuilder (); tag.append('<' ); while ((ch = pr.read()) != -1 && ch != '>' ) { tag.append((char ) ch); } tag.append('>' ); System.out.println("找到标签: " + tag); } else { StringBuilder text = new StringBuilder (); while (ch != -1 && ch != '<' ) { text.append((char ) ch); ch = pr.read(); } if (text.length() > 0 ) { System.out.println("文本内容: " + text); } if (ch == '<' ) { pr.unread(ch); } } } } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 解析 CSV 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public static void parseCSV () { String csv = "张三,\"北京,朝阳区\",25,\"工程师,Java\"" ; try (StringReader sr = new StringReader (csv); PushbackReader pr = new PushbackReader (sr, 10 )) { System.out.println("\n=== CSV解析 ===" ); System.out.println("原文: " + csv); System.out.print("解析字段: " ); boolean inQuotes = false ; StringBuilder field = new StringBuilder (); int ch; while ((ch = pr.read()) != -1 ) { char c = (char ) ch; if (c == '"' ) { inQuotes = !inQuotes; continue ; } if (c == ',' && !inQuotes) { System.out.print(field + " | " ); field.setLength(0 ); } else { field.append(c); } } System.out.println(field); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 表达式解析器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public static void expressionParser () { String expr = "10+20*30-40/5" ; try (StringReader sr = new StringReader (expr); PushbackReader pr = new PushbackReader (sr, 10 )) { System.out.println("\n=== 表达式解析 ===" ); System.out.println("表达式: " + expr); System.out.print("解析结果: " ); int ch; while ((ch = pr.read()) != -1 ) { char c = (char ) ch; if (Character.isDigit(c)) { StringBuilder num = new StringBuilder (); num.append(c); while ((ch = pr.read()) != -1 && Character.isDigit((char ) ch)) { num.append((char ) ch); } System.out.print(num); if (ch != -1 && !Character.isDigit((char ) ch)) { pr.unread(ch); } } else if (c == '+' || c == '-' || c == '*' || c == '/' ) { System.out.print(" " + c + " " ); } } System.out.println(); } catch (IOException e) { e.printStackTrace(); } }
打印字符输出流 PrintWriter 提供了方便的打印方法( print、 println、 printf) , 可以将各种数据类型格式化为文本输出。 它是 System.out 和 System.err 的类型, 也是日志输出的常用类。
提供 print、 println、 printf 等便捷方法
自动刷新( 可选)
不会抛出 IOException
支持格式化输出
可以包装任何 Writer 或 OutputStream
⭐⭐ 基本打印 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 public static void basicPrinting () { try (PrintWriter pw = new PrintWriter ("basic_print.txt" )) { pw.print("Hello" ); pw.print(" " ); pw.print("World" ); pw.println(); pw.println("This is a new line" ); pw.println("String: " + "Java" ); pw.println("Integer: " + 100 ); pw.println("Double: " + 3.14159 ); pw.println("Boolean: " + true ); pw.println("Character: " + 'A' ); pw.println("Object: " + new Date ()); System.out.println("基本打印完成" ); } catch (FileNotFoundException e) { e.printStackTrace(); } }
⭐⭐ 格式化输出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public static void formattedOutput () { try (PrintWriter pw = new PrintWriter ("formatted.txt" )) { pw.printf("Hello, %s!%n" , "World" ); pw.printf("整数: %d, 浮点数: %.2f%n" , 100 , 3.14159 ); pw.println("\n学生成绩表:" ); pw.printf("%-10s %-10s %-10s%n" , "姓名" , "年龄" , "成绩" ); pw.printf("%-10s %-10d %-10.2f%n" , "张三" , 20 , 85.5 ); pw.printf("%-10s %-10d %-10.2f%n" , "李四" , 21 , 92.0 ); pw.printf("%-10s %-10d %-10.2f%n" , "王五" , 19 , 78.5 ); Date now = new Date (); pw.printf("当前时间: %tF %<tT%n" , now); pw.printf("千分位: %,d%n" , 1234567 ); pw.printf("科学计数法: %e%n" , 12345.6789 ); System.out.println("格式化输出完成" ); } catch (FileNotFoundException e) { e.printStackTrace(); } }
⭐⭐ 日志记录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public static void loggingExample () { try (PrintWriter log = new PrintWriter (new BufferedWriter (new FileWriter ("app.log" )), true )) { String[] levels = {"INFO" , "WARNING" , "ERROR" , "DEBUG" }; String[] messages = { "Application started" , "Configuration loaded successfully" , "Database connection established" , "User logged in: admin" , "Warning: Low disk space" , "Error: File not found" , "Processing request #12345" , "Application shutdown" }; for (int i = 0 ; i < messages.length; i++) { String level = levels[i % levels.length]; String timestamp = String.format("%tF %<tT" , new Date ()); log.printf("[%s] [%s] %s%n" , timestamp, level, messages[i]); Thread.sleep(100 ); } System.out.println("日志已写入 " + logFile); } catch (IOException | InterruptedException e) { e.printStackTrace(); } System.out.println("\n日志内容预览:" ); try (BufferedReader br = new BufferedReader (new FileReader (logFile))) { String line; int count = 0 ; while ((line = br.readLine()) != null && count < 5 ) { System.out.println(line); count++; } System.out.println("..." ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 生成报表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public static void generateReport () { String[][] salesData = { {"产品" , "Q1" , "Q2" , "Q3" , "Q4" , "总计" }, {"产品A" , "1200" , "1350" , "1400" , "1600" , "5550" }, {"产品B" , "800" , "950" , "1100" , "1250" , "4100" }, {"产品C" , "500" , "600" , "700" , "800" , "2600" }, {"总计" , "2500" , "2900" , "3200" , "3650" , "12250" } }; try (PrintWriter pw = new PrintWriter (new FileWriter ("sales_report.txt" ))) { pw.println("=" .repeat(70 )); pw.printf("销售报表 - %tF%n" , new Date ()); pw.println("=" .repeat(70 )); pw.println(); for (int i = 0 ; i < salesData[0 ].length; i++) { pw.printf("%-12s" , salesData[0 ][i]); } pw.println(); pw.println("-" .repeat(70 )); for (int i = 1 ; i < salesData.length; i++) { for (int j = 0 ; j < salesData[i].length; j++) { pw.printf("%-12s" , salesData[i][j]); } pw.println(); } pw.println("-" .repeat(70 )); int total = Integer.parseInt(salesData[4 ][5 ]); double avg = total / 4.0 ; pw.printf("年度总销售额: %,d%n" , total); pw.printf("季度平均销售额: %,.2f%n" , avg); System.out.println("报表已生成: sales_report.txt" ); } catch (IOException e) { e.printStackTrace(); } }
⭐⭐ 错误处理 1 2 3 4 5 6 7 8 9 10 11 12 public static void errorHandling () { try (PrintWriter pw2 = new PrintWriter (new FileWriter ("valid.txt" ))) { pw2.println("正常写入" ); if (pw2.checkError()) { System.out.println("写入错误" ); } else { System.out.println("写入成功" ); } } catch (IOException e) { System.out.println("I/O错误: " + e.getMessage()); } }
压缩流 可以将文件压缩为ZIP、 GZIP、 JAR包。
ZipEntry 和 ZipFile ZipEntry 和 ZipFile 分别是用于创建 ZIP 条目, 和读取 ZIP 内容的类。 在 java.util.zip 包中。
⭐⭐ 属性方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ZipEntry entry = new ZipEntry ("docs/readme.txt" );String name = entry.getName(); entry.setName("new_name.txt" ); long compressedSize = entry.getCompressedSize();entry.setCompressedSize(1024L ); long size = entry.getSize();entry.setSize(2048L ); int method = entry.getMethod(); entry.setMethod(ZipEntry.DEFLATED); long time = entry.getTime();entry.setTime(System.currentTimeMillis()); byte [] extra = entry.getExtra();entry.setExtra(extraBytes); String comment = entry.getComment();entry.setComment("This is a compressed file" ); boolean isDir = entry.isDirectory();Object clone = entry.clone();
⭐⭐ CRC32 校验 1 2 3 4 5 6 7 ZipEntry entry = new ZipEntry ("data.txt" );entry.setMethod(ZipEntry.STORED); CRC32 crc = new CRC32 ();crc.update(dataBytes); entry.setCrc(crc.getValue());
⭐⭐ 压缩方法 1 2 3 4 5 6 7 8 9 10 11 12 public static final int STORED = 0 ;public static final int DEFLATED = 8 ;ZipEntry entry = new ZipEntry ("small.txt" );entry.setMethod(ZipEntry.STORED); ZipEntry entry2 = new ZipEntry ("large.txt" );entry2.setMethod(ZipEntry.DEFLATED);
⭐⭐ 获取条目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 try (ZipFile zipFile = new ZipFile ("archive.zip" )) { ZipEntry entry = zipFile.getEntry("docs/readme.txt" ); Enumeration<? extends ZipEntry > entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry e = entries.nextElement(); System.out.println(e.getName()); } int size = zipFile.size(); System.out.println("ZIP文件包含 " + size + " 个条目" ); }
⭐⭐ 读取内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 try (ZipFile zipFile = new ZipFile ("archive.zip" )) { ZipEntry entry = zipFile.getEntry("data.txt" ); if (entry != null && !entry.isDirectory()) { try (InputStream is = zipFile.getInputStream(entry)) { byte [] buffer = new byte [1024 ]; int len; ByteArrayOutputStream baos = new ByteArrayOutputStream (); while ((len = is.read(buffer)) != -1 ) { baos.write(buffer, 0 , len); } String content = baos.toString("UTF-8" ); System.out.println(content); } } }
⭐⭐ 其他方法 1 2 3 4 5 6 7 8 9 10 11 try (ZipFile zipFile = new ZipFile ("archive.zip" )) { String comment = zipFile.getComment(); System.out.println("ZIP注释: " + comment); String name = zipFile.getName(); }
⭐⭐ 从 ZIP 中提取所有文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 public static void extractAll (String zipPath, String outputDir) throws IOException { try (ZipFile zipFile = new ZipFile (zipPath, Charset.forName("UTF-8" ))) { Enumeration<? extends ZipEntry > entries = zipFile.entries(); int extracted = 0 ; while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); String outputPath = outputDir + File.separator + entry.getName(); if (entry.isDirectory()) { Files.createDirectories(Paths.get(outputPath)); } else { Path outputFile = Paths.get(outputPath); Files.createDirectories(outputFile.getParent()); try (InputStream is = zipFile.getInputStream(entry); OutputStream os = new FileOutputStream (outputFile.toFile())) { byte [] buffer = new byte [8192 ]; int len; while ((len = is.read(buffer)) != -1 ) { os.write(buffer, 0 , len); } extracted++; if (extracted % 100 == 0 ) { System.out.printf("已提取 %d 个文件...%n" , extracted); } } } } System.out.printf("提取完成! 共提取 %d 个文件到: %s%n" , extracted, outputDir); } }
JarEntry 和 JarFile JarEntry 和 JarFile 类, 它们是专门用于处理 JAR( Java Archive) 文件的类。
JAR 文件本质上是带有特殊清单文件( MANIFEST.MF) 的 ZIP 文件。 JarFile 和 JarEntry 继承自 ZipFile 和 ZipEntry, 并增加了对 JAR 特有功能( 如清单、 签名等) 的支持。
⭐⭐ 读取条目 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 import java.io.*;import java.util.*;import java.util.jar.*;import java.util.zip.*;public class JarFileBasics { public static void openJarFile (String jarPath) throws IOException { try (JarFile jarFile = new JarFile (jarPath)) { System.out.println("JAR 文件: " + jarFile.getName()); System.out.println("条目总数: " + jarFile.size()); Manifest manifest = jarFile.getManifest(); if (manifest != null ) { System.out.println("包含清单文件" ); printManifest(manifest); } } } public static void listJarContents (String jarPath) throws IOException { try (JarFile jarFile = new JarFile (jarPath)) { System.out.println("=== JAR 文件内容 ===" ); System.out.println("文件: " + jarFile.getName()); System.out.println("-" .repeat(60 )); Enumeration<JarEntry> entries = jarFile.entries(); int fileCount = 0 ; int dirCount = 0 ; while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (entry.isDirectory()) { dirCount++; System.out.printf("[目录] %s%n" , entry.getName()); } else { fileCount++; System.out.printf("[文件] %s (%,d bytes)%n" , entry.getName(), entry.getSize()); } } System.out.println("-" .repeat(60 )); System.out.printf("总计: %d 个文件, %d 个目录%n" , fileCount, dirCount); } } public static void readJarEntry (String jarPath, String entryName) throws IOException { try (JarFile jarFile = new JarFile (jarPath)) { JarEntry entry = jarFile.getJarEntry(entryName); if (entry == null ) { System.out.println("未找到条目: " + entryName); return ; } System.out.println("读取条目: " + entryName); System.out.println("大小: " + entry.getSize() + " bytes" ); try (InputStream is = jarFile.getInputStream(entry); BufferedReader reader = new BufferedReader ( new InputStreamReader (is))) { String line; int lineNum = 0 ; while ((line = reader.readLine()) != null && lineNum < 20 ) { System.out.printf("%4d: %s%n" , ++lineNum, line); } if (reader.readLine() != null ) { System.out.println("... (内容已截断)" ); } } } } private static void printManifest (Manifest manifest) { Attributes attributes = manifest.getMainAttributes(); System.out.println("\n=== 清单内容 ===" ); for (Object key : attributes.keySet()) { System.out.printf("%s: %s%n" , key, attributes.get(key)); } Map<String, Attributes> entries = manifest.getEntries(); if (!entries.isEmpty()) { System.out.println("\n=== 条目属性 ===" ); for (Map.Entry<String, Attributes> entry : entries.entrySet()) { System.out.println("条目: " + entry.getKey()); for (Object key : entry.getValue().keySet()) { System.out.printf(" %s: %s%n" , key, entry.getValue().get(key)); } } } } public static void main (String[] args) { try { String jarPath = "example.jar" ; openJarFile(jarPath); listJarContents(jarPath); readJarEntry(jarPath, "META-INF/MANIFEST.MF" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 创建 JAR 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 import java.io.*;import java.util.jar.*;import java.util.*;public class JarFileCreator { public static void createSimpleJar (String jarPath, String[] files) throws IOException { try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath))) { Manifest manifest = new Manifest (); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0" ); attributes.put(Attributes.Name.CREATED_BY, "Java Application" ); JarEntry manifestEntry = new JarEntry ("META-INF/MANIFEST.MF" ); jos.putNextEntry(manifestEntry); manifest.write(jos); jos.closeEntry(); for (String filePath : files) { File file = new File (filePath); if (!file.exists()) continue ; JarEntry entry = new JarEntry (file.getName()); entry.setTime(file.lastModified()); jos.putNextEntry(entry); try (FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); System.out.println("添加文件: " + file.getName()); } System.out.println("JAR 创建成功: " + jarPath); } } public static void createExecutableJar (String jarPath, String mainClass, String[] classFiles) throws IOException { try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath))) { Manifest manifest = new Manifest (); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0" ); attributes.put(Attributes.Name.MAIN_CLASS, mainClass); attributes.put(Attributes.Name.CLASS_PATH, "." ); JarEntry manifestEntry = new JarEntry ("META-INF/MANIFEST.MF" ); jos.putNextEntry(manifestEntry); manifest.write(jos); jos.closeEntry(); for (String classFile : classFiles) { File file = new File (classFile); String entryName = classFile.replace(File.separator, "/" ); JarEntry entry = new JarEntry (entryName); entry.setTime(file.lastModified()); jos.putNextEntry(entry); try (FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); System.out.println("添加类: " + entryName); } System.out.println("可执行 JAR 创建成功: " + jarPath); System.out.println("运行命令: java -jar " + jarPath); } } public static void createJarWithResources (String jarPath, Map<String, byte []> resources) throws IOException { try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath))) { Manifest manifest = new Manifest (); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0" ); jos.putNextEntry(new JarEntry ("META-INF/MANIFEST.MF" )); manifest.write(jos); jos.closeEntry(); for (Map.Entry<String, byte []> resource : resources.entrySet()) { JarEntry entry = new JarEntry (resource.getKey()); jos.putNextEntry(entry); jos.write(resource.getValue()); jos.closeEntry(); System.out.println("添加资源: " + resource.getKey() + " (" + resource.getValue().length + " bytes)" ); } } } public static void createJarFromDirectory (String sourceDir, String jarPath) throws IOException { try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath))) { Path sourcePath = Paths.get(sourceDir); Files.walk(sourcePath) .filter(path -> !Files.isDirectory(path)) .forEach(path -> { try { String entryName = sourcePath.relativize(path).toString() .replace("\\" , "/" ); JarEntry entry = new JarEntry (entryName); entry.setTime(Files.getLastModifiedTime(path).toMillis()); jos.putNextEntry(entry); Files.copy(path, jos); jos.closeEntry(); System.out.println("添加: " + entryName); } catch (IOException e) { e.printStackTrace(); } }); System.out.println("JAR 创建完成: " + jarPath); } } public static void main (String[] args) { try { String[] files = {"README.txt" , "config.properties" }; createSimpleJar("simple.jar" , files); String[] classes = {"com/example/Main.class" , "com/example/Utils.class" }; createExecutableJar("app.jar" , "com.example.Main" , classes); createJarFromDirectory("build/classes" , "output.jar" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 修改和更新 JAR 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 import java.io.*;import java.util.*;import java.util.jar.*;import java.util.zip.*;public class JarFileModifier { public static void updateManifest (String jarPath, Map<String, String> attributes) throws IOException { File tempFile = File.createTempFile("jar" , ".tmp" ); try (JarFile jarFile = new JarFile (jarPath); JarOutputStream jos = new JarOutputStream ( new FileOutputStream (tempFile))) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (entry.getName().equals("META-INF/MANIFEST.MF" )) { continue ; } jos.putNextEntry(new JarEntry (entry.getName())); try (InputStream is = jarFile.getInputStream(entry)) { byte [] buffer = new byte [8192 ]; int len; while ((len = is.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); } Manifest manifest = jarFile.getManifest(); if (manifest == null ) { manifest = new Manifest (); } Attributes mainAttributes = manifest.getMainAttributes(); for (Map.Entry<String, String> attr : attributes.entrySet()) { mainAttributes.put(new Attributes .Name(attr.getKey()), attr.getValue()); } JarEntry manifestEntry = new JarEntry ("META-INF/MANIFEST.MF" ); jos.putNextEntry(manifestEntry); manifest.write(jos); jos.closeEntry(); } Files.move(tempFile.toPath(), Paths.get(jarPath), StandardCopyOption.REPLACE_EXISTING); System.out.println("清单更新成功" ); } public static void addFilesToJar (String jarPath, Map<String, byte []> newFiles) throws IOException { File tempFile = File.createTempFile("jar" , ".tmp" ); try (JarFile jarFile = new JarFile (jarPath); JarOutputStream jos = new JarOutputStream ( new FileOutputStream (tempFile))) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); jos.putNextEntry(new JarEntry (entry.getName())); try (InputStream is = jarFile.getInputStream(entry)) { byte [] buffer = new byte [8192 ]; int len; while ((len = is.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); } for (Map.Entry<String, byte []> newFile : newFiles.entrySet()) { JarEntry entry = new JarEntry (newFile.getKey()); jos.putNextEntry(entry); jos.write(newFile.getValue()); jos.closeEntry(); System.out.println("添加: " + newFile.getKey()); } } Files.move(tempFile.toPath(), Paths.get(jarPath), StandardCopyOption.REPLACE_EXISTING); } public static void removeFilesFromJar (String jarPath, List<String> filesToRemove) throws IOException { File tempFile = File.createTempFile("jar" , ".tmp" ); try (JarFile jarFile = new JarFile (jarPath); JarOutputStream jos = new JarOutputStream ( new FileOutputStream (tempFile))) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String entryName = entry.getName(); if (filesToRemove.contains(entryName)) { System.out.println("删除: " + entryName); continue ; } jos.putNextEntry(new JarEntry (entryName)); try (InputStream is = jarFile.getInputStream(entry)) { byte [] buffer = new byte [8192 ]; int len; while ((len = is.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); } } Files.move(tempFile.toPath(), Paths.get(jarPath), StandardCopyOption.REPLACE_EXISTING); System.out.println("文件删除完成" ); } public static void main (String[] args) { try { String jarPath = "app.jar" ; Map<String, String> attrs = new HashMap <>(); attrs.put("Implementation-Version" , "2.0" ); attrs.put("Built-By" , "Developer" ); updateManifest(jarPath, attrs); Map<String, byte []> newFiles = new HashMap <>(); newFiles.put("config/new.properties" , "version=2.0\ncreated=2024-01-01" .getBytes()); addFilesToJar(jarPath, newFiles); List<String> toRemove = Arrays.asList("old-config.properties" ); removeFilesFromJar(jarPath, toRemove); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 高级 JAR 文件操作 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 import java.io.*;import java.util.*;import java.util.jar.*;import java.util.zip.*;public class AdvancedJarFileOperations { public static void readJarSignatures (String jarPath) throws IOException { try (JarFile jarFile = new JarFile (jarPath, true )) { Enumeration<JarEntry> entries = jarFile.entries(); System.out.println("=== 签名验证 ===" ); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); if (name.startsWith("META-INF/" ) && (name.endsWith(".SF" ) || name.endsWith(".DSA" ) || name.endsWith(".RSA" ) || name.endsWith(".EC" ))) { System.out.println("签名文件: " + name); try (InputStream is = jarFile.getInputStream(entry); BufferedReader reader = new BufferedReader ( new InputStreamReader (is))) { String line; while ((line = reader.readLine()) != null ) { if (line.contains("SHA-256-Digest" ) || line.contains("SHA1-Digest" )) { System.out.println(" " + line); } } } } } } } public static void verifyJarIntegrity (String jarPath) throws IOException { try (JarFile jarFile = new JarFile (jarPath, true )) { System.out.println("JAR 文件签名验证: " + (jarFile.isSigned() ? "已签名" : "未签名" )); Enumeration<JarEntry> entries = jarFile.entries(); boolean allValid = true ; while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (!entry.isDirectory() && !entry.getName().startsWith("META-INF/" )) { try (InputStream is = jarFile.getInputStream(entry)) { byte [] buffer = new byte [8192 ]; while (is.read(buffer) != -1 ) { } System.out.printf("✓ 验证通过: %s%n" , entry.getName()); } catch (SecurityException e) { System.err.printf("✗ 签名验证失败: %s - %s%n" , entry.getName(), e.getMessage()); allValid = false ; } } } if (allValid) { System.out.println("所有条目验证通过" ); } else { System.err.println("JAR 文件包含未通过验证的条目" ); } } } public static void getClassPathInfo (String jarPath) throws IOException { try (JarFile jarFile = new JarFile (jarPath)) { Manifest manifest = jarFile.getManifest(); if (manifest != null ) { Attributes attributes = manifest.getMainAttributes(); String classPath = attributes.getValue("Class-Path" ); if (classPath != null ) { System.out.println("Class-Path: " + classPath); String[] paths = classPath.split(" " ); for (String path : paths) { System.out.println(" " + path.trim()); } } String mainClass = attributes.getValue("Main-Class" ); if (mainClass != null ) { System.out.println("Main-Class: " + mainClass); } String createdBy = attributes.getValue("Created-By" ); if (createdBy != null ) { System.out.println("Created-By: " + createdBy); } String implementationVersion = attributes.getValue("Implementation-Version" ); if (implementationVersion != null ) { System.out.println("Version: " + implementationVersion); } } } } public static List<String> searchClasses (String jarPath, String className) throws IOException { List<String> results = new ArrayList <>(); String searchPattern = className.replace('.' , '/' ); try (JarFile jarFile = new JarFile (jarPath)) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); if (name.endsWith(".class" ) && name.contains(searchPattern)) { String classFullName = name.replace('/' , '.' ) .replace(".class" , "" ); results.add(classFullName); } } } return results; } public static void main (String[] args) { try { String jarPath = "app.jar" ; getClassPathInfo(jarPath); verifyJarIntegrity(jarPath); readJarSignatures(jarPath); List<String> classes = searchClasses(jarPath, "com.example" ); System.out.println("\n找到的类:" ); for (String clazz : classes) { System.out.println(" " + clazz); } } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ JAR 文件分析和诊断 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 import java.io.*;import java.util.*;import java.util.jar.*;import java.util.zip.*;public class JarFileAnalyzer { public static void analyzeJarStructure (String jarPath) throws IOException { System.out.println("=== JAR 文件分析报告 ===" ); System.out.println("文件: " + jarPath); try (JarFile jarFile = new JarFile (jarPath)) { System.out.println("\n--- 基本信息 ---" ); System.out.println("大小: " + new File (jarPath).length() + " bytes" ); System.out.println("条目总数: " + jarFile.size()); System.out.println("是否签名: " + (jarFile.isSigned() ? "是" : "否" )); Manifest manifest = jarFile.getManifest(); if (manifest != null ) { System.out.println("\n--- 清单分析 ---" ); Attributes attrs = manifest.getMainAttributes(); for (Object key : attrs.keySet()) { System.out.printf("%-20s: %s%n" , key, attrs.get(key)); } } System.out.println("\n--- 内容统计 ---" ); int classCount = 0 ; int resourceCount = 0 ; int metaInfCount = 0 ; long totalSize = 0 ; Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); if (name.endsWith(".class" )) { classCount++; } else if (name.startsWith("META-INF/" )) { metaInfCount++; } else { resourceCount++; } totalSize += entry.getSize(); } System.out.println("类文件: " + classCount); System.out.println("资源文件: " + resourceCount); System.out.println("META-INF 文件: " + metaInfCount); System.out.println("总大小: " + totalSize + " bytes" ); System.out.println("\n--- 包结构 ---" ); Set<String> packages = new TreeSet <>(); entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); if (name.endsWith(".class" )) { String packageName = name.substring(0 , name.lastIndexOf('/' )); packages.add(packageName.replace('/' , '.' )); } } for (String pkg : packages) { System.out.println(" " + pkg); } } } public static void findDuplicateClasses (String jarPath) throws IOException { System.out.println("\n=== 重复类检测 ===" ); Map<String, List<JarEntry>> classMap = new HashMap <>(); try (JarFile jarFile = new JarFile (jarPath)) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String name = entry.getName(); if (name.endsWith(".class" )) { classMap.computeIfAbsent(name, k -> new ArrayList <>()).add(entry); } } } boolean hasDuplicates = false ; for (Map.Entry<String, List<JarEntry>> entry : classMap.entrySet()) { if (entry.getValue().size() > 1 ) { System.out.println("重复类: " + entry.getKey()); hasDuplicates = true ; } } if (!hasDuplicates) { System.out.println("未发现重复类" ); } } public static void checkCompatibility (String jarPath) throws IOException { System.out.println("\n=== 兼容性检查 ===" ); try (JarFile jarFile = new JarFile (jarPath)) { Manifest manifest = jarFile.getManifest(); if (manifest != null ) { Attributes attrs = manifest.getMainAttributes(); String createdBy = attrs.getValue("Created-By" ); if (createdBy != null && createdBy.contains("javac" )) { System.out.println("编译版本: " + createdBy); } String classPath = attrs.getValue("Class-Path" ); if (classPath != null ) { System.out.println("依赖项:" ); for (String dep : classPath.split(" " )) { System.out.println(" " + dep.trim()); } } } Enumeration<JarEntry> entries = jarFile.entries(); Set<Integer> versions = new HashSet <>(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (entry.getName().endsWith(".class" )) { try (InputStream is = jarFile.getInputStream(entry)) { byte [] magic = new byte [8 ]; if (is.read(magic) == 8 ) { int majorVersion = ((magic[6 ] & 0xFF ) << 8 ) | (magic[7 ] & 0xFF ); versions.add(majorVersion); } } } } if (!versions.isEmpty()) { System.out.println("Class 文件版本: " + versions); for (int version : versions) { System.out.println(" 需要 Java " + getJavaVersion(version)); } } } } private static String getJavaVersion (int majorVersion) { switch (majorVersion) { case 52 : return "8" ; case 53 : return "9" ; case 54 : return "10" ; case 55 : return "11" ; case 56 : return "12" ; case 57 : return "13" ; case 58 : return "14" ; case 59 : return "15" ; case 60 : return "16" ; case 61 : return "17" ; case 62 : return "18" ; case 63 : return "19" ; case 64 : return "20" ; default : return "Unknown (" + majorVersion + ")" ; } } public static void main (String[] args) { try { analyzeJarStructure("app.jar" ); findDuplicateClasses("app.jar" ); checkCompatibility("app.jar" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 高效读取 JAR 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public class JarFileOptimization { public static class JarFileCache { private static final Map<String, JarFile> cache = new HashMap <>(); public static synchronized JarFile getJarFile (String path) throws IOException { if (!cache.containsKey(path)) { cache.put(path, new JarFile (path)); } return cache.get(path); } public static synchronized void closeAll () throws IOException { for (JarFile jarFile : cache.values()) { jarFile.close(); } cache.clear(); } } public static Map<String, byte []> batchReadEntries(String jarPath, List<String> entryNames) throws IOException { Map<String, byte []> result = new HashMap <>(); try (JarFile jarFile = new JarFile (jarPath)) { for (String entryName : entryNames) { JarEntry entry = jarFile.getJarEntry(entryName); if (entry != null && !entry.isDirectory()) { try (InputStream is = jarFile.getInputStream(entry); ByteArrayOutputStream baos = new ByteArrayOutputStream ()) { byte [] buffer = new byte [8192 ]; int len; while ((len = is.read(buffer)) != -1 ) { baos.write(buffer, 0 , len); } result.put(entryName, baos.toByteArray()); } } } } return result; } public static void readLargeJarWithMapping (String jarPath) throws IOException { try (RandomAccessFile raf = new RandomAccessFile (jarPath, "r" ); FileChannel channel = raf.getChannel()) { MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_ONLY, 0 , channel.size()); System.out.println("JAR 文件大小: " + channel.size() + " bytes" ); } } }
ZIP 压缩与解压 ZipInputStream 和 ZipOutputStream 类, 它们是专门用于处理 ZIP 格式的压缩流。 ZipInputStream 是用于读取 ZIP 文件内容的输入流, 采用顺序读取的方式。 ZipOutputStream 是用于创建 ZIP 文件的输出流, 支持压缩和存储。
⭐⭐ 读取 ZIP 文件并提取所有内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public static void extractZip (String zipPath, String outputDir) throws IOException { try (ZipInputStream zis = new ZipInputStream ( new FileInputStream (zipPath), StandardCharsets.UTF_8)) { ZipEntry entry; byte [] buffer = new byte [8192 ]; int count = 0 ; while ((entry = zis.getNextEntry()) != null ) { String entryName = entry.getName(); Path outputPath = Paths.get(outputDir, entryName); if (entry.isDirectory()) { Files.createDirectories(outputPath); System.out.println("创建目录: " + entryName); } else { Files.createDirectories(outputPath.getParent()); try (FileOutputStream fos = new FileOutputStream (outputPath.toFile())) { int len; while ((len = zis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); } } System.out.printf("提取文件: %s (%d bytes)%n" , entryName, entry.getSize()); count++; } zis.closeEntry(); } System.out.println("提取完成, 共 " + count + " 个文件" ); } }
⭐⭐ 读取 ZIP 中指定文件的内容 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static byte [] readFileFromZip(String zipPath, String targetFileName) throws IOException { try (ZipInputStream zis = new ZipInputStream (new FileInputStream (zipPath))) { ZipEntry entry; while ((entry = zis.getNextEntry()) != null ) { if (!entry.isDirectory() && entry.getName().equals(targetFileName)) { ByteArrayOutputStream baos = new ByteArrayOutputStream (); byte [] buffer = new byte [8192 ]; int len; while ((len = zis.read(buffer)) != -1 ) { baos.write(buffer, 0 , len); } zis.closeEntry(); return baos.toByteArray(); } zis.closeEntry(); } } return null ; }
⭐⭐ 逐行读取 ZIP 中的文本文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void readTextFileLineByLine (String zipPath, String entryName) throws IOException { try (ZipInputStream zis = new ZipInputStream ( new FileInputStream (zipPath))) { ZipEntry entry; while ((entry = zis.getNextEntry()) != null ) { if (entry.getName().equals(entryName) && !entry.isDirectory()) { try (BufferedReader reader = new BufferedReader ( new InputStreamReader (zis, StandardCharsets.UTF_8))) { String line; int lineNum = 0 ; while ((line = reader.readLine()) != null ) { System.out.printf("%d: %s%n" , ++lineNum, line); } } zis.closeEntry(); break ; } zis.closeEntry(); } } }
⭐⭐ 处理大文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 import java.io.*;import java.util.zip.*;public class LargeZipProcessor { public static void processLargeZip (String zipPath, EntryProcessor processor) throws IOException { try (ZipInputStream zis = new ZipInputStream (new BufferedInputStream (new FileInputStream (zipPath)))) { ZipEntry entry; byte [] buffer = new byte [8192 ]; while ((entry = zis.getNextEntry()) != null ) { if (!entry.isDirectory()) { processor.process(entry, zis); } zis.closeEntry(); } } } @FunctionalInterface public interface EntryProcessor { void process (ZipEntry entry, InputStream is) throws IOException; } public static long calculateTotalSize (String zipPath) throws IOException { long totalSize = 0 ; try (ZipInputStream zis = new ZipInputStream (new FileInputStream (zipPath))) { ZipEntry entry; while ((entry = zis.getNextEntry()) != null ) { if (!entry.isDirectory()) { totalSize += entry.getSize(); } zis.closeEntry(); } } return totalSize; } public static void searchContent (String zipPath, String searchText) throws IOException { try (ZipInputStream zis = new ZipInputStream (new FileInputStream (zipPath))) { ZipEntry entry; byte [] buffer = new byte [8192 ]; while ((entry = zis.getNextEntry()) != null ) { if (!entry.isDirectory()) { ByteArrayOutputStream baos = new ByteArrayOutputStream (); int len; while ((len = zis.read(buffer)) != -1 ) { baos.write(buffer, 0 , len); } String content = baos.toString("UTF-8" ); if (content.contains(searchText)) { System.out.println("找到匹配文件: " + entry.getName()); System.out.println("匹配文本位置: " + content.indexOf(searchText)); } } zis.closeEntry(); } } } public static void main (String[] args) { try { long totalSize = calculateTotalSize("large.zip" ); System.out.printf("总大小: %.2f MB%n" , totalSize / (1024.0 * 1024 )); searchContent("archive.zip" , "important" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 压缩单个文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public static void compressFile (String filePath, String zipPath) throws IOException { try (ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream (zipPath)); FileInputStream fis = new FileInputStream (filePath)) { File file = new File (filePath); ZipEntry entry = new ZipEntry (file.getName()); entry.setTime(file.lastModified()); entry.setSize(file.length()); zos.putNextEntry(entry); byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { zos.write(buffer, 0 , len); } zos.closeEntry(); System.out.println("压缩完成: " + filePath); } }
⭐⭐ 压缩目录 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public static void compressDirectory (String dirPath, String zipPath) throws IOException { try (ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream (zipPath))) { Path sourceDir = Paths.get(dirPath); Files.walk(sourceDir) .filter(path -> !Files.isDirectory(path)) .forEach(path -> { try { String entryName = sourceDir.relativize(path).toString() .replace("\\" , "/" ); ZipEntry entry = new ZipEntry (entryName); entry.setTime(Files.getLastModifiedTime(path).toMillis()); zos.putNextEntry(entry); Files.copy(path, zos); zos.closeEntry(); System.out.println("添加文件: " + entryName); } catch (IOException e) { e.printStackTrace(); } }); System.out.println("目录压缩完成: " + zipPath); } }
⭐⭐ 压缩多个文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 public static void compressFiles (List<String> filePaths, String zipPath) throws IOException { try (ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream (zipPath))) { for (String filePath : filePaths) { File file = new File (filePath); if (!file.exists()) { System.err.println("文件不存在: " + filePath); continue ; } try (FileInputStream fis = new FileInputStream (file)) { ZipEntry entry = new ZipEntry (file.getName()); entry.setTime(file.lastModified()); entry.setSize(file.length()); if (file.length() < 1024 ) { entry.setMethod(ZipEntry.STORED); entry.setCompressedSize(file.length()); CRC32 crc = new CRC32 (); byte [] buffer = new byte [8192 ]; int len; try (FileInputStream fis2 = new FileInputStream (file)) { while ((len = fis2.read(buffer)) != -1 ) { crc.update(buffer, 0 , len); } } entry.setCrc(crc.getValue()); } else { entry.setMethod(ZipEntry.DEFLATED); } zos.putNextEntry(entry); byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { zos.write(buffer, 0 , len); } zos.closeEntry(); System.out.println("添加文件: " + file.getName() + " (" + file.length() + " bytes)" ); } } } }
⭐⭐ 带过滤器的压缩 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public static void compressWithFilter (String dirPath, String zipPath, FileFilter filter) throws IOException { try (ZipOutputStream zos = new ZipOutputStream (new FileOutputStream (zipPath))) { zos.setLevel(Deflater.BEST_COMPRESSION); Path sourceDir = Paths.get(dirPath); Files.walk(sourceDir) .filter(path -> !Files.isDirectory(path)) .filter(path -> filter.accept(path.toFile())) .forEach(path -> { try { String entryName = sourceDir.relativize(path).toString() .replace("\\" , "/" ); ZipEntry entry = new ZipEntry (entryName); entry.setTime(Files.getLastModifiedTime(path).toMillis()); if (entryName.endsWith(".jpg" ) || entryName.endsWith(".png" )) { entry.setMethod(ZipEntry.STORED); entry.setSize(Files.size(path)); entry.setCompressedSize(Files.size(path)); CRC32 crc = new CRC32 (); Files.copy(path, crc); entry.setCrc(crc.getValue()); } zos.putNextEntry(entry); Files.copy(path, zos); zos.closeEntry(); } catch (IOException e) { e.printStackTrace(); } }); } }
⭐⭐ 设置压缩参数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static void configureCompression (String zipPath) throws IOException { try (ZipOutputStream zos = new ZipOutputStream (new FileOutputStream (zipPath))) { zos.setLevel(Deflater.BEST_COMPRESSION); Deflater deflater = new Deflater (Deflater.BEST_COMPRESSION); deflater.setStrategy(Deflater.DEFAULT_STRATEGY); zos.setComment("Created at: " + new Date ()); zos.setMethod(ZipOutputStream.DEFLATED); } }
⭐⭐ 使用缓冲流提高性能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public static void optimizedCompression (String sourcePath, String zipPath) throws IOException { try (ZipOutputStream zos = new ZipOutputStream ( new BufferedOutputStream (new FileOutputStream (zipPath), 65536 )); BufferedInputStream bis = new BufferedInputStream ( new FileInputStream (sourcePath), 65536 )) { zos.setLevel(Deflater.BEST_SPEED); zos.setMethod(ZipOutputStream.DEFLATED); ZipEntry entry = new ZipEntry (new File (sourcePath).getName()); zos.putNextEntry(entry); byte [] buffer = new byte [65536 ]; int len; while ((len = bis.read(buffer)) != -1 ) { zos.write(buffer, 0 , len); } zos.closeEntry(); } }
⭐⭐ 并行压缩多个文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public static void parallelCompress (List<String> files, String zipPath) throws IOException, InterruptedException { List<Path> tempFiles = new ArrayList <>(); try { files.parallelStream().forEach(file -> { try { Path tempFile = Files.createTempFile("zip_temp_" , ".tmp" ); try (ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream (tempFile.toFile())); FileInputStream fis = new FileInputStream (file)) { ZipEntry entry = new ZipEntry (new File (file).getName()); zos.putNextEntry(entry); byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { zos.write(buffer, 0 , len); } zos.closeEntry(); } synchronized (tempFiles) { tempFiles.add(tempFile); } } catch (IOException e) { e.printStackTrace(); } }); try (ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream (zipPath))) { for (Path tempFile : tempFiles) { try (FileInputStream fis = new FileInputStream (tempFile.toFile())) { } } } } finally { for (Path tempFile : tempFiles) { Files.deleteIfExists(tempFile); } } }
⭐⭐ 使用内存映射文件处理大文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public static void compressLargeFile (String filePath, String zipPath) throws IOException { Path path = Paths.get(filePath); long fileSize = Files.size(path); try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ); ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream (zipPath))) { ZipEntry entry = new ZipEntry (path.getFileName().toString()); entry.setSize(fileSize); zos.putNextEntry(entry); MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_ONLY, 0 , fileSize); byte [] bytes = new byte [8192 ]; while (buffer.hasRemaining()) { int remaining = buffer.remaining(); int readSize = Math.min(bytes.length, remaining); buffer.get(bytes, 0 , readSize); zos.write(bytes, 0 , readSize); } zos.closeEntry(); } }
⭐⭐ 大文件处理避免内存溢出 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public void processLargeZip (String zipPath) throws IOException { try (ZipInputStream zis = new ZipInputStream (new FileInputStream (zipPath))) { ZipEntry entry; byte [] buffer = new byte [8192 ]; while ((entry = zis.getNextEntry()) != null ) { if (!entry.isDirectory()) { try (OutputStream os = new FileOutputStream (entry.getName())) { int len; while ((len = zis.read(buffer)) != -1 ) { os.write(buffer, 0 , len); } } } zis.closeEntry(); } } }
⭐⭐ 处理中文文件名 1 2 3 4 5 6 7 8 9 10 ZipInputStream zis = new ZipInputStream ( new FileInputStream ("archive.zip" ), Charset.forName("GBK" ) ); ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream ("output.zip" ), StandardCharsets.UTF_8 );
GZIP 压缩与解压 GZIPInputStream 和 GZIPOutputStream 类, 它们是专门用于处理 GZIP 格式的压缩流。 GZIP 是 GNU ZIP 的缩写, 是一种单一文件的压缩格式, 与 ZIP 不同:
GZIP 只能压缩单个文件, 不能包含多个文件
通常与 tar 配合使用( tar.gz 或 .tgz)
压缩率通常比 ZIP 更高
广泛用于 Linux/Unix 环境
⭐⭐ 压缩数据 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 import java.io.*;import java.util.zip.*;public class GZIPOutputStreamBasic { public static void compressFile (String sourceFile, String gzipFile) throws IOException { try (FileInputStream fis = new FileInputStream (sourceFile); GZIPOutputStream gzos = new GZIPOutputStream ( new FileOutputStream (gzipFile))) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { gzos.write(buffer, 0 , len); } System.out.println("压缩完成: " + sourceFile + " -> " + gzipFile); } } public static byte [] compressString(String text) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream (); GZIPOutputStream gzos = new GZIPOutputStream (baos)) { gzos.write(text.getBytes(StandardCharsets.UTF_8)); gzos.finish(); return baos.toByteArray(); } } public static void main (String[] args) { try { compressFile("data.txt" , "data.txt.gz" ); String text = "Hello, GZIP Compression!" ; byte [] compressed = compressString(text); System.out.println("原始大小: " + text.length() + " bytes" ); System.out.println("压缩后大小: " + compressed.length + " bytes" ); System.out.println("压缩率: " + (1 - (double )compressed.length / text.length()) * 100 + "%" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 高级配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 import java.io.*;import java.util.zip.*;public class AdvancedGZIPOutputStream { public static void configureGZIP (String sourceFile, String gzipFile) throws IOException { try (FileInputStream fis = new FileInputStream (sourceFile); GZIPOutputStream gzos = new GZIPOutputStream ( new BufferedOutputStream (new FileOutputStream (gzipFile))) { { def.setLevel(Deflater.BEST_COMPRESSION); def.setStrategy(Deflater.DEFAULT_STRATEGY); } }) { byte [] buffer = new byte [65536 ]; int len; while ((len = fis.read(buffer)) != -1 ) { gzos.write(buffer, 0 , len); } System.out.println("使用最高压缩比完成压缩" ); } } public static void setGZIPMetadata (String sourceFile, String gzipFile) throws IOException { try (FileInputStream fis = new FileInputStream (sourceFile); GZIPOutputStream gzos = new GZIPOutputStream ( new FileOutputStream (gzipFile)) { { this .name = sourceFile; this .modTime = System.currentTimeMillis() / 1000 ; } }) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { gzos.write(buffer, 0 , len); } } } public static void compressWithLevel (String sourceFile, String gzipFile, int level) throws IOException { try (FileInputStream fis = new FileInputStream (sourceFile); GZIPOutputStream gzos = new GZIPOutputStream ( new FileOutputStream (gzipFile)) { { def.setLevel(level); } }) { byte [] buffer = new byte [8192 ]; int len; long startTime = System.currentTimeMillis(); while ((len = fis.read(buffer)) != -1 ) { gzos.write(buffer, 0 , len); } long endTime = System.currentTimeMillis(); File compressedFile = new File (gzipFile); System.out.println("压缩级别: " + level); System.out.println("压缩后大小: " + compressedFile.length() + " bytes" ); System.out.println("压缩时间: " + (endTime - startTime) + " ms" ); } } public static void main (String[] args) { try { String testFile = "large_file.txt" ; System.out.println("=== 压缩级别对比 ===" ); compressWithLevel(testFile, "level_0.gz" , Deflater.NO_COMPRESSION); compressWithLevel(testFile, "level_1.gz" , Deflater.BEST_SPEED); compressWithLevel(testFile, "level_5.gz" , 5 ); compressWithLevel(testFile, "level_9.gz" , Deflater.BEST_COMPRESSION); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 解压文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 import java.io.*;import java.util.zip.*;public class GZIPInputStreamBasic { public static void decompressFile (String gzipFile, String outputFile) throws IOException { try (GZIPInputStream gzis = new GZIPInputStream ( new FileInputStream (gzipFile)); FileOutputStream fos = new FileOutputStream (outputFile)) { byte [] buffer = new byte [8192 ]; int len; while ((len = gzis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); } System.out.println("解压完成: " + gzipFile + " -> " + outputFile); } } public static String decompressString (byte [] compressed) throws IOException { try (ByteArrayInputStream bais = new ByteArrayInputStream (compressed); GZIPInputStream gzis = new GZIPInputStream (bais); ByteArrayOutputStream baos = new ByteArrayOutputStream ()) { byte [] buffer = new byte [8192 ]; int len; while ((len = gzis.read(buffer)) != -1 ) { baos.write(buffer, 0 , len); } return baos.toString(StandardCharsets.UTF_8); } } public static void readGZIPLineByLine (String gzipFile) throws IOException { try (GZIPInputStream gzis = new GZIPInputStream ( new FileInputStream (gzipFile)); BufferedReader reader = new BufferedReader ( new InputStreamReader (gzis, StandardCharsets.UTF_8))) { String line; int lineNum = 0 ; while ((line = reader.readLine()) != null ) { System.out.printf("%d: %s%n" , ++lineNum, line); } } } public static void main (String[] args) { try { decompressFile("data.txt.gz" , "data_decompressed.txt" ); byte [] compressed = compressString("Hello GZIP!" ); String decompressed = decompressString(compressed); System.out.println("解压后的字符串: " + decompressed); readGZIPLineByLine("log.txt.gz" ); } catch (IOException e) { e.printStackTrace(); } } private static byte [] compressString(String text) throws IOException { try (ByteArrayOutputStream baos = new ByteArrayOutputStream (); GZIPOutputStream gzos = new GZIPOutputStream (baos)) { gzos.write(text.getBytes(StandardCharsets.UTF_8)); return baos.toByteArray(); } } }
⭐⭐ 高级读取功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 import java.io.*;import java.util.zip.*;public class AdvancedGZIPInputStream { public static void readGZIPHeader (String gzipFile) throws IOException { try (GZIPInputStream gzis = new GZIPInputStream ( new FileInputStream (gzipFile))) { java.lang.reflect.Field field = GZIPInputStream.class .getDeclaredField("crc" ); field.setAccessible(true ); CRC32 crc = (CRC32) field.get(gzis); System.out.println("CRC32: " + crc.getValue()); byte [] buffer = new byte [8192 ]; while (gzis.read(buffer) != -1 ) { } } catch (Exception e) { e.printStackTrace(); } } public static void decompressWithProgress (String gzipFile, String outputFile) throws IOException { File compressed = new File (gzipFile); long totalSize = compressed.length(); long processed = 0 ; int lastProgress = 0 ; try (GZIPInputStream gzis = new GZIPInputStream ( new FileInputStream (gzipFile)); FileOutputStream fos = new FileOutputStream (outputFile)) { byte [] buffer = new byte [8192 ]; int len; while ((len = gzis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); processed += len; int progress = (int ) (processed * 100 / totalSize); if (progress > lastProgress) { System.out.printf("\r解压进度: %d%%" , progress); lastProgress = progress; } } System.out.println("\n解压完成!" ); } } public static byte [] decompressToBytes(String gzipFile) throws IOException { try (GZIPInputStream gzis = new GZIPInputStream ( new FileInputStream (gzipFile)); ByteArrayOutputStream baos = new ByteArrayOutputStream ()) { byte [] buffer = new byte [8192 ]; int len; while ((len = gzis.read(buffer)) != -1 ) { baos.write(buffer, 0 , len); } return baos.toByteArray(); } } public static void main (String[] args) { try { decompressWithProgress("large_file.gz" , "large_file.txt" ); byte [] content = decompressToBytes("config.gz" ); System.out.println("文件大小: " + content.length + " bytes" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 处理 TAR.GZ 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 import java.io.*;import java.util.zip.*;import java.nio.file.*;import java.util.*;public class TarGZHandler { public static void createTarGZ (String sourceDir, String tarGzFile) throws IOException { try (FileOutputStream fos = new FileOutputStream (tarGzFile); GZIPOutputStream gzos = new GZIPOutputStream (fos); TarOutputStream tos = new TarOutputStream (gzos)) { Path sourcePath = Paths.get(sourceDir); Files.walk(sourcePath) .filter(path -> !Files.isDirectory(path)) .forEach(path -> { try { String entryName = sourcePath.relativize(path).toString() .replace("\\" , "/" ); TarEntry entry = new TarEntry (path.toFile(), entryName); tos.putNextEntry(entry); Files.copy(path, tos); tos.closeEntry(); System.out.println("添加: " + entryName); } catch (IOException e) { e.printStackTrace(); } }); System.out.println("创建 tar.gz 完成: " + tarGzFile); } } public static void extractTarGZ (String tarGzFile, String outputDir) throws IOException { try (FileInputStream fis = new FileInputStream (tarGzFile); GZIPInputStream gzis = new GZIPInputStream (fis); TarInputStream tis = new TarInputStream (gzis)) { TarEntry entry; while ((entry = tis.getNextEntry()) != null ) { File outputFile = new File (outputDir, entry.getName()); if (entry.isDirectory()) { outputFile.mkdirs(); } else { outputFile.getParentFile().mkdirs(); try (FileOutputStream fos = new FileOutputStream (outputFile)) { byte [] buffer = new byte [8192 ]; int len; while ((len = tis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); } } } System.out.println("提取: " + entry.getName()); } } } public static void main (String[] args) { try { createTarGZ("docs" , "docs.tar.gz" ); extractTarGZ("docs.tar.gz" , "extracted" ); } catch (IOException e) { e.printStackTrace(); } } } <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-compress</artifactId> <version>1.25 .0 </version> </dependency>
⭐⭐ 缓冲区优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 public class GZIPPerformance { public static void optimizeBuffer (String sourceFile, String gzipFile) throws IOException { int bufferSize = 65536 ; try (FileInputStream fis = new FileInputStream (sourceFile); BufferedInputStream bis = new BufferedInputStream (fis, bufferSize); FileOutputStream fos = new FileOutputStream (gzipFile); GZIPOutputStream gzos = new GZIPOutputStream ( new BufferedOutputStream (fos, bufferSize))) { byte [] buffer = new byte [bufferSize]; int len; long startTime = System.currentTimeMillis(); while ((len = bis.read(buffer)) != -1 ) { gzos.write(buffer, 0 , len); } long endTime = System.currentTimeMillis(); System.out.println("压缩时间: " + (endTime - startTime) + " ms" ); } } public static void parallelCompress (List<String> files, String outputDir) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); List<Future<?>> futures = new ArrayList <>(); for (String file : files) { futures.add(executor.submit(() -> { try { String gzipFile = outputDir + File.separator + new File (file).getName() + ".gz" ; compressFile(file, gzipFile); } catch (IOException e) { e.printStackTrace(); } })); } for (Future<?> future : futures) { try { future.get(); } catch (ExecutionException e) { e.printStackTrace(); } } executor.shutdown(); System.out.println("并行压缩完成" ); } private static void compressFile (String source, String target) throws IOException { try (FileInputStream fis = new FileInputStream (source); GZIPOutputStream gzos = new GZIPOutputStream ( new FileOutputStream (target))) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { gzos.write(buffer, 0 , len); } } } }
⭐⭐ 内存映射文件优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 import java.nio.*;import java.nio.channels.*;import java.nio.file.*;public class MappedGZIP { public static void compressWithMappedFile (String sourceFile, String gzipFile) throws IOException { Path path = Paths.get(sourceFile); long fileSize = Files.size(path); try (FileChannel channel = FileChannel.open(path, StandardOpenOption.READ); FileOutputStream fos = new FileOutputStream (gzipFile); GZIPOutputStream gzos = new GZIPOutputStream (fos)) { MappedByteBuffer buffer = channel.map( FileChannel.MapMode.READ_ONLY, 0 , fileSize); byte [] bytes = new byte [8192 ]; long startTime = System.currentTimeMillis(); while (buffer.hasRemaining()) { int remaining = buffer.remaining(); int readSize = Math.min(bytes.length, remaining); buffer.get(bytes, 0 , readSize); gzos.write(bytes, 0 , readSize); } long endTime = System.currentTimeMillis(); System.out.println("内存映射压缩时间: " + (endTime - startTime) + " ms" ); } } public static void decompressWithMappedFile (String gzipFile, String outputFile) throws IOException { try (FileInputStream fis = new FileInputStream (gzipFile); GZIPInputStream gzis = new GZIPInputStream (fis); RandomAccessFile raf = new RandomAccessFile (outputFile, "rw" ); FileChannel channel = raf.getChannel()) { ByteArrayOutputStream baos = new ByteArrayOutputStream (); byte [] buffer = new byte [8192 ]; int len; while ((len = gzis.read(buffer)) != -1 ) { baos.write(buffer, 0 , len); } byte [] decompressed = baos.toByteArray(); MappedByteBuffer outBuffer = channel.map( FileChannel.MapMode.READ_WRITE, 0 , decompressed.length); outBuffer.put(decompressed); System.out.println("内存映射解压完成, 大小: " + decompressed.length + " bytes" ); } } }
⭐⭐ 处理大文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 public class LargeFileHandler { public static void processLargeGZIP (String gzipFile) throws IOException { try (GZIPInputStream gzis = new GZIPInputStream ( new BufferedInputStream (new FileInputStream (gzipFile), 65536 )); BufferedReader reader = new BufferedReader ( new InputStreamReader (gzis, StandardCharsets.UTF_8))) { String line; int lineCount = 0 ; while ((line = reader.readLine()) != null ) { processLine(line); lineCount++; if (lineCount % 10000 == 0 ) { System.out.println("已处理 " + lineCount + " 行" ); } } System.out.println("共处理 " + lineCount + " 行" ); } } private static void processLine (String line) { } }
⭐⭐ 资源管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 public class GZIPResourceManagement { public static void properClose (String source, String target) throws IOException { try (FileInputStream fis = new FileInputStream (source); GZIPOutputStream gzos = new GZIPOutputStream ( new FileOutputStream (target))) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { gzos.write(buffer, 0 , len); } } } public static void ensureFinish (String source, String target) throws IOException { GZIPOutputStream gzos = null ; try { FileInputStream fis = new FileInputStream (source); gzos = new GZIPOutputStream (new FileOutputStream (target)); byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { gzos.write(buffer, 0 , len); } gzos.finish(); } finally { if (gzos != null ) { gzos.close(); } } } }
JAR 文件处理 JarInputStream 和 JarOutputStream 类, 它们是专门用于流式处理 JAR 文件的类。 继承自 ZipInputStream 和 ZipOutputStream, 增加了对 JAR 清单( Manifest) 和签名文件的支持。
⭐⭐ 创建 JAR 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 import java.io.*;import java.util.jar.*;import java.util.zip.*;public class JarOutputStreamBasic { public static void createBasicJar (String jarPath, String[] files) throws IOException { try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath))) { for (String filePath : files) { File file = new File (filePath); if (!file.exists()) continue ; JarEntry entry = new JarEntry (file.getName()); entry.setTime(file.lastModified()); jos.putNextEntry(entry); try (FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); System.out.println("添加: " + file.getName()); } System.out.println("JAR 创建成功: " + jarPath); } } public static void createJarWithManifest (String jarPath, String mainClass, String[] classFiles) throws IOException { Manifest manifest = new Manifest (); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0" ); attributes.put(Attributes.Name.MAIN_CLASS, mainClass); attributes.put(Attributes.Name.CREATED_BY, "JarOutputStream Example" ); try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath), manifest)) { for (String classFile : classFiles) { File file = new File (classFile); String entryName = classFile.replace(File.separator, "/" ); JarEntry entry = new JarEntry (entryName); entry.setTime(file.lastModified()); jos.putNextEntry(entry); try (FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); System.out.println("添加类: " + entryName); } System.out.println("可执行 JAR 创建成功: " + jarPath); System.out.println("Main-Class: " + mainClass); } } public static void createJarWithDirectories (String jarPath, String sourceDir) throws IOException { try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath))) { Path sourcePath = Paths.get(sourceDir); Files.walk(sourcePath) .filter(path -> !Files.isDirectory(path)) .forEach(path -> { try { String entryName = sourcePath.relativize(path).toString() .replace("\\" , "/" ); JarEntry entry = new JarEntry (entryName); entry.setTime(Files.getLastModifiedTime(path).toMillis()); jos.putNextEntry(entry); Files.copy(path, jos); jos.closeEntry(); System.out.println("添加: " + entryName); } catch (IOException e) { e.printStackTrace(); } }); } } public static void main (String[] args) { try { String[] files = {"README.txt" , "config.properties" }; createBasicJar("simple.jar" , files); String[] classes = {"com/example/Main.class" , "com/example/Utils.class" }; createJarWithManifest("app.jar" , "com.example.Main" , classes); createJarWithDirectories("structure.jar" , "build/classes" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 高级 JAR 创建 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 import java.io.*;import java.util.jar.*;import java.util.zip.*;import java.util.*;import java.nio.file.*;public class AdvancedJarOutputStream { public static void createVersionedJar (String jarPath, String version, String[] files) throws IOException { Manifest manifest = new Manifest (); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0" ); attributes.put(new Attributes .Name("Implementation-Version" ), version); attributes.put(new Attributes .Name("Implementation-Title" ), "My Application" ); attributes.put(new Attributes .Name("Implementation-Vendor" ), "My Company" ); attributes.put(new Attributes .Name("Build-Date" ), new Date ().toString()); try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath), manifest)) { for (String filePath : files) { File file = new File (filePath); JarEntry entry = new JarEntry (file.getName()); entry.setTime(file.lastModified()); jos.putNextEntry(entry); try (FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); } System.out.println("版本化 JAR 创建成功: " + jarPath); System.out.println("版本: " + version); } } public static void createJarWithClassPath (String jarPath, String mainClass, List<String> dependencies, String[] classFiles) throws IOException { Manifest manifest = new Manifest (); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0" ); attributes.put(Attributes.Name.MAIN_CLASS, mainClass); if (!dependencies.isEmpty()) { StringBuilder classPath = new StringBuilder (); for (String dep : dependencies) { if (classPath.length() > 0 ) classPath.append(" " ); classPath.append(dep); } attributes.put(Attributes.Name.CLASS_PATH, classPath.toString()); } try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath), manifest)) { for (String classFile : classFiles) { File file = new File (classFile); String entryName = classFile.replace(File.separator, "/" ); JarEntry entry = new JarEntry (entryName); jos.putNextEntry(entry); try (FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); } } System.out.println("带 Class-Path 的 JAR 创建成功" ); System.out.println("Class-Path: " + dependencies); } public static void createJarWithCustomAttributes (String jarPath, Map<String, String> customAttrs, Map<String, byte []> entries) throws IOException { Manifest manifest = new Manifest (); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0" ); for (Map.Entry<String, String> attr : customAttrs.entrySet()) { attributes.put(new Attributes .Name(attr.getKey()), attr.getValue()); } for (Map.Entry<String, byte []> entry : entries.entrySet()) { Attributes entryAttrs = new Attributes (); entryAttrs.put(new Attributes .Name("SHA-256-Digest" ), calculateSHA256(entry.getValue())); manifest.getEntries().put(entry.getKey(), entryAttrs); } try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath), manifest)) { for (Map.Entry<String, byte []> entry : entries.entrySet()) { JarEntry jarEntry = new JarEntry (entry.getKey()); jos.putNextEntry(jarEntry); jos.write(entry.getValue()); jos.closeEntry(); System.out.println("添加: " + entry.getKey()); } } } public static void createSignedJar (String jarPath, String[] files) throws IOException { Manifest manifest = new Manifest (); Attributes attributes = manifest.getMainAttributes(); attributes.put(Attributes.Name.MANIFEST_VERSION, "1.0" ); try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (jarPath), manifest)) { for (String filePath : files) { File file = new File (filePath); JarEntry entry = new JarEntry (file.getName()); jos.putNextEntry(entry); try (FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); } System.out.println("JAR 创建完成, 可以使用 jarsigner 签名" ); System.out.println("签名命令: jarsigner " + jarPath + " mykeystore" ); } } private static String calculateSHA256 (byte [] data) { try { java.security.MessageDigest md = java.security.MessageDigest.getInstance("SHA-256" ); byte [] hash = md.digest(data); return java.util.Base64.getEncoder().encodeToString(hash); } catch (Exception e) { return "" ; } } public static void main (String[] args) { try { String[] files = {"app.properties" , "logo.png" }; createVersionedJar("versioned.jar" , "2.0.1" , files); List<String> deps = Arrays.asList("lib/common.jar" , "lib/utils.jar" ); String[] classes = {"com/app/Main.class" }; createJarWithClassPath("app-with-deps.jar" , "com.app.Main" , deps, classes); Map<String, String> attrs = new HashMap <>(); attrs.put("Built-By" , "Developer" ); attrs.put("Build-Time" , new Date ().toString()); Map<String, byte []> entries = new HashMap <>(); entries.put("data.txt" , "Hello World" .getBytes()); entries.put("config/settings.xml" , "<config>...</config>" .getBytes()); createJarWithCustomAttributes("custom.jar" , attrs, entries); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 读取 JAR 文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 import java.io.*;import java.util.jar.*;import java.util.zip.*;public class JarInputStreamBasic { public static void readJarFile (String jarPath) throws IOException { try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { Manifest manifest = jis.getManifest(); if (manifest != null ) { System.out.println("=== 清单内容 ===" ); Attributes attributes = manifest.getMainAttributes(); for (Object key : attributes.keySet()) { System.out.printf("%s: %s%n" , key, attributes.get(key)); } } System.out.println("\n=== JAR 内容 ===" ); JarEntry entry; int fileCount = 0 ; while ((entry = jis.getNextJarEntry()) != null ) { if (entry.isDirectory()) { System.out.printf("[目录] %s%n" , entry.getName()); } else { System.out.printf("[文件] %s (%,d bytes)%n" , entry.getName(), entry.getSize()); if (entry.getName().endsWith(".properties" )) { System.out.println(" 内容预览:" ); byte [] buffer = new byte [200 ]; int len = jis.read(buffer); if (len > 0 ) { System.out.println(" " + new String (buffer, 0 , Math.min(len, 100 ))); } } fileCount++; } jis.closeEntry(); } System.out.println("\n总计: " + fileCount + " 个文件" ); } } public static void extractJar (String jarPath, String outputDir) throws IOException { try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { JarEntry entry; while ((entry = jis.getNextJarEntry()) != null ) { File outputFile = new File (outputDir, entry.getName()); if (entry.isDirectory()) { outputFile.mkdirs(); System.out.println("创建目录: " + entry.getName()); } else { outputFile.getParentFile().mkdirs(); try (FileOutputStream fos = new FileOutputStream (outputFile)) { byte [] buffer = new byte [8192 ]; int len; while ((len = jis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); } } System.out.println("提取文件: " + entry.getName()); } jis.closeEntry(); } } System.out.println("解压完成: " + outputDir); } public static void readManifestOnly (String jarPath) throws IOException { try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { Manifest manifest = jis.getManifest(); if (manifest != null ) { System.out.println("=== JAR 清单信息 ===" ); Attributes mainAttrs = manifest.getMainAttributes(); System.out.println("\n主属性:" ); for (Object key : mainAttrs.keySet()) { System.out.printf(" %s: %s%n" , key, mainAttrs.get(key)); } Map<String, Attributes> entries = manifest.getEntries(); if (!entries.isEmpty()) { System.out.println("\n条目属性:" ); for (Map.Entry<String, Attributes> entry : entries.entrySet()) { System.out.println(" " + entry.getKey() + ":" ); for (Object key : entry.getValue().keySet()) { System.out.printf(" %s: %s%n" , key, entry.getValue().get(key)); } } } } else { System.out.println("JAR 文件不包含清单" ); } } } public static void main (String[] args) { try { String jarPath = "app.jar" ; readJarFile(jarPath); extractJar(jarPath, "extracted" ); readManifestOnly(jarPath); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 高级 JAR 读取 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 import java.io.*;import java.util.jar.*;import java.util.*;import java.security.*;import java.security.cert.*;public class AdvancedJarInputStream { public static void verifyJarSignature (String jarPath) throws IOException { try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath), true )) { System.out.println("=== 签名验证 ===" ); Manifest manifest = jis.getManifest(); JarEntry entry; boolean hasSignature = false ; while ((entry = jis.getNextJarEntry()) != null ) { String name = entry.getName(); if (name.startsWith("META-INF/" ) && (name.endsWith(".SF" ) || name.endsWith(".DSA" ) || name.endsWith(".RSA" ) || name.endsWith(".EC" ))) { hasSignature = true ; System.out.println("找到签名文件: " + name); } if (!entry.isDirectory() && !name.startsWith("META-INF/" )) { try { byte [] buffer = new byte [8192 ]; while (jis.read(buffer) != -1 ) { } System.out.printf("✓ 验证通过: %s%n" , name); } catch (SecurityException e) { System.err.printf("✗ 签名验证失败: %s - %s%n" , name, e.getMessage()); } } jis.closeEntry(); } if (!hasSignature) { System.out.println("JAR 文件未签名" ); } } } public static void getCertificateInfo (String jarPath) throws IOException { try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath), true )) { System.out.println("=== 证书信息 ===" ); JarEntry entry; while ((entry = jis.getNextJarEntry()) != null ) { if (!entry.isDirectory() && !entry.getName().startsWith("META-INF/" )) { byte [] buffer = new byte [8192 ]; while (jis.read(buffer) != -1 ) { } java.security.cert.Certificate[] certs = entry.getCertificates(); if (certs != null && certs.length > 0 ) { System.out.println("\n条目: " + entry.getName()); for (int i = 0 ; i < certs.length; i++) { java.security.cert.Certificate cert = certs[i]; System.out.printf(" 证书 %d:%n" , i + 1 ); System.out.printf(" 类型: %s%n" , cert.getType()); if (cert instanceof X509Certificate) { X509Certificate x509 = (X509Certificate) cert; System.out.printf(" 颁发者: %s%n" , x509.getIssuerDN()); System.out.printf(" 主题: %s%n" , x509.getSubjectDN()); System.out.printf(" 有效期: %s 至 %s%n" , x509.getNotBefore(), x509.getNotAfter()); } } } } jis.closeEntry(); } } catch (IOException e) { e.printStackTrace(); } } public static List<String> searchClasses (String jarPath, String className) throws IOException { List<String> results = new ArrayList <>(); String searchPattern = className.replace('.' , '/' ); try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { JarEntry entry; while ((entry = jis.getNextJarEntry()) != null ) { String name = entry.getName(); if (name.endsWith(".class" ) && name.contains(searchPattern)) { String classFullName = name.replace('/' , '.' ) .replace(".class" , "" ); results.add(classFullName); } jis.closeEntry(); } } return results; } public static void extractFilesByType (String jarPath, String outputDir, String extension) throws IOException { try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { JarEntry entry; int count = 0 ; while ((entry = jis.getNextJarEntry()) != null ) { String name = entry.getName(); if (!entry.isDirectory() && name.endsWith(extension)) { File outputFile = new File (outputDir, name); outputFile.getParentFile().mkdirs(); try (FileOutputStream fos = new FileOutputStream (outputFile)) { byte [] buffer = new byte [8192 ]; int len; while ((len = jis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); } } System.out.println("提取: " + name); count++; } jis.closeEntry(); } System.out.println("提取了 " + count + " 个 " + extension + " 文件" ); } } public static void analyzeDependencies (String jarPath) throws IOException { System.out.println("=== 依赖分析 ===" ); Set<String> packages = new TreeSet <>(); try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { JarEntry entry; while ((entry = jis.getNextJarEntry()) != null ) { String name = entry.getName(); if (name.endsWith(".class" )) { try { byte [] buffer = new byte [8192 ]; ByteArrayOutputStream baos = new ByteArrayOutputStream (); int len; while ((len = jis.read(buffer)) != -1 ) { baos.write(buffer, 0 , len); } String className = name.replace('/' , '.' ).replace(".class" , "" ); int lastDot = className.lastIndexOf('.' ); if (lastDot > 0 ) { String pkg = className.substring(0 , lastDot); packages.add(pkg); } } catch (IOException e) { } } jis.closeEntry(); } } System.out.println("包含的包:" ); for (String pkg : packages) { System.out.println(" " + pkg); } } public static void main (String[] args) { try { String jarPath = "app.jar" ; verifyJarSignature(jarPath); getCertificateInfo(jarPath); List<String> classes = searchClasses(jarPath, "com.example" ); System.out.println("\n找到的类:" ); for (String clazz : classes) { System.out.println(" " + clazz); } extractFilesByType(jarPath, "resources" , ".properties" ); analyzeDependencies(jarPath); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 流式处理大文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 import java.io.*;import java.util.jar.*;import java.util.zip.*;public class StreamJarProcessing { public static void streamProcessJar (String jarPath, EntryProcessor processor) throws IOException { try (JarInputStream jis = new JarInputStream ( new BufferedInputStream (new FileInputStream (jarPath), 65536 ))) { JarEntry entry; byte [] buffer = new byte [65536 ]; while ((entry = jis.getNextJarEntry()) != null ) { if (!entry.isDirectory()) { processor.process(entry, jis); } jis.closeEntry(); } } } @FunctionalInterface public interface EntryProcessor { void process (JarEntry entry, InputStream is) throws IOException; } public static void transformJar (String inputJar, String outputJar, EntryTransformer transformer) throws IOException { try (JarInputStream jis = new JarInputStream ( new FileInputStream (inputJar)); JarOutputStream jos = new JarOutputStream ( new FileOutputStream (outputJar))) { Manifest manifest = jis.getManifest(); if (manifest != null ) { } JarEntry entry; while ((entry = jis.getNextJarEntry()) != null ) { if (entry.isDirectory()) { jis.closeEntry(); continue ; } JarEntry newEntry = transformer.transform(entry); jos.putNextEntry(newEntry); byte [] buffer = new byte [8192 ]; int len; while ((len = jis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } jos.closeEntry(); jis.closeEntry(); System.out.println("处理: " + entry.getName()); } } } @FunctionalInterface public interface EntryTransformer { JarEntry transform (JarEntry original) ; } public static void filterJar (String inputJar, String outputJar, EntryFilter filter) throws IOException { try (JarInputStream jis = new JarInputStream ( new FileInputStream (inputJar)); JarOutputStream jos = new JarOutputStream ( new FileOutputStream (outputJar))) { Manifest manifest = jis.getManifest(); if (manifest != null ) { } JarEntry entry; int kept = 0 ; int skipped = 0 ; while ((entry = jis.getNextJarEntry()) != null ) { if (filter.shouldKeep(entry)) { JarEntry newEntry = new JarEntry (entry.getName()); newEntry.setTime(entry.getTime()); jos.putNextEntry(newEntry); byte [] buffer = new byte [8192 ]; int len; while ((len = jis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } jos.closeEntry(); kept++; } else { skipped++; } jis.closeEntry(); } System.out.printf("过滤完成: 保留 %d, 跳过 %d%n" , kept, skipped); } } @FunctionalInterface public interface EntryFilter { boolean shouldKeep (JarEntry entry) ; } public static void mergeJars (List<String> jarPaths, String outputJar) throws IOException { try (JarOutputStream jos = new JarOutputStream ( new FileOutputStream (outputJar))) { Set<String> processedEntries = new HashSet <>(); for (String jarPath : jarPaths) { System.out.println("处理: " + jarPath); try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { JarEntry entry; while ((entry = jis.getNextJarEntry()) != null ) { String name = entry.getName(); if (!processedEntries.contains(name)) { processedEntries.add(name); JarEntry newEntry = new JarEntry (name); newEntry.setTime(entry.getTime()); jos.putNextEntry(newEntry); byte [] buffer = new byte [8192 ]; int len; while ((len = jis.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } jos.closeEntry(); } jis.closeEntry(); } } } System.out.println("合并完成, 共 " + processedEntries.size() + " 个条目" ); } } public static void main (String[] args) { try { streamProcessJar("large.jar" , (entry, is) -> { System.out.println("处理: " + entry.getName()); byte [] buffer = new byte [8192 ]; long size = 0 ; int len; while ((len = is.read(buffer)) != -1 ) { size += len; } System.out.println(" 大小: " + size + " bytes" ); }); transformJar("input.jar" , "output.jar" , entry -> { JarEntry newEntry = new JarEntry ("prefixed/" + entry.getName()); newEntry.setTime(entry.getTime()); return newEntry; }); filterJar("app.jar" , "filtered.jar" , entry -> entry.getName().endsWith(".class" )); List<String> jars = Arrays.asList("lib1.jar" , "lib2.jar" , "lib3.jar" ); mergeJars(jars, "merged.jar" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 import java.io.*;import java.util.jar.*;import java.util.zip.*;import java.util.concurrent.*;public class JarStreamOptimization { public static void createOptimizedJar (String jarPath, String sourceDir) throws IOException { try (JarOutputStream jos = new JarOutputStream ( new BufferedOutputStream (new FileOutputStream (jarPath), 65536 ))) { Path sourcePath = Paths.get(sourceDir); Files.walk(sourcePath) .filter(path -> !Files.isDirectory(path)) .forEach(path -> { try { String entryName = sourcePath.relativize(path).toString() .replace("\\" , "/" ); JarEntry entry = new JarEntry (entryName); jos.putNextEntry(entry); try (InputStream is = new BufferedInputStream ( Files.newInputStream(path), 65536 )) { byte [] buffer = new byte [65536 ]; int len; while ((len = is.read(buffer)) != -1 ) { jos.write(buffer, 0 , len); } } jos.closeEntry(); } catch (IOException e) { e.printStackTrace(); } }); } } public static void parallelProcessJars (List<String> jarPaths, JarProcessor processor) throws InterruptedException { ExecutorService executor = Executors.newFixedThreadPool( Runtime.getRuntime().availableProcessors()); List<Future<?>> futures = new ArrayList <>(); for (String jarPath : jarPaths) { futures.add(executor.submit(() -> { try { processor.process(jarPath); } catch (IOException e) { e.printStackTrace(); } })); } for (Future<?> future : futures) { try { future.get(); } catch (ExecutionException e) { e.printStackTrace(); } } executor.shutdown(); System.out.println("并行处理完成" ); } @FunctionalInterface public interface JarProcessor { void process (String jarPath) throws IOException; } public static class JarIndexCache { private final Map<String, Map<String, JarEntryInfo>> cache = new ConcurrentHashMap <>(); public void indexJar (String jarPath) throws IOException { Map<String, JarEntryInfo> index = new HashMap <>(); try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { JarEntry entry; while ((entry = jis.getNextJarEntry()) != null ) { if (!entry.isDirectory()) { long position = getEntryPosition(jis); index.put(entry.getName(), new JarEntryInfo (entry, position)); } jis.closeEntry(); } } cache.put(jarPath, index); System.out.println("索引构建完成: " + jarPath + " (" + index.size() + " 个条目)" ); } public JarEntryInfo getEntryInfo (String jarPath, String entryName) { Map<String, JarEntryInfo> index = cache.get(jarPath); return index != null ? index.get(entryName) : null ; } private long getEntryPosition (JarInputStream jis) { try { java.lang.reflect.Field field = ZipInputStream.class .getDeclaredField("pos" ); field.setAccessible(true ); return field.getLong(jis); } catch (Exception e) { return -1 ; } } static class JarEntryInfo { final JarEntry entry; final long position; JarEntryInfo(JarEntry entry, long position) { this .entry = entry; this .position = position; } public JarEntry getEntry () { return entry; } public long getPosition () { return position; } } } public static void comparePerformance (String jarPath) throws IOException { long start1 = System.currentTimeMillis(); try (JarInputStream jis = new JarInputStream ( new FileInputStream (jarPath))) { JarEntry entry; while ((entry = jis.getNextJarEntry()) != null ) { if (!entry.isDirectory()) { byte [] buffer = new byte [8192 ]; while (jis.read(buffer) != -1 ) { } } jis.closeEntry(); } } long time1 = System.currentTimeMillis() - start1; long start2 = System.currentTimeMillis(); try (JarFile jarFile = new JarFile (jarPath)) { Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); if (!entry.isDirectory()) { try (InputStream is = jarFile.getInputStream(entry)) { byte [] buffer = new byte [8192 ]; while (is.read(buffer) != -1 ) { } } } } } long time2 = System.currentTimeMillis() - start2; System.out.println("JarInputStream 耗时: " + time1 + " ms" ); System.out.println("JarFile 耗时: " + time2 + " ms" ); System.out.println("性能差异: " + (time1 > time2 ? "JarFile 快 " + (time1 - time2) : "JarInputStream 快 " + (time2 - time1)) + " ms" ); } public static void main (String[] args) { try { createOptimizedJar("optimized.jar" , "build/classes" ); List<String> jars = Arrays.asList("jar1.jar" , "jar2.jar" , "jar3.jar" ); parallelProcessJars(jars, jarPath -> { System.out.println("处理: " + jarPath); }); comparePerformance("large.jar" ); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } }
⭐⭐
计算校验和 CheckedInputStream 和 CheckedOutputStream 是 Java 中用于在数据流传输过程中计算校验和的类。 它们通常与其他流配合使用, 确保数据的完整性。
检测数据传输或存储过程中的错误
验证数据完整性
常用于文件压缩( 如 ZIP 文件) 的 CRC32 校验
⭐⭐ Java 提供了两种校验和实现 1 2 3 4 5 Checksum crc32 = new CRC32 ();Checksum adler32 = new Adler32 ();
⭐⭐ 写入文件并计算校验和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 import java.io.*;import java.util.zip.*;public class CheckedOutputStreamBasic { public static void writeWithChecksum (String filePath, byte [] data) throws IOException { Checksum checksum = new CRC32 (); try (CheckedOutputStream cos = new CheckedOutputStream ( new FileOutputStream (filePath), checksum)) { cos.write(data); long checksumValue = cos.getChecksum().getValue(); System.out.println("CRC32 校验和: " + checksumValue); System.out.println("十六进制: " + Long.toHexString(checksumValue)); } } public static void writeWithAdler32 (String filePath, byte [] data) throws IOException { Checksum checksum = new Adler32 (); try (CheckedOutputStream cos = new CheckedOutputStream ( new FileOutputStream (filePath), checksum)) { cos.write(data); long checksumValue = cos.getChecksum().getValue(); System.out.println("Adler32 校验和: " + checksumValue); } } public static void writeInChunks (String filePath, String[] chunks) throws IOException { Checksum checksum = new CRC32 (); try (CheckedOutputStream cos = new CheckedOutputStream ( new BufferedOutputStream (new FileOutputStream (filePath)), checksum)) { for (String chunk : chunks) { byte [] data = chunk.getBytes(); cos.write(data); System.out.printf("写入块: %s (当前校验和: %d)%n" , chunk, checksum.getValue()); } System.out.println("最终校验和: " + checksum.getValue()); } } public static void main (String[] args) { try { String text = "Hello, CheckedOutputStream!" ; writeWithChecksum("test.txt" , text.getBytes()); writeWithAdler32("test2.txt" , text.getBytes()); String[] chunks = {"Hello" , " " , "World" , "!" }; writeInChunks("chunks.txt" , chunks); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 批量创建校验和文件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 import java.io.*;import java.util.zip.*;public class ZipWithChecksum { public static void createZipWithChecksum (String zipPath, String filePath) throws IOException { try (ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream (zipPath))) { File file = new File (filePath); ZipEntry entry = new ZipEntry (file.getName()); ByteArrayOutputStream baos = new ByteArrayOutputStream (); Checksum checksum = new CRC32 (); try (CheckedOutputStream cos = new CheckedOutputStream (baos, checksum); FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { cos.write(buffer, 0 , len); } } entry.setMethod(ZipEntry.STORED); entry.setSize(file.length()); entry.setCompressedSize(file.length()); entry.setCrc(checksum.getValue()); zos.putNextEntry(entry); zos.write(baos.toByteArray()); zos.closeEntry(); System.out.println("ZIP 创建成功, CRC32: " + checksum.getValue()); } } public static void createMultiFileZip (String zipPath, List<String> filePaths) throws IOException { try (ZipOutputStream zos = new ZipOutputStream ( new FileOutputStream (zipPath))) { for (String filePath : filePaths) { File file = new File (filePath); if (!file.exists()) continue ; Checksum checksum = new CRC32 (); ByteArrayOutputStream baos = new ByteArrayOutputStream (); try (CheckedOutputStream cos = new CheckedOutputStream (baos, checksum); FileInputStream fis = new FileInputStream (file)) { byte [] buffer = new byte [8192 ]; int len; while ((len = fis.read(buffer)) != -1 ) { cos.write(buffer, 0 , len); } } ZipEntry entry = new ZipEntry (file.getName()); entry.setMethod(ZipEntry.STORED); entry.setSize(file.length()); entry.setCompressedSize(file.length()); entry.setCrc(checksum.getValue()); zos.putNextEntry(entry); zos.write(baos.toByteArray()); zos.closeEntry(); System.out.printf("添加文件: %s (CRC: %d)%n" , file.getName(), checksum.getValue()); } } } public static void main (String[] args) { try { createZipWithChecksum("output.zip" , "data.txt" ); List<String> files = Arrays.asList("file1.txt" , "file2.txt" , "image.jpg" ); createMultiFileZip("multi.zip" , files); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 读取文件并验证校验和 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 import java.io.*;import java.util.zip.*;public class CheckedInputStreamBasic { public static void readWithChecksum (String filePath, long expectedChecksum) throws IOException { Checksum checksum = new CRC32 (); try (CheckedInputStream cis = new CheckedInputStream ( new FileInputStream (filePath), checksum)) { byte [] buffer = new byte [8192 ]; int len; long totalBytes = 0 ; while ((len = cis.read(buffer)) != -1 ) { totalBytes += len; } long actualChecksum = cis.getChecksum().getValue(); System.out.println("读取字节数: " + totalBytes); System.out.println("实际校验和: " + actualChecksum); System.out.println("期望校验和: " + expectedChecksum); if (actualChecksum == expectedChecksum) { System.out.println("✓ 校验和验证通过" ); } else { System.err.println("✗ 校验和验证失败! 文件可能已损坏" ); } } } public static void readInChunks (String filePath) throws IOException { Checksum checksum = new Adler32 (); try (CheckedInputStream cis = new CheckedInputStream ( new BufferedInputStream (new FileInputStream (filePath)), checksum)) { byte [] buffer = new byte [1024 ]; int len; int chunkNum = 0 ; while ((len = cis.read(buffer)) != -1 ) { chunkNum++; System.out.printf("读取块 %d: %d 字节 (当前校验和: %d)%n" , chunkNum, len, checksum.getValue()); } System.out.println("最终校验和: " + checksum.getValue()); } } public static void main (String[] args) { try { Checksum checksum = new CRC32 (); byte [] data = "Test data" .getBytes(); checksum.update(data, 0 , data.length); long expected = checksum.getValue(); try (FileOutputStream fos = new FileOutputStream ("test.txt" )) { fos.write(data); } readWithChecksum("test.txt" , expected); readInChunks("test.txt" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 验证 ZIP 文件完整性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 import java.io.*;import java.util.zip.*;public class ZipIntegrityChecker { public static void verifyZipIntegrity (String zipPath) throws IOException { try (ZipFile zipFile = new ZipFile (zipPath)) { Enumeration<? extends ZipEntry > entries = zipFile.entries(); boolean allValid = true ; while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); if (!entry.isDirectory() && entry.getCrc() != -1 ) { long storedCrc = entry.getCrc(); Checksum checksum = new CRC32 (); try (InputStream is = zipFile.getInputStream(entry); CheckedInputStream cis = new CheckedInputStream (is, checksum)) { byte [] buffer = new byte [8192 ]; while (cis.read(buffer) != -1 ) { } long calculatedCrc = checksum.getValue(); if (storedCrc == calculatedCrc) { System.out.printf("✓ %s (CRC: %d)%n" , entry.getName(), calculatedCrc); } else { System.err.printf("✗ %s 损坏! 存储: %d, 计算: %d%n" , entry.getName(), storedCrc, calculatedCrc); allValid = false ; } } } } if (allValid) { System.out.println("所有文件验证通过! " ); } else { System.err.println("ZIP 文件包含损坏的条目! " ); } } } public static void extractWithVerification (String zipPath, String outputDir) throws IOException { try (ZipFile zipFile = new ZipFile (zipPath)) { Enumeration<? extends ZipEntry > entries = zipFile.entries(); int successCount = 0 ; int failCount = 0 ; while (entries.hasMoreElements()) { ZipEntry entry = entries.nextElement(); File outputFile = new File (outputDir, entry.getName()); if (entry.isDirectory()) { outputFile.mkdirs(); continue ; } outputFile.getParentFile().mkdirs(); try (InputStream is = zipFile.getInputStream(entry); CheckedInputStream cis = new CheckedInputStream (is, new CRC32 ()); FileOutputStream fos = new FileOutputStream (outputFile)) { byte [] buffer = new byte [8192 ]; int len; while ((len = cis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); } long calculatedCrc = cis.getChecksum().getValue(); if (entry.getCrc() == -1 || entry.getCrc() == calculatedCrc) { System.out.printf("✓ 提取成功: %s%n" , entry.getName()); successCount++; } else { System.err.printf("✗ CRC 不匹配: %s%n" , entry.getName()); failCount++; outputFile.delete(); } } } System.out.printf("解压完成: 成功 %d, 失败 %d%n" , successCount, failCount); } } public static void main (String[] args) { try { verifyZipIntegrity("archive.zip" ); extractWithVerification("archive.zip" , "extracted" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 网络传输完整性校验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 import java.io.*;import java.net.*;import java.util.zip.*;public class NetworkTransferWithChecksum { public static void sendWithChecksum (Socket socket, byte [] data) throws IOException { try (OutputStream os = socket.getOutputStream(); CheckedOutputStream cos = new CheckedOutputStream (os, new CRC32 ()); DataOutputStream dos = new DataOutputStream (cos)) { dos.writeInt(data.length); dos.write(data); long checksum = cos.getChecksum().getValue(); dos.writeLong(checksum); System.out.println("数据已发送, 长度: " + data.length + ", 校验和: " + checksum); } } public static byte [] receiveWithVerification(Socket socket) throws IOException { try (InputStream is = socket.getInputStream(); CheckedInputStream cis = new CheckedInputStream (is, new CRC32 ()); DataInputStream dis = new DataInputStream (cis)) { int dataLength = dis.readInt(); byte [] data = new byte [dataLength]; dis.readFully(data); long sentChecksum = dis.readLong(); long calculatedChecksum = cis.getChecksum().getValue(); if (sentChecksum == calculatedChecksum) { System.out.println("✓ 数据验证通过" ); return data; } else { throw new IOException ("数据损坏! 发送: " + sentChecksum + ", 计算: " + calculatedChecksum); } } } public static void sendFile (Socket socket, File file) throws IOException { try (FileInputStream fis = new FileInputStream (file); CheckedInputStream cis = new CheckedInputStream (fis, new CRC32 ()); OutputStream os = socket.getOutputStream(); DataOutputStream dos = new DataOutputStream (os)) { dos.writeUTF(file.getName()); long fileSize = file.length(); dos.writeLong(fileSize); byte [] buffer = new byte [8192 ]; int len; while ((len = cis.read(buffer)) != -1 ) { dos.write(buffer, 0 , len); } long checksum = cis.getChecksum().getValue(); dos.writeLong(checksum); System.out.println("文件发送完成: " + file.getName() + " (校验和: " + checksum + ")" ); } } public static void main (String[] args) { new Thread (() -> { try (ServerSocket serverSocket = new ServerSocket (8888 )) { Socket socket = serverSocket.accept(); byte [] data = receiveWithVerification(socket); System.out.println("接收到的数据: " + new String (data)); } catch (IOException e) { e.printStackTrace(); } }).start(); try { Thread.sleep(1000 ); Socket socket = new Socket ("localhost" , 8888 ); sendWithChecksum(socket, "Hello, Network!" .getBytes()); } catch (IOException | InterruptedException e) { e.printStackTrace(); } } }
⭐⭐ 文件复制并验证 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 import java.io.*;import java.util.zip.*;public class FileCopyWithChecksum { public static void copyFileWithChecksum (String source, String target) throws IOException { Checksum sourceChecksum = new CRC32 (); Checksum targetChecksum = new CRC32 (); try (FileInputStream fis = new FileInputStream (source); CheckedInputStream cis = new CheckedInputStream (fis, sourceChecksum); FileOutputStream fos = new FileOutputStream (target); CheckedOutputStream cos = new CheckedOutputStream (fos, targetChecksum)) { byte [] buffer = new byte [8192 ]; int len; while ((len = cis.read(buffer)) != -1 ) { cos.write(buffer, 0 , len); } } long sourceCrc = sourceChecksum.getValue(); long targetCrc = targetChecksum.getValue(); System.out.println("源文件 CRC: " + sourceCrc); System.out.println("目标文件 CRC: " + targetCrc); if (sourceCrc == targetCrc) { System.out.println("✓ 文件复制成功, 完整性验证通过" ); } else { System.err.println("✗ 文件复制可能出错, CRC 不匹配! " ); } } public static void backupWithChecksum (String source, String backupDir) throws IOException { File sourceFile = new File (source); File backupFile = new File (backupDir, sourceFile.getName() + ".bak" ); File checksumFile = new File (backupDir, sourceFile.getName() + ".crc" ); Checksum checksum = new CRC32 (); try (FileInputStream fis = new FileInputStream (sourceFile); CheckedInputStream cis = new CheckedInputStream (fis, checksum); FileOutputStream fos = new FileOutputStream (backupFile)) { byte [] buffer = new byte [8192 ]; int len; while ((len = cis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); } } try (DataOutputStream dos = new DataOutputStream ( new FileOutputStream (checksumFile))) { dos.writeLong(checksum.getValue()); } System.out.println("备份完成: " + backupFile.getAbsolutePath()); System.out.println("校验和文件: " + checksumFile.getAbsolutePath()); System.out.println("CRC32: " + checksum.getValue()); } public static void restoreWithVerification (String backupPath, String checksumPath, String restorePath) throws IOException { long storedChecksum; try (DataInputStream dis = new DataInputStream ( new FileInputStream (checksumPath))) { storedChecksum = dis.readLong(); } Checksum checksum = new CRC32 (); try (FileInputStream fis = new FileInputStream (backupPath); CheckedInputStream cis = new CheckedInputStream (fis, checksum); FileOutputStream fos = new FileOutputStream (restorePath)) { byte [] buffer = new byte [8192 ]; int len; while ((len = cis.read(buffer)) != -1 ) { fos.write(buffer, 0 , len); } } long calculatedChecksum = checksum.getValue(); if (storedChecksum == calculatedChecksum) { System.out.println("✓ 文件恢复成功, 完整性验证通过" ); } else { System.err.println("✗ 文件可能损坏! 存储: " + storedChecksum + ", 计算: " + calculatedChecksum); new File (restorePath).delete(); } } public static void main (String[] args) { try { copyFileWithChecksum("source.txt" , "copy.txt" ); backupWithChecksum("important.txt" , "backup" ); restoreWithVerification("backup/important.txt.bak" , "backup/important.txt.crc" , "restored.txt" ); } catch (IOException e) { e.printStackTrace(); } } }
⭐⭐ 大文件分块校验 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 import java.io.*;import java.util.zip.*;public class LargeFileChecksum { public static long calculateLargeFileChecksum (String filePath) throws IOException { Checksum checksum = new CRC32 (); try (FileInputStream fis = new FileInputStream (filePath); CheckedInputStream cis = new CheckedInputStream ( new BufferedInputStream (fis, 65536 ), checksum)) { byte [] buffer = new byte [65536 ]; while (cis.read(buffer) != -1 ) { } } return checksum.getValue(); } public static List<Long> calculateBlockChecksums (String filePath, int blockSize) throws IOException { List<Long> checksums = new ArrayList <>(); try (FileInputStream fis = new FileInputStream (filePath); BufferedInputStream bis = new BufferedInputStream (fis, 65536 )) { byte [] buffer = new byte [blockSize]; int blockNumber = 0 ; while (true ) { Checksum checksum = new CRC32 (); int bytesRead = bis.read(buffer); if (bytesRead == -1 ) break ; checksum.update(buffer, 0 , bytesRead); checksums.add(checksum.getValue()); System.out.printf("块 %d: 读取 %d 字节, CRC: %d%n" , ++blockNumber, bytesRead, checksum.getValue()); } } return checksums; } public static boolean verifyLargeFile (String filePath, List<Long> expectedChecksums, int blockSize) throws IOException { try (FileInputStream fis = new FileInputStream (filePath); BufferedInputStream bis = new BufferedInputStream (fis, 65536 )) { byte [] buffer = new byte [blockSize]; int blockNumber = 0 ; for (Long expectedChecksum : expectedChecksums) { Checksum checksum = new CRC32 (); int bytesRead = bis.read(buffer); if (bytesRead == -1 ) { System.err.println("文件结束, 但还有更多校验和" ); return false ; } checksum.update(buffer, 0 , bytesRead); long actualChecksum = checksum.getValue(); if (expectedChecksum != actualChecksum) { System.err.printf("块 %d 校验失败! 期望: %d, 实际: %d%n" , blockNumber, expectedChecksum, actualChecksum); return false ; } System.out.printf("块 %d 验证通过%n" , ++blockNumber); } if (bis.read(buffer) != -1 ) { System.err.println("文件有多余数据" ); return false ; } return true ; } } public static void backupLargeFile (String sourcePath, String backupDir, int blockSize) throws IOException { File backupDirectory = new File (backupDir); backupDirectory.mkdirs(); List<Long> checksums = new ArrayList <>(); try (FileInputStream fis = new FileInputStream (sourcePath); BufferedInputStream bis = new BufferedInputStream (fis, 65536 )) { byte [] buffer = new byte [blockSize]; int blockNumber = 0 ; while (true ) { Checksum checksum = new CRC32 (); int bytesRead = bis.read(buffer); if (bytesRead == -1 ) break ; checksum.update(buffer, 0 , bytesRead); checksums.add(checksum.getValue()); String blockFile = backupDir + File.separator + "block_" + String.format("%04d" , ++blockNumber) + ".dat" ; try (FileOutputStream fos = new FileOutputStream (blockFile)) { fos.write(buffer, 0 , bytesRead); } System.out.printf("备份块 %d: %s (CRC: %d)%n" , blockNumber, blockFile, checksum.getValue()); } } String checksumFile = backupDir + File.separator + "checksums.dat" ; try (DataOutputStream dos = new DataOutputStream ( new FileOutputStream (checksumFile))) { dos.writeInt(checksums.size()); for (Long crc : checksums) { dos.writeLong(crc); } dos.writeInt(blockSize); } System.out.println("备份完成, 共 " + checksums.size() + " 个块" ); } public static void main (String[] args) { try { long crc = calculateLargeFileChecksum("large_file.dat" ); System.out.println("文件 CRC32: " + crc); List<Long> blockChecksums = calculateBlockChecksums("large_file.dat" , 1024 * 1024 ); System.out.println("分块校验完成, 共 " + blockChecksums.size() + " 块" ); backupLargeFile("large_file.dat" , "backup_blocks" , 1024 * 1024 ); } catch (IOException e) { e.printStackTrace(); } } }
并发编程 Java 并发编程是指在 Java 编程语言中, 编写能够同时执行多个任务的程序的技术。 它主要涉及多线程、 同步机制、 并发工具类和并发数据结构等内容, 目的是充分利用多核处理器的计算能力, 提高程序的执行效率和响应速度。
进程与线程
进程: 一个正在运行的程序实例, 是操作系统进行资源分配的基本单位。 它拥有自己独立的内存空间( 如堆、 方法区) 、 文件描述符等资源。
线程: 进程内部的一个执行单元, 是 CPU 进行任务调度的基本单位。 一个进程可以包含多个线程, 这些线程共享进程的资源, 但各自拥有独立的程序计数器、 虚拟机栈和本地方法栈。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 public class ProcessVsThread { public static void main (String[] args) { Thread currentThread = Thread.currentThread(); System.out.println("当前线程: " + currentThread.getName()); System.out.println("线程ID: " + currentThread.getId()); System.out.println("优先级: " + currentThread.getPriority()); System.out.println("状态: " + currentThread.getState()); System.out.println("\n所有活动线程 (包含系统守护线程):" ); for (Thread t : Thread.getAllStackTraces().keySet()) { if (t != null ) { String daemonFlag = t.isDaemon() ? "[守护]" : "[用户]" ; System.out.println(" " + daemonFlag + " " + t.getName() + " - " + t.getState() + " (组:" + t.getThreadGroup().getName() + ")" ); } } } }
生命周期 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 public class ThreadLifeCycle { public static void main (String[] args) throws InterruptedException { Thread thread = new Thread (() -> { try { System.out.println("线程运行中..." ); Thread.sleep(2000 ); synchronized (ThreadLifeCycle.class) { ThreadLifeCycle.class.wait(); } } catch (InterruptedException e) { e.printStackTrace(); } }); System.out.println("创建后: " + thread.getState()); thread.start(); System.out.println("启动后: " + thread.getState()); Thread.sleep(100 ); System.out.println("sleep后: " + thread.getState()); thread.join(); System.out.println("结束后: " + thread.getState()); } }
创建线程 ⭐⭐ 继承 Thread 类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public class ExtendsThread extends Thread { private String name; public ExtendsThread (String name) { this .name = name; } @Override public void run () { for (int i = 0 ; i < 5 ; i++) { System.out.println(name + " 执行: " + i); try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main (String[] args) { ExtendsThread t1 = new ExtendsThread ("线程1" ); ExtendsThread t2 = new ExtendsThread ("线程2" ); t1.start(); t2.start(); try { t1.join(); t2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程结束" ); } }
⭐⭐ 实现 Runnable 接口( 推荐) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 public class ImplementsRunnable implements Runnable { private String name; public ImplementsRunnable (String name) { this .name = name; } @Override public void run () { for (int i = 0 ; i < 5 ; i++) { System.out.println(name + " 执行: " + i + ", 线程: " + Thread.currentThread().getName()); try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main (String[] args) { ImplementsRunnable task1 = new ImplementsRunnable ("任务1" ); ImplementsRunnable task2 = new ImplementsRunnable ("任务2" ); Thread thread1 = new Thread (task1, "线程A" ); Thread thread2 = new Thread (task2, "线程B" ); thread1.start(); thread2.start(); Thread thread3 = new Thread (() -> { for (int i = 0 ; i < 5 ; i++) { System.out.println("Lambda线程: " + i); try { Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } } }, "Lambda线程" ); thread3.start(); } }
⭐⭐ 实现 Callable 接口( 有返回值) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import java.util.concurrent.*;public class ImplementsCallable implements Callable <String> { private String taskName; public ImplementsCallable (String taskName) { this .taskName = taskName; } @Override public String call () throws Exception { System.out.println(taskName + " 开始执行" ); Thread.sleep(2000 ); return taskName + " 执行完成, 返回值: " + System.currentTimeMillis(); } public static void main (String[] args) throws Exception { ExecutorService executor = Executors.newFixedThreadPool(3 ); Future<String> future1 = executor.submit(new ImplementsCallable ("任务1" )); Future<String> future2 = executor.submit(new ImplementsCallable ("任务2" )); System.out.println(future1.get()); System.out.println(future2.get()); CompletionService<String> completionService = new ExecutorCompletionService <>(executor); for (int i = 0 ; i < 5 ; i++) { final int taskId = i; completionService.submit(() -> { Thread.sleep((long )(Math.random() * 3000 )); return "任务" + taskId + "完成" ; }); } for (int i = 0 ; i < 5 ; i++) { Future<String> future = completionService.take(); System.out.println(future.get()); } executor.shutdown(); } }
同步机制 ⭐⭐ synchronized 关键字 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public class SynchronizedDemo { private int count = 0 ; public synchronized void increment () { count++; } public static synchronized void staticMethod () { System.out.println("静态同步方法" ); } public void add () { synchronized (this ) { count++; } } private final Object lock = new Object (); public void addWithLock () { synchronized (lock) { count++; } } private static class Buffer { private final int [] data = new int [10 ]; private int count = 0 ; public synchronized void produce (int value) throws InterruptedException { while (count == data.length) { System.out.println("缓冲区满, 生产者等待" ); wait(); } data[count++] = value; System.out.println("生产: " + value + ", 数量: " + count); notifyAll(); } public synchronized int consume () throws InterruptedException { while (count == 0 ) { System.out.println("缓冲区空, 消费者等待" ); wait(); } int value = data[--count]; System.out.println("消费: " + value + ", 剩余: " + count); notifyAll(); return value; } } public static void main (String[] args) { Buffer buffer = new Buffer (); Thread producer = new Thread (() -> { for (int i = 0 ; i < 20 ; i++) { try { buffer.produce(i); Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread consumer = new Thread (() -> { for (int i = 0 ; i < 20 ; i++) { try { buffer.consume(); Thread.sleep(150 ); } catch (InterruptedException e) { e.printStackTrace(); } } }); producer.start(); consumer.start(); } }
⭐⭐ Lock 接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 import java.util.concurrent.locks.*;import java.util.concurrent.TimeUnit;public class LockDemo { private final ReentrantLock lock = new ReentrantLock (); private final Condition condition = lock.newCondition(); private int count = 0 ; public void increment () { lock.lock(); try { count++; System.out.println(Thread.currentThread().getName() + " 增加后: " + count); } finally { lock.unlock(); } } public boolean tryIncrement () { try { if (lock.tryLock(1 , TimeUnit.SECONDS)) { try { count++; return true ; } finally { lock.unlock(); } } } catch (InterruptedException e) { e.printStackTrace(); } return false ; } public void interruptibleIncrement () throws InterruptedException { lock.lockInterruptibly(); try { count++; } finally { lock.unlock(); } } private final ReentrantLock fairLock = new ReentrantLock (true ); public void fairLockDemo () { for (int i = 0 ; i < 5 ; i++) { new Thread (() -> { fairLock.lock(); try { System.out.println(Thread.currentThread().getName() + " 获得锁" ); Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } finally { fairLock.unlock(); } }, "线程" + i).start(); } } private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock (); private final Lock readLock = readWriteLock.readLock(); private final Lock writeLock = readWriteLock.writeLock(); private int sharedData = 0 ; public void read () { readLock.lock(); try { System.out.println(Thread.currentThread().getName() + " 读取: " + sharedData); Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } finally { readLock.unlock(); } } public void write (int value) { writeLock.lock(); try { sharedData = value; System.out.println(Thread.currentThread().getName() + " 写入: " + value); Thread.sleep(200 ); } catch (InterruptedException e) { e.printStackTrace(); } finally { writeLock.unlock(); } } public static void main (String[] args) { LockDemo demo = new LockDemo (); for (int i = 0 ; i < 3 ; i++) { new Thread (() -> { for (int j = 0 ; j < 5 ; j++) { demo.read(); } }, "读线程" + i).start(); } for (int i = 0 ; i < 2 ; i++) { final int value = i; new Thread (() -> { for (int j = 0 ; j < 3 ; j++) { demo.write(value); } }, "写线程" + i).start(); } } }
线程通信 ⭐⭐ wait/notify 机制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 public class WaitNotifyDemo { private static class SharedResource { private String message; private boolean hasMessage = false ; public synchronized void produce (String message) throws InterruptedException { while (hasMessage) { System.out.println("等待消费..." ); wait(); } this .message = message; hasMessage = true ; System.out.println("生产: " + message); notify(); } public synchronized String consume () throws InterruptedException { while (!hasMessage) { System.out.println("等待生产..." ); wait(); } String result = message; hasMessage = false ; System.out.println("消费: " + result); notify(); return result; } } public static void main (String[] args) { SharedResource resource = new SharedResource (); Thread producer = new Thread (() -> { String[] messages = {"消息1" , "消息2" , "消息3" , "消息4" , "消息5" }; for (String msg : messages) { try { resource.produce(msg); Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread consumer = new Thread (() -> { for (int i = 0 ; i < 5 ; i++) { try { resource.consume(); Thread.sleep(1500 ); } catch (InterruptedException e) { e.printStackTrace(); } } }); producer.start(); consumer.start(); } }
⭐⭐ Condition 接口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 import java.util.concurrent.locks.*;import java.util.LinkedList;import java.util.Queue;public class ConditionDemo { private final ReentrantLock lock = new ReentrantLock (); private final Condition notFull = lock.newCondition(); private final Condition notEmpty = lock.newCondition(); private final Queue<Integer> queue = new LinkedList <>(); private final int capacity = 5 ; public void produce (int value) throws InterruptedException { lock.lock(); try { while (queue.size() == capacity) { System.out.println("队列满, 生产者等待" ); notFull.await(); } queue.offer(value); System.out.println("生产: " + value + ", 队列大小: " + queue.size()); notEmpty.signal(); } finally { lock.unlock(); } } public int consume () throws InterruptedException { lock.lock(); try { while (queue.isEmpty()) { System.out.println("队列空, 消费者等待" ); notEmpty.await(); } int value = queue.poll(); System.out.println("消费: " + value + ", 队列大小: " + queue.size()); notFull.signal(); return value; } finally { lock.unlock(); } } public static void main (String[] args) { ConditionDemo demo = new ConditionDemo (); for (int i = 0 ; i < 3 ; i++) { final int producerId = i; new Thread (() -> { for (int j = 0 ; j < 10 ; j++) { try { demo.produce(producerId * 100 + j); Thread.sleep(500 ); } catch (InterruptedException e) { e.printStackTrace(); } } }, "生产者" + i).start(); } for (int i = 0 ; i < 2 ; i++) { new Thread (() -> { for (int j = 0 ; j < 15 ; j++) { try { demo.consume(); Thread.sleep(800 ); } catch (InterruptedException e) { e.printStackTrace(); } } }, "消费者" + i).start(); } } }
线程池 ⭐⭐ 线程池的创建和使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 import java.util.concurrent.*;import java.util.concurrent.atomic.AtomicInteger;public class ThreadPoolDemo { public static void main (String[] args) { ExecutorService fixedPool = Executors.newFixedThreadPool(5 ); ExecutorService singlePool = Executors.newSingleThreadExecutor(); ExecutorService cachedPool = Executors.newCachedThreadPool(); ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(3 ); ExecutorService workStealingPool = Executors.newWorkStealingPool(); ThreadPoolExecutor customPool = new ThreadPoolExecutor ( 2 , 5 , 60 , TimeUnit.SECONDS, new LinkedBlockingQueue <>(10 ), new ThreadFactory () { private final AtomicInteger threadNumber = new AtomicInteger (1 ); @Override public Thread newThread (Runnable r) { Thread t = new Thread (r, "custom-pool-thread-" + threadNumber.getAndIncrement()); t.setDaemon(false ); t.setPriority(Thread.NORM_PRIORITY); return t; } }, new ThreadPoolExecutor .CallerRunsPolicy() ); Runnable task = () -> { String threadName = Thread.currentThread().getName(); System.out.println(threadName + " 执行任务" ); try { Thread.sleep(1000 ); } catch (InterruptedException e) { e.printStackTrace(); } }; for (int i = 0 ; i < 20 ; i++) { customPool.execute(task); } Future<String> future = customPool.submit(() -> { Thread.sleep(2000 ); return "任务结果" ; }); try { System.out.println(future.get(3 , TimeUnit.SECONDS)); } catch (Exception e) { e.printStackTrace(); } CompletionService<String> completionService = new ExecutorCompletionService <>(customPool); for (int i = 0 ; i < 10 ; i++) { final int taskId = i; completionService.submit(() -> { Thread.sleep((long )(Math.random() * 3000 )); return "任务" + taskId + "完成" ; }); } for (int i = 0 ; i < 10 ; i++) { try { Future<String> result = completionService.take(); System.out.println(result.get()); } catch (Exception e) { e.printStackTrace(); } } customPool.shutdown(); try { if (!customPool.awaitTermination(60 , TimeUnit.SECONDS)) { customPool.shutdownNow(); } } catch (InterruptedException e) { customPool.shutdownNow(); } } }
⭐⭐ 自定义线程池配置 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class CustomThreadPoolConfig { private static final int CPU_CORES = Runtime.getRuntime().availableProcessors(); public static ExecutorService cpuIntensivePool () { return new ThreadPoolExecutor ( CPU_CORES, CPU_CORES, 0L , TimeUnit.MILLISECONDS, new LinkedBlockingQueue <>() ); } public static ExecutorService ioIntensivePool () { int threadCount = CPU_CORES * 2 ; return new ThreadPoolExecutor ( threadCount, threadCount, 60L , TimeUnit.SECONDS, new LinkedBlockingQueue <>(1000 ), new ThreadPoolExecutor .CallerRunsPolicy() ); } public static class MonitoredThreadPool extends ThreadPoolExecutor { public MonitoredThreadPool (int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) { super (corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue); } @Override protected void beforeExecute (Thread t, Runnable r) { System.out.println("准备执行任务: " + t.getName()); System.out.println("活跃线程数: " + getActiveCount()); System.out.println("队列大小: " + getQueue().size()); } @Override protected void afterExecute (Runnable r, Throwable t) { System.out.println("任务执行完成" ); System.out.println("已完成任务数: " + getCompletedTaskCount()); } @Override protected void terminated () { System.out.println("线程池已终止" ); } } }
并发集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 import java.util.concurrent.*;import java.util.concurrent.atomic.*;public class ConcurrentCollectionDemo { public static void main (String[] args) throws InterruptedException { ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap <>(); map.putIfAbsent("key" , 1 ); map.computeIfAbsent("key2" , k -> 2 ); map.merge("key3" , 1 , Integer::sum); map.forEach((k, v) -> System.out.println(k + "=" + v)); CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList <>(); list.add("A" ); list.add("B" ); list.addIfAbsent("C" ); ConcurrentLinkedQueue<String> queue = new ConcurrentLinkedQueue <>(); queue.offer("元素1" ); String element = queue.poll(); ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue <>(10 ); new Thread (() -> { for (int i = 0 ; i < 20 ; i++) { try { blockingQueue.put(i); System.out.println("生产: " + i); Thread.sleep(100 ); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); new Thread (() -> { for (int i = 0 ; i < 20 ; i++) { try { Integer value = blockingQueue.take(); System.out.println("消费: " + value); Thread.sleep(150 ); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); DelayQueue<DelayedElement> delayQueue = new DelayQueue <>(); delayQueue.put(new DelayedElement ("任务1" , 3000 )); delayQueue.put(new DelayedElement ("任务2" , 1000 )); while (!delayQueue.isEmpty()) { DelayedElement element2 = delayQueue.take(); System.out.println("执行: " + element2.getData() + ", 延迟: " + element2.getDelay(TimeUnit.MILLISECONDS) + "ms" ); } SynchronousQueue<String> syncQueue = new SynchronousQueue <>(); new Thread (() -> { try { syncQueue.put("数据" ); System.out.println("数据已放入" ); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); new Thread (() -> { try { Thread.sleep(1000 ); String data = syncQueue.take(); System.out.println("数据已取出: " + data); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } static class DelayedElement implements Delayed { private final String data; private final long startTime; public DelayedElement (String data, long delayMillis) { this .data = data; this .startTime = System.currentTimeMillis() + delayMillis; } @Override public long getDelay (TimeUnit unit) { long diff = startTime - System.currentTimeMillis(); return unit.convert(diff, TimeUnit.MILLISECONDS); } @Override public int compareTo (Delayed o) { return Long.compare(this .startTime, ((DelayedElement) o).startTime); } public String getData () { return data; } } }
原子类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 import java.util.concurrent.atomic.*;import java.util.concurrent.CountDownLatch;public class AtomicDemo { public static void main (String[] args) throws InterruptedException { AtomicInteger atomicInt = new AtomicInteger (0 ); AtomicLong atomicLong = new AtomicLong (0 ); AtomicBoolean atomicBoolean = new AtomicBoolean (false ); atomicInt.incrementAndGet(); atomicInt.getAndIncrement(); atomicInt.addAndGet(5 ); atomicInt.compareAndSet(0 , 10 ); AtomicIntegerArray atomicArray = new AtomicIntegerArray (10 ); atomicArray.incrementAndGet(0 ); atomicArray.compareAndSet(0 , 1 , 5 ); AtomicReference<String> atomicRef = new AtomicReference <>("初始值" ); atomicRef.compareAndSet("初始值" , "新值" ); class Person { volatile int age; } Person person = new Person (); AtomicIntegerFieldUpdater<Person> ageUpdater = AtomicIntegerFieldUpdater.newUpdater(Person.class, "age" ); ageUpdater.incrementAndGet(person); LongAdder adder = new LongAdder (); LongAccumulator accumulator = new LongAccumulator (Long::sum, 0 ); int threadCount = 100 ; int incrementsPerThread = 10000 ; CountDownLatch latch = new CountDownLatch (threadCount); AtomicLong atomicLong2 = new AtomicLong (0 ); long start1 = System.currentTimeMillis(); for (int i = 0 ; i < threadCount; i++) { new Thread (() -> { for (int j = 0 ; j < incrementsPerThread; j++) { atomicLong2.incrementAndGet(); } latch.countDown(); }).start(); } latch.await(); long end1 = System.currentTimeMillis(); System.out.println("AtomicLong耗时: " + (end1 - start1) + "ms, 结果: " + atomicLong2.get()); CountDownLatch latch2 = new CountDownLatch (threadCount); LongAdder longAdder = new LongAdder (); long start2 = System.currentTimeMillis(); for (int i = 0 ; i < threadCount; i++) { new Thread (() -> { for (int j = 0 ; j < incrementsPerThread; j++) { longAdder.increment(); } latch2.countDown(); }).start(); } latch2.await(); long end2 = System.currentTimeMillis(); System.out.println("LongAdder耗时: " + (end2 - start2) + "ms, 结果: " + longAdder.sum()); } }
常见问题 ⭐⭐ 线程安全与并发 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 public class ThreadSafeBestPractices { public static class ImmutablePerson { private final String name; private final int age; public ImmutablePerson (String name, int age) { this .name = name; this .age = age; } public String getName () { return name; } public int getAge () { return age; } } public static class ThreadLocalExample { private static final ThreadLocal<SimpleDateFormat> dateFormat = ThreadLocal.withInitial(() -> new SimpleDateFormat ("yyyy-MM-dd" )); public String formatDate (Date date) { return dateFormat.get().format(date); } } public static class Singleton { private static volatile Singleton instance; private Singleton () {} public static Singleton getInstance () { if (instance == null ) { synchronized (Singleton.class) { if (instance == null ) { instance = new Singleton (); } } } return instance; } } public static class ConcurrentExample { private final Semaphore semaphore = new Semaphore (3 ); private final CountDownLatch latch = new CountDownLatch (5 ); private final CyclicBarrier barrier = new CyclicBarrier (3 ); public void useSemaphore () throws InterruptedException { semaphore.acquire(); try { } finally { semaphore.release(); } } public void useCountDownLatch () throws InterruptedException { latch.await(); } public void useCyclicBarrier () throws InterruptedException, BrokenBarrierException { barrier.await(); } } public static class DeadlockPrevention { private final Object lock1 = new Object (); private final Object lock2 = new Object (); public void badMethod1 () { synchronized (lock1) { synchronized (lock2) { } } } public void badMethod2 () { synchronized (lock2) { synchronized (lock1) { } } } public void goodMethod1 () { synchronized (lock1) { synchronized (lock2) { } } } public void goodMethod2 () { synchronized (lock1) { synchronized (lock2) { } } } public void tryLockMethod () throws InterruptedException { ReentrantLock lock1 = new ReentrantLock (); ReentrantLock lock2 = new ReentrantLock (); while (true ) { if (lock1.tryLock()) { try { if (lock2.tryLock(1 , TimeUnit.SECONDS)) { try { break ; } finally { lock2.unlock(); } } } finally { lock1.unlock(); } } Thread.sleep(100 ); } } } }
⭐⭐ 线程安全问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 public class ThreadSafetyIssues { private static volatile boolean flag = false ; private static AtomicInteger counter = new AtomicInteger (0 ); private static class ReorderingIssue { private int x = 0 ; private int y = 0 ; private volatile int a = 0 ; private volatile int b = 0 ; } public static void main (String[] args) { synchronized (ThreadSafetyIssues.class) { } ReentrantLock lock = new ReentrantLock (); lock.lock(); try { } finally { lock.unlock(); } counter.incrementAndGet(); ConcurrentHashMap<String, String> map = new ConcurrentHashMap <>(); flag = true ; } }
⭐⭐ 性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 public class PerformanceOptimization { public class FineGrainedLock { private final Object lock1 = new Object (); private final Object lock2 = new Object (); private int value1 = 0 ; private int value2 = 0 ; public void increment1 () { synchronized (lock1) { value1++; } } public void increment2 () { synchronized (lock2) { value2++; } } } public class ReadWriteLockExample { private final ReadWriteLock rwLock = new ReentrantReadWriteLock (); private final Lock readLock = rwLock.readLock(); private final Lock writeLock = rwLock.writeLock(); private int data = 0 ; public int read () { readLock.lock(); try { return data; } finally { readLock.unlock(); } } public void write (int value) { writeLock.lock(); try { data = value; } finally { writeLock.unlock(); } } } public class LockFreeExample { private final AtomicReference<Node> head = new AtomicReference <>(); private static class Node { final int value; Node next; Node(int value) { this .value = value; } } public void push (int value) { Node newNode = new Node (value); Node oldHead; do { oldHead = head.get(); newNode.next = oldHead; } while (!head.compareAndSet(oldHead, newNode)); } } }
图形界面 Java 的图形用户界面( GUI) 工具包发展历程清晰, 主要经历了从 AWT、 Swing 到 JavaFX 的演进。 目前, JavaFX是官方主推的现代 GUI 框架, 而 Swing 仍在维护大量遗留系统。
⭐⭐ AWT AWT 是Java最早的GUI库, 随 JDK 1.0 于1995年发布。 它的核心设计思想是“ 重量级” , 即每个 Java 组件在底层操作系统中都有一个对应的、 由原生代码实现的“ 对等体” ( Peer) 。
优点:
内置可用: 作为java.awt包的一部分, 无需任何额外配置。
外观原生: 由于直接调用系统组件, 程序外观与当前操作系统保持一致。
缺点:
跨平台一致性差: 在不同操作系统上, 组件的外观和行为可能存在差异, 违背了Java“ 一次编写, 到处运行” 的初衷。
功能有限: 提供的组件较少, 难以开发复杂的现代界面。
重量级: 对系统资源占用相对较高。
现状: 已不再用于新项目开发, 但它是Swing的基础, 其事件处理模型至今仍在使用。
⭐⭐ Swing 为了解决 AWT 的缺陷, Swing 在 JDK 1.2 中被引入。 它几乎完全用 Java 编写, 因此被称为“ 轻量级” 组件。
优点:
跨平台一致性: Swing绘制自己的组件, 不依赖原生API, 保证了在所有平台上外观一致。
丰富的组件集: 提供了比AWT更多的组件, 如表格( JTable) 、 树( JTree) 等。
可插拔的外观: 可以在运行时轻松改变程序的外观和感觉( Look and Feel) , 甚至模拟Windows或Mac的风格。
缺点:
外观“ 复古” : 默认外观较为传统, 难以打造极具现代感或高度定制化的精美界面。
性能开销: 相比直接调用原生API, 渲染速度可能稍慢。
现状: 仍是Java SE的一部分, 无需额外依赖, 在维护大量遗留企业级应用和开发内部工具时被广泛使用。
⭐⭐ JavaFX JavaFX 被设计为 Swing 的继任者, 从 Java 8 开始被纳入 JDK( JDK 8u451 版本及之后被移除) , 之后成为一个独立的开源项目OpenJFX, 需要像管理普通第三方库一样, 手动引入依赖。 之所以在 JDK 中移除 JavaFX, 并不是因为这项技术被“ 放弃” 了, 恰恰相反, 这是为了给它一个更自由、 更快的发展环境而做出的战略调整。 对于大量开发服务器端应用、 不需要图形界面的开发者来说, JavaFX 成了“ 无用” 的负担。 将其分离后, JDK 回归为更纯粹的核心开发工具包。 如果开发者需要 JavaFX, 可以像引入其他第三方库一样按需添加, 使 JDK 更加轻量和专注。
优点:
现代化的UI: 支持CSS样式表, 可以轻松实现圆角、 阴影、 渐变等精美外观。
硬件加速: 其图形引擎( Prism) 利用硬件加速, 动画和图形渲染性能更好。
强大的布局和特性: 提供了VBox、 HBox等灵活布局, 并原生支持FXML( 一种XML格式) , 允许像设计网页一样通过声明式代码分离界面和逻辑。
丰富的内置组件: 内置了图表、 WebView( 用于嵌入网页) 等高级控件。
缺点:
环境配置: 需要手动添加模块路径或通过Maven/Gradle管理依赖。
现状: 是Java GUI开发的官方推荐和未来方向, 适用于所有新项目, 特别是那些需要现代化界面和丰富交互的应用。
架构图 主体架构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 图形用户界面 │ ├── 1. GUI 组件体系 │ ├── AWT 组件 (重量级) │ └── Swing 组件 (轻量级) │ ├── 2. 图形与颜色体系 │ ├── 颜色 (Color, SystemColor) │ ├── 图形 (Graphics, Graphics2D) │ ├── 画笔 (Paint, Stroke, Composite) │ ├── 形状 (Shape, Area) │ └── 字体 (Font, FontMetrics, TextLayout) │ ├── 3. 图像处理体系 │ ├── 图像 (Image, BufferedImage) │ ├── 图标 (ImageIcon) │ ├── 读写 (ImageIO) │ └── 滤镜 (BufferedImageOp) │ ├── 4. 布局管理器体系 │ ├── 基本布局 (Flow, Border, Grid, Card) │ ├── 高级布局 (GridBag, Box, Group) │ └── 自定义布局 (LayoutManager) │ ├── 5. 事件处理体系 │ ├── 事件对象 (EventObject) │ ├── 事件类型 (Action, Mouse, Key, Window...) │ ├── 监听器接口 (EventListener) │ └── 适配器类 (Adapter) │ ├── 6. 打印体系 │ ├── 打印任务 (PrinterJob) │ ├── 打印接口 (Printable, Pageable) │ └── 打印服务 (javax.print) │ ├── 7. 媒体与音频体系 │ ├── 音频片段 (AudioClip) │ ├── 采样音频 (javax.sound.sampled) │ └── MIDI音频 (javax.sound.midi) │ ├── 8. 辅助技术体系 │ ├── 可访问性 (Accessible) │ └── 焦点管理 (KeyboardFocusManager) │ ├── 9. 拖放与剪贴板体系 │ ├── 拖放 (Drag and Drop) │ └── 剪贴板 (Clipboard) │ ├── 10. 输入法体系 │ ├── 输入上下文 (InputContext) │ └── 输入法请求 (InputMethodRequest) │ ├── 11. 外观与主题体系 │ ├── 外观管理 (UIManager) │ ├── 外观实现 (LookAndFeel) │ ├── UI委托 (ComponentUI) │ └── 边框 (Border) │ └── 12. 其他工具类 ├── Toolkit / Robot ├── Cursor / Point / Dimension / Rectangle / Insets ├── 渲染提示 (RenderingHints) └── 并发工具 (SwingWorker)
组件相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 java.lang.Object │ └── java.awt.Component (所有 GUI 组件的抽象基类) │ ├── AWT 组件体系 (重量级组件) │ │ │ ├── 1. 基本组件 │ │ ├── java.awt.Button (按钮) │ │ ├── java.awt.Label (标签) │ │ ├── java.awt.TextComponent (文本组件基类) │ │ │ ├── java.awt.TextField (单行文本框) │ │ │ └── java.awt.TextArea (多行文本框) │ │ ├── java.awt.List (列表) │ │ ├── java.awt.Choice (下拉选择框) │ │ ├── java.awt.Checkbox (复选框/单选按钮) │ │ ├── java.awt.Scrollbar (滚动条) │ │ └── java.awt.Canvas (绘图画布) │ │ │ └── 2. 容器组件 │ └── java.awt.Container (容器基类) │ │ │ ├── java.awt.Panel (面板容器) │ │ └── java.applet.Applet (Applet容器) │ │ │ ├── java.awt.ScrollPane (滚动面板) │ │ │ └── java.awt.Window (窗口基类) │ │ │ ├── java.awt.Frame (主窗口) │ │ └── javax.swing.JFrame (Swing主窗口) │ │ │ └── java.awt.Dialog (对话框基类) │ ├── java.awt.FileDialog (文件对话框) │ └── javax.swing.JDialog (Swing对话框) │ └── Swing 组件体系 (轻量级组件) │ └── javax.swing.JComponent (Swing组件基类) │ ├── 1. 按钮类组件 │ ├── JButton (普通按钮) │ ├── JToggleButton (切换按钮) │ │ ├── JCheckBox (复选框) │ │ └── JRadioButton (单选按钮) │ └── JMenuItem (菜单项) │ ├── JMenu (菜单) │ ├── JCheckBoxMenuItem (复选框菜单项) │ └── JRadioButtonMenuItem (单选按钮菜单项) │ ├── 2. 文本类组件 │ ├── JLabel (标签) │ ├── JTextField (单行文本框) │ │ ├── JPasswordField (密码框) │ │ └── JFormattedTextField (格式化文本框) │ ├── JTextArea (多行文本框) │ │ └── JTextPane (样式化文本窗格) │ └── JEditorPane (编辑器窗格) │ ├── 3. 选择类组件 │ ├── JComboBox (下拉组合框) │ ├── JList (列表) │ ├── JSpinner (数值选择器) │ ├── JSlider (滑块) │ └── JProgressBar (进度条) │ ├── 4. 容器类组件 │ ├── JPanel (通用面板) │ ├── JScrollPane (滚动面板) │ ├── JSplitPane (分割面板) │ ├── JTabbedPane (选项卡面板) │ ├── JToolBar (工具栏) │ ├── JLayeredPane (分层面板) │ │ └── JDesktopPane (桌面面板 - MDI容器) │ └── JRootPane (根面板) │ ├── 5. 高级组件 │ ├── JTable (表格) │ ├── JTree (树) │ ├── JFileChooser (文件选择器) │ └── JColorChooser (颜色选择器) │ ├── 6. 菜单类组件 │ ├── JMenuBar (菜单栏) │ ├── JMenu (菜单) │ ├── JMenuItem (菜单项) │ ├── JCheckBoxMenuItem (复选框菜单项) │ ├── JRadioButtonMenuItem (单选按钮菜单项) │ └── JPopupMenu (弹出菜单) │ ├── 7. 窗口类组件 │ ├── JFrame (主窗口) - 继承自Frame │ ├── JDialog (对话框) - 继承自Dialog │ ├── JApplet (Applet) - 继承自Applet │ └── JInternalFrame (内部框架) │ └── 8. 其他组件 ├── JSeparator (分隔线) ├── JToolTip (工具提示) ├── JScrollBar (滚动条) ├── JProgressBar (进度条) └── JSpinner (微调按钮)
颜色相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 java.lang.Object │ ├── java.awt.Color (RGB/HSB 颜色) │ │ │ ├── 预定义颜色常量 │ │ ├── Color.WHITE (白色) - rgb(255,255,255) │ │ ├── Color.BLACK (黑色) - rgb(0,0,0) │ │ ├── Color.RED (红色) - rgb(255,0,0) │ │ ├── Color.GREEN (绿色) - rgb(0,255,0) │ │ ├── Color.BLUE (蓝色) - rgb(0,0,255) │ │ ├── Color.YELLOW (黄色) - rgb(255,255,0) │ │ ├── Color.CYAN (青色) - rgb(0,255,255) │ │ ├── Color.MAGENTA (品红) - rgb(255,0,255) │ │ ├── Color.ORANGE (橙色) - rgb(255,200,0) │ │ ├── Color.PINK (粉色) - rgb(255,175,175) │ │ ├── Color.GRAY (灰色) - rgb(128,128,128) │ │ ├── Color.DARK_GRAY (深灰) - rgb(64,64,64) │ │ ├── Color.LIGHT_GRAY (浅灰) - rgb(192,192,192) │ │ └── Color.TRANSLUCENT (透明) - rgba(0,0,0,0) │ │ │ ├── 构造方式 │ │ ├── Color(int r, int g, int b) - RGB 分量 (0-255) │ │ ├── Color(int r, int g, int b, int a) - RGBA 分量 │ │ ├── Color(int rgb) - 32位RGB值 (0xRRGGBB) │ │ ├── Color(int rgba, boolean hasalpha) - 32位RGBA值 │ │ ├── Color(float r, float g, float b) - RGB 分量 (0.0-1.0) │ │ └── Color(ColorSpace cspace, float[] comps, float alpha) - 颜色空间 │ │ │ └── 静态工厂方法 │ ├── getColor(String name) - 从系统属性获取颜色 │ ├── getHSBColor(float h, float s, float b) - HSB 转 RGB │ ├── decode(String hex) - 十六进制字符串解析 (#RRGGBB) │ └── HSBtoRGB(float h, float s, float b) - HSB 转 RGB 值 │ └── java.awt.SystemColor (系统主题颜色) │ ├── SystemColor.control (控件背景色) ├── SystemColor.controlText (控件文本色) ├── SystemColor.text (文本背景色) ├── SystemColor.textText (文本前景色) ├── SystemColor.window (窗口背景色) ├── SystemColor.windowText (窗口文本色) ├── SystemColor.menu (菜单背景色) ├── SystemColor.menuText (菜单文本色) ├── SystemColor.activeCaption (活动标题栏) ├── SystemColor.activeCaptionText (活动标题栏文本) ├── SystemColor.inactiveCaption (非活动标题栏) ├── SystemColor.inactiveCaptionText (非活动标题栏文本) ├── SystemColor.desktop (桌面背景) ├── SystemColor.info (信息提示背景) └── SystemColor.infoText (信息提示文本)
图形相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 java.lang.Object │ ├── java.awt.Graphics (抽象图形上下文) │ │ │ └── java.awt.Graphics2D (增强2D图形上下文) │ │ │ ├── 图形绘制方法 │ │ ├── draw(Shape s) - 绘制形状轮廓 │ │ ├── fill(Shape s) - 填充形状 │ │ ├── drawLine(x1,y1,x2,y2) - 绘制直线 │ │ ├── drawRect(x,y,w,h) - 绘制矩形轮廓 │ │ ├── fillRect(x,y,w,h) - 填充矩形 │ │ ├── drawRoundRect(x,y,w,h,aw,ah) - 绘制圆角矩形轮廓 │ │ ├── fillRoundRect(x,y,w,h,aw,ah) - 填充圆角矩形 │ │ ├── drawOval(x,y,w,h) - 绘制椭圆轮廓 │ │ ├── fillOval(x,y,w,h) - 填充椭圆 │ │ ├── drawArc(x,y,w,h,sa,aa) - 绘制弧形 │ │ ├── fillArc(x,y,w,h,sa,aa) - 填充扇形 │ │ ├── drawPolygon(xPoints,yPoints,n) - 绘制多边形轮廓 │ │ ├── fillPolygon(xPoints,yPoints,n) - 填充多边形 │ │ └── drawString(str,x,y) - 绘制文本 │ │ │ ├── 图形属性设置 │ │ ├── setColor(Color c) - 设置颜色 │ │ ├── setFont(Font f) - 设置字体 │ │ ├── setPaint(Paint p) - 设置画笔 │ │ ├── setStroke(Stroke s) - 设置笔触 │ │ ├── setComposite(Composite c) - 设置混合模式 │ │ ├── setTransform(AffineTransform tx) - 设置变换 │ │ └── setRenderingHint(key,value) - 设置渲染提示 │ │ │ ├── 坐标变换 │ │ ├── translate(x,y) - 平移 │ │ ├── rotate(theta) - 旋转 │ │ ├── scale(sx,sy) - 缩放 │ │ └── shear(shx,shy) - 切变 │ │ │ └── 图像绘制 │ ├── drawImage(Image img, x, y, observer) - 绘制图像 │ └── drawImage(Image img, x, y, w, h, observer) - 缩放绘制 │ ├── java.awt.Shape (形状接口) │ │ │ │ │ └── 定义几何形状的轮廓 │ │ │ └── java.awt.geom.* (2D几何图形包) │ │ │ ├── 基本形状 │ │ ├── java.awt.geom.Line2D (直线) │ │ │ ├── Line2D.Float (浮点坐标直线) │ │ │ └── Line2D.Double (双精度直线) │ │ │ │ │ ├── java.awt.geom.Rectangle2D (矩形) │ │ │ ├── Rectangle2D.Float │ │ │ └── Rectangle2D.Double │ │ │ └── java.awt.Rectangle (整数矩形) │ │ │ │ │ ├── java.awt.geom.RoundRectangle2D (圆角矩形) │ │ │ ├── RoundRectangle2D.Float │ │ │ └── RoundRectangle2D.Double │ │ │ │ │ ├── java.awt.geom.Ellipse2D (椭圆) │ │ │ ├── Ellipse2D.Float │ │ │ └── Ellipse2D.Double │ │ │ │ │ ├── java.awt.geom.Arc2D (弧形) │ │ │ ├── Arc2D.Float │ │ │ ├── Arc2D.Double │ │ │ └── Arc2D.OPEN / CHORD / PIE (弧形类型) │ │ │ │ │ ├── java.awt.geom.QuadCurve2D (二次曲线) │ │ │ ├── QuadCurve2D.Float │ │ │ └── QuadCurve2D.Double │ │ │ │ │ └── java.awt.geom.CubicCurve2D (三次曲线) │ │ ├── CubicCurve2D.Float │ │ └── CubicCurve2D.Double │ │ │ ├── 复合形状 │ │ ├── java.awt.geom.GeneralPath (通用路径) │ │ │ └── 支持 moveTo, lineTo, quadTo, curveTo 等操作 │ │ │ │ │ ├── java.awt.geom.Path2D (路径2D) │ │ │ ├── Path2D.Float │ │ │ └── Path2D.Double │ │ │ │ │ └── java.awt.geom.Area (区域) │ │ ├── add(Area) - 并集 │ │ ├── subtract(Area) - 差集 │ │ ├── intersect(Area) - 交集 │ │ └── exclusiveOr(Area) - 异或 │ │ │ └── 辅助类 │ ├── java.awt.geom.Point2D (点) │ │ ├── Point2D.Float │ │ └── Point2D.Double │ │ └── java.awt.Point (整数点) │ │ │ ├── java.awt.geom.Dimension2D (尺寸) │ │ └── java.awt.Dimension (整数尺寸) │ │ │ └── java.awt.geom.AffineTransform (仿射变换) │ ├── translate(x,y) - 平移 │ ├── rotate(theta) - 旋转 │ ├── scale(sx,sy) - 缩放 │ ├── shear(shx,shy) - 切变 │ └── concatenate(tx) - 变换连接 │ ├── java.awt.GraphicsConfigTemplate (图形配置模板) │ └── java.awt.GraphicsConfiguration (图形配置) │ └── java.awt.GraphicsDevice (图形设备) ├── GraphicsDevice.TYPE_RASTER_SCREEN (屏幕设备) ├── GraphicsDevice.TYPE_PRINTER (打印设备) └── GraphicsDevice.TYPE_IMAGE_BUFFER (图像缓冲设备)
布局相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 java.lang.Object │ ├── 1. 核心接口 │ ├── java.awt.LayoutManager (布局管理器接口) │ └── java.awt.LayoutManager2 (增强布局管理器接口) │ ├── 2. AWT 布局管理器 │ ├── java.awt.FlowLayout (流式布局) │ ├── java.awt.BorderLayout (边界布局) │ ├── java.awt.GridLayout (网格布局) │ ├── java.awt.CardLayout (卡片布局) │ └── java.awt.GridBagLayout (网格包布局) │ ├── 3. Swing 布局管理器 │ ├── javax.swing.BoxLayout (盒式布局) │ ├── javax.swing.GroupLayout (组布局) │ ├── javax.swing.SpringLayout (弹簧布局) │ └── javax.swing.OverlayLayout (覆盖布局) │ └── 4. 其他布局 ├── java.awt.GridBagConstraints (网格包约束) ├── javax.swing.Box (盒式容器) └── javax.swing.Box.Filler (填充组件)
事件相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 事件处理体系 │ ├── 事件对象、 类型 │ │ │ ├── java.util.EventObject (事件基类) - 所有事件对象的顶层父类, 封装事件源对象 │ │ │ ├── java.awt.event.ActionEvent (动作事件) - 按钮点击、 菜单选择、 文本框回车等触发 │ │ │ ├── java.awt.event.MouseEvent (鼠标事件) - 鼠标点击、 移动、 拖拽、 进入/离开组件等 │ │ │ ├── java.awt.event.KeyEvent (键盘事件) - 键盘按键按下、 释放、 输入字符等 │ │ │ ├── java.awt.event.WindowEvent (窗口事件) - 窗口打开、 关闭、 激活、 最小化等 │ │ │ ├── java.awt.event.ItemEvent (项目事件) - 复选框、 单选按钮、 下拉列表等选项变化 │ │ │ ├── java.awt.event.AdjustmentEvent (调整事件) - 滚动条移动、 滑块位置改变 │ │ │ ├── java.awt.event.FocusEvent (焦点事件) - 组件获得焦点、 失去焦点 │ │ │ ├── java.awt.event.ComponentEvent (组件事件) - 组件移动、 大小改变、 显示/隐藏 │ │ │ │ │ ├── java.awt.event.ContainerEvent (容器事件) - 容器中添加或移除组件 │ │ │ │ │ └── java.awt.event.InputEvent (输入事件) - 输入设备事件的抽象基类 │ │ │ │ │ ├── java.awt.event.MouseEvent (鼠标事件) - 继承自InputEvent, 包含鼠标按钮、 点击次数、 修饰键等信息 │ │ │ │ │ └── java.awt.event.KeyEvent (键盘事件) - 继承自InputEvent, 包含按键码、 字符、 修饰键等信息 │ │ │ ├── java.awt.event.TextEvent (文本事件) - 文本组件内容发生变化 │ │ │ ├── java.awt.event.HierarchyEvent (层次事件) - 组件层次结构发生变化 │ │ │ ├── javax.swing.event.DocumentEvent (文档事件) - 文本组件文档内容改变( Swing) │ │ │ ├── javax.swing.event.ListSelectionEvent (列表选择事件) - 列表或表格选中项改变( Swing) │ │ │ ├── javax.swing.event.TreeSelectionEvent (树选择事件) - 树组件节点选中状态改变( Swing) │ │ │ ├── javax.swing.event.TableModelEvent (表格模型事件) - 表格数据模型发生变化( Swing) │ │ │ ├── javax.swing.event.CaretEvent (光标事件) - 文本组件光标位置改变( Swing) │ │ │ ├── javax.swing.event.PopupMenuEvent (弹出菜单事件) - 弹出菜单显示或隐藏( Swing) │ │ │ ├── javax.swing.event.AncestorEvent (祖先事件) - 组件添加或移除到容器层次时触发( Swing) │ │ │ └── java.beans.PropertyChangeEvent (属性变化事件) - JavaBean组件属性值发生变化 │ ├── 监听器 │ │ │ ├── java.util.EventListener (监听器标记接口) - 所有事件监听器的顶层标记接口 │ │ │ ├── java.awt.event.ActionListener (动作监听器) - 处理按钮点击、 菜单选择等动作事件 │ │ │ ├── java.awt.event.MouseListener (鼠标监听器) - 处理鼠标点击、 进入、 离开、 按下、 释放 │ │ │ ├── java.awt.event.MouseMotionListener (鼠标移动监听器) - 处理鼠标移动、 拖拽 │ │ │ ├── java.awt.event.MouseWheelListener (鼠标滚轮监听器) - 处理鼠标滚轮滚动 │ │ │ ├── java.awt.event.KeyListener (键盘监听器) - 处理按键按下、 释放、 输入字符 │ │ │ ├── java.awt.event.WindowListener (窗口监听器) - 处理窗口打开、 关闭、 激活、 最小化 │ │ │ ├── java.awt.event.WindowFocusListener (窗口焦点监听器) - 处理窗口获得或失去焦点 │ │ │ ├── java.awt.event.WindowStateListener (窗口状态监听器) - 处理窗口最大化、 最小化等状态变化 │ │ │ ├── java.awt.event.ItemListener (项目监听器) - 处理复选框、 单选按钮、 下拉列表选项变化 │ │ │ ├── java.awt.event.AdjustmentListener (调整监听器) - 处理滚动条、 滑块位置变化 │ │ │ ├── java.awt.event.FocusListener (焦点监听器) - 处理组件获得或失去焦点 │ │ │ ├── java.awt.event.ComponentListener (组件监听器) - 处理组件移动、 大小改变、 显示隐藏 │ │ │ ├── java.awt.event.ContainerListener (容器监听器) - 处理容器中添加或移除组件 │ │ │ ├── java.awt.event.TextListener (文本监听器) - 处理文本组件内容变化 │ │ │ ├── java.awt.event.HierarchyListener (层次监听器) - 处理组件层次结构变化 │ │ │ ├── java.awt.event.HierarchyBoundsListener (层次边界监听器) - 处理组件层次中祖先位置大小变化 │ │ │ ├── java.awt.event.InputMethodListener (输入法监听器) - 处理输入法文本变化 │ │ │ ├── javax.swing.event.DocumentListener (文档监听器) - 处理文本组件文档内容的插入、 删除、 修改 │ │ │ ├── javax.swing.event.ListSelectionListener (列表选择监听器) - 处理列表、 表格选中项变化 │ │ │ ├── javax.swing.event.TreeSelectionListener (树选择监听器) - 处理树节点选中状态变化 │ │ │ ├── javax.swing.event.TreeExpansionListener (树展开监听器) - 处理树节点展开或折叠 │ │ │ ├── javax.swing.event.TableModelListener (表格模型监听器) - 处理表格数据模型变化 │ │ │ ├── javax.swing.event.CaretListener (光标监听器) - 处理文本组件光标位置变化 │ │ │ ├── javax.swing.event.PopupMenuListener (弹出菜单监听器) - 处理弹出菜单显示、 隐藏、 取消 │ │ │ ├── javax.swing.event.AncestorListener (祖先监听器) - 处理组件被添加到容器或从容器移除 │ │ │ ├── javax.swing.event.ChangeListener (变化监听器) - 处理滑块、 进度条、 选项卡等状态变化 │ │ │ ├── javax.swing.event.HyperlinkListener (超链接监听器) - 处理编辑器窗格中超链接点击 │ │ │ ├── javax.swing.event.InternalFrameListener (内部框架监听器) - 处理内部框架打开、 关闭、 激活等 │ │ │ └── java.beans.PropertyChangeListener (属性变化监听器) - 处理JavaBean组件属性值变化 │ └── 适配器 │ ├── java.awt.event.WindowAdapter (窗口适配器) │ ├── java.awt.event.MouseAdapter (鼠标适配器) │ ├── java.awt.event.KeyAdapter (键盘适配器) │ ├── java.awt.event.FocusAdapter (焦点适配器) │ ├── java.awt.event.ComponentAdapter (组件适配器) │ ├── java.awt.event.ContainerAdapter (容器适配器) │ ├── java.awt.event.HierarchyBoundsAdapter (层次边界适配器) │ └── javax.swing.event.InternalFrameAdapter (内部框架适配器)
媒体相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 java.lang.Object │ ├── 1. 音频播放体系 │ ├── java.applet.AudioClip (音频片段接口) │ └── javax.sound.sampled.* (采样音频) │ ├── 2. MIDI 音频体系 │ └── javax.sound.midi.* (MIDI音频) │ ├── 3. 音频输入输出体系 │ ├── javax.sound.sampled.SourceDataLine (播放) │ └── javax.sound.sampled.TargetDataLine (录音) │ ├── 4. 音频格式与转换体系 │ └── javax.sound.sampled.AudioFormat (音频格式) │ ├── 5. 音频文件读写体系 │ └── javax.sound.sampled.AudioSystem (音频系统) │ ├── 6. 音效与处理体系 │ └── javax.sound.sampled.spi.* (服务提供者接口) │ └── 7. 系统提示音体系 └── java.awt.Toolkit.beep() (系统提示音)
外观相关 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 java.lang.Object │ ├── javax.swing.UIManager (外观管理器) │ │ │ └── 核心静态方法 │ │ │ ├── 外观设置 │ │ ├── setLookAndFeel(LookAndFeel) - 设置外观 │ │ ├── setLookAndFeel(String className) - 通过类名设置 │ │ └── getLookAndFeel() - 获取当前外观 │ │ │ ├── 外观获取 │ │ ├── getInstalledLookAndFeels() - 获取已安装外观列表 │ │ ├── getSystemLookAndFeelClassName() - 获取系统外观类名 │ │ └── getCrossPlatformLookAndFeelClassName() - 获取跨平台外观类名 │ │ │ ├── 资源管理 │ │ ├── get(Object key) - 获取UI资源 │ │ ├── getColor(Object key) - 获取颜色 │ │ ├── getFont(Object key) - 获取字体 │ │ ├── getIcon(Object key) - 获取图标 │ │ ├── put(Object key, Object value) - 设置UI资源 │ │ └── getDefaults() - 获取默认资源表 │ │ │ ├── 组件更新 │ │ └── updateComponentUI(Component) - 更新组件UI │ │ │ └── 外观切换 │ └── UIManager.setLookAndFeel() + SwingUtilities.updateComponentTreeUI(Component) │ ├── javax.swing.LookAndFeel (外观抽象类) │ │ │ ├── 核心方法 │ │ ├── getName() - 获取外观名称 │ │ ├── getID() - 获取外观ID │ │ ├── getDescription() - 获取外观描述 │ │ ├── isNativeLookAndFeel() - 是否本地外观 │ │ ├── isSupportedLookAndFeel() - 是否支持当前平台 │ │ │ │ │ ├── getDefaults() - 获取默认资源表 │ │ ├── getLayoutStyle() - 获取布局样式 │ │ │ │ │ ├── initialize() - 初始化外观 │ │ └── uninitialize() - 反初始化外观 │ │ │ ├── 静态辅助方法 │ │ ├── installColors(JComponent, String bgKey, String fgKey) - 安装颜色 │ │ ├── makeIcon(Class, String) - 创建图标 │ │ └── installBorder(JComponent, String) - 安装边框 │ │ │ └── 子类实现 │ ├── javax.swing.plaf.basic.BasicLookAndFeel (基础外观) │ ├── javax.swing.plaf.metal.MetalLookAndFeel (Metal外观) │ ├── javax.swing.plaf.nimbus.NimbusLookAndFeel (Nimbus外观) │ ├── com.sun.java.swing.plaf.windows.WindowsLookAndFeel (Windows外观) │ └── com.apple.laf.AquaLookAndFeel (Mac外观) │ ├── javax.swing.plaf.ComponentUI (组件UI基类) │ │ │ ├── 核心方法 │ │ ├── createUI(JComponent) - 创建UI实例 │ │ ├── installUI(JComponent) - 安装UI │ │ ├── uninstallUI(JComponent) - 卸载UI │ │ │ │ │ ├── paint(Graphics, JComponent) - 绘制组件 │ │ ├── update(Graphics, JComponent) - 更新组件 │ │ │ │ │ ├── getPreferredSize(JComponent) - 获取首选大小 │ │ ├── getMinimumSize(JComponent) - 获取最小大小 │ │ └── getMaximumSize(JComponent) - 获取最大大小 │ │ │ └── 子类UI (按组件类型) │ │ │ ├── 按钮UI │ │ ├── javax.swing.plaf.ButtonUI │ │ ├── javax.swing.plaf.basic.BasicButtonUI │ │ └── javax.swing.plaf.metal.MetalButtonUI │ │ │ ├── 标签UI │ │ ├── javax.swing.plaf.LabelUI │ │ └── javax.swing.plaf.basic.BasicLabelUI │ │ │ ├── 文本UI │ │ ├── javax.swing.plaf.TextUI │ │ ├── javax.swing.plaf.basic.BasicTextFieldUI │ │ └── javax.swing.plaf.metal.MetalTextFieldUI │ │ │ ├── 表格UI │ │ ├── javax.swing.plaf.TableUI │ │ ├── javax.swing.plaf.basic.BasicTableUI │ │ └── javax.swing.plaf.metal.MetalTableUI │ │ │ ├── 树UI │ │ ├── javax.swing.plaf.TreeUI │ │ └── javax.swing.plaf.basic.BasicTreeUI │ │ │ ├── 列表UI │ │ ├── javax.swing.plaf.ListUI │ │ └── javax.swing.plaf.basic.BasicListUI │ │ │ ├── 滚动窗格UI │ │ ├── javax.swing.plaf.ScrollPaneUI │ │ └── javax.swing.plaf.basic.BasicScrollPaneUI │ │ │ ├── 滑块UI │ │ ├── javax.swing.plaf.SliderUI │ │ └── javax.swing.plaf.basic.BasicSliderUI │ │ │ ├── 进度条UI │ │ ├── javax.swing.plaf.ProgressBarUI │ │ └── javax.swing.plaf.basic.BasicProgressBarUI │ │ │ ├── 选项卡UI │ │ ├── javax.swing.plaf.TabbedPaneUI │ │ └── javax.swing.plaf.basic.BasicTabbedPaneUI │ │ │ └── 菜单UI │ ├── javax.swing.plaf.MenuBarUI │ ├── javax.swing.plaf.MenuItemUI │ └── javax.swing.plaf.basic.BasicMenuBarUI │ └── javax.swing.border.Border (边框接口) │ ├── 核心方法 │ ├── paintBorder(Component, Graphics, int, int, int, int) - 绘制边框 │ ├── getBorderInsets(Component) - 获取边框内边距 │ └── isBorderOpaque() - 是否不透明 │ └── 边框实现类 │ ├── 1. 线条边框 │ └── javax.swing.border.LineBorder │ ├── LineBorder(Color) - 单色线条 │ ├── LineBorder(Color, int) - 指定粗细 │ └── LineBorder(Color, int, boolean) - 圆角可选 │ ├── 2. 斜面边框 │ ├── javax.swing.border.BevelBorder │ │ ├── RAISED - 凸起斜面 │ │ └── LOWERED - 凹陷斜面 │ │ │ └── javax.swing.border.SoftBevelBorder │ └── 柔化斜面效果 │ ├── 3. 蚀刻边框 │ └── javax.swing.border.EtchedBorder │ ├── RAISED - 凸起蚀刻 │ └── LOWERED - 凹陷蚀刻 │ ├── 4. 标题边框 │ └── javax.swing.border.TitledBorder │ ├── 标题位置: TOP, BOTTOM, LEFT, RIGHT │ ├── 标题对齐: LEFT, CENTER, RIGHT │ └── 标题字体: 可自定义 │ ├── 5. 复合边框 │ └── javax.swing.border.CompoundBorder │ ├── CompoundBorder(Border outside, Border inside) │ └── 组合多个边框 │ ├── 6. 空边框 │ └── javax.swing.border.EmptyBorder │ └── EmptyBorder(int top, int left, int bottom, int right) │ ├── 7. 材质边框 │ └── javax.swing.border.MatteBorder │ ├── MatteBorder(int, int, int, int, Color) │ ├── MatteBorder(int, int, int, int, Icon) │ └── 支持颜色或图标填充 │ └── 8. 抽象边框 └── javax.swing.border.AbstractBorder ├── 边框基类 └── 扩展自定义边框
组件容器 主窗口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import javax.swing.*;import java.awt.*;public class JFrameExample { public static void main (String[] args) { JFrame frame = new JFrame ("Swing 示例" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400 , 300 ); frame.setLocationRelativeTo(null ); frame.setLayout(new FlowLayout ()); JButton button = new JButton ("点击我" ); frame.add(button); frame.setVisible(true ); } }
对话框 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class JDialogExample { public static void main (String[] args) { JFrame parent = new JFrame ("父窗口" ); parent.setSize(300 , 200 ); parent.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); JDialog dialog = new JDialog (parent, "对话框" , true ); dialog.setSize(200 , 150 ); dialog.setLocationRelativeTo(parent); dialog.add(new JLabel ("这是一个模态对话框" )); JButton showBtn = new JButton ("显示对话框" ); showBtn.addActionListener(e -> dialog.setVisible(true )); parent.add(showBtn); parent.setVisible(true ); } }
面板 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class JPanelExample { public static void main (String[] args) { JFrame frame = new JFrame ("JPanel 示例" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400 , 300 ); JPanel panel = new JPanel (); panel.setLayout(new GridLayout (2 , 2 )); panel.setBackground(Color.LIGHT_GRAY); panel.setBorder(BorderFactory.createTitledBorder("控制面板" )); panel.add(new JButton ("按钮1" )); panel.add(new JButton ("按钮2" )); panel.add(new JButton ("按钮3" )); panel.add(new JButton ("按钮4" )); frame.add(panel); frame.setVisible(true ); } }
基本组件 按钮 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class JButtonExample { public static void main (String[] args) { JFrame frame = new JFrame ("按钮示例" ); frame.setLayout(new FlowLayout ()); JButton button1 = new JButton ("普通按钮" ); JButton button2 = new JButton ("带图标按钮" , new ImageIcon ("icon.png" )); button1.setMnemonic(KeyEvent.VK_M); button1.setToolTipText("这是一个普通按钮" ); button1.addActionListener(e -> { JOptionPane.showMessageDialog(frame, "按钮被点击了! " ); }); frame.add(button1); frame.add(button2); frame.pack(); frame.setVisible(true ); } }
标签 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 public class JLabelExample { public static void main (String[] args) { JFrame frame = new JFrame ("标签示例" ); frame.setLayout(new FlowLayout ()); JLabel label1 = new JLabel ("普通文本标签" ); Icon icon = new ImageIcon ("icon.png" ); JLabel label2 = new JLabel ("带图标标签" , icon, SwingConstants.LEFT); JLabel label3 = new JLabel ("<html><font color='red'>红色文本</font><br/>第二行</html>" ); label1.setHorizontalAlignment(SwingConstants.CENTER); frame.add(label1); frame.add(label2); frame.add(label3); frame.pack(); frame.setVisible(true ); } }
文本框 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 public class JTextFieldExample { public static void main (String[] args) { JFrame frame = new JFrame ("文本框示例" ); frame.setLayout(new GridLayout (3 , 2 , 5 , 5 )); JTextField textField1 = new JTextField (); textField1.setText("默认文本" ); textField1.setColumns(15 ); JTextField textField2 = new JTextField (); textField2.setColumns(15 ); setPlaceholder(textField2, "请输入用户名" ); JTextField textField3 = new JTextField (); textField3.setColumns(15 ); textField3.setDocument(new PlainDocument () { @Override public void insertString (int offs, String str, AttributeSet a) throws BadLocationException { if (getLength() + str.length() <= 10 ) { super .insertString(offs, str, a); } } }); frame.add(new JLabel ("普通文本框:" )); frame.add(textField1); frame.add(new JLabel ("带提示文本:" )); frame.add(textField2); frame.add(new JLabel ("限制10字符:" )); frame.add(textField3); frame.pack(); frame.setVisible(true ); } private static void setPlaceholder (JTextField textField, String placeholder) { textField.setText(placeholder); textField.setForeground(Color.GRAY); textField.addFocusListener(new FocusAdapter () { @Override public void focusGained (FocusEvent e) { if (textField.getText().equals(placeholder)) { textField.setText("" ); textField.setForeground(Color.BLACK); } } @Override public void focusLost (FocusEvent e) { if (textField.getText().isEmpty()) { textField.setText(placeholder); textField.setForeground(Color.GRAY); } } }); } }
文本域 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class JTextAreaExample { public static void main (String[] args) { JFrame frame = new JFrame ("文本域示例" ); JTextArea textArea = new JTextArea (10 , 30 ); textArea.setLineWrap(true ); textArea.setWrapStyleWord(true ); JScrollPane scrollPane = new JScrollPane (textArea); scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); frame.add(scrollPane); frame.pack(); frame.setVisible(true ); } }
复选框 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class JCheckBoxExample { public static void main (String[] args) { JFrame frame = new JFrame ("复选框示例" ); frame.setLayout(new FlowLayout ()); JCheckBox checkBox1 = new JCheckBox ("选项1" ); JCheckBox checkBox2 = new JCheckBox ("选项2" , true ); JCheckBox checkBox3 = new JCheckBox ("选项3" ); checkBox1.addItemListener(e -> { System.out.println("选项1: " + (e.getStateChange() == ItemEvent.SELECTED)); }); JButton btn = new JButton ("获取选中状态" ); btn.addActionListener(e -> { String selected = "选中的选项: " ; if (checkBox1.isSelected()) selected += "选项1 " ; if (checkBox2.isSelected()) selected += "选项2 " ; if (checkBox3.isSelected()) selected += "选项3 " ; JOptionPane.showMessageDialog(frame, selected); }); frame.add(checkBox1); frame.add(checkBox2); frame.add(checkBox3); frame.add(btn); frame.pack(); frame.setVisible(true ); } }
单选按钮 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class JRadioButtonExample { public static void main (String[] args) { JFrame frame = new JFrame ("单选按钮示例" ); frame.setLayout(new FlowLayout ()); JRadioButton radio1 = new JRadioButton ("男" ); JRadioButton radio2 = new JRadioButton ("女" ); ButtonGroup group = new ButtonGroup (); group.add(radio1); group.add(radio2); radio1.setSelected(true ); radio1.addActionListener(e -> System.out.println("性别: 男" )); radio2.addActionListener(e -> System.out.println("性别: 女" )); frame.add(radio1); frame.add(radio2); frame.pack(); frame.setVisible(true ); } }
下拉列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public class JComboBoxExample { public static void main (String[] args) { JFrame frame = new JFrame ("下拉列表示例" ); frame.setLayout(new FlowLayout ()); String[] items = {"北京" , "上海" , "广州" , "深圳" }; JComboBox<String> comboBox = new JComboBox <>(items); comboBox.setEditable(true ); comboBox.addActionListener(e -> { String selected = (String) comboBox.getSelectedItem(); System.out.println("选中: " + selected); }); frame.add(comboBox); frame.pack(); frame.setVisible(true ); } }
列表 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 public class JListExample { public static void main (String[] args) { JFrame frame = new JFrame ("列表示例" ); DefaultListModel<String> model = new DefaultListModel <>(); model.addElement("苹果" ); model.addElement("香蕉" ); model.addElement("橙子" ); model.addElement("葡萄" ); JList<String> list = new JList <>(model); list.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION); JScrollPane scrollPane = new JScrollPane (list); scrollPane.setPreferredSize(new Dimension (200 , 150 )); list.addListSelectionListener(e -> { if (!e.getValueIsAdjusting()) { List<String> selected = list.getSelectedValuesList(); System.out.println("选中: " + selected); } }); frame.add(scrollPane); frame.pack(); frame.setVisible(true ); } }
高级组件 表格 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 public class JTableExample { public static void main (String[] args) { JFrame frame = new JFrame ("表格示例" ); String[] columns = {"姓名" , "年龄" , "性别" }; Object[][] data = { {"张三" , 20 , "男" }, {"李四" , 25 , "女" }, {"王五" , 30 , "男" } }; JTable table = new JTable (data, columns); table.setRowHeight(25 ); table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JScrollPane scrollPane = new JScrollPane (table); scrollPane.setPreferredSize(new Dimension (400 , 200 )); table.getColumnModel().getColumn(0 ).setCellRenderer(new DefaultTableCellRenderer () { @Override public Component getTableCellRendererComponent (JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component c = super .getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (row % 2 == 0 ) { c.setBackground(new Color (240 , 240 , 240 )); } else { c.setBackground(Color.WHITE); } return c; } }); frame.add(scrollPane); frame.pack(); frame.setVisible(true ); } }
树结构 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class JTreeExample { public static void main (String[] args) { JFrame frame = new JFrame ("树示例" ); DefaultMutableTreeNode root = new DefaultMutableTreeNode ("根目录" ); DefaultMutableTreeNode node1 = new DefaultMutableTreeNode ("文件夹1" ); DefaultMutableTreeNode node2 = new DefaultMutableTreeNode ("文件夹2" ); node1.add(new DefaultMutableTreeNode ("文件1.txt" )); node1.add(new DefaultMutableTreeNode ("文件2.txt" )); node2.add(new DefaultMutableTreeNode ("文件3.txt" )); root.add(node1); root.add(node2); JTree tree = new JTree (root); JScrollPane scrollPane = new JScrollPane (tree); scrollPane.setPreferredSize(new Dimension (300 , 200 )); tree.addTreeSelectionListener(e -> { DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); if (node != null ) { System.out.println("选中: " + node.getUserObject()); } }); frame.add(scrollPane); frame.pack(); frame.setVisible(true ); } }
布局管理 流式布局 1 2 JPanel panel = new JPanel (new FlowLayout (FlowLayout.CENTER, 10 , 10 ));
边界布局 1 2 3 4 5 6 JPanel panel = new JPanel (new BorderLayout (5 , 5 ));panel.add(new JButton ("北" ), BorderLayout.NORTH); panel.add(new JButton ("南" ), BorderLayout.SOUTH); panel.add(new JButton ("东" ), BorderLayout.EAST); panel.add(new JButton ("西" ), BorderLayout.WEST); panel.add(new JButton ("中" ), BorderLayout.CENTER);
网格布局 1 2 JPanel panel = new JPanel (new GridLayout (3 , 2 , 5 , 5 ));
网格包布局 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 public class GridBagLayoutExample { public static void main (String[] args) { JFrame frame = new JFrame ("GridBagLayout 示例" ); frame.setLayout(new GridBagLayout ()); GridBagConstraints gbc = new GridBagConstraints (); gbc.fill = GridBagConstraints.HORIZONTAL; gbc.insets = new Insets (5 , 5 , 5 , 5 ); gbc.gridx = 0 ; gbc.gridy = 0 ; frame.add(new JLabel ("姓名:" ), gbc); gbc.gridx = 1 ; gbc.gridy = 0 ; gbc.gridwidth = 2 ; frame.add(new JTextField (15 ), gbc); gbc.gridx = 0 ; gbc.gridy = 1 ; gbc.gridwidth = 1 ; frame.add(new JLabel ("年龄:" ), gbc); gbc.gridx = 1 ; gbc.gridy = 1 ; frame.add(new JTextField (5 ), gbc); frame.pack(); frame.setVisible(true ); } }
箱式布局 1 2 3 4 5 JPanel panel = new JPanel ();panel.setLayout(new BoxLayout (panel, BoxLayout.Y_AXIS)); panel.add(new JButton ("按钮1" )); panel.add(Box.createVerticalStrut(10 )); panel.add(new JButton ("按钮2" ));
事件处理 点击事件 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class EventExample { public static void main (String[] args) { JFrame frame = new JFrame ("事件示例" ); frame.setLayout(new FlowLayout ()); JButton button = new JButton ("点击" ); JTextField textField = new JTextField (15 ); JLabel label = new JLabel ("等待操作..." ); button.addActionListener(e -> { label.setText("按钮被点击了! " ); textField.setText("" ); }); button.addMouseListener(new MouseAdapter () { @Override public void mouseEntered (MouseEvent e) { button.setBackground(Color.LIGHT_GRAY); } @Override public void mouseExited (MouseEvent e) { button.setBackground(null ); } }); textField.addKeyListener(new KeyAdapter () { @Override public void keyReleased (KeyEvent e) { label.setText("输入: " + textField.getText()); } }); frame.add(button); frame.add(textField); frame.add(label); frame.pack(); frame.setVisible(true ); } }
单窗口关闭 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class SingleWindowApp { public static void main (String[] args) { JFrame frame = new JFrame ("单窗口应用" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(400 , 300 ); frame.setVisible(true ); } }
多窗口关闭 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class MultiWindowApp { private static int openWindows = 0 ; public static void main (String[] args) { createMainWindow(); JButton newWindowBtn = new JButton ("打开新窗口" ); } private static void createMainWindow () { JFrame mainWindow = new JFrame ("主窗口" ); openWindows++; mainWindow.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); mainWindow.addWindowListener(new WindowAdapter () { @Override public void windowClosing (WindowEvent e) { if (openWindows == 1 ) { int result = JOptionPane.showConfirmDialog( mainWindow, "是否退出程序? " , "退出确认" , JOptionPane.YES_NO_OPTION ); if (result == JOptionPane.YES_OPTION) { System.exit(0 ); } } else { openWindows--; mainWindow.dispose(); } } }); mainWindow.setSize(400 , 300 ); mainWindow.setVisible(true ); } }
带线程窗口 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 public class AppWithBackgroundThread { private static volatile boolean running = true ; private static Thread backgroundThread; public static void main (String[] args) { startBackgroundThread(); JFrame frame = new JFrame ("后台任务应用" ); frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE); frame.addWindowListener(new WindowAdapter () { @Override public void windowClosing (WindowEvent e) { int option = JOptionPane.showConfirmDialog( frame, "后台任务正在运行, 确定要退出吗? " , "确认退出" , JOptionPane.YES_NO_OPTION ); if (option == JOptionPane.YES_OPTION) { running = false ; try { backgroundThread.join(3000 ); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } System.exit(0 ); } } }); frame.setSize(400 , 300 ); frame.setVisible(true ); } private static void startBackgroundThread () { backgroundThread = new Thread (() -> { while (running) { try { System.out.println("后台任务执行中..." ); Thread.sleep(2000 ); } catch (InterruptedException e) { Thread.currentThread().interrupt(); break ; } } System.out.println("后台线程已停止" ); }); backgroundThread.setDaemon(false ); backgroundThread.start(); } }
高级特性 自定义外观 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class LookAndFeelExample { public static void main (String[] args) { try { UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName()); } catch (Exception e) { e.printStackTrace(); } JFrame frame = new JFrame ("外观示例" ); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setSize(300 , 200 ); frame.setVisible(true ); } }
定时器 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class TimerExample { public static void main (String[] args) { JFrame frame = new JFrame ("定时器示例" ); frame.setLayout(new FlowLayout ()); JLabel label = new JLabel ("0" ); label.setFont(new Font ("Arial" , Font.BOLD, 24 )); Timer timer = new Timer (1000 , e -> { int value = Integer.parseInt(label.getText()); label.setText(String.valueOf(value + 1 )); }); JButton startBtn = new JButton ("开始" ); JButton stopBtn = new JButton ("停止" ); startBtn.addActionListener(e -> timer.start()); stopBtn.addActionListener(e -> timer.stop()); frame.add(label); frame.add(startBtn); frame.add(stopBtn); frame.pack(); frame.setVisible(true ); } }
多线程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 public class SwingThreadExample { public static void main (String[] args) { SwingUtilities.invokeLater(() -> { createAndShowGUI(); }); } private static void createAndShowGUI () { JFrame frame = new JFrame ("多线程示例" ); frame.setLayout(new FlowLayout ()); JLabel label = new JLabel ("等待..." ); JButton button = new JButton ("开始任务" ); button.addActionListener(e -> { new Thread (() -> { try { Thread.sleep(3000 ); SwingUtilities.invokeLater(() -> { label.setText("任务完成! " ); }); } catch (InterruptedException ex) { ex.printStackTrace(); } }).start(); label.setText("任务执行中..." ); }); frame.add(label); frame.add(button); frame.pack(); frame.setVisible(true ); } }
常见问题 线程安全 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public class BestPractice { public static void main (String[] args) { SwingUtilities.invokeLater(() -> { JFrame frame = new JFrame (); frame.setVisible(true ); }); } private void updateLabel (JLabel label, String text) { if (SwingUtilities.isEventDispatchThread()) { label.setText(text); } else { SwingUtilities.invokeLater(() -> label.setText(text)); } } }
资源管理 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 public class ResourceManagement { public static void main (String[] args) { JFrame frame = new JFrame (); frame.addWindowListener(new WindowAdapter () { @Override public void windowClosing (WindowEvent e) { cleanup(); System.exit(0 ); } }); try (InputStream is = new FileInputStream ("file.txt" )) { } catch (IOException e) { e.printStackTrace(); } } private static void cleanup () { } }
性能优化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 public class PerformanceOptimization { private static final Color HIGHLIGHT_COLOR = new Color (200 , 200 , 255 ); public class CustomPanel extends JPanel { public CustomPanel () { setDoubleBuffered(true ); } @Override protected void paintComponent (Graphics g) { super .paintComponent(g); } } private JComponent createExpensiveComponent () { if (expensiveComponent == null ) { expensiveComponent = new ExpensiveComponent (); } return expensiveComponent; } }
界面卡顿 1 2 3 4 5 6 7 8 9 10 11 12 13 button.addActionListener(e -> { longRunningOperation(); }); button.addActionListener(e -> { new Thread (() -> { longRunningOperation(); SwingUtilities.invokeLater(() -> updateUI()); }).start(); });
组件刷新 1 2 3 4 5 panel.add(newButton); panel.revalidate(); panel.repaint();
内存泄漏 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class MyFrame extends JFrame { private Timer timer; public MyFrame () { timer = new Timer (1000 , e -> updateTime()); timer.start(); } @Override public void dispose () { if (timer != null ) { timer.stop(); timer = null ; } super .dispose(); } }
学习资源
视频
韩顺平: 零基础 30 天学会 Java: https://www.bilibili.com/video/BV1fh411y7R8
宋红康: 全网最全Java零基础入门教程: https://www.bilibili.com/video/BV1Kb411W75N
JDK8 新特性详解: https://www.bilibili.com/video/BV1k64y1R7sA
Java 核心技术卷: https://www.bilibili.com/video/BV18u411q7jv
【 尚硅谷】 大厂必备技术之JUC并发编程2021最新版: https://www.bilibili.com/video/BV1Kw411Z7dF
黑马程序员全面深入学习Java并发编程: https://www.bilibili.com/video/BV16J411h7Rd
文档
菜鸟教程 Java: https://www.runoob.com/java/java-tutorial.html
菜鸟教程 Java 8: https://www.runoob.com/java/java8-new-features.html
廖雪峰 Java 教程: https://www.liaoxuefeng.com/wiki/1252599548343744
Java API 中文文档: https://www.matools.com/api/java8
Java 并发知识点总结: https://github.com/CL0610/Java-concurrency
书籍
《 Java 核心技术卷 I》 : https://pan.baidu.com/s/1G36MF1XrLhKMaBpNcfM61g?pwd=ep11
工具
在线编写运行: https://c.runoob.com/compile/10/
游戏
Codegym: https://codegym.cc/zh
练手项目
牛客题库: https://www.nowcoder.com/intelligentTest
Java 实现简单计算器: https://www.lanqiao.cn/courses/185
Eclipse 实现 Java 编辑器: https://www.lanqiao.cn/courses/287
一本糊涂账: https://how2j.cn/module/104.html
Java 五子棋: https://blog.csdn.net/cnlht/article/details/8176130
Java 中国象棋: https://blog.csdn.net/cnlht/article/details/8205733
JAVA GUI 图书馆管理系统: https://github.com/uboger/LibraryManager
JAVA 坦克大战小游戏: https://github.com/wangzhengyi/TankWar
Swing 编写的俄罗斯方块: https://github.com/HelloClyde/Tetris-Swing
小小记账本: https://github.com/xenv/SmallAccount