关于 java:4. 异常处理与调试
一、异常核心语法
1.1 try-catch-finally
:异常捕获与处理结构
1)作用
-
用于捕获和处理程序运行过程中可能发生的异常
-
防止程序因异常中断,提高代码的鲁棒性(健壮性)
2)基本语法结构:
try {// 可能抛出异常的代码块
} catch (ExceptionType1 e1) {// 处理 ExceptionType1 类型的异常
} catch (ExceptionType2 e2) {// 处理 ExceptionType2 类型的异常
} finally {// 无论是否发生异常,都会执行(如关闭资源)
}
3)示例讲解:
public class Demo {public static void main(String[] args) {try {int a = 10 / 0; // 运行时异常:ArithmeticException} catch (ArithmeticException e) {System.out.println("错误:除数不能为零!");} finally {System.out.println("程序结束,释放资源。");}}
}
运行结果:
错误:除数不能为零!
程序结束,释放资源。
4)多个 catch
:
try {String s = null;System.out.println(s.length());
} catch (NullPointerException e) {System.out.println("空指针异常!");
} catch (Exception e) {System.out.println("其他异常:" + e.getMessage());
}
建议先写具体异常,再写父类(Exception
),否则子类异常无法被捕获。
5)finally
详解
-
无论 try 块是否抛出异常,finally 总会执行
-
通常用于释放资源,如关闭文件、数据库连接
FileInputStream fis = null;
try {fis = new FileInputStream("data.txt");// 读取文件
} catch (IOException e) {System.out.println("读取失败");
} finally {if (fis != null) {try {fis.close(); // 一定要关闭资源} catch (IOException e) {e.printStackTrace();}}
}
1.2 throw
:手动抛出异常对象
1)作用
throw
用于在代码中主动抛出一个异常实例,可以抛出任何 Throwable
的子类。
2)语法格式:
throw new 异常类型("异常描述");
3)示例:
public void checkAge(int age) {if (age < 0) {throw new IllegalArgumentException("年龄不能为负数");}
}
-
程序执行到
throw
语句时会立即抛出异常并中断执行 -
如果这个异常没有在方法内被
try-catch
捕获,必须用throws
声明
1.3 throws
:方法声明异常
1)作用
-
用于方法签名中,声明该方法可能抛出哪些异常
-
告诉调用者:你要么用
try-catch
处理,要么继续throws
抛
2)语法格式:
返回类型 方法名(...) throws 异常类型1, 异常类型2 {// 可能抛出异常的代码
}
3)示例:
public void readFile(String path) throws IOException {FileReader fr = new FileReader(path); // FileReader 会抛 IOException
}
调用时:
try {readFile("test.txt");
} catch (IOException e) {System.out.println("文件读取失败:" + e.getMessage());
}
4)throw
vs throws
区别对比表:
特性 | throw | throws |
---|---|---|
用途 | 抛出异常对象 | 声明异常可能被抛出 |
位置 | 方法体内 | 方法声明处 |
后面跟的内容 | 异常对象(new) | 异常类(不带 new) |
示例 | throw new IOException("失败") | throws IOException |
1.4 综合例子:使用 throw
+ throws
+ try-catch
public class User {public void login(String username) throws Exception {if (username == null || username.isEmpty()) {throw new Exception("用户名不能为空");}System.out.println("登录成功");}public static void main(String[] args) {User user = new User();try {user.login(""); // 会抛出异常} catch (Exception e) {System.out.println("捕获到异常: " + e.getMessage());} finally {System.out.println("登录尝试结束");}}
}
输出:
捕获到异常: 用户名不能为空
登录尝试结束
1.5 实际用法
场景 | 应用 |
---|---|
开发中 | 用 try-catch 处理用户输入、文件读取等不确定行为 |
SDK 调试 | 通过日志堆栈 catch (Exception e) 观察调用流程 |
逆向分析中 | Hook 异常处理函数,绕过 throw 抛出的错误(例如:校验失败) |
安全测试中 | 利用错误提示、异常堆栈进行路径发现或代码注入入口分析 |
1.6 小结
try-catch-finally
│
├─ try:放入可能出错的代码
├─ catch:处理指定异常类型
├─ finally:一定执行,用于释放资源
│
throw:主动抛出异常对象
throws:方法声明可能抛出哪些异常
二、自定义异常类
2.1 自定义异常类的作用
在 Java 中,除了使用系统提供的异常(如 NullPointerException
, IOException
),我们还可以根据自己的业务逻辑需求定义新的异常类。
自定义异常的典型用途:
-
表示业务逻辑错误(例如:余额不足、权限异常)
-
抛出更清晰可读、可追踪的错误
-
与项目的模块/组件解耦,提高代码可维护性
-
在调试或逆向中,定位异常的抛出源
2.2 自定义异常类的本质
Java 中所有异常类,最终都继承自:
java.lang.Throwable├── Error // 错误(虚拟机错误等)└── Exception // 异常├── RuntimeException(运行时异常)└── 其他受检异常(IOException 等)
我们自定义的异常通常继承自:
1)Exception
(受检异常)
-
必须用
try-catch
或throws
处理
2)RuntimeException
(非受检异常)
-
编译器不强制捕获
-
更灵活,适合应用内部逻辑异常
2.3 自定义异常类的语法
1)继承 Exception
(受检异常)
public class MyCheckedException extends Exception {public MyCheckedException() {super();}public MyCheckedException(String message) {super(message);}public MyCheckedException(String message, Throwable cause) {super(message, cause);}
}
2)继承 RuntimeException
(非受检异常)
public class MyRuntimeException extends RuntimeException {public MyRuntimeException(String message) {super(message);}
}
2.4 使用自定义异常的例子
示例 1:余额不足异常
// 自定义异常类
public class InsufficientBalanceException extends Exception {public InsufficientBalanceException(String message) {super(message);}
}
示例 2:在业务代码中使用
public class BankAccount {private double balance = 100.0;public void withdraw(double amount) throws InsufficientBalanceException {if (amount > balance) {throw new InsufficientBalanceException("余额不足,取款失败!");}balance -= amount;}
}
示例 3:调用者处理异常
public class Test {public static void main(String[] args) {BankAccount account = new BankAccount();try {account.withdraw(150.0); // 触发异常} catch (InsufficientBalanceException e) {System.out.println("异常捕获:" + e.getMessage());}}
}
2.5 规范建议(编写自定义异常)
建议项 | 内容 |
---|---|
类名 | 以 Exception 结尾(如 LoginFailedException ) |
构造方法 | 提供 String message 、Throwable cause 构造器 |
继承方式 | 业务类建议继承 Exception ,内部错误建议继承 RuntimeException |
包名 | 放在 com.xxx.exception 包下,统一管理 |
2.6 自定义异常在调试/逆向中的价值
在调试中:
-
通过日志或堆栈跟踪定位自定义异常的抛出点
-
比系统异常更具语义性,便于快速理解错误
在逆向中:
-
某些 SDK 或加密逻辑会用自定义异常抛出校验错误
-
通过 Frida/日志/trace 定位异常类名和抛出位置
-
分析异常触发条件,进而绕过或构造伪装数据
2.7 小结
自定义异常类
├─ 为什么要自定义?
├─ 继承 Exception / RuntimeException
├─ 如何定义:构造器 + 命名规范
├─ 如何使用:抛出 throw + 声明 throws
├─ 实际场景:业务逻辑错误、逆向定位
三、常见异常类型
3.1 NullPointerException
(空指针异常)
1)定义
空指针异常是指:访问了一个为 null
的引用对象的方法或字段时引发的异常。
java.lang.NullPointerException
2)常见触发场景
触发语句 | 说明 |
---|---|
obj.toString() | obj 为 null,调用方法抛异常 |
obj.field | 访问成员变量时,obj 为 null |
arr[0] | arr 是 null,访问数组元素抛异常 |
list.get(0) | list 是 null,而非空但索引越界时会抛 IndexOutOfBoundsException |
3)示例代码
public class Demo {public static void main(String[] args) {String s = null;System.out.println(s.length()); // NullPointerException}
}
4)调试方式
-
查看异常栈信息(Exception stack trace)
-
从堆栈中定位异常行号和方法名
-
使用 IDE 的断点或日志逐步排查 null 来源
栈追踪示例:
Exception in thread "main" java.lang.NullPointerExceptionat Demo.main(Demo.java:4)
5)如何防止空指针
方法 | 示例 |
---|---|
非空判断 | if (obj != null) |
Optional | Optional.ofNullable(obj).orElse(defaultValue) |
IDE 工具提醒 | IntelliJ IDEA 有 null 检测功能 |
Lombok 的 @NonNull 注解 | 编译时校验是否为 null |
3.2 ClassCastException
(类强制类型转换异常)
1)定义
该异常表示:试图将某个对象强制转换为不是其实际类型的类时引发的异常。
java.lang.ClassCastException
2)常见触发场景
Object obj = new Integer(5);
String str = (String) obj; // 报 ClassCastException
虽然 obj
是 Object
类型,但实际它是 Integer
,不能强制转成 String
。
3)示例代码
public class Demo {public static void main(String[] args) {Object obj = "Hello";Integer num = (Integer) obj; // ClassCastException}
}
输出异常信息:
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integerat Demo.main(Demo.java:4)
4)如何避免 ClassCastException
方法 | 示例 |
---|---|
使用 instanceof 判断 | if (obj instanceof Integer) |
使用泛型(推荐) | List<String> 比 List<Object> 更安全 |
明确接口和实现类边界 | 不随意强转接口实现 |
开发规范约束 | 明确参数传递和接收类型 |
3.3 调试角度分析
异常类型 | 常见错误栈信息 | 调试关键点 |
---|---|---|
NullPointerException | 指向具体行的空对象访问 | 追踪为 null 的变量,检查赋值 |
ClassCastException | 指出无法转换的两个类 | 看实际对象的类型(getClass() )和要转的类 |
3.4 在逆向/安全分析中的价值
1)辅助分析代码结构
-
App crash 日志中出现
ClassCastException
可能表示有 代码逻辑混淆或伪装 -
出现
NullPointerException
时,反编译代码可定位关键对象未初始化
2)模拟异常绕过检查
try {if (!licenseValid) {throw new NullPointerException("验证失败");}
} catch (Exception e) {// 验证失败后中断return;
}
可以 Hook 掉这个 throw
,或者强改 licenseValid
为 true,绕过验证。
3.5 小结
异常 | 本质 | 触发时机 | 预防方法 |
---|---|---|---|
NullPointerException | 空引用访问 | 访问 null 的变量、方法或数组 | 非空检查、Optional |
ClassCastException | 类型错误 | 强转成非实际类型 | instanceof 判断、泛型 |
四、堆栈追踪分析
4.1 什么是堆栈追踪(Stack Trace)?
堆栈追踪是 Java 程序在运行中发生异常或错误时,JVM 自动打印的一系列方法调用栈信息,描述了从异常发生点逐层向上传递调用关系。
示例异常信息(Stack Trace):
Exception in thread "main" java.lang.NullPointerExceptionat com.example.MyClass.myMethod(MyClass.java:10)at com.example.App.main(App.java:5)
4.2 Stack Trace 的结构组成
以典型的一条栈帧为例:
at com.example.MyClass.myMethod(MyClass.java:10)
部分 | 含义 |
---|---|
at | 表示当前调用栈的一帧 |
com.example.MyClass | 异常发生的类 |
myMethod | 异常发生的方法 |
(MyClass.java:10) | 源码文件及第几行发生了异常(10 行) |
4.3 堆栈追踪常见异常样式(举例对照)
1)NullPointerException(空指针)
Exception in thread "main" java.lang.NullPointerExceptionat com.demo.DemoClass.printName(DemoClass.java:15)at com.demo.DemoClass.main(DemoClass.java:7)
分析:
-
异常发生在
DemoClass.java
的第 15 行 -
是从
main()
调用printName()
时触发的
2)ClassCastException(类强制转换)
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integerat com.test.CastTest.main(CastTest.java:8)
分析:
-
明确告诉你:实际是
String
,你试图转成Integer
-
报错发生在第 8 行
4.4 如何进行堆栈追踪分析?
步骤 1:从上到下阅读栈帧(第一条才是出错点)
-
第一条是异常抛出的具体位置
-
后面的每条是“谁调用了它”
步骤 2:对照源码或反编译代码,定位具体代码行
-
使用 IDE 跳转对应行(Ctrl + 单击)
-
或用
jadx
,JD-GUI
,Fernflower
反编译查看.class
步骤 3:结合异常类型,分析出错条件
-
是不是参数为 null?
-
是不是数据转换错误?
-
是不是调用了非法对象?
4.5 逆向调试场景中的应用
1)分析 App 崩溃日志
Caused by: java.lang.RuntimeException: 解密失败at com.app.secure.SecurityManager.decrypt(SecurityManager.java:87)at com.app.net.NetHandler.getUserInfo(NetHandler.java:122)
这说明:
-
decrypt()
方法出错,可能在使用 AES、RSA 时 key 异常 -
你可以重点 hook 这一段代码,或者跟进加密过程逻辑
2)分析 Web 安全漏洞、异常行为
java.lang.IllegalArgumentException: 参数不合法at com.web.api.AuthHandler.checkToken(AuthHandler.java:52)at com.web.api.UserController.getUser(UserController.java:19)
说明:
-
checkToken
方法校验失败,抛出异常 -
可用于判断接口是否有 token 依赖点,或可伪造点
3)结合 Frida 实现实时监控异常
可以使用 Frida 追踪所有异常抛出点:
Java.perform(function () {var Exception = Java.use("java.lang.Exception");Exception.$init.overload('java.lang.String').implementation = function (msg) {console.log("Exception 被抛出: " + msg);return this.$init(msg);};
});
4.6 分析:Caused by
和 Suppressed
Caused by
有时异常是嵌套异常,会看到:
Exception in thread "main" java.lang.Exception: 顶层异常
Caused by: java.io.IOException: 文件不存在at com.file.Reader.read(Reader.java:45)
-
Caused by
表示底层真正触发的异常 -
要分析最底层原因
Suppressed
当使用 try-with-resources 时,可能出现:
Suppressed: java.lang.Exception: 关闭资源失败
说明主异常之后还有资源释放过程中的异常。
4.7 小结(堆栈分析三步法)
堆栈分析三步法:
1. 定位第 1 行出错代码(准确行号)
2. 判断异常类型及触发原因
3. 向上追踪调用链,分析调用过程
4.8 实战建议
场景 | 技巧 |
---|---|
逆向异常分析 | 拿到 crash log,反编译对应类,查找触发条件 |
安全测试中断点排查 | 抓住抛异常的函数,设置 Frida Hook 或 JDWP 调试点 |
Web 渗透中判断逻辑 | 利用返回错误堆栈,看系统是怎么解析参数和验证身份 |
相关文章:
关于 java:4. 异常处理与调试
一、异常核心语法 1.1 try-catch-finally:异常捕获与处理结构 1)作用 用于捕获和处理程序运行过程中可能发生的异常 防止程序因异常中断,提高代码的鲁棒性(健壮性) 2)基本语法结构: try {…...
Java基础 Day26
一、网络编程简介 1、概念 网络编程指在网络通信协议下,不同计算机上运行的程序,进行数据传输 2、软件架构 (1)CS架构(客户端和服务端) 在用户本地有一个客户端程序,在远程有一个服务器端程…...
android lifeCycleOwner生命周期
一 Fragment中 viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) 什么时候执行? 让我分析一下相关问题: 关于 onPause 时的数据更新: viewLifecycleOwner.lifecycleScope.launch {viewLifecycleOwner.repeatOnLifecycle(Lifecycle.Sta…...
高防IP能抗住500G攻击吗?
在当今互联网环境中,网络安全问题日益严峻,尤其是针对网站的DDoS攻击,更是让众多站长头疼不已。而高防IP作为应对此类攻击的有效手段,其性能与稳定性成为了大家关注的焦点。那么,高防IP真的能抗住500G的超大流量攻击吗…...
工作流引擎-10-什么是 BPM?
工作流引擎系列 工作流引擎-00-流程引擎概览 工作流引擎-01-Activiti 是领先的轻量级、以 Java 为中心的开源 BPMN 引擎,支持现实世界的流程自动化需求 工作流引擎-02-BPM OA ERP 区别和联系 工作流引擎-03-聊一聊流程引擎 工作流引擎-04-流程引擎 activiti 优…...

day1-小白学习JAVA---JDK安装和环境变量配置(mac版)
JDK安装和环境变量配置 我的电脑系统一、下载JDK1、oracle官网下载适合的JDK安装包,选择Mac OS对应的版本。 二、安装三、配置环境变量1、终端输入/usr/libexec/java_home -V查询所在的路径,复制备用2、输入ls -a3、检查文件目录中是否有.bash_profile文…...
每日温度(力扣-739)
【题目描述】 给定一个整数数组 temperatures ,表示每天的温度,返回一个数组 answer ,其中 answer[i] 是指对于第 i 天,下一个更高温度出现在几天后。如果气温在这之后都不会升高,请在该位置用 0 来代替。 【输出输出样…...
QT中子线程触发主线程弹窗并阻塞等待用户响应-传统信号槽实现
目录 QT中子线程触发主线程弹窗并阻塞等待用户响应传统信号槽实现实现思路具体步骤1. 定义信号与槽2. 异步任务中触发弹窗3. 主线程处理弹窗4. 连接信号与槽关键点总结 更简单实现 QT中子线程触发主线程弹窗并阻塞等待用户响应 传统信号槽实现 场景需求:在子线程执…...
HarmonyOS鸿蒙系统深度运维指南
一、开发与调试环境全链路配置 工具链部署标准流程 HDC 3.0调试套件:支持分布式设备的跨端调试与性能分析,需配置端口转发规则(默认调试端口:8080)KaihongOS桌面开发环境:集成DevEco Studi…...
SpringBoot多租户系统的5种架构设计方案
多租户(Multi-tenancy)是一种软件架构模式,允许单个应用实例服务于多个客户(租户),同时保持租户数据的隔离性和安全性。 通过合理的多租户设计,企业可以显著降低运维成本、提升资源利用率,并实现更高效的服务交付。 本文将分享S…...

数据分析实战1(Excel制作报表)
Excel数据链接:【课程4.0】第2章_Excel.zip - 飞书云文档 1、拿到数据第一步 备份数据 ctrlshiftL:筛选 相关快捷键:(alt:自动求和、ctrlshift5:转换为%) 2、环比、同比 环比(本…...

本地部署大模型llm+RAG向量检索问答系统 deepseek chatgpt
项目视频讲解: 本地部署大模型llm+RAG向量检索问答系统 deepseek chatgpt_哔哩哔哩_bilibili 运行结果:...
设备健康管理的战略升维:用预测性维护重构企业竞争力
第一章 传统维护的沉默成本:被低估的利润黑洞 当轴承振动值突破安全阈值时,制造企业损失的远非维修费用。某重型装备制造厂的案例揭示了典型多米诺效应:传动系统突发故障导致36小时停产,触发订单违约金(合约金额的9%&…...
Redis事务详解:原理、使用与注意事项
文章目录 Redis事务详解:原理、使用与注意事项什么是Redis事务Redis事务的基本使用基本事务示例事务执行过程 Redis事务的错误处理1. 入队错误2. 执行错误 WATCH命令:乐观锁实现Redis事务的局限性事务的最佳实践Lua脚本总结 Redis事务详解:原…...
提升 GitHub Stats 的 6 个关键策略
哈哈,GitHub 的 “B-” 评级 其实是个玄学问题,但确实有一些 快速提升的技巧!你的数据看起来 提交数(147)和 PR(9)不算少,但 Stars(21)和贡献项目数ÿ…...
CSS Animation 详解
CSS Animation 允许元素平滑地从一个样式状态过渡到另一个样式状态。通过设置关键帧(keyframes),可以控制动画序列中的中间步骤。 一、核心概念 1.关键帧(Keyframes) 使用 keyframes 规则定义动画序列通过百分比或 …...

LabVIEW 中内存释放相关问题
在LabVIEW 编程领域,内存管理是一个关键且复杂的议题。我们常常关注 LabVIEW 如何将内存释放回操作系统(OS),以及是否有方法确保在特定数据结构(如队列、变体属性、动态数据引用 DVR 等)销毁、删除或清空后…...
【HarmonyOS 5】鸿蒙中的UIAbility详解(三)
【HarmonyOS 5】鸿蒙中的UIAbility详解(三) 一、前言 本文是鸿蒙中的UIAbility详解系列的最终章。主要针对UIAbility的冷启动和热启动,对于want数据的处理。UIAbility的备份恢复,UIAbility的接续等高级功能的概念和使用讲解。 …...

基于内存高效算法的 LLM Token 优化:一个有效降低 API 成本的技术方案
在使用 OpenAI、Claude、Gemini 等大语言模型 API 构建对话系统时,开发者普遍面临成本不断上升的挑战。无论是基于检索增强生成(RAG)的应用还是独立的对话系统,这些系统都需要维护对话历史以确保上下文的连贯性,类似于…...
vue-11(命名路由和命名视图)
命名路由和命名视图 命名路由和命名视图提供了组织和导航 Vue.js 应用程序的强大方法,尤其是在它们的复杂性增加时。它们提供了一种语义更合理、可维护的路由方法,使您的代码更易于理解和修改。命名路由允许您按名称引用路由,而不是依赖 URL…...
(附代码)自定义 LangChain 文档分割器,深入探索 LangChain 文档分割策略与应用
自定义文档分割器 在 LangChain 中,如果内置的文档分割器均没办法完成需求,还可以根据特定的需求实现自定义文档分割器(一般极少),实现的方法也非常简单,继承文本分割器基类 TextSplitter,在构造…...

Python打卡训练营Day42
DAY 42 Grad-CAM与Hook函数 知识点回顾 回调函数lambda函数hook函数的模块钩子和张量钩子Grad-CAM的示例 作业:理解下今天的代码即可 import torch import torch.nn as nn import torch.nn.functional as F import torchvision import torchvision.transforms as tr…...

基于微信小程序的scratch学习系统
博主介绍:java高级开发,从事互联网行业六年,熟悉各种主流语言,精通java、python、php、爬虫、web开发,已经做了六年的毕业设计程序开发,开发过上千套毕业设计程序,没有什么华丽的语言࿰…...
MATLAB实战:机器学习分类回归示例
以下是一个使用MATLAB的Statistics and Machine Learning Toolbox实现分类和回归任务的完整示例代码。代码包含鸢尾花分类、手写数字分类和汽车数据回归任务,并评估模型性能。 %% 加载内置数据集 % 鸢尾花数据集(分类) load fisheriris; X_i…...
动态库导出符号与extern “C“
1. windows下动态库导出符号 根据C/C语法规则,函数声明中的修饰符(如__declspec(dllexport))可以放在返回类型之前或返回类型之后、函数名之前。这两种方式在功能上是等价的,编译器会以相同的方式处理。 __declspec(dllexport) …...
小知识:STM32 printf 重定向(串口输出)--让数据 “开口说话” 的关键技巧
引言 在 C 语言开发中,printf函数是我们调试程序、输出数据的得力助手,它能将格式化的数据输出到标准输出设备(通常是屏幕)。然而,在嵌入式领域,STM32 单片机并没有默认的显示设备,要让printf函…...
`docker commit` 和 `docker save`区别
理解 docker commit 和 docker save 之间的区别对于正确管理 Docker 镜像非常重要。让我们详细解释一下这两个命令的作用及其区别。 1. docker commit 作用: docker commit roop-builder roop:v1 命令的作用是基于一个正在运行的容器 roop-builder 创建一个新的镜…...

【C++ 多态】—— 礼器九鼎,釉下乾坤,多态中的 “风水寻龙诀“
欢迎来到一整颗红豆的博客✨,一个关于探索技术的角落,记录学习的点滴📖,分享实用的技巧🛠️,偶尔还有一些奇思妙想💡 本文由一整颗红豆原创✍️,感谢支持❤️!请尊重原创…...

SCSAI平台面向对象建模技术的设计与实现
一、核心设计思想 SCSAI平台的核心目标是通过元建模(Meta-Modeling)技术实现面向对象建模的零编码化。其核心思想为: 自反性设计:定义ObjectClassInfo (OCI)为元类(Meta-Class),所有对象类均为…...

pikachu通关教程-CSRF
CSRF(get) 用bp进行抓包 选择action value值的修改 点击test in browser copy然后放在bp代理的浏览器上,会出现一个提交按钮,这时候点击之后信息就被修改了。 CSRF(post) 请求的方式不同,其他都是一样 CSRF Token 存在cookie 首先要先下载一…...