Java异常处理之try...catch...finally详解

异常处理机制已经成为判断一门编程语言是否成熟的标准之一,其对代码的健壮性有很大影响。一直以来异常处理使用不是很得心应手,今天对异常进行了较为深入的学习,这篇主要是对try…catch…finally的一个总结。

一.java继承体系

Java语言为异常处理提供了丰富的异常类,这些类之间有严格的继承关系。如图:

从图中我们可以看出,所有的类都是继承于Throwable这个父类,java将所有的非正常情况分为两种:Error(错误)和Exception(异常),Error错误一般是于虚拟机相关的问题,如系统崩溃、虚拟机错误、动态链接失败等,这种错误是无法恢复或不可能捕获的,而我们能处理的是Exception类下的错误。Exception则分为两大类,RuntimeException(运行时异常)和其他异常(Checked异常),其他异常(Checked异常)是各种形式的编译错误,是我们必须显示处理才可以通过变异的;而运行时错误顾名思义就是程序已经通过了编译,在运行时出现的错误,若是对这些异常置之不理会导致程序停止运行、占用资源无法释放甚至导致系统崩溃。

二.java异常处理机制及实现方法

1.主要依赖于try、catch、finally、throw和throws这五个关键字。(throw和throws本篇不涉及)

2.try…catch…finally处理机制:try关键字后跟一个花括号栝起的代码块(即使该代码块只有一行也不能省略花括号),简称try块。catch对应异常类型和代码块,用于表明更改catch块用于处理该种类型的异常。一个try块后可以跟多个catch块。在catch块后还可以跟一个finally块,finally块用于回收在try块里打开的资源。

这样讲过于抽象,那我们看几个例子:

e.g.1 try…catch语句块

//功能:对输入的两个数进行相除运算
public class DivTest {
  public static void main(String[] args) {
    try {
      int a = Integer.parseInt(args[0]);
      int b = Integer.parseInt(args[1]);
      int c = a/b;
      System.out.println("您输入的两个数相除的结果是:" + c);
    } catch(IndexOutOfBoundsException ie) {
      System.out.println("数组越界");
    } catch(NumberFormatException ne) {
      System.out.println("数字格式异常");
    } catch(ArithmeticException ae) {
      System.out.println("算术异常");
    } catch(Exception e) {
      System.out.println("未知异常");
    }
  }
}

以上代码我们看到,对不同的异常情况作了不同的处理:输入参数不够会发生数组越界异常、输入参数不是数字发生数字格式异常、若输入第二个数是0,则发生除0异常,调用算术异常进行处理、出现其他异常时那么该异常对象必定是Exception类或其子类的实例,java调用Exception类对其进行处理,前三种异常类均是RuntimeException的子类。在使用try…catch语句块时需要知道或注意以下几点:

1) 处理过程:代码在执行的时候,进入try块,若是在try块中出现了异常,系统会自动生成一个一场对象,该对象被提交给java运行时环境,这就是异常的抛出;在java运行时环境收到异常对象时则把该对象交给catch块处理,这个过程叫做异常的捕获;若找到相应的catch块就执行catch块中的代码,若没有找到,则运行时环境终止,程序也退出。

2) 执行一次try块只执行一个catch块

3) 有多个catch块并有继承关系的情况下必须先写子类后写父类(即先捕获小异常再捕获大异常),若写反在编译时就会报错

4) Java7提供的多异常捕获:在Java7之前,每一个catch块只能捕获一种异常,但从java7开始,一个catch块可以捕获多种类型的异常。在使用多异常捕获应注意两点:

  • (1) 多种异常之间用竖线( | )隔开
  • (2) 多种异常对象被final隐式修饰,因此程序不能对其重新赋值

以下代码是多异常捕获的例子:

e.g.2

//多异常捕获
public class MultiExceptionTest {
  public static void main(String[] args) {
    try {
      int a = Integer.parseInt(args[0]);
      int b = Integer.parseInt(args[1]);
      int c = a/b;
      System.out.println("您输入的两个数相除的结果是:" + c);
    } catch(IndexOutOfBoundsException|NumberFormatException|
    ArithmeticException ie) {
      System.out.println("数组越界或数字格式异常或算术异常");
      ie = new ArithmeticExcrption("test");  //①
    } catch(Exception e) {
      System.out.println("未知异常");
      e = new RuntimeException("test");  //②
    }
  }
}

可以看出,以上代码中,①号代码是错误的,因为ie是被final隐式修饰的对象,②号代码是正确的

3. 使用finally回收资源:有些时候我们在try块中打开了一些物理资源(例如数据库链接、网络连接和磁盘文件等),这些资源都应进行显示回收。有人说java不是有垃圾回收机制吗?java的垃圾回收机制是自动回收堆内存中对象所占用的内存,而物理资源是不会自动回收的。

finally重点学习以下几点:

  • 1) 执行过程以及引入finally的原因:finally最后执行并且最后执行,物理资源回收放在finally块中的原因就是finally块一定会被执行。相反,若是放在try块中,在执行之前就出现异常则跳转至catch块中,则回收资源的代码不会被执行;同样的,若是放在catch块中,若不发生异常,那么catch块就不会被执行
  • 2) 若是在catch快中有return语句,则先执行完finally中的程序后再回到catch块中并执行return语句
  • 3) 若是在finally中有return语句,那么try块和catch块中的return语句都会失效,不会被执行
  • 4) 若在catch块中强制退出虚拟机,如使用System.exit(1)语句,则会直接退出程序,finally也不会得到执行

e.g.3

//该类功能:打开a.txt文件,在finally块中对资源进行回收
/* 对代码中一些方法的解释:
 * 所有异常都包含以下几种访问异常信息的常用方法:
 * getMessage():返回该异常的详细描述字符串
 * printStackTrace():将该异常的跟踪栈信息输出到标准错误输出
 * printStaceTrace(PrintStack s):将该异常的跟踪栈信息到执行输出流
 * getStackTrace():返回该异常的跟踪栈信息
 **/
public class FinallyTest {
  public static void main(String[] args) {
    FileInputStream fis = null;
    try {
      fis = new FileInputStream("a.txt");
    }catch(IOException ioe) {
      System.out.println(ioe.getMessage());
      return;      //①
      System.exit(1);  //②
    }finally {
      if(fis != null) {
        try{
          fis.close();
        }catch(IOException ioe) {
          ioe.printStackTrace();
        }
      }
      System.out.println("执行finally块里的资源回收!");
    }
  }
}

注释掉②号代码运行以上程序,我们看到的结果是:

a.txt (系统找不到该文件。)
程序已经执行了finally里的资源回收!

注释掉①号代码运行以上程序,我们看到的结果是:

a.txt (系统找不到该文件。)

4. 嵌套

例如e.g.3代码所示,finally块中还嵌套了一个try…catch语句块,这种在try块、catch块或finally块中包含完整的异常处理流程的情形被称为异常的嵌套。一般对嵌套深度没有限制,但是层次太深的嵌套会降低可读性。

5.Java7的自动关闭资源的try语句:

在java7之前,我们必须像e.g.3中的代码一样手动关闭文件,回收资源。在Java7中增强了try语句的功能,它允许在try关键字后紧跟一对圆括号,圆括号可以声明、初始化一个或多个资源,此处的资源指的是那些必须在程序结束时显示关闭的资源,try语句在该语句结束时自动关闭这些资源。这些资源实现类必须实现AutoCloseable或Closeable接口,实现这两个接口就必须实现close()方法。

注:Closeable是AutoCloseable接口的子接口,Closeable接口里的close()方法声明抛出了IOException,因此它的实现类在实现close()方法时只能声明抛出IOException或其子类;AutoCloseable接口里的close()方法声明抛出了Exception,因此它的实现类在实现close()方法时能抛出任何异常。Java7几乎把所有的“资源类”(包括文件IO的各种类、JDBC编程的Connection、Statement等接口)进行了改写,改写后的资源类都实现了AutoCloseable或Closeable接口

e.g.4

//使用自动回收资源的try语句
public class AutoCloseTest {
  public static void main(String[] args) throws IOException {
    try(
    //声明、初始化两个可关闭的资源,try语句会自动关闭这两个资源
    BufferedReader br = new BufferedReader(
    new FileReader("AutoCloseTest.java"));
    PrintStream ps = new PrintStream(
    new FileOutputStream("a.txt"))) {
      //使用两个资源
      System.out.println(br.readLine());
      ps.println("自动关闭资源的try语句")
    }
  }
}

以上try语句块后的圆括号中声明、初始化了两个IO流,由于BufferedReader、PrintStream都实现了Closeable接口,所以try语句会自动关闭它们。自动关闭资源的try语句块相当于包含了隐式的finally块用于关闭资源,这个try语句可以没有catch块也可以没有finally块,大大减少了代码的长度。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对我们的支持。如果你想了解更多相关内容请查看下面相关链接

(0)

相关推荐

  • 浅谈十个常见的Java异常出现原因

    异常是 Java 程序中经常遇到的问题,我想每一个 Java 程序员都讨厌异常,一 个异常就是一个 BUG,就要花很多时间来定位异常问题. 1.NullPointerException 空指针异常,操作一个 null 对象的方法或属性时会抛出这个异常.具体看上篇文章:空指针常见案例. 2.OutofOutofMemoryError 内存出现异常的一种异常,这不是程序能控制的,是指要分配的对象的内存超出了当前最大的堆内存,需要调整堆内存大小(-Xmx)以及优化程序. 3.IOException I

  • Java的异常类型总结

    Java的设计目的是让程序员有机会设计一个没有错误的应用程序.当应用程序与资源或用户交互时,程序员可能会知道一些异常,这些异常是可以处理的.不幸的是,也有程序员无法控制或简单忽略的例外情况.简而言之,并不是所有的异常都是相同的,因此程序员需要考虑几种类型. 异常是导致程序无法在其预期的执行中运行的事件.异常有三种类型--检查异常.错误和运行时异常. The Checked Exception(检查异常) 已检查异常是Java应用程序应该能够处理的异常.例如,如果应用程序从文件中读取数据,它应该能

  • Java中内存区域的划分与异常详解

    前言 JAVA内存区域主要由程序计数器.java 虚拟机栈.本地方法栈.Java堆.方法区以及运行时常量池组成.本文将给大家详细介绍关于Java内存区域的划分与异常的相关内容,下面话不多说了,来一起看看详细的介绍吧. 运行时数据区域 JVM在运行Java程序时候会将内存划分为若干个不同的数据区域. 程序计数器 线程私有.可看作是 当前线程所执行的字节码的行号指示器 ,字节码解释器的工作是通过改变这个计数值来读取下一条要执行的字节码指令. 多线程是通过线程轮流切换并分配处理器执行时间来实现的,任何

  • Java异常跟踪栈定义与用法示例

    本文实例讲述了Java异常跟踪栈定义与用法.分享给大家供大家参考,具体如下: 一.异常跟踪栈简介 异常对象的printStackTrace方法用于打印异常的跟踪栈信息,根据printStackTrace方法的输出结果,我们可以找到异常的源头,并跟踪到异常一路触发的过程. 二.main方法中异常跟踪栈的应用 1 代码示例 class SelfException extends RuntimeException { SelfException(){} SelfException(String msg

  • Java异常处理中的各种细节汇总

    前言 今天我们来讨论一下,程序中的错误处理. 在任何一个稳定的程序中,都会有大量的代码在处理错误,有一些业务错误,我们可以通过主动检查判断来规避,可对于一些不能主动判断的错误,例如 RuntimeException,我们就需要使用 try-catch-finally 语句了. 有人说,错误处理并不难啊,try-catch-finally 一把梭,try 放功能代码,在 catch 中捕获异常.处理异常,finally 中写那些无论是否发生异常,都要执行的代码,这很简单啊. 处理错误的代码,确实并

  • Java如何自定义异常打印非堆栈信息详解

    前言 在学习Java的过程中,想必大家都一定学习过异常这个篇章,异常的基本特性和使用这里就不再多讲了.什么是异常?我不知道大家都是怎么去理解的,我的理解很简单,那就是不正常的情况,比如我现在是个男的,但是我却有着女人所独有的东西,在我看来这尼玛肯定是种异常,简直不能忍.想必大家都能够理解看懂,并正确使用. 但是,光学会基本异常处理和使用不够的,在工作中出现异常并不可怕,有时候是需要使用异常来驱动业务的处理,例如: 在使用唯一约束的数据库的时候,如果插入一条重复的数据,那么可以通过捕获唯一约束异常

  • Java异常处理之try...catch...finally详解

    异常处理机制已经成为判断一门编程语言是否成熟的标准之一,其对代码的健壮性有很大影响.一直以来异常处理使用不是很得心应手,今天对异常进行了较为深入的学习,这篇主要是对try-catch-finally的一个总结. 一.java继承体系 Java语言为异常处理提供了丰富的异常类,这些类之间有严格的继承关系.如图: 从图中我们可以看出,所有的类都是继承于Throwable这个父类,java将所有的非正常情况分为两种:Error(错误)和Exception(异常),Error错误一般是于虚拟机相关的问题

  • Java异常处理机制try catch流程详解

    在项目中遇到try...catch...语句,因为对Java异常处理机制的流程不是很清楚,导致对相关逻辑代码不理解.所以现在来总结Java异常处理机制的处理流程: 1.异常处理的机制如下:在方法中用 try... catch... 语句捕获并处理异常,catch 语句可以有多个,用来匹配多个不同类型的异常.对于处理不了的异常或者要转型的异常,在方法的声明处通过 throws 声明异常,通过throw语句拋出异常,即由上层的调用方法来处理该异常. try { 逻辑程序块 } catch(Excep

  • 基于java涉及父子类的异常详解

    java中的异常涉及到父子类的问题,可以归纳为一句话:子类的构造函数抛出的异常必须包含父类的异常,子类的方法可以选择抛出"范围小于等于"父类的异常或不抛出异常. 1. 为什么构造函数必须抛出包含父类的异常? 在<thingking in java>中有这么一段话: 异常限制:当覆盖方法时,只能抛出在基类方法的异常说明中列出的那些异常 异常限制对构造器不起作用,你会发现StormyInning的构造器可以抛出任何异常,而不必理会基类构造函数所抛出的异常.然而因为必须构造函数必

  • java 异常捕获及处理案例详解

    目录 一.Java异常简介 二.Java异常的分类 三.异常的使用及执行流程 四.自定义异常 一.Java异常简介 什么是异常? 程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常.异常发生时,是任程序自生自灭,立刻退出终止.在Java中即,Java在编译或运行或者运行过程中出现的错误. Java提供了更加优秀的解决办法:异常处理机制. 异常处理机制能让程序在异常发生时,按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持

  • Java实现Excel导入导出操作详解

    目录 前言 1. 功能测试 1.1 测试准备 1.2 数据导入 1.2.1 导入解析为JSON 1.2.2 导入解析为对象(基础) 1.2.3 导入解析为对象(字段自动映射) 1.2.4 导入解析为对象(获取行号) 1.2.5 导入解析为对象(获取原始数据) 1.2.6 导入解析为对象(获取错误提示) 1.2.7 导入解析为对象(限制字段长度) 1.2.8 导入解析为对象(必填字段验证) 1.2.9 导入解析为对象(数据唯一性验证) 1.3 数据导出 1.3.1 动态导出(基础) 1.3.2 动

  • java异步编程CompletableFuture使用示例详解

    目录 一.简单介绍 二.常见操作 1.使用默认线程池 2.使用自定义线程池 3.获取线程的执行结果 三.处理异步结算的结果 四.异常处理 五.组合 CompletableFuture 六.并行运行多个 CompletableFuture 七.案例 1.从多个平台获取书价格 2.从任意一个平台获取结果就返回 一.简单介绍 CompletableFuture 同时实现了 Future 和 CompletionStage 接口. public class CompletableFuture<T> i

  • java CompletableFuture异步任务编排示例详解

    目录 前言 同步串行 异步串行 并行任务 多任务结果合并计算 任一任务完成 快速失败 注意 前言 在之前的项目开发中,都没怎么使用过CompletableFuture的功能,只听说过和异步编程有关.为了能够在将来有需要的时候用得上,这两天花了点时间学习了一下,并简单地总结一下如何使用CompletableFuture完成异步任务编排. 先创建一个自定义的线程池,后续所有代码都会使用到: private static final ThreadPoolExecutor THREAD_POOL_EXE

  • java操作mongoDB查询的实例详解

    java操作mongo查询的实例详解 前言: MongoDB是一个基于分布式文件存储的数据库.由C++语言编写.旨在为WEB应用提供可扩展的高性能数据存储解决方案. MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的.他支持的数据结构非常松散,是类似json的bson格式,因此可以存储比较复杂的数据类型.Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且

  • Java并发编程Semaphore计数信号量详解

    Semaphore 是一个计数信号量,它的本质是一个共享锁.信号量维护了一个信号量许可集.线程可以通过调用acquire()来获取信号量的许可:当信号量中有可用的许可时,线程能获取该许可:否则线程必须等待,直到有可用的许可为止. 线程可以通过release()来释放它所持有的信号量许可(用完信号量之后必须释放,不然其他线程可能会无法获取信号量). 简单示例: package me.socketthread; import java.util.concurrent.ExecutorService;

  • java 中自定义OutputFormat的实例详解

    java 中 自定义OutputFormat的实例详解 实例代码: package com.ccse.hadoop.outputformat; import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.util.StringTokenizer; import org.apache.hadoop.conf.Configuration; import org.apa

随机推荐