Java编程思想12章---通过异常(Exception)处理错误

一、异常基础(Exception)

1、何为异常

         所谓异常,就是程序运行过程中发生了意料之外的事。所能做的就是从当前环境跳出并将问题交给上一级环境。比如妈妈说还剩了个土豆去把皮儿削了炒个土豆丝,我们定义了一个方法要削土豆皮 pealPotato(),接收一个Potato对象(即剩的那个土豆)来削皮。

public void pealPotato(Potato po)
{
    //削土豆皮...
}

但是发现没有剩下的土豆了,即po==null,那还削个锤子。这时候削土豆皮 这个方法已经不能执行了(因为没有土豆怎么削),必须要报错,告诉上一级环境(妈妈),说没有剩的土豆了,是要出去买土豆还是换个菜就是上一级环境要考虑的问题了。

这时候就要抛出异常

throws new NullPointerException();    //所要操作的对象不存在

2、捕获异常

        如果在方法内部抛出异常,这个方法将在抛出异常的过程中结束。要是不希望方法就此结束,就需要在方法内设置一个特殊的块来捕获异常。这个块称为try块

try{
    //可能会产生异常的代码
}catch(Type1 e1){
    //Type1情况下,要怎么处理
}catch(Type2 e2){
    //Type2情况下要怎么处理
}
...

比如我们在削土豆时,可能会遇到无土豆可削的情况,也可能会有削皮刀损坏、不小心划伤手等情况

try{
    //削土豆
}catch(没有土豆 e1){
    //去买2斤
}catch(削皮刀损坏 e2){
    //用菜刀凑活
}catch(划伤手 e3){
    //暂停削土豆,包扎要紧
}catch(其他意外 e4){
    //不干了,蘸大酱也挺好,削个屁的土豆
}
...

 3、创建自定义异常

        不必拘泥于Java自带的异常类型,可以创建自定义的异常类型。要自己定义异常,必须从已有的异常种类继承,最好是选择意思相近的异常类继承。最简单的方法是让编译器产生默认构造器。

class NullPotato extends Exception{}//不存在要削皮的土豆
class ParerDamage extends Exception{}//削皮刀损坏
class FingerHurt extends Exception{}//伤着手了

还可以定义一个接收字符串的构造器。对于异常来说最重要的就是异常的名字,在大多数情况下默认构造器已经够用了。

class ParerDamage extends Exception{
    public ParerDamage(){}
    public ParerDamage(String s) {super(s)}
}

4、异常说明

        把方法可能会抛出的异常告知要使用此方法的程序员。比如妈妈要我们削土豆皮,我们把可能会出现的异常提前告诉她。这样给她一些心理准备,发生异常后能及时解决。我们称之为异常说明

void pealPotato(Potato po) throws NullPotato,ParerDamage,FingerHurt,OhterException{ 
    //... 
}

void pealPotato(Potato po) {//不能抛出异常(除了RuntimeException继承而来的异常),遇到困难自己解决喽
}

当然可以说明异常而不产生异常,而不能不说明异常而产生异常。你不说你遇到的困难,别人都以为你是躺赢的🤕。

5、捕获所有异常

try{
    //
}catch(Type1 e1){
    //
}catch(Type2 e2){
    //
}catch(Type3 e3){
    //
}
...
catch(Exception e4){
    //
}

最后捕获Exception异常,把他放在处理程序列表的结尾,防止其他类型的异常被提前捕获。

二、重新抛出异常/异常链  

catch(Exception e)
{
    ....
    throw e;    //抛出异常到更高级的环境中,后续的catch语句将会被忽略
}

虽然重新抛出了异常,但是其异常信息或异常抛出地点,仍然是原始异常产生的地点。

异常的栈轨迹可以通过printStackTrace()方法,可以输出到标准错误或者标准输出

e.printStackTrace();
e.printStackTrace(System.out);
e.printStackTrace(System.err);

 重新抛出异常并将异常发生地点定义在重新抛出的地点

throw (Exception)e.fillInStackTrace();

  

三、异常的限制

当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常

1. 父类方法抛出异常,子类可抛可不抛,抛也必须抛异常说明里列出的那些异常

class BaseballException extends Exception{}
class Foul extends BaseballException{}
class PopFoul extends Foul{}
class Strike extends BaseballException{}


public abstract class Inning
{
    public abstract void atBat() throws Strike,Foul;
}

public class StormInning extends Inning
{
    public void atBat() throws Foul{};

    //也可以抛出父类异常说明里的子异常,比如PopFoul是Foul的子类异常
    public void atBat() throws popFoul{}
}

2. 父类方法不抛出异常,子类覆盖的方法不可抛出异常

3. 父类构造器抛出异常,子类构造器必须包含该异常

public abstract class Inning
{
    public Inning() throws BaseballException{} //构造函数这里抛出BaseballException
}


class StormException extends Exception
class Rained extends StormException

public class StormInning extends Inning
{
    public Storming() throws Rained, BaseballException{} //这里也必须抛出BaseballException
}

4、继承父类与实现接口抛出异常的冲突

        若一个类继承了父类的某个方法,又要实现某个接口,接口方法与父类方法名称相同时,不能抛出不同类型的异常。

interface Storm{//要实现的接口
	public void event() throws RainedOut;
    ...
}

abstract class Inning{//要继承的类

	public void event() throws BaseballException{}
	...
}

public class StormInning extends Inning implements Storm {

    //该方法不可被实现,因为接口抛出的异常时RainedOut,而父类抛出的异常是BaseballException
    //二者冲突,不可实现。要避免这样的情况发生
    public void event() throws BaseballException{};

    //但是可以不抛出异常
     public void event(){}

    //也可以抛出接口方法里的RainedOut异常,可能是最新的JDK改了接口的优先级
    public void event() throws RainedOut{};
}

四、finally清理

对于一些代码,不管try块里面的异常是否抛出,都要执行某些命令,这时候便可以使用finally语句。

try {
		//可能会抛出异常的模块
}catch(A a1){
	面对情况A的处理方式
}catch(B b1){
	面对情况B的处理方式
}
	...
finally {
	不管面对什么样的情况,都会执行的代码块
}

finally语句能用来做什么,垃圾回收机制,无论try块里发生了什么,内存总能得到释放。但是Java有垃圾回收机制,所以可以使用finally语句把内存之外的资源恢复到初始状态,如已经打开的文件或网络连链接,屏幕上画的图形,甚至是外部世界的开关(保证开关无论无何最终都是关闭状态)。

把try块放到while循环里,就建立了一个“程序执行之前必须要达到”的条件。

class NotEnoughException extends Exception
{
	private static final long serialVersionUID = 1L;	
}

/*
 * 假设有一个人打劫我们,要舍财保命,但是我们又不想损失太多,又不知道歹徒的心理预期是多少,就一点一点的拿给他,
 * 因为有finally语句,无论如何都可以执行,用来判断是否满足条件,从而跳出循环。
 */
public class FinallyTest {

	static int count = 0;	//我们已经交给歹徒的钱
	static int expectation = 100;	//歹徒心理预期的钱
	public static void main(String[] args) {
		while(true)
		{
			try {
				System.out.println("我递给了歹徒一块,总共交了"+(++count)+"块");;	//我们一块一块的掏给他
				if(count < expectation) {//如果我们给的钱不够歹徒心理预期抛出一个异常,钱不够!
					throw new NotEnoughException();
				}
			}catch(NotEnoughException e) {
				System.out.println("”才"+count+"块钱,还不够“");
			}
			finally {//Finally语句总是能运行
				if(count == expectation) {		//每交完钱歹徒也会与自己心理预期比较一下,如果满足预期了
					System.out.println("”算你小子识相“");
					break;						//歹徒放一句狠话就走了
				}
				else {
					System.out.println("”麻利儿的,把钱全交出来“");	//如果没有满足预期,必然会继续要
				}
					
			}
		}
	}
	
}

不想写这一章了,脑子乱的要死。。。。。以后有机会再补充

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值