【软件构造】 Exceptions(异常)



一、异常的定义

程序运行时,发生的不被期望的事件,它阻止了程序按照程序员的预期正常执行,这就是异常。

二、异常的分类

在这里插入图片描述
Error:描述了Java运行时系统内部很少发生的内部系统错误和资源耗尽情况(例如:虚拟机错误,链接错误)。
不要抛出Error对象,因为程序员对此通常无能为力。
常见的Error有:Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。

Exception:程序本身可以捕获并且可以处理的异常。

除此之外异常还分为Unchecked异常和Checked异常。

Unchecked异常:Error+RuntimeException。可以不处理,编译时也没有问题,但执行时出现就导致程序失败,代表程序中的潜在bug。

Checked异常:Exception中除RuntimeException之外的异常。编译器会检查此类异常,必须捕获并指定错误处理器handler,否则编译无法通过。

三、异常的处理机制

声明异常:throws
抛出异常:throw
捕获异常:try,catch,finally

3.1 throws

用于方法声明上,表示该方法会抛出一个异常,但不进行处理,交给该方法的调用者来处理异常。
举例


public class Throws {
	public static void main(String[] args) throws IOException{
        readFile();
    }
 
    public static void readFile() throws IOException {
        InputStream is = new FileInputStream("D:/Lab2/ch01.txt");
    }

}

在Throws类中,有两个方法。一个是readFile方法,该方法是向指定目录读取一个文件,如果文件读取失败,就会抛出一个IOException。而main方法调用readFile。观察结果:
在这里插入图片描述
表示在线程main中出现了异常FileNotFoundException。

3.2 throw

throw: 用在方法内部,表示抛出一个异常,传递给调用者处,并结束方法。

public class Throws {
	 public static void main(String[] args) {
	      int a =  Throws.div(1,0);
	      System.out.println(a);
	    }
	 
	   public static int div(int a,int b)
	      {
	            if(b==0)
	              throw new ArithmeticException("除数不能为0");
	            return a/b;
	     }
}

查看结果
在这里插入图片描述
这里要注意throw和throws的区别。
虽然两者都是抛出异常,但是throws用于方法签名上,表示可能会抛出某种异常,而throw用于方法内部,执行throw一定会抛出异常。

并且注意到我们上面讲到的Unchecked异常和Checked异常。如果throw抛出一个Checked异常,要么用try-catch进行捕获处理,要么用throws抛给上级调用者。
在这里插入图片描述

3.3 try-catch-finally

try用于监控代码是否会产生异常,若产生异常,就交给catch进行处理。无论是否产生异常,最后都会执行finall语句

try {
   //监视代码,一旦返现异常则直接跳转至catch,
   // 如果没有异常则直接跳转至finally
} catch (Exception e) {
   //如果发现异常则进行处理或向上抛出。
} finally {
  //必选执行的代码块,不管是否有异常发生,
}

我们对上面的除零代码进行变化,用try-catch捕获处理。

public class Throws {
	 public static void main(String[] args) {
	      try {
		  int a =  Throws.div(1,0);
	      }
	      catch(ArithmeticException e){
	    	  System.out.println("除数不能为0!");
	      }
	      finally {
	    	  System.out.println("这是finally语句");
	      }
	      
	    }
	 
	   public static int div(int a,int b)
	      {
	            if(b==0)
	              throw new ArithmeticException();
	            return a/b;
	     }
}

执行结果
在这里插入图片描述

四、例题

为了更好的理解上面的知识,我们来看一道例题。
在这里插入图片描述
这是一道跟异常有关的题目,给出了异常的堆栈跟踪信息。
我们知道方法的调用是遵循堆栈的先进后出的方式。
观察题目中的堆栈跟踪信息,首先告诉我们在main方法中出现了异常CarAlreadyParkingException,之后给了我们四个方法,那么根据堆栈先进后出的方式,我们可以知道,先打印出的方法应该是最后调用的。由此我们可以知道,客户端应该是调用D中的main方法,main方法调用C中的parking方法,C中的parking方法调用B中的parking方法,以此类推。直到A中的parking方法产生了一个异常。
由此我们模拟一下代码

public class MyException extends Exception {//自己定义的异常类
    private int detail;
    MyException(int a){
        detail = a;
    }
    public String toString(){
        return "MyException ["+ detail + "]";
    }
}

public class A {//A类
	
  public static void parking() throws MyException{
       
    A.compute(5);
    System.out.println("这是A");
}
  public static void compute(int a) throws MyException{//当a大于10时会抛出异常
      System.out.println("Called compute(" + a + ")");
      if(a > 10){
          throw new MyException(a);
      }
      System.out.println("Normal exit!");
  }
}
public class B {
public static void parking() throws MyException{
        
        A.parking();
        System.out.println("这是B");
}
}
public class C {
public static void parking() throws MyException{
        
        B.parking();
        System.out.println("这是C");
}
}
public class D {
	public static void main(String [] args) throws MyException{
       
        C.parking();
        System.out.println("这是D");
}
}

上面的代码模仿了题目。其中D类的main方法调用C的parking方法,C的parking方法调用B的parking方法,B的parking方法调用A的parking方法,A的parking方法调用compute方法,compute方法在a大于10时会抛出异常。
这里要注意我在每个类的方法调用时,后面打印一句“这是X”,目的是验证方法的调用顺序。
我们首先令a=5,让方法正常运行,查看结果。
在这里插入图片描述
我们可以看到,打印出的顺序正好与我们调用方法的顺序相反,这就说明方法的调用是先进后出的方式。
之后我们令a=20,再次查看。
在这里插入图片描述
我们发现,这跟题目中给定堆栈跟踪信息类似,说明我们的代码逻辑正确。
对于A选项,我们就知道了异常发生的第一现场应该是打印的第一句,也就是A类的111行。
对于B选项,该异常并不能确定是unchecked异常还是checked异常。
对于C选项,如果该异常是unchecked异常,那么方法签名上就不需要有throws。
对于D选项,该异常可能在被捕获后,再次抛出。
例如

public class D {
	public static void main(String [] args) throws MyException{
        try {
        C.parking();
        System.out.println("这是D");
        }
        catch(MyException e) {
        	throw e;
        }
}
}

在D的main方法中,我们用try-catch捕获异常,但是处理时,只是把异常再次向上抛出。查看结果
在这里插入图片描述
与之前的结果一样。
所以该题选B。

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值