Java异常处理体系详细易懂

异常处理基本概念

所谓的异常简单来看就是非正常状态,和正常情况不一样有错误产生。而在Java上异常就是程序上的错误,我们编写程序时经常会产生错误,这些错误分成了编译期间的错误和运行时的错误。而异常处理就是对这些错误进行捕获和解决。

为了更直观的认识什么时异常处理,我们举一个例子来看:我们学习过数组,知道数组是由索引值来获取数组元素的,而索引值越界就会产生错误提示。
在这里插入图片描述

Java异常体系

在这里插入图片描述

Throwable 类

Throwable 位于 java.lang 包下,它是 Java 语言中所有错误(Error)和异常(Exception)的父类。Throwable 包含了其线程创建时线程执行堆栈的快照,它提供了 printStackTrace() 等接口用于获取堆栈跟踪数据等信息。

主要方法:

fillInStackTrace: 用当前的调用栈层次填充 Throwable 对象栈层次,添加到栈层次任何先前信息中;

getMessage: 返回关于发生的异常的详细信息。这个消息在 Throwable 类的构造函数中初始化了;

getCause: 返回一个 Throwable 对象代表异常原因;

getStackTrace: 返回一个包含堆栈层次的数组。下标为 0 的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底;

printStackTrace: 打印 toString() 结果和栈层次到 System.err,即错误输出流。

Error 类

Error 是 Throwable 的一个直接子类,它可以指示合理的应用程序不应该尝试捕获的严重问题。这些错误在应用程序的控制和处理能力之外,编译器不会检查 Error,对于设计合理的应用程序来说,即使发生了错误,本质上也无法通过异常处理来解决其所引起的异常状况。

常见 Error:

AssertionError:断言错误;

VirtualMachineError:虚拟机错误;

UnsupportedClassVersionError:Java 类版本错误;

OutOfMemoryError :内存溢出错误。

Exception 类

Exception 是 Throwable 的一个直接子类。它指示合理的应用程序可能希望捕获的条件。

Exception 又包括 Unchecked Exception(非检查异常)和 Checked Exception(检查异常)两大类别。

Unchecked Exception (非检查异常)

Unchecked Exception 是编译器不要求强制处理的异常,包含 RuntimeException 以及它的相关子类。我们编写代码时即使不去处理此类异常,程序还是会编译通过。

常见非检查异常:

NullPointerException:空指针异常;

public class NullTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Date d = null;
		try {
			System.out.println(d.after(new Date()));
		}
		catch(NullPointerException ne) {//NUllPointerException异常
			System.out.println("空指针异常");
		}
		catch(Exception e) {//Exception异常
			System.out.println("未知错误");
		}
		
	}
输出结果:空指针异常

ArithmeticException:算数异常;

ArrayIndexOutOfBoundsException:数组下标越界异常;

ClassCastException:类型转换异常。

public class DivTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		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("未知异常!");
		}
	}

2.3.2 Checked Exception(检查异常)
Checked Exception 是编译器要求必须处理的异常,除了 RuntimeException 以及它的子类,都是 Checked Exception 异常。我们在程序编写时就必须处理此类异常,否则程序无法编译通过。

常见检查异常:

IOException:IO 异常

SQLException:SQL 异常

Java异常机制五个重要关键字及用法

Java的五个关键字分别为:
try: try块里面放置可能引发异常的代码
catch: 里面放置对应异常处理的代码
finally: 用于回收try块中打卡的物理资源
throws: 用于声明该方法可能抛出的异常,主要在方法签名中使用
throw: 用于抛出一个实际的异常

throw抛出异常

如果执行try块里的业务逻辑代码时出现异常,系统会自动生成一个异常对象,该异常对象被提交给Java运行环境时,这个过程被称为抛出(throw)异常。

public class ExceptionDemo1 {

	public static void divide(int a,int b) {
		if(b==0) {
			throw new ArithmeticException("除数不能为零");
		}
		System.out.println(a/b);
	}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		divide(2,0);
	}
}

在这里插入图片描述

try-catch捕获处理异常

当Java运行时环境收到异常对象时,会寻找能处理该异常对象的catch块,如果找到合适的catch块,则把异常对象交给该catch块处理,这个给过程被称为捕获(catch)异常;如果Java运行时环境找不到捕获异常的catch块,则运行时环境终止,Java程序也将退出。

try{
	//业务实现代码
}
catch(Exveption e){
	//异常处理代码
}

注意
1.try和catch块后的花括号{。。。}是不可以省略的。
2.try块里声明的变量是代码块内局部变量,只能在try块内有效。

使用throws声明抛出异常

使用throws抛出异常的思路是:当前方法不知道如何处理这种类型的异常,该异常应该阻止一级调用者处理;如果main方法也不知如何处理这种类型的异常也可以使用throws声明来抛出异常。然后会将该异常交给JVM处理,JVM对异常的处理方法就是,打印异常的跟踪栈信息,这就是前面程序遇到异常后自动结束的原因。

public class ThrowsTest {

	public static void main(String[] args) throws IOException{
		// TODO Auto-generated method stub
		Object fis = new FileInputStream("a.txt");
	}
}
运行结果:
Exception in thread "main" java.io.FileNotFoundException: a.txt (系统找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(Unknown Source)
	at java.io.FileInputStream.<init>(Unknown Source)
	at java.io.FileInputStream.<init>(Unknown Source)

使用finally回收资源

有些时候,程序会在try块中打卡一些物理资源,例如数据库连接,网络连接和磁盘文件等,这些物理资源必须显式回收。你会说Java不是有Java的垃圾回收机制嘛。当Java的垃圾回收机制不会回收任何物理资源,垃圾回收机制只能回收堆内存中对象所占用的内存。所以为了保证一定可以回收try块中打卡的物理资源,异常处理机制提供了finally块。不管try块中代码是否出现异常,也不管哪一个catch块被执行,甚至在try块中或catch块中执行了return 语句,finally块总是会被执行。但有一种情况不会,看下面示例代码。

public class FinallyTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		FileInputStream fis = null;
		try {
			fis = new FileInputStream("a.txt");
		}
		catch(IOException ioe) {
			System.out.println(ioe.getMessage());
			//使用return强制放回
			return;
			//使用exit退出Java虚拟机
			//System.exit(1);
		}
		finally {
			if(fis != null)
			{
				try {
					fis.close();
				}catch(IOException ioe) {
					ioe.printStackTrace();
				}
			}
			System.out.println("执行finally块里的资源回收!");
		}
	}
}

如果在catch块中Java中加入return,虽然return语句会强制方法结束,但一定会先执行finally块里的代码,结果如下:

a.txt (系统找不到指定的文件。)
执行finally块里的资源回收!

但如何使用exit直接退出Java虚拟机,我们会看到finally块并没有被执行,所以如果在异常处理代码中使用System.exit(1)语言来退出虚拟机,则finally块将会失去执行机会。

a.txt (系统找不到指定的文件。)

如何自定义异常类

在通常情况下,程序很少自行抛出系统异常,因为异常的类名也就包含了该异常的有用信息。所以在选择抛出异常时,因选择合适的异常类,从而可以明确的描述该异常情况。所以给出了一种用户自定义的异常方式。

用户自定的异常类通常应该继承Exception类,但如果需要自定义Runtime 异常,则应该继承RuntimeException基类。
定义异常类通常需要提供两个构造器:
1.无参数构造器
2.带一个字符串参数的构造器,这个字符串作为该异常对象的描述信息,也就是异常对象getMessage()方法的放回值。

public class AuctionException extends Exception
{
//无参构造器
public AuctionException(){}
//带一个字符串参数的构造器
public AuctionException(String msg)
{
	super(msg);
}

异常处理规则

成功的异常处理应该具有以下特点
1.使程序代码混乱最小化
2.捕获并保留诊断信息
3.通知合适人员
4.采用合适的方式结束异常活动

不要过度使用异常

过度使用异常主要表现在:
1.把异常和普通错误混淆在一起,不再编写任何错误处理代码。而是以简单地抛出异常来代替所有的错误处理。
2.使用异常处理来代替流程控制

不要使用过于庞大的try块

避免使用CatchAll语句

不要忽略捕获到的异常

既然已经捕获到异常,那catch块理应处理并修复这个错误,而不是瞒天过海,所有人都看不到任何异常,但整个应用可能已经彻底坏了。仅在catch块里打印错误跟踪信息稍微好点,但仅仅比空白异常多了几行异常处理,从瞒天过海变成了简单期满。
通常建议对异常处理采取适当措施:
1.处理异常:对异常进行合适的修复,然后绕过异常发生的地方继续执行;或者用别的数据镜像计算,以代替期望的返回值,或者提示用户重新操作,,,总之,对于Checked异常,程序应该尽量修复。
2.重新抛出异常:把当前运行环境下能做的事情尽量做完,然后进行异常转译,把异常包装成但前层的异常,重新抛出给上层调用者。
3.在合适的层处理异常:如果当前层不清楚如何处理异常,就不要在当前层使用catch语句来捕获该异常,直接使用throws声明抛出异常,让上层调用者来负责处理该异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值