编译阶段与运行阶段
首先,厘清两个阶段——Compile time和Run time:
编译时(Compile Time)
- 定义:编译时是指源代码转换为字节码的过程,这一步由Java编译器完成。
- 主要活动:
- 语法检查:检查代码是否符合Java语言的语法规则。
- 类型检查:确保变量、方法等使用正确的数据类型。
- 生成字节码:将源代码转换为字节码文件(.class)。
- 异常检查:在编译时,编译器会检查程序中的Checked异常是否已经被显式地处理。如果没有正确处理,编译器会报错。
运行时(Runtime)
- 定义:运行时是指JVM(Java虚拟机)执行字节码的过程,即程序实际运行的阶段。
- 主要活动:
- 加载类:JVM加载所需的类。
- 执行代码:按照程序逻辑执行字节码指令。
- 内存管理:管理内存的分配和回收。
- 异常处理:在运行时,如果代码抛出异常,JVM会检查是否有相应的捕获(catch)块处理异常。如果没有,异常会沿调用栈向上传递,最终可能导致程序终止。
异常处理机制
在Java中,异常处理机制提供了一种结构化的方式来处理程序运行过程中可能发生的错误和异常情况。异常处理通过使用 try-catch-finally
块来实现,确保程序能够从异常中恢复或有序地终止。
异常处理的基本构造
-
try 块:
- 包含可能抛出异常的代码。
- 如果在
try
块中抛出异常,程序会跳转到相应的catch
块。
-
catch 块:
- 捕获并处理特定类型的异常。
- 可以有多个
catch
块来处理不同类型的异常。
-
finally 块:
- 可选的,包含无论是否发生异常都要执行的代码。
- 通常用于释放资源,例如关闭文件或数据库连接。
-
throw 语句:
- 手动抛出一个异常。
-
throws 关键字:
- 用于方法签名中,声明该方法可能抛出的异常类型。
异常分类
Java 中的异常主要分为两类:Checked 异常和 Unchecked 异常。
Checked 异常
- 定义:在编译时检查的异常,必须显式处理,否则编译无法通过。
- 继承关系:继承自
Exception
类,但不包括RuntimeException
及其子类。 - 处理方式:必须使用
try-catch
块捕获或在方法签名中使用throws
声明。 - 示例:
public void readFile(String filePath) throws IOException { FileReader reader = new FileReader(filePath); // 可能抛出 IOException }
Unchecked 异常
- 定义:在运行时检查的异常,编译器不强制要求显式处理。
- 继承关系:继承自
RuntimeException
类。 - 处理方式:可以选择处理,也可以不处理。未处理的 unchecked 异常会沿着调用栈向上传递,最终可能导致程序终止。
- 示例:
public void divide(int a, int b) { if (b == 0) { throw new ArithmeticException("Division by zero"); } int result = a / b; }
示例代码
以下是一个示例代码,展示了如何使用
try-catch-finally
块处理异常,并区分 Checked 和 Unchecked 异常: -
import java.io.FileReader; import java.io.IOException; public class ExceptionHandlingExample { public static void main(String[] args) { // Checked exception example try { readFile("nonexistentfile.txt"); } catch (IOException e) { System.out.println("Caught IOException: " + e.getMessage()); } // Unchecked exception example try { divide(10, 0); } catch (ArithmeticException e) { System.out.println("Caught ArithmeticException: " + e.getMessage()); } finally { System.out.println("Finally block executed"); } } public static void readFile(String filePath) throws IOException { FileReader reader = new FileReader(filePath); reader.close(); } public static void divide(int a, int b) { if (b == 0) { throw new ArithmeticException("Division by zero"); } int result = a / b; System.out.println("Result: " + result); } }
但是,用try-catch结构捕获unchecked异常往往是脱裤子放屁的行为。因为这类异常是由于程序员的疏忽所导致,如果意识到自己出错,就会直接修改代码规避错误,而不是try-catch这个unchecked异常——因为就算你不做try-catch,系统在运行时也会抛出uncheck异常。就像你考试时明知自己某一题犯了错误但不去改正,反而在试卷上提前告诉老师自己这道题做错了。