哈喽大家!最近写代码的时候,我被异常折磨得够呛:写 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();
        }
    }
}

四、课设里常见的异常处理踩坑指南

  1. 空指针异常(NullPointerException):写代码的时候,调用对象的方法前一定要判空!比如if (list != null && !list.isEmpty()),别偷懒直接调用list.get(0)
  2. 捕获异常太宽泛:很多同学喜欢直接catch (Exception e),这样会把所有异常都吞掉,出了问题根本不知道是哪里错了。最好是精准捕获特定异常,比如catch (IOException e)
  3. finally 里写了 return:千万别在 finally 里写 return!finally 里的 return 会覆盖 try 和 catch 里的返回值,而且会隐藏异常信息,调试的时候根本找不到问题
  4. 不打印异常信息:很多同学写catch的时候只写个e.printStackTrace(),或者干脆不写,出了问题根本看不到错误栈。建议课设里用System.err.println(e.getMessage())打印关键信息,方便调试

五、写在最后

以前总觉得异常处理是 “麻烦事”,直到写课设的时候被各种异常搞崩溃,才明白它的重要性。其实异常处理不是为了应付编译器,而是为了让我们的程序在出错的时候,能优雅地处理,而不是直接崩溃。

后面写课设或者项目的时候,别再忽略异常处理啦,多花几分钟写好 try-catch,调试的时候能省超多事~有什么问题欢迎在评论区一起讨论呀!

更多推荐