【Java】理解异常 异常处理

太过爱你忘了你带给我的痛 2024-04-03 09:15 162阅读 0赞

异常处理

Error 和Exception

ExceptionError, ⼆者都是Java 异常处理的重要⼦类, 各⾃都包含⼤量⼦类。均继承自Throwable类。

Error表⽰系统级的错误, 是java 运⾏环境内部错误或者硬件问题, 不能指望程序来处理这样的问题, 除了退出运⾏外别⽆选择, 它是Java 虚拟机抛出的。

Exception 表⽰程序需要捕捉、需要处理的常, 是由与程序设计的不完善⽽出现的问题, 程序必须处理的问题。

异常类型

Java 中的异常, 主要可以分为两⼤类, 即受检异常(checked exception)和非受检异常( unchecked exception)。

受检异常

对于受检异常来说, 如果⼀个⽅法在声明的过程中证明了其要有受检异常抛出:

  1. public void test() throws new Exception{
  2. }

那么,当我们在程序中调⽤他的时候, ⼀定要对该异常进⾏处理( 捕获或者向上抛出),否则是⽆法编译通过的。这是⼀种强制规范。

这种异常在IO操作中⽐较多。⽐如FileNotFoundException, 当我们使⽤IO流处理⼀个⽂件的时候, 有⼀种特殊情况, 就是⽂件不存在。 所以 在⽂件处理时他会显⽰抛出FileNotFoundException, ⽬的就是告诉这个⽅法的调⽤者,这个⽅法不保证⼀定可以成功, 是有可能找不到对应的⽂件的,你要明确的对这种情况做特殊处理。

当我们希望我们的⽅法调⽤者, 明确的处理⼀些特殊情况的时候, 就应该使⽤受检异常。

非受检异常

对于⾮受检异常来说, ⼀般是运⾏时异常, 继承⾃RuntimeException。在编写代码的时候, 不需要显⽰的捕获,但是如果不捕获, 在运⾏期如果发⽣异常就会中断程序的执⾏。

这种异常⼀般可以理解为是代码原因导致的。⽐如发⽣空指针、数组越界等。所以,只要代码写的没问题, 这些异常都是可以避免的。也就不需要我们显⽰的进⾏处理。试想⼀下, 如果你要对所有可能发⽣空指针的地⽅做异常处理的话, 那相当于你的所有代码都需要做这件事。

异常相关关键字

try⽤来指定⼀块预防所有异常的程序;

catch⼦句紧跟在try 块后⾯, ⽤来指定你想要捕获的异常的类型;

finally 为确保⼀段代码不管发⽣什么异常状况都要被执⾏;

throw 语句⽤来明确地抛出⼀个异常;

throws⽤来声明⼀个⽅法可能抛出的各种异常;

正确处理异常

异常的处理⽅式有两种:

1、⾃⼰处理(捕获)。

2、向上抛,交给调⽤者处理。

异常,千万不能捕获了之后什么也不做,或者只是使⽤e.printStacktrace。

处理异常的原则:自己明确知道如何处理的,就处理掉;不知道如何处理的,就交给调⽤者处理。

自定义异常

⾃定义异常就是开发人员⾃⼰定义的异常, ⼀般通过继承Exception 的⼦类的⽅式实现。

编写⾃定义异常类实际上是继承⼀个API 标准异常类,⽤新定义的异常处理信息覆盖原有信息的过程。这种⽤法在Web 开发中也⽐较常见,一般可以⽤来⾃定义业务异常。如余额不⾜、重复提交等。这种⾃定义异常有业务含义,更容易让上层理解和处理

异常链

“异常链”是Java 中⾮常流⾏的异常处理概念, 是指在进⾏⼀个异常处理时抛出了另外⼀个异常, 由此产⽣了⼀个异常链条。

该技术⼤多⽤于将“ 受检查异常” ( checked exception) 封装成为“⾮受检查异常”( unchecked exception)或者RuntimeException。

并且要注意的是,如果因为异常你决定抛出⼀个新的异常,新的异常⼀定要包含原有的异常,这样, 处理程序才可以通过getCause()initCause()⽅法来访问异常最终的根源。

从Java 1.4 版本开始,几乎所有的异常都支持异常链。

以下是Throwable 中支持异常链的方法和构造函数。

  1. Throwable getCause()
  2. Throwable initCause(Throwable)
  3. Throwable(String, Throwable)
  4. Throwable(Throwable)

initCauseThrowable 构造函数的Throwable 参数是导致当前异常的异常。getCause 返回导致当前异常的异常,initCause 设置当前异常的原因。

以下示例显示如何使用异常链:

  1. try {
  2. //...
  3. } catch (IOException e) {
  4. throw new SampleException("Other IOException", e);
  5. }

上面的示例中,当捕获到IOException 时,将创建一个新的SampleException 异常,并附加原始的异常原因,并将异常链抛出到下一个更高级别的异常处理程序。

try-with-resources

Java 里,对于文件操作IO 流、数据库连接等开销非常昂贵的资源,用完之后必须及时通过close 方法将其关闭,否则资源会一直处于打开状态,这样可能会导致内存泄露等问题。

关闭资源的常用方式就是在finally块里是释放,即调用close方法。比如,我们经常
会写这样的代码:

  1. public static void main(String[] args) {
  2. BufferedReader br = null;
  3. try {
  4. String line;
  5. br = new BufferedReader(new FileReader("d:\\123.txt"));
  6. while ((line = br.readLine()) != null) {
  7. System.out.println(line);
  8. }
  9. } catch (IOException e) {
  10. // handle exception
  11. } finally {
  12. try {
  13. if (br != null) {
  14. br.close();
  15. }
  16. } catch (IOException ex) {
  17. // handle exception
  18. }
  19. }
  20. }

从Java 7 开始,jdk 提供了一种更好的方式关闭资源,使用try-with-resources语句,改写一下上面的代码,效果如下:

  1. public static void main(String... args) {
  2. try (BufferedReader br = new BufferedReader(new FileReader("d:\\123.txt"))) {
  3. String line;
  4. while ((line = br.readLine()) != null) {
  5. System.out.println(line);
  6. }
  7. } catch (IOException e) {
  8. // handle exception
  9. }
  10. }

finally 和return 的执行顺序

try()⾥⾯有⼀个return语句, 那么后⾯的finally{}⾥⾯的code 会不会被执⾏, 什么时候执⾏, 是在return 前还是return 后?

如果try中有return语句, 那么finally中的代码还是会执⾏。因为return表⽰的是
整个⽅法体返回, 所以,finally 中的语句会在return 之前执⾏。

但是return 前执行的finally 块内,对数据的修改效果对于引用类型和值类型会不同:

  1. //测试修改值类型
  2. static int f() {
  3. int ret = 0;
  4. try {
  5. return ret; // 返回0,finally 内的修改效果不起作用
  6. } finally {
  7. ret++;
  8. System.out.println("finally 执行");
  9. }
  10. }
  11. // 测试修改引用类型
  12. static int[] f2(){
  13. int[] ret = new int[]{
  14. 0};
  15. try {
  16. return ret; // 返回[1],finally 内的修改效果起了作用
  17. } finally {
  18. ret[0]++;
  19. System.out.println("finally 执行");
  20. }
  21. }

发表评论

表情:
评论列表 (有 0 条评论,162人围观)

还没有评论,来说两句吧...

相关阅读