第1章 异常与异常处理(下)

1 - 5:Java中的异常抛出及自定义异常

Java中异常的抛出有两个关键字:throw和throws。下面是throws的用法,使用在方法里的,用来声明将要抛出何种类型的异常(声明)。throws是写在方法名和方法体的参数表之后,在方法体之前。用它修饰的方法向调用者表明该方法可能会抛出某种类型的异常。在这里可以之抛出一种类型异常也可以抛出多种类型。每个类型异常中间用逗号隔开。具体的方法体里可以调用一些会抛出异常的方法,或者可以先抛出一个异常,着里就用了一个throw关键字。throw是个动词,他写在方法体里面。它表明的就是具体的抛出异常这个动作。如果某个方法调用到了会抛出异常的方法,那必须添加try-catch语句去尝试捕获这种异常,或者添加throws声明将异常抛出给更上面一层的调用者处理。

这是throws用法:

public void 方法名(参数列表){
	throws异常列表{
		//调用会抛出异常的方法或者:
		throw new Exception();
	}
}

我们看一下这个代码:

public void divide(int one, int two) throws Exception{
	if(two == 0){
		throw new Exception("两数相除,除数不能为0!");
	}else{
		System.out,println("两数相除,结果为" + one/two);
	}
}

在方法divide中,声明抛出Exception的异常,当变量two为0,而会抛出一个新的Exception异常,信息是两数相除,除数不能为0。

再看下面:

public void compute(){
	/*
	*
	*此处省略计算代码
	*
	*/
	try{
		divide(5, 0);
	}catch(Exception e){
		System.out.println(e.getMessage());
	}
}

就像咱们列举了可以处理的情况,通过try-catch语句块尝试捕获处理divide方法中抛出的异常。

还有一个方法:

public void compute() throws Exception{
	/*
	*
	*此处省略计算代码
	*
	*/
	divide(5, 0);
}

是向咱们演示了不能处理的情况。当调用者不能处理异常的时候,则将该异常继续声明抛出给更上一层的调用者处理。

这就像工厂生产过程中的出现原料用完了,员工对于这种异常无能为力,要向上级反映处理。虽然Java标准类库中提供了很丰富的异常种类,但可能也会遇到我们实际应用情景用到了Java类库中没有的异常,比如我想要一个喝大了异常,类库很定没有。这时候我们就出现了有“自定义异常”的概念。自定义异常就是咱们自己定义的异常类型,这个自定义异常必须继承Java标准类库中意思相近的异常类型,或者直接继承所以异常类型的积累,就是Exception类型。

现在就写一个自定义类型吧,刚才我们说喝大了这个类型,在Eclipse里新建一个类,叫DrunkException,后面要继承异常的类型,这边继承Exception类,咱们也可以想它的父类Exception那样,为自定义异常添加一个带有一个字符串类型参数的构造器,而咱们只需要在含参的构造器中调用它的父类Exception的构造方法就可以,然后再把参数传进去,因为有的因为有了一个含参构造器,编译器就不会为咱们的自定义异常类补充一个无参的构造器。而有时候我们又需要用到,那我们再手动添加一个无参构造器。

public class DrunkException extends Exception {
	
	public DrunkException() {
		
	}
	public DrunkExceptipn(String message) {
		super(message);
	}
}
//第3-8行都是构造器

多数情况下咱们写到这自定义异常就写完了,我们也可以给自定义异常添加更多相对复杂的功能。

 

1 - 6:练习题

自定义异常类的父类可以是( )

A、Error

B、VirtuaMachineError

C、Exception

D、Thread

 

答案:C。解析:Exception 是异常类,自定义异常要继承于 Exception 类或者其子类

 

1 - 7:Java中的异常链

有时候我们能把捕获的异常包装成一个新的异常,然后在新的异常里添加对原始异常的引用,再把这个新异常抛出。他们就像是链式反应一样,一个导致另一个。在Java中,这种情况叫做异常链。我们看看代码吧。

我们还在test包下新建一个类,类名叫ChainTest,生成main方法,这个类完成的工作:第一个方法(Test1):抛出“喝大了”异常;第二个方法(Test2):调用Test1,捕获“喝大了”异常,并包装成运行时异常,继续抛出;main方法中调用test2,尝试捕获test2方法抛出的异常。

先写第一个方法test1:

看到这里有个错误,点一下这个错误再点一下第15行这句话,会发现这样的情况:

实际上就是没有添加抛出声明,双击抛出声明,变成这样就没问题了:

然后看test2,这样写:

发现还有错,test1里面其实是抛出了一个DrunkException,但在test2没对它进行处理,所以我们要在这里加一个try-catch包围,然后再catch块中咱们会把DrunkException包装成一个新的运行时异常然后抛出。

咱们先新建一个RuntimeException,起名叫newExc,RenException newExc = new RuntimeException("司机一滴酒,亲人两行泪");,然后调用newExc的initCause方法并把捕获的DrunkException放进去,然后抛出新异常:

然后我们要在main方法中调用test2,在main中先创一个ChainTest实例,然后用try-catch去包围ChainTest实例的test2方法,catch后面写Exception e,然后调用一下Exception的printStackTrace方法,打印一下就可以了。

全部代码:

package imooc.exception.test;

public class ChainTest {

	/*
	 * 这个类完成的工作:第一个方法(Test1):抛出“喝大了”异常;
	 * 第二个方法(Test2):调用Test1,捕获“喝大了”异常,并包装成运行时异常,继续抛出;
	 * main方法中调用test2,尝试捕获test2方法抛出的异常
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		ChainTest ct = new ChainTest();
		try {
			ct.test2();
		}catch(Exception e) {
			e.printStackTrace();
		}
	}

	public void test1() throws DrunkException {
		throw new DrunkException("喝车别开酒!");
	}
	
	public void test2() { 
		try {
			test1();
		} catch (DrunkException e) {
			// TODO 自动生成的 catch 块
			//e.printStackTrace();这个就不需要了
			RuntimeException newExc = new RuntimeException("司机一滴酒,亲人两行泪"/*e*/);//新建一个RuntimeException,起名叫newExc
			newExc.initCause(e);//调用newExc的initCause方法并把捕获的DrunkException放进去
			throw newExc;//抛出新异常
		}
	}
}

/*
控制台结果:
java.lang.RuntimeException: 司机一滴酒,亲人两行泪
	at imooc.exception.test.ChainTest.test2(ChainTest.java:30)
	at imooc.exception.test.ChainTest.main(ChainTest.java:14)
Caused by: imooc.exception.test.DrunkException: 喝车别开酒!
	at imooc.exception.test.ChainTest.test1(ChainTest.java:21)
	at imooc.exception.test.ChainTest.test2(ChainTest.java:26)
	... 1 more
*/

这里可以看到控制台输出,第一行java.Lang.RuntimeException就是说咱们在main方法中捕获了一个运行时异常,然后提示信息是司机一滴酒,亲人两行泪,下面看caused by,由此句可以看出该运行时异常是由喝大了的异常引起的,而喝大了的异常是在21、26行抛出的。这就像刚才说的,新的异常中包含原始异常的所有信息,根据这点,咱们可以一行一行的追溯最初异常的位置。这个例子中咱们通过调用新异常的(这里有个方法但我没听清),去引用了原始异常,从而实现了异常链的功能。

其实我们还有一种比较简便的方法:

首先定义到test2,把RuntimeException newExc = new RuntimeException("司机一滴酒,亲人两行泪"/*e*/);中的司机一滴酒,亲人两行泪删掉,在里面直接输入e,然后把newExc.initCause(e);注释,在运行,结果为:

/*
java.lang.RuntimeException: imooc.exception.test.DrunkException: 喝车别开酒!
	at imooc.exception.test.ChainTest.test2(ChainTest.java:30)
	at imooc.exception.test.ChainTest.main(ChainTest.java:14)
Caused by: imooc.exception.test.DrunkException: 喝车别开酒!
	at imooc.exception.test.ChainTest.test1(ChainTest.java:21)
	at imooc.exception.test.ChainTest.test2(ChainTest.java:26)
	... 1 more
*/

我们首先看到捕获的是运行时异常,该运行时异常是由一个喝大了的异常引起的。下面Caused By的喝大了显示出喝车别开酒。

上面这两种写法就是咱们实现链功能的两种基本写法。

 

 

1 - 8:练习题

下列关于异常的描述中,错误的是( )

A、Exception 的父类是 Throwable

B、使用 try-catch-finally 语句捕获并处理异常

C、可以使用 throw 语句抛出异常

D、捕获到的异常只能用当前方法中处理,不能用其他方法处理

 

答案:D。解释:捕获到的异常,可以在当前方法的 catch 块中处理,也可抛出给调用者去处理

 

1 - 9:经验总结

1、处理运行时异常时,采用逻辑去合理规避的同时辅助try-catch处理。这样基本就能做到没有漏网之鱼。

2、在写完多重catch块之后咱们还可以加一个catch(Exception)来处理可能会被遗漏的异常。

3、对于不确定的代码,也可以加上try-catch,处理潜在异常。

4、对于异常我们要尽量处理,因为异常说明很多问题,比如程序问题或环境问题等,如果不去处理程序就会在健壮性上就会大打折扣,而在处理过程中,最忌讳的就是仅仅调用printStackTrace()去打印输出或用系统输出方法打印输出,最好是打印输出异常原因同时加以其他操作,比如业务回滚。

5、具体如何处理方法,还要根据不同业务需求应用场景及不同异常类型去灵活应变。

6、尽量添加final语句块去释放占用资源,尤其是有网络连接和连接数据库的情况下。

 

模拟借书系统

要求:

1、定义字符串数组保存图书信息。2、提示用户输入,分别按“书名”、“图书序号”查找图书。3、根据输入信息进行适当异常处理:ⅰ如果输入类型错误,抛出“错误命令异常”并提示重新输入,ⅱ如果书名不存在,抛出“图书不存在异常”并提示重新输入,ⅲ如果图书序号超过字符串数组范围,抛出“图书不存在异常”提示重新输入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值