Java的异常类以及异常处理机制

一、异常类

首先,废话不多说,先上图,根据图,我们再来慢慢讲解
在这里插入图片描述
由上图我们可以知道,在java中,所有的异常类都继承自Throwable类。而Throwable类有两个实现类,分别是Error类和Exception类

1.Error

程序在执行过程中所遇到的硬件或操作系统的错误。错误对程序而言是致命的,将导致程序无法运行。是程序无法处理的错误,表示运行应用程序中较严重问题。大多数与程序员执行的操作无关,而表示代码运行时JVM出现的问题,这些异常发生时,JVM会选择线程终止。常见的错误有内存溢出(OOM)、虚拟机运行错误(Virtual MachineError)。

2.Exception

是程序正常运行中,可以预料的意外情况。是程序本身可以处理的异常。它又分为两个子类,分别是IOException,RuntimeException。

(1)IOException:
非运行时异常,javac强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally或者throws)。在方法中要么用try-catch语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。例如我们使用Thread.sleep方法,文件IO操作时,编译器会提醒我们加上try{}catch{}

(2)RuntimeException:
运行时异常,javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。但是在程序运行时会抛出异常,结束程序运行。比如我们在定义了数组之后,访问数组容量以外的元素会报错,产生ArrayIndexOutOfBoundsException;还有除数为0的操作等。

二、异常处理机制

在编写代码处理异常时,对于检查异常,一般有2种不同的处理方式:使用try…catch…finally语句块处理它。或者,使用throws 声明交给函数调用者caller去解决。
(1)使用throws例子:假如我们不同throws
(方法抛出异常用throws,try块抛出异常用throw)

public class Tts {
	
	public int divide(int a,int b){
		int res=a/b;
		return res;
	}

}

public class Tt {	

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

		Tts t = new Tts();
		int res=0;
		res = t.divide(5, 0);
		System.out.println(res);
	}

}

我们会发现程序并不会提醒我们需要用try catch处理异常,而当我们执行时,(除数为0)当然也就抛出了异常。
那当我们给divide方法加上throws Exception后,会发现编译器要求我们必须进行异常处理。,那么我们利用throws便可以提醒调用者此方法可能会有异常抛出,我们应该对异常进行强制相关处理。

public class Tt {

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

		Tts t = new Tts();
		int res=0;
		try {
			res = t.divide(5, 0);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("做处理。。。。。");
			//e.printStackTrace();
		}
		System.out.println(res);
	}

}

结果:
做处理。。。。。
0
(2)使用try{}catch{}(finally{})进行异常处理
通过上面的例子我们也就知道try{}catch{}是用来进行异常处理的,因为在程序运行时,我们总不能把异常显示给用户,体验不好,而且还有安全隐患。那么针对这种情况,我们一般都是对异常在前台进行隐藏并打印相关语句进行处理,例如上述的例子,异常发生时,我打印出“做处理。。。”而不是把异常结果直接抛给用户。
那么,关于try{}catch{}(finally{})的执行顺序又是如何的呢?


public class Tt {
	public static void main(String[] args) throws InterruptedException{
		
		char a = 'b';
		int aa=a-97;
		Tts t = new Tts();
		int res=0;
		try {
			System.out.println("我会执行");
			res = t.divide(5, 1);
			System.out.println("我是否会执行?");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("做处理。。。。。");
			//e.printStackTrace();
		}finally{
			System.out.println("我一定要执行。。。。");
		}
		System.out.println(res);
	}

}

还是刚刚的例子,当程序无异常时,情况如下:
我会执行
我是否会执行?
我一定要执行。。。。
5

public class Tt {
	public static void main(String[] args) throws InterruptedException{
		
		char a = 'b';
		int aa=a-97;
		Tts t = new Tts();
		int res=0;
		try {
			System.out.println("我会执行");
			res = t.divide(5, 0);
			System.out.println("我是否会执行?");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			System.out.println("做处理。。。。。");
			//e.printStackTrace();
		}finally{
			System.out.println("我一定要执行。。。。");
		}
		System.out.println(res);
	}

}

当程序发生异常时,情况如下:
我会执行
做处理。。。。。
我一定要执行。。。。
0

可以看到,不管是否发生异常,finally里面的代码一定会执行,而如果发生异常,在try中异常下面的代码不执行,如果不发生异常,catch中的代码不执行。

那如果搭配上return,程序的执行顺序又会如何呢?,我们对程序做如下修改:


public class Tts {
	
	public int divide(int a,int b){
		int res=0;
		try {
			System.out.println("我会执行");
			res=a/b;
			return res;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			return 55;
		}finally{
			System.out.println("我来啦");
			return 11;
		}
	}

}

public class Tt {

	public static void main(String[] args) throws InterruptedException{
		
		char a = 'b';
		int aa=a-97;
		Tts t = new Tts();
		int res=0;
		res = t.divide(5, 1);
		System.out.println(res);
	}

}

结果:
我会执行
我来啦
11

public class Tt {

	public static void main(String[] args) throws InterruptedException{
		
		char a = 'b';
		int aa=a-97;
		Tts t = new Tts();
		int res=0;
		res = t.divide(5, 0);
		System.out.println(res);
	}

}

结果:
我会执行
我来啦
11
从上述结果来看,如果在finally中添加return,那么返回值会覆盖先前的return,不管是否出现异常。原因是jvm为方法创建了一个栈帧,而栈是先进后出的,那么后面执行的finally中的return就会先出栈返回,从而覆盖正确结果。
再将finally中的return注释掉

public class Tts {
	
	public int divide(int a,int b){
		int res=0;
		try {
			System.out.println("我会执行");
			res=a/b;
			return res;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			return 55;
		}finally{
			System.out.println("我来啦");
			//return 11;
		}
	}

}

结果:
我会执行
我来啦
55
出现异常就会只return catch中的数。

三、自定义异常类

我们可以通过继承Exception类来生成一个自定义的异常,通常这个异常用来在捕获时产生一些我们想要的特定的影响(这里我觉得有点AOP的意思~~)这里就不再演示代码了,各位读者可以自己动手尝试一下。

四、Java中的异常处理关键字是什么?

throw: 有时我们明确要创建异常对象然后抛出它来停止程序的正常处理。throw关键字用于向运行时抛出异常来处理它。

throws: 当我们在方法中抛出任何已检查的异常而不处理它时,我们需要在方法签名中使用throws关键字让调用者程序知道该方法可能抛出的异常。调用方法可以处理这些异常或使用throws关键字将其传播给它的调用方法。我们可以在throws子句中提供多个异常,也可以与main()方法一起使用。

try-catch: 我们在代码中使用try-catch块进行异常处理。try是块的开始,catch是在try块的末尾处理异常。我们可以使用try有多个catch块,try-catch块也可以嵌套。catch块需要一个应该是Exception类型的参数。

finally: finally块是可选的,只能用于try-catch块。由于异常会暂停执行过程,因此我们可能会打开一些不会关闭的资源,因此我们可以使用finally块。finally块总是被执行,无论是否发生异常。

五、异常链

把捕获的异常包装成一个新的异常,在新的异常中添加对新的异常的引用,再把新异常抛出,就像是链式反应一样,这种就叫异常链。

public static void main(String[] args) {

		A a = new A();// 创建chainTest实例
		try {
			a.test2();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

	public void test1() throws DrunkException {
		throw new DrunkException("喝车别开酒");
	}

	public void test2(){
	   try{
	       test1();
	   }catch (DrunkException e){
	       RuntimeException newExc=new RuntimeException("司机一滴酒亲人两行泪");//含参构造器   
	       newExc.initCause(e);//调用newExc的init方法,把捕获的DrunkException传进去
	       throw newExc;//抛出新异常
	   }
	}

结果

喝酒
java.lang.RuntimeException: 司机一滴酒亲人两行泪
	at A.test2(A.java:37)
	at A.main(A.java:22)
Caused by: DrunkException
	at A.test1(A.java:30)
	at A.test2(A.java:35)
	... 1 more

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值