深入分析Java异常

java异常分为两大类,Checked异常和Runtime异常,Checked异常都是在编译阶段可以被处理的异常。

Checked异常和Runtime异常的区别和联系

  • Checked异常都是可以被处理的异常,在程序中必须显式地处理Checked异常,如果没有处理,那么编译就会报错。而Runtime异常可以不被显式的处理;
  • 都是Exception的子类,继承了RuntimeException的就是Runtime异常,其他的就是Checked异常。

常见异常类

列举几个常见的运行时异常RuntimeException:

  • IndexOutOfBoundException:数组越界异常;
  • NullPointerException:空指针异常;
  • ClassCastException:类转换异常;
  • NumberFormatException:数字格式异常;
  • ArithmeticException:运算异常。

列举几个非运行时异常(Checked异常):

  • SQLException:SQL异常;
  • IOException:IO异常;
  • FileNotFoundException:文件找不到异常,是IOException的子类;
  • InterruptedException:中断异常,一般用在多线程编程;
  • ClassNotFoundException:类找不到。

Error错误

Error错误一般指与虚拟机相关的问题,如系统崩溃,虚拟机错误,动态链接失败等,这种错误无法恢复或不可被捕获,将导致应用程序中断。通常应用程序也无法处理这些错误,因此程序中不应该试图使用catch来捕获Error对象。在方法定义时,也无需throws Error对象。

Checked异常的使用

前面提到了Checked必须显式的处理,不然编译报错,比如声明一个文件输入流:

FileInputStream fis = new FileInputStream("test.md");

这段代码编译会报错

Unhandled exception type FileNotFoundException

因此必须显式的处理它,处理Checked异常的方式一般有两种:

如果知道如何处理,那么最好使用try…catch...块处理:

//Checked异常必须被显式处理
try {
 FileInputStream fis = new FileInputStream("test.md");
} catch (FileNotFoundException e) {
 e.printStackTrace();
 System.out.println("文件不存在!");
}

如果不知道如何处理,那么就在方法中抛出,由上一级调用者处理:

public static void main(String[] args) throws FileNotFoundException {

 //Checked异常必须被显式处理
 //在main方法中抛出异常,交给JVM处理,JVM对异常的处理办法就是打印跟踪栈信息,并终止程序运行
 FileInputStream fis = new FileInputStream("test.md");
}

使用throw自行抛出异常

有的时候根据业务需要,我们在程序里面会自行抛出异常,比如如果读取的文件内容为空,我们就认为这是一个异常,这时候我们可以使用throw来主动抛出异常,并且用catch捕获它:

//使用throw主动抛出异常
try {
 FileInputStream fis = new FileInputStream("test.md");
 if(fis.read() == 0) {
  throw new IOException("空文件");
 }
} catch (IOException e) {
 e.printStackTrace();
}

如果throw抛出的是一个runtime异常,那么程序可以用try…catch…捕获,也可以不用理会。

异常链处理

在真实的企业级应用中,我们往往不会讲底层的异常暴露给上层应用,比如不会把SQL异常暴露到用户界面上。一是对于用户而言,看到SQL异常对他们也没啥帮助,二是对于恶意用户而言,暴露底层异常不安全。

那么如何屏蔽底层异常呢?通常的做法是:程序先捕获原始异常,然后抛出一个新的业务异常,新的业务异常包含了对用户的提示信息,这种处理方式成为异常转译。下面演示一个创建用户的程序如何屏蔽底层异常:

//演示异常链,创建用户
public void createSubscriber(int subId) throws BusinessException {
 try {
  //创建用户的逻辑......

 }catch(Exception e){
  //处理并保存原始异常...

  //抛出新的业务异常
  throw new BusinessException("用户创建失败");
 }
}

可以看到程序把原始异常隐藏起来,仅向上提供必要的异常提示信息,可以保证底层异常不会扩展到表现层,这完全符合对象的封装原则。

这种把捕获一个异常然后抛出另一个异常,并把原始异常信息保存下来,是一种典型的链式处理,在设计模式中被称为责任链模式。

使用异常的几个建议

我们使用异常是为了实现几个目标:

  • 使程序代码混乱最小化;
  • 捕获并保留诊断信息;
  • 通知合适的人员;
  • 采用合适的方式结束异常活动

针对这些目标,我们应该做到:

1、不要过度使用和依赖它:异常很方便,但是不要把正常的逻辑处理都使用异常处理,比如

//原始代码
if(fileSize > 100){
 Sysotem.out.println("文件过大,请重新上传");
 continue;
}
//改成使用异常
if(fileSize > 100){
 throw new Exception("文件过大,请重新上传");
}
//这样做,很明显不负责任。
  1. 不要在try里面写很多代码:这样可能增加异常分析的难度,并且大量的代码可能需要大量的catch来捕获不同的异常;
  2. 避免使用catch来捕获所有类型的异常:比如catch(Throwable t)或者catch(Exception e)这样,对所有异常使用同样的逻辑处理,不得不写很多if语句处理不同情况,得不偿失,并且这种捕获方式可能将Error、Runtime等可能导致程序终止的异常捕获,从而“压制”了异常,一些关键异常可能被悄悄忽略;
  3. 不要忽略捕获到的异常:catch应该做一些有用的事情,不要为空或者只打印异常,catch块为空就是瞒天过海,程序出了错误,所有人看不到任何异常,但是程序可能已经坏了!在捕获到异常的时候,要么处理它,要么抛出新异常,要么向上抛出并在合适的地方处理异常。
(0)

相关推荐

  • 导入项目出现Java多个工程相互引用异常A cycle was detected in the build path of project的解决办法

    将一个项目导入最烦的是遇到各种报错,前段时间搞的一个项目,各个功能模块单独作为一个工程,然后不同工程之间相互调用,这里会报这么一个·错误a cycle was detected in the build path of project,这个错误是因为eclipse默认的编译提示级别过高造成的,只要将编译级别降低即可,之后项目即可正常编译. 现在分享下方案: 1.Eclipse/STS -> Window -> Preferences... -> Java -> Compiler -

  • Java异常处理学习心得

    程序中总是存在着各种问题,为了使在程序执行过程中能正常运行,使用Java提供的异常处理机制捕获可能发生的异常,对异常进行处理并使程序能正常运行.这就是Java的异常处理. 一.可捕获的异常 Java中可以捕获的异常分为可控式和运行时异常. 1. 可控式异常 在Java中把那些可以预知的错误,在程序编译时就能对程序中可能存在的错误进行处理,并给出具体的错误信息,这些错误称为可控式异常.常用的可控式异常如下: 异常说明 IOException 当发生某种I/O异常时,抛出此异常 SQLExcepti

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

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

  • java简单自定义异常实例代码

    此处主要便于对异常类的使用上,方便大家理解,以一个公约数为例做了一个简单自定义异常的处理代码如下: 如果操作者输入数字符合要求程序运行,不符合则抛出错误. package 自定义异常简单实例; import java.util.Scanner; public class CommonDivisor { static Scanner in; public void gongyue(int m,int n) throws Exception{ if(m<0||n<0) { throw new Ex

  • 详解Java中的checked异常和unchecked异常区别

    (一)Java的异常层次结构 要想明白Java中checked Exception和unchecked Exception的区别,我们首先来看一下Java的异常层次结构. 这是一个简化的Java异常层次结构示意图,需要注意的是所有的类都是从Throwable继承而来,下一层则分为两个结构,Error和Exception.其中Error类层次描述了Java运行时系统的内部错误和资源耗尽错误,这种错误除了简单的报告给用户,并尽力阻止程序安全终止之外,一般也米有别的解决办法了. (二)unchecke

  • 利用Java8 Optional如何避免空指针异常详解

    前言 空指针是我们最常见也最讨厌的异常,为了防止空指针异常,你不得在代码里写大量的非空判断. Java 8引入了一个新的Optional类.用于避免空指针的出现,也无需在写大量的if(obj!=null)这样的判断了,前提是你得将数据用Optional装着,它就是一个包裹着对象的容器. 都说没有遇到过空指针异常的程序员不是Java程序员,null确实引发过很多问题.Java 8中引入了一个叫做java.util.Optional的新类可以避免null引起的诸多问题. 我们看看一个null引用能导

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

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

  • Java异常处理的五个关键字

    异常:异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的. 异常处理关键字:try.catch.finally.throw.throws 注意事项: 错误不是异常,而是脱离程序员控制的问题. 所有的异常类是从 java.lang.Exception 类继承的子类. 异常类有两个主要的子类:IOException 类和 RuntimeException 类. Java有很多的内置异常类. 异常大致分类: 用户输入了非法数据. 要打开的文件不存在. 网络通信时连接中断

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

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

  • Java学习笔记之异常处理

    本文实例为大家分享了Java异常处理的具体代码,供大家参考,具体内容如下 一.异常的分类 1.由Java虚拟机抛出的异常(Error):程序无法处理的问题,用户不用去进行处理(虚拟机错误丶内存溢出错误丶线程死锁) 2.Exception异常:程序本身可以进行处理的异常 1).非检查异常(Unchecked Exception):编译器不需要强制处理的异常(空指针异常丶数组下标越界异常丶算数异常丶类型转换异常) 2).检查异常(checked Exception):编译器需要强制处理的异常(IOE

随机推荐