JavaSE 第九章 异常

9.1 异常概述与异常体系结构

在使用计算机语言进行项目开发的过程中,即使程序员把代码写得尽善尽美, 在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避 免的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等。

  • 异常:在Java语言中,将程序执行中发生的不正常情况称为“异常” 。 (开发过程中的语法错误和逻辑错误不是异常)

  • Java程序在执行过程中所发生的异常事件可分为两类:

    • Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError和OOM。一般不编写针对性 的代码进行处理。

    • Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使 用针对性的代码进行处理。例如: 空指针访问 、试图读取不存在的文件 、网络连接中断 、数组角标越界

  • 对于这些错误,一般有两种解决方法:一是遇到错误就终止程序 的运行。另一种方法是由程序员在编写程序时,就考虑到错误的 检测、错误消息的提示,以及错误的处理。

  • 捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生。 比如:除数为0,数组下标越界等

  • 分类:编译时异常和运行时异常

9.1.1 运行时异常

  • 运行时异常是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序员应该积极避免的。java.lang.RuntimeException类及其子类都是运行时异常

在这里插入图片描述

  • 之前所遇到过的ClassCastExceptionArrayIndexOutOfBoundsExceptionNullPointerException都属于运行时异常
  • 对于这类异常可以不做处理,因为这类异常很普遍,若是全处理,可能会对程序的可读性和运行效率产生影响

8.1.2 编译时异常

  • 编译时异常是指编译器要求必须处理的异常,即程序在运行时由于外界因素造成的一般性异常。编译器要求Java程序必须捕获或声明所有编译时异常

  • 对于这类异常,如果程序不处理,可能会带来意想不到的结果

9.2 常见异常

  • java.lang.RuntimeException 运行时异常

    • ClassCastException 类转换异常
    • ArrayIndexOutOfBoundsException 数组下标越界异常
    • NullPointerException 空指针异常
    • NumberFormatException 数字类型异常
  • java.io.IOException IO异常

    • FileNotFoundException 文件没找到异常
    • EOFException 流结束异常
  • java.lang.ClassNotFoundException 类没找到异常

  • java.sql.SQLException sql异常

9.2.1 ArrayIndexOutOfBoundsException数组下标越界异常

  • 访问到并不存在的数组空间
/**
 * 数组下标越界异常
 */
public class ArrayIndexOutOfBoundsException {
    public static void main(String[] args) {
        int[] arr = new int[5] ;

        System.out.println(arr[5]);
    }
}

在这里插入图片描述

9.2.2 NullPointerException 空指针异常

  • 引用数据类型通过 . 去调用方法或属性时,如果引用数据类型为null,则会报错
/**
 * 空指针异常
 */
public class NullPointerException {
    public static void main(String[] args) {
        // 创建一个Student对象,将其赋值为bull
        Student student = new Student();
        student = null ;

        student.method();
    }
}

class Student {

    public void method() {
        System.out.println("method.....");
    }
}

在这里插入图片描述

9.2.3 ArithmeticException 运算条件异常

  • 当出现错误的运算条件时抛出该异常
/**
 * 运算条件异常
 */
public class ArithmeticException {
    public static void main(String[] args) {
        int a = 0 ;
        int b = 1 ;
        System.out.println(b / a);  // 众所周知,0是不能作为除数的
    }
}

在这里插入图片描述

ps:

  • 在除法算式中,除号后面的数叫做除数,除号前面的数叫做被除数。 如:6÷2=3,其中除号后面的2就是除数,6为被除数

  • 两个数之间相除有两种读法,分别读作“除”和“除以”。被除数读在前用“除以”表示,而除数读在前则用“除”表示。例如“15÷3”读作“15除以3”或读作“3除15”。

9.2.4 ClassCastException 类转换异常

  • 在对没有子父类关系的类的对象进行转换时会出现
public class ClassCastException {
    public static void main(String[] args) {
        // 使用多态向上转型的性质,创建一个String类对象
        Object obj = new String() ;
        // 因为Object类是ClassCastException类的父类,编译时不会报错
        ClassCastException cce = (ClassCastException) obj ;

        System.out.println(cce);
    }
}

在这里插入图片描述

9.3 异常处理机制一:try-catch-finally

  • 在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行x/y运算时,要检测分母是否为0,数据是否为空,输入的数据是不是数字等。这些我们都可以采用if-else语句进行判断,但是过多的if-else语句会导致代码臃肿,可读性差。因此采用异常处理机制。
  • Java异常处理

Java采用的异常处理机制是将异常处理的代码集中到一起,与正常的代码分开,使程序变得简洁、优雅且易于维护

public class Demo1 {
    public static void main(String[] args) {
        
        Demo1 d = new Demo1() ;
        d.methodA();
    }

    public void methodA() {
        
        methodB();
    }

    public void methodB() {
        
        methodC();
    }

    public void methodC() {
        System.out.println(1 / 0);
    }
}

在这里插入图片描述

  • 为保证程序的正常执行,代码必须对可能出现的异常进行处理

    • 如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理,如果异常没有在调用者方法中处理,它会继续抛给调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程被称为捕获(catch)异常

    • 如果一个异常被抛给了main()方法,且main()方法也没有处理,则程序中止。

    • 程序员通常只能处理Exception,对error也无能为力

  • try-catch-finally语句处理异常的格式:
try {
	可能产生异常的代码块
} catch(异常类型1 变量名1) {
	当异常类型1发生时的处置措施
} catch(异常类型2 变量名2) {
	当异常类型2发生时的处置措施
}
......
[finally {
	无论是否发生异常都必定执行的代码
}]
  • try

    • 捕获异常的第一步就是将try{...} 语句块选定捕获异常的范围,将可能出现异常的代码放到try语句块中
  • catch(异常类型 变量名)

    • 在catch语句块中是对异常对象进行处理的代码。每个try语句块可以伴随 一个或多个catch语句,用于处理可能产生的不同类型的异常对象
  • 如果明确知道产生的是何种异常,可以用该异常类作为catch的参数;也可 以用其父类作为catch的参数。

  • 比 如 : 可 以 用 ArithmeticException 类 作 为 参 数 的 地 方 , 就 可 以 用RuntimeException类作为参数,或者用所有异常的父类Exception类作为参数。 但不能是与ArithmeticException类无关的异常,如NullPointerException(catch 中的语句将不会执行)。

  • 捕获异常的有关信息: 与其它对象一样,可以访问一个异常对象的成员变量或调用它的 方法。

    • getMessage() :获取异常信息,返回字符串

在这里插入图片描述

  • printStackTrace() :获取异常类名和异常信息,以及异常出 现在程序中的位置。无返回值

在这里插入图片描述

  • finally

    • 捕获异常的最后一步是通过finally语句为异常处理提供一个 统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。

    • 不论在try代码块中是否发生了异常事件,catch语句是否执 行,catch语句是否有异常,catch语句中是否有return, finally块中的语句都会被执行。

    • finally语句是可选的

    • finally语句并不影响后面代码的执行

  • 示例:捕获到异常时的执行
public class Demo2 {
    public static void main(String[] args) {
        try {
            System.out.println(1 / 0);
        } catch (ClassCastException e) {
            e.printStackTrace();
            System.out.println("ClassCastException...");
        } catch (ArithmeticException e) {
            e.printStackTrace();
            System.out.println("ArithmeticException...");
        } finally {
            System.out.println("finally....");
        }
        System.out.println("try-catch-finally语句块之外的代码");
    }
}

在这里插入图片描述
在这里插入图片描述

  • 没有捕获到异常时的执行

在这里插入图片描述
在这里插入图片描述

  • 注意:
  • 当我们声明了多个catch语句块时,如果catch中的异常类型没有子父类关系,则声明顺序无所谓;但是当异常类型存在子父类关系时,则要求子类一定声明在父类上面,否则会报错

在这里插入图片描述

  • 前面使用的异常都是RuntimeException类或是它的子类,这些类的异常的特 点是:即使没有使用try和catch捕获,Java自己也能捕获,并且编译通过 ( 但运行时会发生异常使得程序运行终止 )。

  • 如果抛出的异常是IOException等类型的非运行时异常,则必须捕获,否则 编译错误。也就是说,我们必须处理编译时异常,将异常进行捕捉,转化为 运行时异常

示例:

  • 把字符串转为日期类型时,会出现编译错误,不对这个错误进行处理是没办法运行程序的,所以说编译时异常是必须要处理的。
    在这里插入图片描述
  • IO异常
    在这里插入图片描述

9.4 异常处理机制二:throws

  • 声明抛出异常是Java中处理异常的第二种方式

    • 如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这 种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理, 而由该方法的调用者负责处理。

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

  • 示例:
    在这里插入图片描述
    抛出异常
    在这里插入图片描述
import java.io.FileInputStream;
import java.io.IOException;

public class Demo3 {
    public static void main(String[] args) {
        Demo3 t = new Demo3();
        try {
            t.readFile();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public void readFile() throws IOException { // 将可能出现的异常抛给调用者处理
        FileInputStream in = new FileInputStream("test.txt");
        int b;
        b = in.read();
        while (b != -1) {
            System.out.print((char) b);
            b = in.read();
        }
        in.close();
    }
}

在这里插入图片描述

9.4.1 重写方法声明抛出异常的原则

  • 重写方法不能抛出比被重写方法范围更大的异常类型。在多态的情况下, 对methodA()方法的调用异常的捕获按父类声明的异常处理。

在这里插入图片描述

9.5 手动抛出异常

  • Java异常类对象除在程序执行过程中出现异常时由系统自动生成并 抛出,也可根据需要使用人工创建并抛出。

    • 首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运 行环境)。 IOException e = new IOException(); throw e;

    • 可以抛出的异常必须是Throwable或其子类的实例。下面的语句在编译时将 会产生语法错误: throw new String("want to throw");

  • 示例
public class Demo4 {
    public static void main(String[] args) {
        new Demo4().method1();
    }

    public void method1() {
        System.out.println("手动抛出异常");
        throw new ClassCastException();
    }
}

在这里插入图片描述

  • 示例
public class Demo5 {
    public static void main(String[] args) {
        Student student = new Student();
        student.setAge(-20);
        System.out.println(student.getAge());
    }
}

class Student {
    private int age ;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        if (age > 0) {
            this.age = age;
        } else {
            throw new RuntimeException("年龄不能为负数");
        }
    }
}

在这里插入图片描述

9.6 自定义异常类

  • 一般的,用户自定义异常类都是RuntimeException类的子类

  • 自定义异常类通常需要编写几个重载的构造方法

  • 自定义异常需要提供serialVersionUID

  • 自定义异常通过throw抛出

  • 自定义异常的名字非常重要,当异常出现时,可以根据名字判断异常类型

  • 案例:自定义异常类来对年龄的范围进行限制
    自定义异常类:AgeNegativeException
public class AgeNegativeException extends RuntimeException {

    static final long serialVersionUID = 14725836945123L ;

    public AgeNegativeException() {
    }

    public AgeNegativeException(String message) {
        super(message);
    }
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值