Java 异常处理全解析,告别空指针与资源泄漏,写出稳如老狗的代码
·
哈喽大家!最近写代码的时候,我被异常折磨得够呛:写 IO 流复制文件时忘关流,导致资源泄漏;调用接口时没判空,直接报了空指针;还有 try-catch 写得乱七八糟,出了问题根本找不到原因。后来跟着老师的教程系统学了 Java 异常处理,才发现原来异常处理不仅是语法,更是写出稳定代码的关键。今天就把我的学习笔记和踩坑经验分享给大家~
一、为什么异常处理是 Java 开发的必修课?
前面我们聊了 IO 流、集合、多线程这些高频 API,而异常处理就像这些 API 的 “安全护栏”:
- 写 IO 流复制文件时,如果文件不存在,不处理
IOException程序会直接崩溃 - 调用
list.get(0)时,如果 list 是空的,会直接抛出NullPointerException - 数据库操作时,如果不处理
SQLException,可能会导致连接泄漏,占用大量资源
很多同学写课设的时候,总觉得异常处理是 “多余的代码”,结果运行的时候一报错就手忙脚乱。其实掌握了异常处理,不仅能让程序更稳定,还能快速定位问题~
二、Java 异常体系核心结构
Java 的异常体系其实很清晰,核心可以分为三大类,用一张图就能看懂:
plaintext
Throwable
├─ Error(错误):JVM级别的错误,比如内存溢出、栈溢出,程序根本处理不了
└─ Exception(异常)
├─ RuntimeException(运行时异常/非受检异常):比如空指针、数组越界,编译器不强制处理
└─ 受检异常(Checked Exception):比如IOException、SQLException,编译器强制你处理
受检异常 vs 非受检异常对比
表格
| 对比项 | 受检异常 | 非受检异常 |
|---|---|---|
| 编译器要求 | 必须用 try-catch 捕获,或者 throws 声明抛出 | 不强制处理,写不写都行 |
| 产生原因 | 外部因素导致的,比如文件不存在、网络断开、数据库连不上 | 代码逻辑写错了,比如空指针、数组下标越界 |
| 课设里的例子 | 复制文件时的IOException |
调用空对象的方法导致的NullPointerException |
三、异常处理核心语法与最佳实践
1. try-catch-finally 基础用法(IO 流版)
之前写 IO 流复制文件时,我总是忘关流,导致资源泄漏。后来学会了用 try-catch-finally,就再也没踩过这个坑:
java
运行
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class IoExceptionDemo {
public static void main(String[] args) {
FileInputStream in = null;
FileOutputStream out = null;
try {
in = new FileInputStream("source.txt");
out = new FileOutputStream("target.txt");
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
// 捕获异常,打印错误信息,方便排查问题
System.err.println("文件复制出错:" + e.getMessage());
e.printStackTrace();
} finally {
// finally里的代码不管有没有异常都会执行,用来关流最合适
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2. 更优雅的 try-with-resources 写法(Java7+)
后来我发现 Java7 之后有个超方便的语法try-with-resources,不用手动写 finally 关流了,它会自动帮我们关闭实现了AutoCloseable接口的资源,代码一下子清爽了:
java
运行
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class TryWithResourcesDemo {
public static void main(String[] args) {
// 把流对象放在try的括号里,结束后会自动关闭
try (FileInputStream in = new FileInputStream("source.txt");
FileOutputStream out = new FileOutputStream("target.txt")) {
byte[] buffer = new byte[1024];
int len;
while ((len = in.read(buffer)) != -1) {
out.write(buffer, 0, len);
}
} catch (IOException e) {
System.err.println("文件复制出错:" + e.getMessage());
e.printStackTrace();
}
}
}
四、课设里常见的异常处理踩坑指南
- 空指针异常(NullPointerException):写代码的时候,调用对象的方法前一定要判空!比如
if (list != null && !list.isEmpty()),别偷懒直接调用list.get(0) - 捕获异常太宽泛:很多同学喜欢直接
catch (Exception e),这样会把所有异常都吞掉,出了问题根本不知道是哪里错了。最好是精准捕获特定异常,比如catch (IOException e) - finally 里写了 return:千万别在 finally 里写 return!finally 里的 return 会覆盖 try 和 catch 里的返回值,而且会隐藏异常信息,调试的时候根本找不到问题
- 不打印异常信息:很多同学写
catch的时候只写个e.printStackTrace(),或者干脆不写,出了问题根本看不到错误栈。建议课设里用System.err.println(e.getMessage())打印关键信息,方便调试
五、写在最后
以前总觉得异常处理是 “麻烦事”,直到写课设的时候被各种异常搞崩溃,才明白它的重要性。其实异常处理不是为了应付编译器,而是为了让我们的程序在出错的时候,能优雅地处理,而不是直接崩溃。
后面写课设或者项目的时候,别再忽略异常处理啦,多花几分钟写好 try-catch,调试的时候能省超多事~有什么问题欢迎在评论区一起讨论呀!
更多推荐

所有评论(0)