关于 java:4. 异常处理与调试

news2025/6/3 9:38:03

一、异常核心语法

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 区别对比表:

特性throwthrows
用途抛出异常对象声明异常可能被抛出
位置方法体内方法声明处
后面跟的内容异常对象(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-catchthrows 处理

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 messageThrowable 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.NullPointerException
    at Demo.main(Demo.java:4)

5)如何防止空指针

方法示例
非空判断if (obj != null)
OptionalOptional.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

虽然 objObject 类型,但实际它是 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.Integer
    at 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.NullPointerException
    at 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.NullPointerException
    at 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.Integer
    at 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 bySuppressed

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 渗透中判断逻辑利用返回错误堆栈,看系统是怎么解析参数和验证身份

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2395347.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

day1-小白学习JAVA---JDK安装和环境变量配置(mac版)

JDK安装和环境变量配置 我的电脑系统一、下载JDK1、oracle官网下载适合的JDK安装包&#xff0c;选择Mac OS对应的版本。 二、安装三、配置环境变量1、终端输入/usr/libexec/java_home -V查询所在的路径&#xff0c;复制备用2、输入ls -a3、检查文件目录中是否有.bash_profile文…

数据分析实战1(Excel制作报表)

Excel数据链接&#xff1a;【课程4.0】第2章_Excel.zip - 飞书云文档 1、拿到数据第一步 备份数据 ctrlshiftL&#xff1a;筛选 相关快捷键&#xff1a;&#xff08;alt&#xff1a;自动求和、ctrlshift5&#xff1a;转换为%&#xff09; 2、环比、同比 环比&#xff08;本…

本地部署大模型llm+RAG向量检索问答系统 deepseek chatgpt

项目视频讲解: 本地部署大模型llm+RAG向量检索问答系统 deepseek chatgpt_哔哩哔哩_bilibili 运行结果:

LabVIEW 中内存释放相关问题

在LabVIEW 编程领域&#xff0c;内存管理是一个关键且复杂的议题。我们常常关注 LabVIEW 如何将内存释放回操作系统&#xff08;OS&#xff09;&#xff0c;以及是否有方法确保在特定数据结构&#xff08;如队列、变体属性、动态数据引用 DVR 等&#xff09;销毁、删除或清空后…

基于内存高效算法的 LLM Token 优化:一个有效降低 API 成本的技术方案

在使用 OpenAI、Claude、Gemini 等大语言模型 API 构建对话系统时&#xff0c;开发者普遍面临成本不断上升的挑战。无论是基于检索增强生成&#xff08;RAG&#xff09;的应用还是独立的对话系统&#xff0c;这些系统都需要维护对话历史以确保上下文的连贯性&#xff0c;类似于…

Python打卡训练营Day42

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

基于微信小程序的scratch学习系统

博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;熟悉各种主流语言&#xff0c;精通java、python、php、爬虫、web开发&#xff0c;已经做了六年的毕业设计程序开发&#xff0c;开发过上千套毕业设计程序&#xff0c;没有什么华丽的语言&#xff0…

【C++ 多态】—— 礼器九鼎,釉下乾坤,多态中的 “风水寻龙诀“

欢迎来到一整颗红豆的博客✨&#xff0c;一个关于探索技术的角落&#xff0c;记录学习的点滴&#x1f4d6;&#xff0c;分享实用的技巧&#x1f6e0;️&#xff0c;偶尔还有一些奇思妙想&#x1f4a1; 本文由一整颗红豆原创✍️&#xff0c;感谢支持❤️&#xff01;请尊重原创…

SCSAI平台面向对象建模技术的设计与实现

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

pikachu通关教程-CSRF

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

智能体觉醒:AI开始自己“动手”了-自主进化开启任务革命时代

1. 智能体&#xff1a;AI从“工具”到“伙伴”的关键跃迁 1.1 什么是智能体&#xff1f; 智能体&#xff08;Agent&#xff09;是AI的“进化版”——它不再局限于生成文字或图像&#xff0c;而是能像人类一样“规划任务”“调用工具”甚至“协同合作”。例如&#xff0c;一个…

【C++指南】C++ list容器完全解读(二):list模拟实现,底层架构揭秘

. &#x1f493; 博客主页&#xff1a;倔强的石头的CSDN主页 &#x1f4dd;Gitee主页&#xff1a;倔强的石头的gitee主页 ⏩ 文章专栏&#xff1a;《C指南》 期待您的关注 文章目录 引言一、链表节点设计&#xff1a;双向链表的基石1.1 节点类的实现 二、list框架与核心成员函…

[神经网络]使用olivettiface数据集进行训练并优化,观察对比loss结果

结合归一化和正则化来优化网络模型结构&#xff0c;观察对比loss结果 搭建的神经网络&#xff0c;使用olivettiface数据集进行训练&#xff0c;结合归一化和正则化来优化网络模型结构&#xff0c;观察对比loss结果 from sklearn.datasets import fetch_olivetti_faces #倒入数…

华院计算出席信创论坛,分享AI教育创新实践并与燧原科技共同推出教育一体机

5月21日&#xff0c;信创论坛于上海漕河泾会议中心举办。本次论坛以“聚力融合&#xff0c;繁荣生态”为主题&#xff0c;话题聚焦工业制造、交通运输、金融、教育、医疗等领域。华院计算技术&#xff08;上海&#xff09;股份有限公司&#xff08;以下简称“华院计算”&#x…

华为OD机试真题——会议接待 /代表团坐车(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》 华为OD机试真题《会议…

LabVIEW Val (Sgnl) 属性

在 LabVIEW 事件驱动架构中&#xff0c;Val (Sgnl) 属性&#xff08;Value (Signaling)&#xff09;是实现编程触发与用户交互行为一致性的关键技术。与普通 Value 属性不同&#xff0c;Val (Sgnl) 在修改控件值的同时强制生成值改变事件&#xff0c;确保程序逻辑与 UI 交互保持…

STM32G4 电机外设篇(三) TIM1 发波 和 ADC COMP DAC级联

目录 一、STM32G4 电机外设篇&#xff08;三&#xff09; TIM1 发波 和 ADC COMP DAC级联1 TIM1 高级定时器发波1.1 stm32cubemx配置 2 TIM1 ADC COMP DAC级联2.1 stm32cubemx配置 附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^) 一、STM32G4 电机外设篇&#xff08;三&…

DAY 35 超大力王爱学Python

知识点回顾&#xff1a; 三种不同的模型可视化方法&#xff1a;推荐torchinfo打印summary权重分布可视化进度条功能&#xff1a;手动和自动写法&#xff0c;让打印结果更加美观推理的写法&#xff1a;评估模式 作业&#xff1a;调整模型定义时的超参数&#xff0c;对比下效果。…

【数据结构】图的存储(十字链表)

弧节点 tailvex数据域&#xff1a;存储弧尾一端顶点在顺序表中的位置下标&#xff1b;headvex 数据域&#xff1a;存储弧头一端顶点在顺序表中的位置下标&#xff1b;hlink 指针域&#xff1a;指向下一个以当前顶点作为弧头的弧&#xff1b;tlink 指针域&#xff1a;指向下一个…

Redis最佳实践——秒杀系统设计详解

基于Redis的高并发秒杀系统设计&#xff08;十万级QPS&#xff09; 一、秒杀系统核心挑战 瞬时流量洪峰&#xff1a;100万 QPS请求冲击库存超卖风险&#xff1a;精准扣减防止超卖系统高可用性&#xff1a;99.99%服务可用性要求数据强一致性&#xff1a;库存/订单/支付状态同步…