编程语言中(以Java为例)异常处理对程序执行流程的影响非常简单且重要,就和 if...eles 一样基础

“异常是为了‘’中断”,即异常发生后:
(如果什么也不做,亦即没有catch异常)那么异常会沿着方法调用栈往回传播,会让方法调用栈中的每个方法中与该异常相关的代码之后的代码均无法被执行到,直至方法调用栈的栈底。
(如果catch了异常),那么程序会执行 catch 块里面的代码,执行完之后会执行 finally(如果有), 然后继续执行后续代码

Java 的异常处理机制为程序员提供了一种有效的手段来处理运行时可能发生的各种错误和异常情况。它通过捕获处理异常来避免程序的崩溃,同时还能进行适当的恢复或终止操作。下面将从多个角度对异常处理对程序执行流程的影响进行分类说明,并结合详细示例来说明其影响。

1. 正常执行流程与异常中断的流程

在没有异常的情况下,Java 程序按照顺序执行,每个语句依次运行。但在出现异常时,程序的正常执行流程会被中断,并且立即转到异常处理逻辑,跳过异常发生后到异常捕获之间的代码。具体来说,异常处理中断了当前的执行流程,将其交给适当的 catch 块或者上层调用者处理。

示例:正常流程与异常中断

public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println("Start of main method");
        try {
            int result = divide(10, 0);  // 这里会抛出异常
            System.out.println("This will not be printed");
        } catch (ArithmeticException e) {
            System.out.println("Caught an exception: " + e.getMessage());
        }
        System.out.println("End of main method");
    }

    public static int divide(int a, int b) {
        return a / b;  // 0 作为除数会抛出 ArithmeticException
    }
}

执行流程分析

  • divide(10, 0) 执行时,程序遇到除以零的情况,抛出了 ArithmeticException,程序的正常执行流程被中断。
  • 异常被 catch 块捕获并处理,程序跳过了 System.out.println("This will not be printed"),而是直接执行 catch 块中的代码。
  • 最终,程序恢复执行,继续运行 catch 块之后的代码,即打印出 "End of main method"。

2. try-catch 结构中的控制流

try-catch 结构中,Java 程序执行到 try 块时,会尝试执行 try 块中的代码。如果没有异常发生,catch 块不会执行,程序继续执行 try-catch 之后的代码;如果 try 块中的代码抛出了异常,catch 块会被触发,控制权转移到 catch 块。

示例:try-catch 的控制流

public class TryCatchDemo {
    public static void main(String[] args) {
        try {
            System.out.println("Inside try block");
            int result = 10 / 0;  // 抛出 ArithmeticException
            System.out.println("This line will not be executed");
        } catch (ArithmeticException e) {
            System.out.println("Exception caught: " + e);
        }
        System.out.println("After try-catch block");
    }
}

执行流程分析

  • try 块中的 int result = 10 / 0; 会抛出异常,导致 try 块中断,跳转到 catch 块。
  • catch 块捕获异常后处理,并打印异常信息。
  • 最后,程序继续执行 catch 块之后的代码,即打印 "After try-catch block"。

3. finally 块的执行

finally 块是异常处理的一部分,无论是否抛出异常,finally 块中的代码都会被执行。它通常用于资源清理,如关闭文件、数据库连接等。

示例:finally 块的执行

public class FinallyDemo {
    public static void main(String[] args) {
        try {
            System.out.println("Inside try block");
            int result = 10 / 0;  // 抛出 ArithmeticException
        } catch (ArithmeticException e) {
            System.out.println("Exception caught: " + e);
        } finally {
            System.out.println("Finally block executed");
        }
    }
}

执行流程分析

  • try 块中发生 ArithmeticException,控制流转移到 catch 块。
  • 无论是否有异常,finally 块总会执行,打印 "Finally block executed"。

如果没有异常:

public class FinallyNoExceptionDemo {
    public static void main(String[] args) {
        try {
            System.out.println("Inside try block");
            int result = 10 / 2;  // 没有异常
        } catch (ArithmeticException e) {
            System.out.println("Exception caught: " + e);
        } finally {
            System.out.println("Finally block executed");
        }
    }
}

执行流程

  • 没有异常,程序正常执行 try 块中的代码,跳过 catch,然后 finally 块照常执行。

4. 抛出异常的执行流程(throwthrows

  • throw:用于在程序中显式地抛出异常。抛出异常后,当前代码块的执行立即停止,程序跳转到异常处理器。
  • throws:用于方法签名,表示该方法可能会抛出指定类型的异常,调用者需要处理这些异常。

示例:显式抛出异常

public class ThrowDemo {
    public static void main(String[] args) {
        try {
            validateAge(15);  // 传入非法年龄
            System.out.println("This line will not be executed");
        } catch (IllegalArgumentException e) {
            System.out.println("Caught exception: " + e.getMessage());
        }
    }

    public static void validateAge(int age) {
        if (age < 18) {
            throw new IllegalArgumentException("Age must be 18 or older");
        }
    }
}

执行流程分析

  • validateAge(15) 会显式抛出 IllegalArgumentException,异常会立即中断 validateAge 的执行,转移到 catch 块处理。

5. 多重异常捕获

Java 支持多个 catch 块处理不同类型的异常。当 try 块抛出异常时,JVM 会按照 catch 块的顺序依次检查,如果匹配到适当的异常类型,执行相应的 catch 块,其它 catch 块将被跳过。

示例:多重异常捕获

public class MultipleCatchDemo {
    public static void main(String[] args) {
        try {
            int[] arr = new int[5];
            arr[5] = 10;  // 数组越界异常
            int result = 10 / 0;  // 除以零异常
        } catch (ArrayIndexOutOfBoundsException e) {
            System.out.println("Array index out of bounds: " + e);
        } catch (ArithmeticException e) {
            System.out.println("Arithmetic exception: " + e);
        }
    }
}

执行流程分析

  • 数组越界时,程序进入 ArrayIndexOutOfBoundsExceptioncatch 块,打印异常信息,后面的 ArithmeticException 不会被捕获。

6. 异常的传播(Exception Propagation)

如果一个方法中没有处理异常(即没有 try-catch 块),异常会沿着方法调用栈向上传递,直到找到适当的异常处理程序。如果所有调用链上都没有处理异常,程序会终止并打印堆栈信息。

示例:异常传播

public class ExceptionPropagationDemo {
    public static void main(String[] args) {
        try {
            method1();
            System.out.println("This line will not be executed");
        } catch (ArithmeticException e) {
            System.out.println("Exception handled in main: " + e);
        }
    }

    public static void method1() {
        method2();  // 调用 method2
        System.out.println("This line will not be executed");
    }

    public static void method2() {
        int result = 10 / 0;  // 这里抛出 ArithmeticException
        System.out.println("This line will not be executed");
    }
}

执行流程分析

  • method2 抛出异常,但没有 catch 块处理。
  • 异常传播到 method1,但 method1 也没有处理异常。
  • 最终,异常被 main 方法中的 catch 块捕获。
  • 假如,main 方法内也没有 catch 异常,那么该异常如果是特定异常如IOException等,可能会导致当前线程退出,特定场景下甚至可能会导致JVM 退出,表现为用户点了一下鼠标,触发了特定异常,然后整个网站崩溃了(如果是单机服务的话JVM退出就会如此)。

7. 受检异常与非受检异常的影响

Java 将异常分为两大类:受检异常(Checked Exception)和 非受检异常(Unchecked Exception)。

  • 受检异常:必须在编译时处理,要么捕获要么声明抛出,例如 IOException
  • 非受检异常:如 NullPointerExceptionArithmeticException,这些是 RuntimeException 的子类,不强制要求处理。

示例:受检异常的处理

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;

public class CheckedExceptionDemo {
    public static void main(String[] args) {


        try {
            File file = new File("nonexistent.txt");
            FileReader fr = new FileReader(file);  // 可能抛出 FileNotFoundException
        } catch (FileNotFoundException e) {
            System.out.println("File not found: " + e);
        }
    }
}

执行流程分析

  • FileReader 可能抛出 FileNotFoundException,这是一种受检异常,必须在编译时处理。
  • 没有处理受检异常会导致编译错误。

8. .带资源的try,即try with resource, 可以自动关闭资源,当程序调出try 块之后,即执行到 catch 或 finally中的时候,资源已经关闭了,拿不到InputStream了

在 Java 中,try-with-resources 语法是为了简化资源的管理,并确保在使用完资源后自动进行清理(关闭)。通常,资源是指那些实现了 AutoCloseableCloseable 接口的对象,例如文件、输入输出流、数据库连接等。try-with-resources 的核心作用就是在代码块执行完毕之后,无论是否发生异常,都会自动关闭资源。

try-with-resources 的执行流程

当使用 try-with-resources 语句时,资源会在 try 块执行结束或发生异常时自动关闭。这意味着当程序执行到 catchfinally 块时,资源已经关闭了,因此在 catchfinally 中无法再访问已关闭的资源(例如 InputStream 等)。

其执行流程如下:

  1. 资源初始化:在 try 块开始之前,所有资源会在 try 语句中声明并初始化。
  2. try 块执行try 块中的代码正常执行,如果没有异常,则直接执行完后资源关闭。
  3. catch 块执行(可选):如果 try 块中发生了异常,程序会跳转到相应的 catch 块。
  4. 资源自动关闭:不论是否抛出异常,资源都会在 try 块结束后自动关闭。
  5. finally 块执行(可选):如果有 finally 块,程序在关闭资源后会执行 finally 中的代码。

try-with-resources 示例

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

public class TryWithResourcesDemo {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line = br.readLine();  // 正常读取文件
            System.out.println(line);
        } catch (IOException e) {
            System.out.println("Caught an IOException: " + e.getMessage());
        } finally {
            System.out.println("Finally block executed");
        }
    }
}

执行流程:

  1. 资源初始化BufferedReaderFileReadertry 块的开头被创建。
  2. try 块执行br.readLine() 尝试读取文件的内容。如果读取成功且没有异常发生,try 块正常结束。
  3. 资源关闭:无论是否抛出异常,在 try 块执行完毕或异常发生后,BufferedReaderFileReader 都会被自动关闭。
  4. catch 块执行(如果有异常):如果在 try 块中发生了 IOException,程序跳到 catch 块处理异常。
  5. finally 块执行:关闭资源之后,无论是否有异常,finally 块中的代码都会执行。

问题:catchfinally 中无法获取资源

由于 try-with-resources 语法确保资源在 try 块结束时自动关闭,因此在进入 catchfinally 块之前,资源已经被关闭了。如果尝试在 catchfinally 块中再次访问资源,例如 InputStream,则会抛出异常,提示资源已关闭。

示例:无法在 catch 中访问已关闭的资源

import java.io.*;

public class TryWithResourcesCatchDemo {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(new FileReader("nonexistent.txt"))) {
            String line = br.readLine();  // 此行会抛出异常
            System.out.println(line);
        } catch (IOException e) {
            // 资源已经关闭,下面这行代码会抛出 IllegalStateException 或其他类似异常
            try {
                System.out.println("Trying to read after exception: " + br.readLine());  // 不能访问已关闭的资源
            } catch (IOException ex) {
                System.out.println("Caught another IOException: " + ex.getMessage());
            }
        }
    }
}

在上述代码中,br.readLine()catch 块中访问已关闭的 BufferedReader,因此会抛出 IOException,提示资源已关闭。

如何处理在 catchfinally 中访问资源的需求

如果必须在 catchfinally 块中访问资源的内容或获取未完全处理的输入/输出,可以采取以下策略:

  1. 提前读取并存储数据:如果在 try 块中需要读取输入流(如 InputStreamReader),可以在 try 块内部先将数据读入一个变量或数据结构(如 Stringbyte[]),然后在 catchfinally 中使用这些已保存的数据。

  2. 延迟关闭资源:手动管理资源的关闭,避免使用 try-with-resources,以便在异常发生时可以在 catchfinally 中访问资源。但这种做法会让代码复杂化,需要手动确保资源在最终被正确关闭。

示例:提前读取数据

import java.io.*;

public class TryWithResourcesSolutionDemo {
    public static void main(String[] args) {
        StringBuilder fileContent = new StringBuilder();
        try (BufferedReader br = new BufferedReader(new FileReader("test.txt"))) {
            String line;
            while ((line = br.readLine()) != null) {
                fileContent.append(line).append("\n");
            }
        } catch (IOException e) {
            System.out.println("Exception caught: " + e.getMessage());
        } finally {
            System.out.println("File content: " + fileContent.toString());
        }
    }
}

在此示例中,数据在 try 块中被完整读取并存储在 fileContent 中,因此在 finally 块中能够访问文件的内容,而不需要直接依赖已关闭的 BufferedReader 对象。

总结

  • try-with-resources 语法在 try 块结束后会自动关闭资源,无论是否发生异常。
  • 资源在 catchfinally 中已经关闭,因此不能在这些块中再次访问资源,如 InputStreamReader
  • 为了解决在 catchfinally 中访问资源的问题,可以提前将所需数据读取到内存中,或者选择手动管理资源的关闭(但这种做法不推荐,容易出错)。

9. .finally 块中抛出异常,会屏蔽掉 catch 块中异常

在 Java 中,finally 块中的异常确实会屏蔽掉 catch 块中抛出的异常。这是因为 finally 块的执行优先于 catch 块中抛出的异常的传播。在这种情况下,finally 块抛出的异常会取代 catch 块中的异常,导致程序无法感知和处理 catch 块抛出的异常。

让我们详细解释一下这个机制,并通过代码演示其效果。

异常处理流程

  • 当一个异常在 try 块中被抛出时,程序会进入 catch 块处理该异常。
  • 不论 catch 块是否抛出异常,finally 块总是会执行。
  • 如果 finally 块中抛出了新的异常,这个异常会覆盖之前的异常,并导致程序最终抛出的是 finally 中的异常,而不是 catch 中的异常。

示例:finally 块屏蔽 catch 块的异常

以下代码演示了 finally 块如何屏蔽 catch 块中的异常:

public class ExceptionDemo {
    public static void main(String[] args) {
        try {
            throw new RuntimeException("Exception in try block");
        } catch (RuntimeException e) {
            System.out.println("Caught exception: " + e.getMessage());
            throw new RuntimeException("Exception in catch block");  // 在 catch 中抛出新的异常
        } finally {
            System.out.println("Executing finally block");
            throw new RuntimeException("Exception in finally block");  // 在 finally 中抛出新的异常
        }
    }
}

输出:

Caught exception: Exception in try block
Executing finally block
Exception in thread "main" java.lang.RuntimeException: Exception in finally block

执行流程分析:

  1. try:首先,在 try 块中抛出一个 RuntimeException,程序立即跳转到对应的 catch 块。
  2. catch:在 catch 块中,程序捕获到 try 块中的异常,并打印 "Caught exception: Exception in try block"。然后,catch 块再次抛出一个新的异常(Exception in catch block)。
  3. finally:在抛出 catch 块中的异常之后,程序转向 finally 块,finally 块的内容总是会执行。在 finally 块中,再次抛出一个新的异常(Exception in finally block)。
  4. 异常屏蔽:尽管 catch 块中抛出了异常,但由于 finally 块也抛出了异常,finally 块的异常会覆盖(屏蔽)掉 catch 块的异常。因此,最终抛出的异常是 finally 中的异常,而不是 catch 中的异常。

为什么会发生这种情况?

在 Java 中,finally 块的设计目的是确保在不论发生什么情况时,都能执行清理工作或其他必要的后续操作。由于 finally 块优先于异常传播的执行顺序,一旦 finally 块抛出了异常,这个异常会直接影响程序的最终状态。Java 虚拟机会优先处理 finally 块的异常,而不是之前发生的异常。

如何处理这种情况?

为了避免 finally 块中的异常屏蔽掉 catch 块中的异常,常见的做法是:

  1. 不在 finally 块中抛出异常:确保 finally 块中的代码尽量不抛出异常,或者对可能抛出的异常进行捕获和处理。
  2. 记录多个异常:如果必须抛出 finally 中的异常,可以考虑使用 addSuppressed 方法将 finally 中的异常附加到 catch 块中的异常,确保不会丢失任何重要信息。

使用 addSuppressed 处理多个异常

Java 7 引入了 addSuppressed 方法,可以将多个异常一起记录下来。这样,catch 块中的异常不会被 finally 中的异常完全屏蔽。

以下是一个使用 addSuppressed 的示例:

public class ExceptionDemo {
    public static void main(String[] args) {
        RuntimeException primaryException = null;
        try {
            throw new RuntimeException("Exception in try block");
        } catch (RuntimeException e) {
            primaryException = e;
            System.out.println("Caught exception: " + e.getMessage());
            throw new RuntimeException("Exception in catch block");
        } finally {
            System.out.println("Executing finally block");
            try {
                throw new RuntimeException("Exception in finally block");
            } catch (RuntimeException finallyException) {
                if (primaryException != null) {
                    primaryException.addSuppressed(finallyException);  // 将 finally 的异常附加到 catch 的异常上
                } else {
                    throw finallyException;  // 如果没有 catch 异常,直接抛出 finally 的异常
                }
            }
        }
    }
}

输出:

Caught exception: Exception in try block
Executing finally block
Exception in thread "main" java.lang.RuntimeException: Exception in catch block
    Suppressed: java.lang.RuntimeException: Exception in finally block

在这个例子中:

  • try 块抛出了异常,导致 catch 块处理异常。
  • catch 块再次抛出了新的异常,但在 finally 块中也抛出了异常。
  • 为了保留 catch 块中的异常,finally 块中的异常被附加为 catch 异常的“抑制异常”(suppressed exception)。因此,程序不会丢失 finally 块中的异常。

最终的输出显示了 catch 块的异常以及 finally 块中的抑制异常,解决了屏蔽问题。

总结

  1. finally 块抛出的异常会屏蔽 catch 块中的异常,这意味着最终抛出的异常会是 finally 中的异常,而不是 catch 中的异常。
  2. 如果需要保留 catchfinally 中的异常,可以通过 Java 7 引入的 addSuppressed 方法将 finally 块中的异常附加到 catch 块的异常上,避免丢失信息。
  3. 避免在 finally 块中抛出异常,除非有明确的需求,通常 finally 块应该专注于清理工作而非抛出新的异常。

10. **强烈不建议在finally中放置return 语句。

10.0.1.正常情况下 try块会缓存return 值,并在执行return 语句之前插入finally 块进行执行,故finally中的return 会执行,而方法就此return 了,try块中原本的return 语句不会被执行。
10.0.2.更重要的是try块中的return 会被缓存起来,然后执行 fiannly, 此时方法return基本类型 与 引用类型的情况是截然不同的,情况非常隐蔽,现在finally 块中很正常没有return,仅仅在finally 中修改了return 值, 其影响遵守值传递/引用传递原则,那么当方法return 为基本类型值的情况下,try 中被缓存的 return 值完全不会不会被finally块中的修改而影响;当方法return 为引用类型值的情况下,try 中被缓存的 return 值其实会被finally块中的修改而影响,遵循引用传递原则。而详情见如下示例分析
**
下面通过实验来看这几种情况的执行顺序到底是什么。

1、try中有return,finally中没有
public class TryCatchTest {

public static void main(String[] args) {
	System.out.println("test()函数返回:" + test());
}

private static int test(){
	int i = 0;
	try {
		System.out.println("Try block executing: " + ++i);
		return i;
	}catch (Exception e){
		System.out.println("Catch Error executing: " + ++i);
		return -1;
	}finally {
		System.out.println("finally executing: " + ++i);
	}
}

}
结果如下:

Try block executing: 1
finally executing: 2
test()函数返回:1

return的是对象时,看看在finally中改变对象属性,会不会影响try中的return结果。

public class TryCatchTest {
public int vaule = 0;

public static void main(String[] args) {
	System.out.println("test()函数返回:" + test().vaule);
}

private static TryCatchTest test(){
	TryCatchTest t = new TryCatchTest();
	try {
		t.vaule = 1;
		System.out.println("Try block executing: " + t.vaule);
		return t;
	}catch (Exception e){
		t.vaule = -1;
		System.out.println("Catch Error executing: " + t.vaule);
		return t;
	}finally {
		t.vaule = 3;
		System.out.println("finally executing: " + t.vaule);
	}
}

}
Try block executing: 1
finally executing: 3
test()函数返回:3

2、try和finally中均有return
private static int test(){
int i = 0;
try {
System.out.println("Try block executing: " + ++i);
return i;
}catch (Exception e){
System.out.println("Catch Error executing: " + ++i);
return -1;
}finally {
System.out.println("finally executing: " + ++i);
return i;
}
}
结果如下:

Try block executing: 1
finally executing: 2
test()函数返回:2

3、catch和finally中均有return
private static int test(){
int i = 0;
try {
System.out.println("Try block executing: " + ++i);
throw new Exception();
}catch (Exception e){
System.out.println("Catch Error executing: " + ++i);
return -1;
}finally {
System.out.println("finally executing: " + ++i);
return i;
}
}
输出结果:

Try block executing: 1
Catch Error executing: 2
finally executing: 3
test()函数返回:3

总结
1、不管有没有出现异常,finally块中代码都会执行;
2、当try和catch中有return时,finally仍然会执行;
3、finally是在return后面的表达式运算之后执行的;

对于含有return语句的情况,这里我们可以简单地总结如下:

try语句在返回前,将其他所有的操作执行完,保留好要返回的值,而后转入执行finally中的语句,而后分为以下三种情况:

情况一:如果finally中有return语句,则会将try中的return语句“覆盖”掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。

情况二:如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。

情况三:如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况:

1)如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。
2)如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。


11. Java 中为什么要有受检查异常(编译期异常)和非受检查异常(运行时异常)

Java 中继承自RuntimeException的异常都是非受检查异常,不必显式catch 或 throws,除此之外都是受检查异常,编译期要求必须显式catch 或 throws.
图片来源:https://cloud.tencent.com/developer/article/2159540
Java异常类继承层次图

在 Java 中,异常被分为两类:受检查异常(Checked Exception)和非受检查异常(Unchecked Exception)。这两种异常的设计目的在于提供不同的错误处理机制,从而提高代码的可维护性和健壮性。以下是它们之间的主要区别和各自存在的原因:

1. 定义与分类

  • 受检查异常(Checked Exception)

    • 这类异常是指在编译时就需要处理的异常。编译器会检查代码中是否捕获了这些异常或者是否在方法签名中声明了它们。
    • 常见的受检查异常包括 IOExceptionSQLExceptionClassNotFoundException 等。
    • 受检查异常通常表示在正常运行时可能会发生的条件,例如文件未找到或数据库连接失败等。
  • 非受检查异常(Unchecked Exception)

    • 这类异常是在运行时发生的异常,不需要在编译时进行处理。编译器不会强制要求程序员捕获或声明这些异常。
    • 常见的非受检查异常包括 NullPointerExceptionArrayIndexOutOfBoundsExceptionClassCastException 等。
    • 非受检查异常通常是由程序逻辑错误引起的,例如访问空指针、数组越界等。

2. 目的与设计

a. 受检查异常的目的

  1. 强制错误处理

    • 受检查异常的设计目的是强制开发者在编写代码时考虑可能发生的错误。这样做的好处是能够提高代码的健壮性,因为开发者必须提供相应的错误处理机制。
  2. 增强可读性

    • 当方法签名中声明了受检查异常,调用者能清晰地了解该方法可能引发的异常。这使得代码的可读性和可维护性提高,因为开发者可以根据异常类型快速判断如何处理。
  3. 清晰的责任分配

    • 通过强制捕获和处理受检查异常,Java 强调了方法的调用者需要对潜在的错误负责,从而增强了代码的责任感。

b. 非受检查异常的目的

  1. 灵活性

    • 非受检查异常允许开发者在某些情况下灵活处理错误。由于这些异常往往是由于程序逻辑错误引起的,开发者可以选择不处理它们,以避免代码变得冗长和复杂。
  2. 简化错误处理

    • 许多情况下,非受检查异常是不值得处理的。例如,NullPointerException 通常表示代码存在缺陷,而不是一个可以优雅处理的条件。在这些情况下,不强制要求捕获和处理可以简化代码。
  3. 关注业务逻辑

    • 开发者可以将精力集中在业务逻辑上,而不必过度关注错误处理。这样,代码可以保持简洁,减少错误处理逻辑的干扰。

3. 选择合适的异常

Java 允许开发者在选择受检查或非受检查异常时考虑以下因素:

  • 使用受检查异常

    • 当异常是可恢复的,且调用者应该了解并处理它们。例如,网络请求失败、文件读写错误等。
  • 使用非受检查异常

    • 当异常表示程序中的逻辑错误,且开发者应该修复代码而不是处理异常。例如,空指针引用、类型转换错误等。

示例

受检查异常示例:

public void readFile(String filePath) throws IOException {
    FileReader fileReader = new FileReader(filePath);
    // 读取文件内容...
}

在这个例子中,readFile 方法声明了可能会抛出 IOException,调用者必须处理这个异常。

非受检查异常示例:

public void processArray(int[] arr) {
    System.out.println(arr[10]);  // 可能会引发 ArrayIndexOutOfBoundsException
}

在这个例子中,processArray 方法没有声明异常,访问数组越界的错误是逻辑错误,开发者需要修复代码。

总结

Java 中引入受检查异常和非受检查异常的设计旨在提供更灵活且可维护的错误处理机制。受检查异常鼓励开发者处理可能发生的错误,而非受检查异常允许开发者专注于逻辑和业务实现。合理地选择和使用这两类异常,可以使代码更加健壮且可读。

posted @   gongchengship  阅读(70)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
点击右上角即可分享
微信分享提示