Java异常处理--异常处理的方式2:throws

news2025/1/6 20:39:01

文章目录

  • 一、方式2:声明抛出异常类型(throws)
  • 二、throws基本格式
  • 三、 throws 使用举例
    • (1)针对于编译时异常
      • 1、案例1
      • 2、案例2
    • (2)针对于运行时异常
  • 四、 方法重写中throws的要求
    • (1)说明
    • (2)举例1
    • (3)举例2
  • 五、 两种异常处理方式的选择

一、方式2:声明抛出异常类型(throws)

  • 如果在编写方法体的代码时,某句代码可能发生某个编译时异常,不处理编译不通过,但是在当前方法体中可能不适合处理无法给出合理的处理方式,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。

image.png

  • 具体方式:在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。

二、throws基本格式

声明异常格式:在方法的声明处,使用"throws 异常类型1,异常类型2,..."

修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2{   }

在throws后面可以写多个异常类型,用逗号隔开。

【举例1】

public void test() throws 异常类型1,异常类型2,.. {
    //可能存在编译时异常 (运行时异常不用管它)
}

【举例2】

public void readFile(String file)  throws FileNotFoundException,IOException {
	...
	// 读文件的操作可能产生FileNotFoundException或IOException类型的异常
	FileInputStream fis = new FileInputStream(file);
    //...
}

三、 throws 使用举例

(1)针对于编译时异常

1、案例1

以如下代码为例:

public class ThrowsTest {
    public void method1() {
        File file = new File("D:\\hello.txt");

        //可能报FileNotFoundException 文件找不到异常
        FileInputStream fis = new FileInputStream(file);  //流直接操作文件

        //把文件内容直接读到内存中输出
        int data = fis.read();    //可能报IOException 输入输出异常
        while (data != -1) {    //data为-1的时候退出,就是读完了
            System.out.print((char) data);
            data = fis.read();  //可能报IOException 输入输出异常
        }

        //资源关闭
        fis.close();  //可能报IOException 输入输出异常
    }
}

方法method1()中,是一段可能有异常的代码。

此时是编译时异常,必须要进行处理。

image.png

☕处理

在方法小括号后面加上throws,然后根据可能出现异常的类型,比如这里可能出现FileNotFoundExceptionIOException,那么就将它们写在throws后面即可,用逗号隔开。如下:

public class ThrowsTest {
    public void method1() throws FileNotFoundException, IOException {
        //...  ...
    }
}

可以看到现在编译器不报红了,这意味着已经将可能出现的异常处理了。

如下:
image.png


现在针对于method1()方法来说,已经处理了。

若是现在method1()方法在另一个方法method2()里面被调用了,发现此时报红了。如下:

image.png

这里相当于把异常抛给method1()的调用者了。

所以method2()需要处理一下:

public void method2() throws FileNotFoundException, IOException{
    method1();
}

可以看见,不报红了:

image.png

此时method2()又在method3()里面调用了,此时method3()也面临刚才同样的问题:

image.png

此时method3()觉得可以搞定这个问题,它就可以去解决啦:

public void method3() {
    try {
        method2();
    }catch (FileNotFoundException e){
        e.printStackTrace();
    }catch (IOException e){
        e.printStackTrace();
    }
}

若此时method3()在其他方法中也被调用了,比如在main方法中被调用了,此时不会出现异常,因为在method3()中将异常干掉了。
image.png

就相当于method1()那里面有一只狼,method1解决不了,就抛出来,向method2求助,method2也不行,就向method3求助,method3将狼干掉了。

所以main方法里面不再提示有异常要处理。

但是,若此时在main方法里面调用的是method2(),还是需要处理一下这个异常的,因为method2()抛出了异常。

image.png

到main方法就相当于到头了,此时最好不要再抛出去了,下面的代码处理方式就不可取:

public static void main(String[] args) throws FileNotFoundException, IOException{
    method3();
    method2();
}

此时就是将问题抛给虚拟机了,虚拟机也没有方案,程序就会挂掉。整个进程就停掉了。

所以实在不行,就只能这样处理了:

public static void main(String[] args){
    method3();
    try {
        method2();
    }catch (FileNotFoundException e){
        e.printStackTrace();
    }catch (IOException e){
        e.printStackTrace();
    }
}

不要在main方法里面写throws。

🌱代码

package yuyi02;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * ClassName: ThrowsTest
 * Package: yuyi02
 * Description:
 * 以编译时异常举例,运行时异常基本不做处理,直接改代码即可
 *
 * @Author 雨翼轻尘
 * @Create 2024/1/13 0013 7:52
 */
public class ThrowsTest {
    public static void main(String[] args){
        method3();
        try {
            method2();
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void method3() {
        try {
            method2();
        }catch (FileNotFoundException e){
            e.printStackTrace();
        }catch (IOException e){
            e.printStackTrace();
        }
    }

    public static void method2() throws FileNotFoundException, IOException{
        method1();
    }

    public static void method1() throws FileNotFoundException, IOException {
        File file = new File("D:\\hello.txt");

        //可能报FileNotFoundException 文件找不到异常
        FileInputStream fis = new FileInputStream(file);  //流直接操作文件

        //把文件内容直接读到内存中输出
        int data = fis.read();    //可能报IOException 输入输出异常
        while (data != -1) {    //data为-1的时候退出,就是读完了
            System.out.print((char) data);
            data = fis.read();  //可能报IOException 输入输出异常
        }

        //资源关闭
        fis.close();  //可能报IOException 输入输出异常
    }
}

🎲问:是否真正处理了异常?

  • 编译是否能通过的角度看,看成是给出了异常万一要是出现时候的解决方案。此方案就是,继续向上抛出(throws)。
  • 但是,此throws的方式,仅是将可能出现的异常抛给了此方法的调用者。此调用者仍然需要考虑如何处理相关异常。从这个角度来看,throws的方式不算是真正意义上处理了异常。

2、案例2

package com.atguigu.keyword;

public class TestThrowsCheckedException {
    public static void main(String[] args) {
        System.out.println("上课.....");
        try {
            afterClass();//换到这里处理异常
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("准备提前上课");
        }
        System.out.println("上课.....");
    }

    public static void afterClass() throws InterruptedException {
        for(int i=10; i>=1; i--){
            Thread.sleep(1000);//本来应该在这里处理异常
            System.out.println("距离上课还有:" + i + "分钟");
        }
    }
}

(2)针对于运行时异常

throws后面也可以写运行时异常类型,只是运行时异常类型,写或不写对于编译器和程序执行来说都没有任何区别。如果写了,唯一的区别就是调用者调用该方法后,使用try…catch结构时,IDEA可以获得更多的信息,需要添加哪种catch分支。

package com.atguigu.keyword;

import java.util.InputMismatchException;
import java.util.Scanner;

public class TestThrowsRuntimeException {
    public static void main(String[] args) {
        Scanner input = new Scanner(System.in);
        try {
            System.out.print("请输入第一个整数:");
            int a = input.nextInt();
            System.out.print("请输入第二个整数:");
            int b = input.nextInt();
            int result = divide(a,b);
            System.out.println(a + "/" + b +"=" + result);
        } catch (ArithmeticException | InputMismatchException e) {
            e.printStackTrace();
        } finally {
            input.close();
        }
    }

    public static int divide(int a, int b)throws ArithmeticException{
        return a/b;
    }
}

四、 方法重写中throws的要求

(1)说明

方法重写时,对于方法签名是有严格要求的。复习:

1)方法名必须相同
(2)形参列表必须相同
(3)返回值类型
- 基本数据类型和void:必须相同
- 引用数据类型:<=4)权限修饰符:>=,而且要求父类被重写方法在子类中是可见的
(5)不能是staticfinal修饰的方法

当时说到方法的声明格式

权限修饰符 返回值类型 方法名(形参列表) [throws 异常类型] {
	//方法体 
}

方法在声明的时候,可能会抛出异常,关于父类被重写的方法和子类重写的方法,这里其实是有一个要求的:
子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类


方法的重写的要求:(针对于编译时异常来说的)

子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类

此外,对于throws异常列表要求:

  • 如果父类被重写方法的方法签名后面没有 “throws  编译时异常类型”,那么重写方法时,方法签名后面也不能出现“throws  编译时异常类型”。
  • 如果父类被重写方法的方法签名后面有 “throws 编译时异常类型”,那么重写方法时,throws的编译时异常类型必须 <= 被重写方法throws的编译时异常类型,或者不throws编译时异常。
  • 方法重写,对于“throws 运行时异常类型”没有要求。
package com.atguigu.keyword;

import java.io.IOException;

class Father{
    public void method()throws Exception{
        System.out.println("Father.method");
    }
}
class Son extends Father{
    @Override
    public void method() throws IOException,ClassCastException {
        System.out.println("Son.method");
    }
}

(2)举例1

比如现在Son是Father的子类:

public class OverrideTest {

}

class Father{
    public void method1(){

    }
}

class Son extends Father{
    @Override
    public void method1() {

    }
}

假设父类Father中抛出了一个IOException的异常。对于子类Son来说,也可以抛出和父类被重写方法一样的异常。如下:

public class OverrideTest {

}

class Father{
    public void method1() throws IOException {

    }
}

class Son extends Father{
    @Override
    public void method1() throws IOException{

    }
}

一般开发中,都这样写成一样的。

其实,子类Son中,还可以抛一个IOException子类,比如FileNotFoundException。如下:

public class OverrideTest {

}

class Father{
    public void method1() throws IOException {

    }
}

class Son extends Father{
    @Override
    public void method1() throws FileNotFoundException {

    }
}

子类能抛出父类重写方法的异常的子类,而不能是父类重写方法的异常的父类。

image.png


🎲为什么会有上面那样的规则呢?

前面说过一个知识点叫做“多态性”,在多态性的场景下,比如现在声明一个变量f,在右边new了一个子类的对象,如下:

public class OverrideTest {
    public static void main(String[] args) {
        Father f=new Son();
    }
}

然后通过f来调用method1(),此时method1()可能存在编译时异常,需要处理。

image.png

这时候,我们加一个try,将有可能出现异常的代码放入try中:

public static void main(String[] args) {
    Father f=new Son();
    try {
        f.method1();
    }
}

然后要在catch中写上可能出现的异常,这时候要看父类中的可能异常,因为编译的时候它认为调用的是父类的方法。所以此时要看父类中的IOException

如下:

package yuyi02;

import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * ClassName: OverrideTest
 * Package: yuyi02
 * Description:
 *
 * @Author 雨翼轻尘
 * @Create 2024/1/13 0013 15:02
 */
public class OverrideTest {
    public static void main(String[] args) {
        Father f=new Son();
        try {
            f.method1();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

class Father{
    public void method1() throws IOException {

    }
}

class Son extends Father{
    @Override
    public void method1() throws FileNotFoundException {

    }
}

真正运行的时候,实际调用的是子类的方法。调用了子类重写的method1()之后,抛出的异常不能比父类的大,要不然catch不了。
子类的方法在执行的时候抛出的异常父类异常的子类,那么子类的异常对象是可以进入try-catch中的,这其实也体现了多态性。

image.png

相当于实打实地new了一个FileNotFoundException的实例,这里认为是IOException,相当于赋给了IOException,也是多态。

所以子类最大抛出的异常要与父类的异常一样,也可以是父类异常的子类,但不能是父类异常的父类。

多态,编译看左边,运行看右边,但是想要调用子类独有的方法,需要将父类强转成子类才可以调用,俗称“向下转型”。

(3)举例2

若此时父类Father中有一个method2(),并没有声明throws异常。

然后子类Son要重写这个方法,里面有编译时异常,如下:

package yuyi02;

public class OverrideTest {
    public static void main(String[] args) {
        Father f=new Son();
        try {
            f.method1();
        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

class Father{
    public void method2(){

    }
}

class Son extends Father{
    @Override
    public void method2() {

    }
}

这时候可以用throws吗?

不可以

因为在父类Father里面,它没有抛异常,或者是抛出的无穷小异常。所以子类里面也只能抛无穷小的异常,小到没有。

若此时试图去抛一个异常,就不可以,如下:

image.png

父类没有抛,子类就不能抛

若子类里面有异常,但是父类里面没有抛,此时子类就不可以抛异常出去,需要用try-catch-finally来解决。

🚗

以后会看到这样的场景,一个接口里面写了抽象方法,这个抽象方法后面还throws抛出了异常。既然抽象方法没有方法体,那怎么会有异常呢?

这是为了,当这个方法要被重写的时候,实现类重写这个方法可能要抛异常。

为了实现类可以抛异常,所以接口的抽象方法就抛了一个异常。

子类的异常不能大于父类的异常,父类没有抛异常子类就不能抛。


🍻补充1

现在说的异常都是编译器异常,父类没有抛,子类可以抛吗?

按道理是不能的。

但是,看下面的代码:

package yuyi02;

import java.io.FileNotFoundException;
import java.io.IOException;

class Father{
    public void method3(){

    }
}

class Son extends Father{
    @Override
    public void method3() throws RuntimeException{

    }
}

可以发现并没有报错:

image.png

因为对于“运行时异常”,写不写RuntimeException都无所谓。只有在运行的时候才会调用子类的方法。

编译时异常要是不处理,就会报错,比如:

image.png

所以需要处理一下这个编译时异常。若是代码中有运行时异常,在这里不会显示,不显示的话就不用处理。


🍻补充2

之前说“重写”的时候,提到过返回值类型,回顾一下:

关于返回值类型

  • 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型必须是void。
  • 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须与被重写的方法的返回值类型相同。
  • 父类被重写的方法的返回值类型是引用数据类型(比如类),则子类重写的方法的返回值类型可以与被重写的方法的返回值类型相同 或 是被重写的方法的返回值类型的子类。

比如,此时父类的返回值是Number,就是包装类里面数值类型的父类,如下:

class Father{
    public Number method4(){
        return null;
    }
}

子类的返回值,可以是Number,也可以是Number的子类,比如Integer,如下:

class Son extends Father{
    @Override
    public Integer method4() {
        return null;
    }
}

此时用f调用method4,对于父类的方法来讲,返回的是Number,所以接收的时候用Number类型的来接收。实际执行的时候,却是子类重写的方法,这个方法可能返回的是方法子类的对象。

这里只是以一个多态的方式接收而已。(重写的方法里面不能是Number的父类,要不然接收不了)

如下:

public class OverrideTest {
    public static void main(String[] args) {
        Number n=f.method4();
    }
}

五、 两种异常处理方式的选择

前提:对于异常,使用相应的处理方式。此时的异常,主要指的是编译时异常。

🎲开发中,如何选择异常处理的两种方式?(重要、经验之谈)

  • 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,保证不出现内存泄漏。
  • 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally进行处理,不能throws。(父类没有throws,子类也不能throws)
  • 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,我们通常选择使用throws,而方法a中通常选择使用try-catch-finally

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1388697.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

深入了解指针(一)

前言 指针可是数据结构的基础&#xff0c;其内容及重要又繁多&#xff0c;之前没有时间整理出来&#xff0c;现在放假了怎么说也要写一个&#xff0c;既是对前面的总结和梳理&#xff0c;也可以用来以后的查找知识&#xff0c;ok&#xff0c;废话不多&#xff0c;直接启动。 …

网络文件共享服务 FTP

一、存储类型 存储类型分为三种 直连式存储&#xff1a;Direct-Attached Storage&#xff0c;简称DAS 存储区域网络&#xff1a;Storage Area Network&#xff0c;简称SAN&#xff08;可以使用空间&#xff0c;管理也是你来管理&#xff09; 网络附加存储&#xff1a;Network…

kafka系列(二)

本章承接kafka一内容&#xff0c;文章在本人博客主页都有&#xff0c;可以自行点击浏览。 幂等性 请求执行多次&#xff0c;但执行的结果是一致的。 如果&#xff0c;某个系统是不具备幂等性的&#xff0c;如果用户重复提交了某个表格&#xff0c;就可能会造成不良影响。例如…

SecureCRT7中文版安装资源,一键安装

SecureCRT 7 是一款功能强大的终端仿真软件&#xff0c;主要用于安全地远程访问和管理各种网络设备和服务器。 它支持多种协议&#xff0c;如 SSH、Telnet、Rlogin 等&#xff0c;可以连接到不同类型的设备&#xff0c;包括 Unix/Linux 服务器、网络设备、虚拟机等。通过使用 …

黑马程序员JavaWeb开发|案例:tlias智能学习辅助系统(5)登录认证

指路&#xff08;1&#xff09;&#xff08;2&#xff09;&#xff08;3&#xff09;&#xff08;4&#xff09;&#x1f447; 黑马程序员JavaWeb开发|案例&#xff1a;tlias智能学习辅助系统&#xff08;1&#xff09;准备工作、部门管理_tlias智能学习辅助系统的需求分析-CS…

C++的构造析构函数

前言 本篇文章介绍C的构造函数和虚构函数 写在前面 因为介绍构造函数基本都会设计虚函数和虚基类的使用&#xff0c;可以参考之前的文章&#xff1a; C中的虚函数 C的虚基类 构造函数 每个类都分别定义了它的对象被初始化的方式&#xff0c;类通过一个或几个特殊的成员函数…

蓝牙运动耳机哪款好用?运动用哪种耳机好?2024运动蓝牙耳机推荐

​户外活动、健身运动还是激烈跑步&#xff0c;一款出色的运动耳机能够提升整体运动体验。这些耳机不仅具备出色的防汗和稳定佩戴性能&#xff0c;更通过强劲音质为你注入动力。让我们一同探索几款在各种运动场景中脱颖而出的运动耳机吧。 1.南卡开放式耳机&#xff08;00压&a…

二阶贝塞尔曲线生成弧线

概述 本文分享一个二阶贝塞尔曲线曲线生成弧线的算法。 效果 实现 1. 封装方法 class ArcLine {constructor(from, to, num 100) {this.from from;this.to to;this.num num;return this.getPointList();}getPointList() {const { from, to } thisconst ctrlPoint thi…

车灯芯片 H5028L 12V 24V 48V 60V 72V 80V 100V转9V 12V 高低亮 远近光恒流芯片

车灯芯片是车辆照明系统中的重要组成部分&#xff0c;通常采用LED&#xff08;发光二极管&#xff09;技术。以下是车灯芯片的工作原理&#xff1a; LED原理&#xff1a; 车灯芯片主要采用LED作为光源。LED是一种半导体器件&#xff0c;当电流通过LED芯片时&#xff0c;电子和…

(C语言)用函数指针数组实现计算器

一、运算结果&#xff1b; 二、源代码&#xff1b; # define _CRT_SECURE_NO_WARNINGS # include <stdio.h>//实现目录函数&#xff1b; void menum() {//打印目录&#xff1b;printf("***********************************************\n");printf("***…

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖微信小程序端(十二)

购物车相关 1.添加购物车1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计1.1.3 表设计 1.2 代码开发1.2.1 DTO设计1.2.2 Controller层1.2.3 Service层接口1.2.4 Service层实现类1.2.5 Mapper层 2. 查看购物车2.1 需求分析和设计2.1.1 产品原型2.1.2 接口设计 2.2 代码开发2.2.…

安科瑞电动汽车充电桩运维平台 ——安科瑞 顾烊宇

充电桩其功能类似于加油站里面的加油机&#xff0c;可以固定在地面或墙壁&#xff0c;安装于公共建筑&#xff08;公共楼宇、商场、公共停车场等&#xff09;和居民小区停车场或充电站内&#xff0c;可以根据不同的电压等级为各种型号的电动汽车充电。充电桩的输入端与交流电网…

HCIA 网络基础:

应用层 抽象语言-->编码 表示层 编码-->二进制 会话层 建立会话&#xff0c;提供绘画地址。 应用于程序内部进行区分&#xff0c;没有统一标准 上三层主要是软件层面&#xff08;应用 程序处理数据&#xff09; 下四层主要负责数据传输 传输层 端口号 分段 &#xff…

PHP+MySQL组合开发:微信小程序万能建站源码系统 附带完整的搭建教程

随着移动互联网的快速发展&#xff0c;微信小程序已成为企业进行移动营销的重要工具。然而&#xff0c;对于许多中小企业和个人开发者来说&#xff0c;开发一个功能完善、用户体验良好的小程序是一项复杂的任务。罗峰给大家分享一款微信小程序万能建站源码系统。该系统采用PHPM…

C++设计模式-- 2.代理模式 和 外观模式

文章目录 代理模式外观模式角色和职责代码演示一&#xff1a;代码演示二&#xff1a;外观模式适用场景 代理模式 代理模式的定义&#xff1a;为其他对象提供一种代理以控制对这个对象的访问。在某些情况下&#xff0c;一个对象不适合 或不能直接引用另一个对象&#xff0c;而代…

Controller层自定义注解拦截request请求校验

一、背景 笔者工作中遇到一个需求&#xff0c;需要开发一个注解&#xff0c;放在controller层的类或者方法上&#xff0c;用以校验请求参数中(不管是url还是body体内&#xff0c;都要检查&#xff0c;有token参数&#xff0c;且符合校验规则就放行)是否传了一个token的参数&am…

旧衣回收小程序搭建:降低企业成本,提高回收效率!

在人们环保意识提升下&#xff0c;旧衣回收行业受到了大众的关注&#xff0c;同时旧衣回收具有门槛低、利润大的优势。在我国&#xff0c;回收行业不仅帮助普通人就业获利&#xff0c;还对环保做出了较大贡献。因此&#xff0c;旧衣回收行业成为了当下的热门商业模式&#xff0…

C#,入门教程(19)——循环语句(for,while,foreach)的基础知识

上一篇&#xff1a; C#&#xff0c;入门教程(18)——分支语句&#xff08;switch-case&#xff09;的基础知识https://blog.csdn.net/beijinghorn/article/details/124039953 一、for循环 当老师进入教室&#xff0c;从门口开始分别按行、列点名&#xff0c;看看哪位翘课&…

详细介绍如何使用T5实现文本摘要:微调和构建 Gradio 应用程序-含完整源码

对高效文本摘要的需求从未如此迫切。无论您是正在处理冗长研究论文的学生还是浏览新闻文章的专业人士,快速提取关键见解的能力都是非常宝贵的。T5 是一种因多项 NLP 任务而闻名的预训练语言模型,擅长文本摘要。使用 T5 的文本摘要与 Hugging Face API 是无缝的。然而,对 T5 …

二、基础篇 vue计算属性和侦听器

计算属性 模板内的表达式非常便利&#xff0c;但是设计它们的初衷是用于简单运算的。在模板中放入太多的逻辑会让模板过重且难以维护。例如&#xff1a; <div id"example">{{ message.split().reverse().join() }} </div> 在这个地方&#xff0c;模板不…