首页 > 编程语言 >第七章《Java的异常处理》第4节:throw与throws关键字

第七章《Java的异常处理》第4节:throw与throws关键字

时间:2022-12-30 18:01:40浏览次数:61  
标签:Java divide 方法 throw 关键字 catch 异常 throws

​在Java的异常处理系统中,除了前面学过的try、catch和finally关键字之外,还有两个非常重要的关键字,它们分别是throw和throws。这两个关键字之间只相差一个字母,但意义却相差很多。

7.4.1throw关键字的使用

7.3小节曾经讲过:当有内外两层catch时,如果内层catch捕获到异常,那么外层catch就不会重复捕获。如果内层catch捕获到异常后,希望交给外层catch进行处理,那么它就必须把这个异常对象传递给外层catch。如何把异常对象传递到外层catch呢?Java语言中,有一个throw关键字能够完成这个操作。throw这个单词的中文意思是“抛出”,在实际开发中,它能把异常抛给更外层的catch,进而可以让更外层的catch捕获并处理这个异常。以下的【例07_10】就能很好的展示throw关键字的作用。​

【例07_10 使用throw关键字抛出异常】

Exam07_10.java​

public class Exam07_10 {
public static void main(String[] args) {
int a;
try {
try {
a = 1/0;//①
}catch(ArithmeticException e) {//内层catch
System.out.println("内层catch捕获异常");
throw e;//抛出已捕获的异常
}
}catch(Exception e) {//外层catch
System.out.println("外层catch捕获异常");
}
}
}

在【例07_10】中,执行语句①时会产生算术异常,此时内层catch捕获到这个异常并输出“内层catch捕获异常”,但随后通过语句“throw e;”又把该异常抛向更外层。这个异常虽然是从内层catch中抛出的,但对于外层catch而言,异常仍然是来自于外层try,因此外层catch能够捕获到这个异常,所以会输出“外层catch捕获异常”。【例07_10】的运行结果如图7-11所示。​

第七章《Java的异常处理》第4节:throw与throws关键字_Java

图7-11 【例07_10】运行结果​

很多读者不明白为什么在捕获异常之后还要再次抛出它。大家知道:异常对象中封装了某次运行错误的详细信息,有时为了能够把这些错误信息向更外层传递,所以会抛出已经捕获的异常。另外,throw关键字不仅仅能抛出已经捕获的异常对象,它还能抛出程序员自己创建的异常对象,常见的场景就是:当程序员通过判断语句发现程序运行出现不合理状态,就可以通过抛出异常的方式来通知外层这种不合理状态的存在,这种应用技巧将在7.5小节中进行讲解。​

在使用throw关键字时还需注意:并不是所有类型的对象都可以通过throw关键字抛出,Java语言仅允许抛出Throwable类及其子类的对象,如果抛出的是其他类型的对象都会导致语法错误。另外,当程序运行到throw语句时会立刻跳转到相应的catch块中,如果没有相应的catch块捕获被抛出的异常,虚拟机会中止程序运行,所以throw语句后面不能再有其他语句,因为这些语句根本没有机会被执行,如图7-12所示。​

第七章《Java的异常处理》第4节:throw与throws关键字_Java_02

图7-12 throw语句之后不能再有其他语句​

7.4.2throws关键字的使用

在Java语言中,还有一个throws关键字,这个关键字只比throw关键字多了一个字母s,但它们的意义差别很大。throws关键字都写在方法的后面,用来声明这个方法在运行过程中可能会产生哪些异常,具体格式如下。​

返回值类型方法名(参数列表) throws 异常列表{

    语句;

}

为什么要声明一个方法在运行时会抛出哪些异常呢?请看下面的【例07_11】:​

【例07_11 方法内部处理异常】

Exam07_11.java​

public class Exam07_11 {
public static int divide(int a,int b) {
int r = 0;
try {
r = a/b;
}catch(Exception e) {
e.printStackTrace();
}
return r;
}

public static void main(String[] args) {
int x = divide(1,0);//调用divide()方法会出现异常
int y = 2*x; //用divide()方法返回值进行后续计算
System.out.println("运算结果为:"+y);
}
}

在【例07_11】的Exam07_11类中定义了一个用于做整数除法的divide()方法,当main()方法在调用这个方法时,由于传递了不合理的参数导致在divide()方法运行时产生算术异常,在divide()方法的内部对这个异常进行了处理。【例07_11】的运行结果如图7-13所示。​

第七章《Java的异常处理》第4节:throw与throws关键字_throw_03

图7-13 【例07_11】运行结果​

从图7-13可以看出:divide()方法在运行时产生了异常,因而方法的返回值其实是一个无效运算结果。虽然在divide()方法内部对这个异常进行了处理,但是main()方法仍然用这个无效的运算结果完成了后续的计算,这导致后续的计算结果必然是错误的。由此可以得知:divide()方法对异常进行处理并不会阻止整个程序走上错误的运行道路。如果希望在方法运行出现异常的情况下程序能停止后续的计算,就必须把处理异常的工作从divide()方法中挪到main()方法中进行。具体思路是:当在main()方法中调用divide()方法时,如果发现divide()方法运行出现异常,要立即停止后续的计算并且转入catch块中处理异常。既然要把处理异常的工作放在main()方法中进行,就要由divide()方法告知main()方法它在运行过程中可能产生哪些异常,这个告知的工作该如何完成呢?很简单,只需要在定义divide()方法时用throws关键字声明一下方法运行可能抛出的异常,具体实现过程请看下面的【例07_12】:​

【例07_12 使用throws关键字声明方法抛出的异常】

Exam07_12.java​

public class Exam07_12 {
public static int divide(int a,int b) throws Exception{
int r = 0;
r = a/b;
return r;
}

public static void main(String[] args) {
try{
int x = divide(1,0);
int y = 2*x;//后续计算
System.out.println("运算结果为:"+y);
}catch(Exception e) {
System.out.println("运算出现异常!");
}
}
}

【例07_12】的代码由【例07_11】修改而来。在【例07_12】中,divide()方法对异常没有做任何处理,所有处理异常的工作都在main()方法中进行。main()方法把调用divide()方法的语句放在了try块中,并搭配了一个catch对异常进行捕获。而divide()方法只是用throws关键字声明了自身在运行过程中可能会产生哪些异常。【例07_12】的运行结果如图7-14所示。​

第七章《Java的异常处理》第4节:throw与throws关键字_异常_04

图7-14 【例07_12】运行结果​

从图7-14可以看出,divide()方法在运行过程中出现异常后,没有进行后续的计算,而是立即进入main()方法的catch块中对异常进行了处理,从而避免了整个程序走上错误的运行道路。​

有读者会问:如果divide()方法使用throws关键字声明了可能产生的异常,但main()方法并没有对异常进行任何处理,那么这个声明岂不是失去了意义?想要回答这个问题,必须要了解一下Java语言中异常的分类。在Java语言中,把异常分为两类,一类叫“已检查异常”,另一类叫“未检查异常”。Exception有一个子类叫做RuntimeException,RuntimeException和它的子类属于“未检查异常”,而剩余异常类均为“已检查异常”。编译器对已检查异常有强制性处理要求,也就是说:编译器要求程序员必须对可能出现的已检查异常进行处理,否则程序无法通过编译。【例07_12】中divide()方法所声明的Exception也属于已检查异常。divide()方法已经明确声明可能抛出Exception,如果不对divide()方法进行异常处理,将会出现图7-15所示的语法错误。​

第七章《Java的异常处理》第4节:throw与throws关键字_throw_05

图7-15 未处理已检查异常​

从图7-15可以看出,只要一个方法声明了自身可能会抛出已检查异常,该方法的调用者就必须对所声明的异常进行处理,因此throws关键字所做的声明工作不会没有意义。另外,一个方法内部如果有可能出现已检查异常,编译器会要求程序员要么在方法内部就处理这个异常,要么用throws关键字声明异常来告知调用者处理,绝对不允许“置之不理”。​

既然编译器强制要求程序员必须处理检查异常,那么读者就必须知道Java语言中有哪些常见的已检查异常。实际开发过程中,已检查异常往往出现在数据库操作、IO操作和线程操作几个领域。例如图7-7中所示的SQLException、IOException等都属于已检查异常。而算术异常ArithmeticException、数组越界异常ArrayIndexOutOfBoundsException、空指针异常NullPointerException等均属于未检查异常。为什么编译器不强制要求程序员处理未检查异常呢?就是因为未检查异常可以通过判断语句避免它的产生,例如:通过if语句就能提前判断出除数是否为0,这样完全可以避免算术异常的产生。而已检查异常很难通过程序提前判断,所以编译器要求程序员必须设置catch块来处理这些异常,即使在方法内部无法进行处理,也要用throws关键字声明它产生的可能性。​

实际开发过程中,方法使用throws关键字声明所抛出的异常都是已检查异常,不会是未检查异常,因为如果方法声明抛出的是未检查异常,那么编译器并不强制要求处理这个异常,这样会导致声明失去意义。而【例07_12】中,divide()方法在运行过程中所抛出的异常本来是ArithmeticException,它是一个未检查异常,但为了让读者理解throws关键字的作用,所以把divide()方法所声明的异常写成了被编译器强制要求处理的Exception。另外读者还必须清楚:方法使用throws关键字对异常进行声明,并不表示说该方法运行时一定会出现异常,throws关键字只是告知调用者方法运行存在产生异常的“可能性”。​

如果一个方法在运行时可能产生多种异常,可以在throws关键字的后面把每一种可能产生的异常都声明出来,各种异常之间用逗号进行分隔。Java语言还规定:子类重写父类方法时,子类方法所声明的异常范围不能超过父类的原版方法。读者不能单纯的认为声明的异常种类少就表示它的范围小,例如父类中的method()方法被定义为:​

public void method()throws IOException,SQLException{​
...​
}​

而子类中的method()方法被定义为:​

public void method()throws Exception{​
...​
}​

父类的method()方法声明了IOException和SQLException两个异常,而子类的method()方法只声明了Exception一个异常。从表明上看,子类method()方法声明的异常更少,但Exception是所有异常类的祖先,它可以表示所有的异常,因此一个Exception所表示的异常范围已经远远超过IOException和SQLException这两个异常所表示的范围,这种情况下,编译器就会提示重写方法出现了语法错误。

除此文字版教程外,小伙伴们还可以点击这里观看我在本站的视频课程学习Java。


标签:Java,divide,方法,throw,关键字,catch,异常,throws
From: https://blog.51cto.com/mugexuetang/5981052

相关文章

  • 【校招VIP】“推推”Java项目课程:产品原型:产品需求要点分析
    VIP的服务,不一样的校招,大家好,我是校招VIP的大拿老师。今天呢我们继续来看商业实战项目推推小说详情模块的第一节课:产品原型。Java同学一定要理解产品在正式的商业项目里......
  • java代码实现上传文件到谷歌云
    1、runconfiguration添加vm参数:-Dhttps.proxy=http://googleapis-dev.gcp.cloud.uk.hsbc:3128-Dhttps.proxyHost=googleapis-dev.gcp.cloud.uk.hsbc-Dhttps.proxyPort=3......
  • java 中的char , utf16 utf8
    在设计Java时决定采用16位的Unicode字符集....(中间省略)...现在16位的Char类型已经不能满足描述所有Unicode字符的需要了。Java为了解决这个问题的方法是使用码点和代码单......
  • java vop 打印controller的入参和出参
    packagecom.sleep.demo.intercepter;importcom.alibaba.fastjson.JSONObject;importlombok.extern.slf4j.Slf4j;importorg.apache.commons.lang3.ArrayUtils;imp......
  • java自定义注解
    自定义注解importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annot......
  • 【校招VIP】“推推”Java项目课程:接口设计——详情数据和快速验证
    今天继续看一下商业实战项目【推推】小说详情模块的第三期。上节课做了文档设计,这节课做一下接口设计。在前后端分离的项目里,接口设计非常重要。在正常的实习或者开发工作......
  • Java集合快速失败和安全失败机制
    快速失败机制是Java集合的一种错误检测机制,当遍历集合时,集合的结构进行了修改,可能会触发"fail-fast"机制Java.util包中所有集合都被设计为快速失败机制示例代码public......
  • 全文检索工具elasticsearch:第三章: Java程序中的应用
    搭建模块创建二个项目gmall-list-service的appliction.properties:server.port=8073spring.datasource.url=jdbc:mysql://localhost:3306/gmall?characterEncoding=......
  • 使用Java NIO提高服务端程序的性能
      JavaNIO增加了新的SocketChannel、ServerSocketChannel等类来提供对构建高性能的服务端程序的支持。SocketChannel、ServerSocketChannel能够在非阻塞的模式下工作,它......
  • JavaScript 批量大文件上传下载
    ​ 4GB以上超大文件上传和断点续传服务器的实现随着视频网站和大数据应用的普及,特别是高清视频和4K视频应用的到来,超大文件上传已经成为了日常的基础应用需求。但是在......