百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

三十七、Java异常处理(java如何异常处理)

csdh11 2025-04-10 22:03 6 浏览

在Java编程中,异常处理是一项至关重要的技能,让我们能够有效地应对程序运行过程中可能出现的各种错误状况,从而使程序更具健壮性。

什么是异常?

Java异常程序运行时出现的问题或错误的表示,代表了程序正常的控制流程被中断的情况。Java将异常分为两大类:checked异常unchecked异常(也称运行时异常)。

分类

描述

Checked Exception

在编译阶段就需要程序员处理,如果不处理或声明抛出,编译器将拒绝编译。例如,当你试图打开一个不存在的文件时,Java会抛出
java.io.FileNotFoundException ,这是一种checked异常。

Unchecked Exception

在运行时可能发生,但编译器不要求我们必须处理。最常见的unchecked异常是
java.lang.NullPointerException ,当我们试图访问一个
null对象的属性或方法时会出现此类异常。

Java异常处理机制

Java使用 try catch finally 语句结构来处理异常。

try {
  // 可能抛出异常的代码放在这里
  File file = new File("file.txt");
  FileReader reader = new FileReader(file);
} catch (FileNotFoundException e) {
  // 当在try块中抛出FileNotFoundException时,这里的代码会被执行
  System.out.println("文件未找到:" + e.getMessage());
} finally {
  // 不论是否发生异常,finally块中的代码总会被执行
  // 这里通常用于资源清理,如关闭文件流
  if (reader != null) {
    try {
      reader.close();
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

在上述代码中,尝试打开一个文件,如果文件不存在,就会触发 FileNotFoundException ,并在catch块中处理该异常。最后,无论是否发生异常,finally块都会执行,用于关闭文件流以释放系统资源。

手动抛出异常(throw)

通过 throw 关键字,程序员可以主动抛出自定义的异常或系统内置异常。

if (value < 0) {
  throw new IllegalArgumentException("参数值不能为负数");
}

声明方法抛出异常(throws)

在方法签名中,使用 throws 关键字声明方法可能会抛出的异常,将异常处理的责任转移给方法的调用者。

public void readFile(String filePath) throws IOException {
  File file = new File(filePath);
  //...
}

try-with-resources语句

try-with-resources 是 Java 7 引入的一个特性,用于自动管理资源,特别是那些实现了 AutoCloseable Closeable 接口的资源,如文件流数据库连接等。使用 try-with-resources 语句可以确保在 try 代码块执行完毕后,资源能够正确、及时地关闭,即使发生异常也是如此。

示例,假设我们有一个实现了 AutoCloseable 接口的资源类:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class TryWithResourcesExample {
  public static void main(String[] args) {
    // 假设我们有一个需要读取的文件
    String filePath = "example.txt";

    try (BufferedReader reader = new BufferedReader(new FileReader(filePath))) {
      String line;
      while ((line = reader.readLine()) != null) {
        System.out.println(line);
      }
      // 在这里,reader 的 close() 方法会在 try 代码块结束时自动被调用
    } catch (IOException e) {
      e.printStackTrace();
    } // 无需在这里显式调用 reader.close()

    // 此时的 reader 已经被自动关闭,无需担心资源泄露问题
  }
}

在上面的例子中,BufferedReaderAutoCloseable 的一个子接口 Closeable 的实现。当 try 代码块执行完毕时,无论是否发生异常,BufferedReader 的 close() 方法都会被自动调用。这样,就无需在 finally 代码块中显式地关闭资源,从而简化了代码,并减少了忘记关闭资源导致资源泄露的风险。

try-with-resources 语句中的资源声明必须是局部变量并且这些资源在 try 代码块执行完毕后必须能够被关闭。如果资源不能被关闭(即 close() 方法抛出异常),那么这个异常会被抑制,并且原始的异常(如果有的话)会被重新抛出。如果需要处理 close() 方法抛出的异常,可以使用额外的 try-catch 块来捕获。

自定义异常

除了Java内置的异常类外,我们还可以创建自定义的异常类。这通常用于表示特定于应用程序的错误条件。要创建自定义异常类,需要继承自Exception类或其子类,并定义构造函数。

例如:

public class MyCustomException extends Exception {
  public MyCustomException(String message) {
    super(message);
  }
}

然后,可以在需要的地方抛出这个自定义异常:

throw new MyCustomException("自定义异常信息");

Java异常链

异常链是Java异常处理机制中的一个重要特性,允许在抛出新的异常时,将原始异常作为新异常的“原因”传递。这样做有助于保留原始异常的上下文信息,使得在后续处理中能够更准确地了解异常发生的根本原因。

在Java中,可以通过在构造新的异常时,将原始异常作为参数传递给新异常的构造函数,来创建异常链。这样,新异常就会包含原始异常的引用,从而形成一个链式结构。

简单的示例,演示如何使用Java异常链:

public class ExceptionChainExample {
  public static void main(String[] args) {
    try {
      method1();
    } catch (Exception e) {
      e.printStackTrace();
    }
  }

public static void method1() throws Exception {
  try {
    method2();
  } catch (Exception e) {
    throw new MyCustomException("在method1中捕获到异常", e); // 使用原始异常作为新异常的原因
  }
}

public static void method2() throws Exception {
  throw new IOException("method2中发生IO异常"); // 假设这里抛出了一个IOException
}

// 自定义异常类
 static class MyCustomException extends Exception {
    public MyCustomException(String message, Throwable cause) {
      super(message, cause);
    }
  }
}

在这个示例中,method2抛出了一个IOException。当method1捕获到这个异常时,创建了一个新的MyCustomException,并将原始的IOException作为原因传递给了新异常。这样,当在main方法中捕获到MyCustomException时,我们可以通过调用getCause()方法来获取原始的IOException,从而了解异常发生的根本原因。

异常链的好处

异常链的好处在于它保留了原始异常堆栈跟踪信息,使得在调试和排查问题时能够更容易地定位到问题的源头。同时,通过封装原始异常,还可以在自定义异常中添加更多的上下文信息,使得异常信息更加丰富和有用。

在处理异常时,建议总是尽量保留并使用异常链,以便在后续的处理中能够充分利用原始异常的信息。

异常处理的注意点

注意点

描述

避免空的catch块

空的catch块会捕获异常但不做任何处理,这会导致程序在出现问题时继续运行,可能会引发更严重的后果。因此,我们应该在catch块中至少记录异常信息或进行适当的处理。

细化异常处理

尽量捕获具体的异常类型,而不是简单地使用Exception来捕获所有异常。这样可以更准确地定位问题并进行处理。

使用finally块释放资源

finally块中释放资源是一个很好的习惯,无论是否发生异常,这些资源都会被正确释放。

合理设计异常结构

对于复杂的应用程序,合理设计异常结构可以帮助我们更好地管理异常。可以将相关的异常组织在一起,形成一个继承层次结构。

关键术语与概念总结

术语

描述

异常(Exception)

程序执行过程中遇到的问题或错误,如空指针异常(NullPointerException)文件未找到异常(FileNotFoundException)等。

Checked Exception

编译时异常,必须在编写代码时显式处理,如果不处理或声明抛出,编译器会报错。例如, IOException 。

Unchecked Exception

也称运行时异常,运行时可能出现的异常,一般由程序错误引起,如数组越界异常(
ArrayIndexOutOfBoundsException)、空指针异常(NullPointerException)等。这类异常不必强制处理,但如果在运行时发生且未被捕获,程序会终止执行。

try catch finally

try 块放置可能抛出异常的代码。catch 块捕获并处理在 try 块中抛出的异常。finally 块无论是否发生异常,都会被执行的代码块,通常用于资源清理。

throw 关键字

手动抛出一个异常对象

throws 关键字

在方法签名中声明方法可能抛出的异常,将异常处理的责任转移给调用者。

异常链(Exception Chaining)

在一个异常中附加另一个异常,这样可以追踪异常发生的上下文。

自定义异常

通过创建一个新的类继承自 Exception 或其子类,可以定义自己的异常类型。

Java内置异常类

Java内置了许多异常类,这些类都是Throwable类的直接或间接子类。Throwable类有两个主要的子类:ErrorExceptionError类通常表示严重的问题,这些问题通常是Java虚拟机无法或不应该尝试修复的问题,如OutOfMemoryError或StackOverflowError。而Exception类及其子类则用于表示程序可以处理的异常情况。

Exception 类及其子类

一些常见的内置异常类及其描述:

输入输出异常

异常类

描述

IO

Exception

当应用程序发生输入输出异常时抛出。这是输入输出异常的根类。

FileNotFound

Exception

当试图打开指定路径名的文件失败时,抛出此异常。

EOF

Exception

当输入流已经关闭,或者已经到达流的末尾时,抛出此异常。

运行时异常

异常类

描述

Runtime

Exception

是那些可能在Java虚拟机正常运行期间抛出的异常的超类。编译器不会检查这类异常。

NullPointer

Exception

当应用程序试图在需要对象的地方使用null时,抛出该异常。

ArrayIndex

OutOfBounds

Exception

用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。

ClassCast

Exception

当试图将对象强制转换为不是实例的子类时,抛出该异常。

IllegalArgument

Exception

抛出的异常表明向方法传递了一个不合法或不适当的参数。

NumberFormat

Exception

当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。

其他常见异常

异常类

描述

ClassNotFound

Exception

当应用程序试图加载类,而找不到定义类的.class 文件时,抛出该异常。

Interrupted

Exception

当线程在等待、睡眠或占用时,另一个线程中断它时,抛出该异常。

SQL

Exception

提供关于数据库访问错误或其他错误的详细信息。

Error 类及其子类

Error是程序无法处理的严重错误。例如,OutOfMemoryError 表明虚拟机没有更多的内存空间来分配对象,并且垃圾回收器也无法回收更多的空间。

异常类

描述

OutOf

MemoryError

当JVM无法为对象分配足够的内存空间时,会抛出这个错误。这通常发生在应用程序试图创建大量对象,而JVM的堆内存不足以容纳这些对象时。

Stack

OverflowError

当一个方法递归调用过深,或者一个线程请求的栈大小超过了JVM所允许的栈大小时,会抛出这个错误。栈溢出通常意味着程序有逻辑错误,例如无限递归。

NoClassDef

FoundError

当JVM尝试加载一个类,但没有找到定义该类的.class文件时,会抛出这个错误。这通常发生在类路径设置不正确,或者试图动态加载不存在的类时。

Virtual

MachineError

通用的错误类型,用于描述虚拟机运行时的严重问题。有两个常见的子类:StackOverflowError和OutOfMemoryError。

AWTError

与Java的抽象窗口工具包(AWT)相关的错误。AWT用于创建图形用户界面,如果在这个过程中出现严重问题,就可能抛出这个错误。

Assertion

Error

当断言(assert)失败时抛出。断言是编程时用于检查某个条件是否为真的语句,如果条件不为真,则抛出AssertionError。

Thread

Death

表示线程已请求死亡。虽然ThreadDeath是一个Error的子类,但它是唯一一个可以被捕获的Error。通常,应用程序不应该捕获这个错误,除非它们有特殊的处理逻辑。

由于Error类及其子类通常表示无法恢复严重问题,因此当这些错误发生时,应用程序通常无法继续正常运行。在编写Java程序时,虽然不需要显式地处理这些错误,但了解它们以及它们可能的原因仍

相关推荐

探索Java项目中日志系统最佳实践:从入门到精通

探索Java项目中日志系统最佳实践:从入门到精通在现代软件开发中,日志系统如同一位默默无闻却至关重要的管家,它记录了程序运行中的各种事件,为我们排查问题、监控性能和优化系统提供了宝贵的依据。在Java...

用了这么多年的java日志框架,你真的弄懂了吗?

在项目开发过程中,有一个必不可少的环节就是记录日志,相信只要是个程序员都用过,可是咱们自问下,用了这么多年的日志框架,你确定自己真弄懂了日志框架的来龙去脉嘛?下面笔者就详细聊聊java中常用日志框架的...

物理老师教你学Java语言(中篇)(物理专业学编程)

第四章物质的基本结构——类与对象...

一文搞定!Spring Boot3 定时任务操作全攻略

各位互联网大厂的后端开发小伙伴们,在使用SpringBoot3开发项目时,你是否遇到过定时任务实现的难题呢?比如任务调度时间不准确,代码报错却找不到方向,是不是特别头疼?如今,随着互联网业务规模...

你还不懂java的日志系统吗 ?(java的日志类)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

谈谈枚举的新用法--java(java枚举的作用与好处)

问题的由来前段时间改游戏buff功能,干了一件愚蠢的事情,那就是把枚举和运算集合在一起,然后运行一段时间后buff就出现各种问题,我当时懵逼了!事情是这样的,做过游戏的都知道,buff,需要分类型,且...

你还不懂java的日志系统吗(javaw 日志)

一、背景在java的开发中,使用最多也绕不过去的一个话题就是日志,在程序中除了业务代码外,使用最多的就是打印日志。经常听到的这样一句话就是“打个日志调试下”,没错在日常的开发、调试过程中打印日志是常干...

Java 8之后的那些新特性(三):Java System Logger

去年12月份log4j日志框架的一个漏洞,给Java整个行业造成了非常大的影响。这个事情也顺带把log4j这个日志框架推到了争议的最前线。在Java领域,log4j可能相对比较流行。而在log4j之外...

Java开发中的日志管理:让程序“开口说话”

Java开发中的日志管理:让程序“开口说话”日志是程序员的朋友,也是程序的“嘴巴”。它能让程序在运行过程中“开口说话”,告诉我们它的状态、行为以及遇到的问题。在Java开发中,良好的日志管理不仅能帮助...

吊打面试官(十二)--Java语言中ArrayList类一文全掌握

导读...

OS X 效率启动器 Alfred 详解与使用技巧

问:为什么要在Mac上使用效率启动器类应用?答:在非特殊专业用户的环境下,(每天)用户一般可以在系统中进行上百次操作,可以是点击,也可以是拖拽,但这些只是过程,而我们的真正目的是想获得结果,也就是...

Java中 高级的异常处理(java中异常处理的两种方式)

介绍异常处理是软件开发的一个关键方面,尤其是在Java中,这种语言以其稳健性和平台独立性而闻名。正确的异常处理不仅可以防止应用程序崩溃,还有助于调试并向用户提供有意义的反馈。...

【性能调优】全方位教你定位慢SQL,方法介绍下!

1.使用数据库自带工具...

全面了解mysql锁机制(InnoDB)与问题排查

MySQL/InnoDB的加锁,一直是一个常见的话题。例如,数据库如果有高并发请求,如何保证数据完整性?产生死锁问题如何排查并解决?下面是不同锁等级的区别表级锁:开销小,加锁快;不会出现死锁;锁定粒度...

看懂这篇文章,你就懂了数据库死锁产生的场景和解决方法

一、什么是死锁加锁(Locking)是数据库在并发访问时保证数据一致性和完整性的主要机制。任何事务都需要获得相应对象上的锁才能访问数据,读取数据的事务通常只需要获得读锁(共享锁),修改数据的事务需要获...