Java学习笔记【2】:抛出异常

Java系列文章目录

Java基本语法


文章目录

  • Java系列文章目录
  • 前言
  • 一、什么是异常?
    • 1. 异常的定义
    • 2. Java的异常处理机制
    • 3. 异常的多态
      • 3.1 继承
      • 3.2 手写异常
  • 二、异常处理机制
    • 1. throws
      • 1.1. 用于方法内
      • 1.2. 用于声明方法时
      • 1.3. Ducking
    • 2. try/catch/finally
      • 2.1. try/catch/finally块的流程控制
        • 2.1.1. try
        • 2.1.2 catch
        • 2.1.3 finally
      • 2.2 try/catch代码解读
  • 总结
    • 1. 异常处理规则
    • 2. 流程处理


前言

在之前学习Python的时候,就曾经接触过抛出异常 ,但当时对面向对象语言还不是很了解,草草看完就没了下文,实际上并没有搞懂,借着Java中相类似的内容,希望能够真正搞明白,并记录下来作为此次学习笔记。


一、什么是异常?

1. 异常的定义

在Java语言中, 将程序执行中发生的不正常情况称为“异常” 。“异常”是一种Exception类型的对象。

通常,我们可以将异常分为两类:

  1. Error:是程序中无法处理的错误,表示运行应用程序中出现了严重的错误。此类错误一般表示代码运行时JVM出现问题。通常有Virtual MachineError(虚拟机运行错误)、NoClassDefFoundError(类定义错误)等。这种异常我们不应该去处理,应该去检查自己代码的合理性,避免该类异常的出现。
  2. Exception:其它因编程错误或偶然的外在因素导致的一般性问题, 可以使用针对性的代码进行处理。此类错误一般可以分为两类:
    2.1. 运行时异常(不受检异常):是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,如java.lang.RuntimeException类及其子类表示JVM在运行期间可能出现的错误,比如用空值对象的引用(NullPointerException)、数组下标越界(ArrayIndexOutBoundException)等。此类异常属于不可查异常,一般是由程序逻辑错误引起的,在程序中可以选择捕获处理,也可以不处理。但通常来说,一些ide还是会要求你处理这类异常来避免引起其他的错误。
    2.2. 非运行时异常(受检异常):是指编译器要求必须处置的异常。是一些一般性错误,Exception中除RuntimeException极其子类之外的异常。编译器会要求我们必须处理这类异常,有些时候Java内置的对象的某些方法会要求你检查异常,如使用Sequencer类的getSequencer()就必须包在try/catch中。

2. Java的异常处理机制

Java的异常处理机制是一个简捷、轻量化的执行期间例外状况处理方式,它让你能够将处理错误的程序代码摆在一个容易阅读的位置。

处理顺序通常为:

在定义方法时候通过 thows方法抛出Exception
try/catch块中接住抛出的异常

3. 异常的多态

3.1 继承

因为异常是一个类,所以可以被继承。异常可以被继承的好处在于,方法可以不必明确地声明每个可能抛出的异常,可以只声明父类。对于catch块来说,也可以不用对每个可能的异常作处理,只要有一个或少数几个catch可以处理所有的异常就够了。

3.2 手写异常

如果我们想通过抛出异常的方式来测试代码有没有问题的时候,我们通常需要手写一个异常的类,来使用throw-try-catch这一套流程。前面我们说到,抛出的异常是一个类,所以我们自己定义的异常必须继承这个类。

代码示例:

class BadException extends Exception{
}

二、异常处理机制

1. throws

1.1. 用于方法内

throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结 束当前方法的执行。

语法:

throw new 异常类名(参数);

示例代码:

public class DemoThrow {
    public static void main(String[] args) {
        int result =   DemoThrow.div(4,0);
        System.out.println(result);
    }
    public static int div(int a,int b)
    {
        if(b == 0)
            throw new ArithmeticException("异常信息:除数不能为0"); //抛出具体问题,编译时不检测
        return a/b;
    }
}

输出结果: Exception in thread “main” java.lang.ArithmeticException: 异常信息:除数不能为0

1.2. 用于声明方法时

运用于方法声明之上,用于表示当前方法不处理异常,而是提醒该方法的调用者来处理异常。通常用于一个方法(中的语句执行时)可能生成某种异常, 但是并不能确定如何处理这种异常的情况。

语法:

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

示例代码:

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class DemoThrows {
    public static void main(String[] args) throws FileNotFoundException{
        readFile();
    }
    public static  void readFile() throws FileNotFoundException {
        InputStream is = new FileInputStream("test.txt");
    }
}

输出结果: Exception in thread “main” java.io.FileNotFoundException: test.txt (No such file or directory)

1.3. Ducking

像上述实例我们只使用了throws方法,并没有把调用放在try/catch块中,此时throws的方式只是将异常抛给了方法的调用者,并没有真正将异常处理掉,也就是我们所说的“Ducking”。

这只是一种“踢皮球”的行为,此时这些异常被Duck掉后,程序可以通过编译,但是如果希望程序完善的话,还是需要处理掉抛出的异常。

2. try/catch/finally

2.1. try/catch/finally块的流程控制

语法:

try {
        //监视代码执行过程,一旦返现异常则直接跳转至catch,
        // 如果没有异常则直接跳转至finally
} catch (SomeException e) {
        //可选执行的代码块,如果没有任何异常发生则不会执行;
        //如果发现异常则进行处理或向上抛出。
} finally {
        //必选执行的代码块,不管是否有异常发生,
        // 即使发生内存溢出异常也会执行,通常用于处理善后清理工作。
}

2.1.1. try

捕获异常的第一步是用try{}语句块选定捕获异常的范围, 将可能出现异常的方法放在try语句块中进行调用。如果try块失败了,即运行的代码出现异常,会生成一个异常并抛出,这里需要注意的是,一个try块可能会抛出多个异常,而每个异常都会对应一个catch块。出现异常后,流程会马上进入对应的catch块;如果整段try块没有抛出异常,运行结束后,会进入finally块或直接运行程序剩下的代码。

2.1.2 catch

一个catch块接收try块抛出的一个异常。catch块的作用主要是尝试修复抛出的问题,如果无法在catch块中解决抛出的问题,也最好使用printStackTrace()方法描述抛出的异常类型,方便后续的改进。

有多个catch块的时候,需要判断catch的异常间存不存在继承的关系,如果不存在,则catch块之间的排序是没有影响的;如果catch的异常间存在继承的关系,必须保证catch子类的catch块在catch父类的catch块的上方。可以理解为,如果catch父类的catch块先运行了,那么子类catch块将不会有机会被使用,即子类的异常在catch父类的catch块中被处理了。

2.1.3 finally

捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序的其它部分以前,能够对程序的状态作统一的管理。实际上finally模块并不是一个必须的模块,很多时候,让程序运行完try/catch模块后,直接运行剩下的代码内容即可。

2.2 try/catch代码解读

public class Student {
    String studentNo;
    int programmingScore;

    // 输入学生信息,输入成功返回true;否则返回false
    // 可能会遇到的异常:学号输入错误,成绩输入错误
    public boolean InputInformation (String studentNo, int programmingScore) throws NoException, ScoreException{
        boolean flag;
        if (studentNo.matches("[j][p]\\d{10}")) { // 正则表达式判断输入的账号前两位是不是为jp,即后10位为数字
            flag = true;
        } else {
            throw new NoException("英方账号输入错误!");
        }
        if (programmingScore > 100 || programmingScore < 0) {
            flag = false;
            throw new ScoreException("学生成绩不应该高于100,或者低于0!");
        } else {
            flag = true;
        }
        return flag;
    }

    public static void main(String[] args) {
        Scanner ip = new Scanner(System.in);
        Student s = new Student();
        s.studentNo = ip.next();
        s.programmingScore = ip.nextInt();
        try {
            System.out.println("Input student information now!");
            s.InputInformation(s.studentNo, s.programmingScore);
            System.out.println("Input information successfully!");
        } catch (NoException ne) {
            System.out.println("Please input reasonable student number again!");
        } catch (ScoreException sc) {
            System.out.println("Please input reasonable student programming score again!");
        }
    }
}

class NoException extends Exception {
    NoException(String s) {
        System.out.println(s);
    }
}

class ScoreException extends Exception {
    ScoreException(String s) {
        System.out.println(s);
    }
}

输入:ab2222 95
输出:
Input student information now!
英方账号输入错误!
Please input reasonable student number again!


总结

1. 异常处理规则

  1. catch与finally不能没有catch
  2. try与catch之间不能有程序
  3. try一定要有catch或finally
  4. 只带有finally的try必须要声明异常

2. 流程处理

  1. 如果try或catch块有return指令,finally还是会执行!流程会跳到finally然后再回到return指令。
  2. 有声明(throw)就会抛出异常,但没有try/catch块,所以就会duck掉异常留给调用方。
  3. 对于多重异常来说,catch的顺序很重要。
  4. 如果有匹配的catch,它就会忽略掉这个catch后面所有的catch。

你可能感兴趣的:(#,基础语法,Java,java,学习,开发语言)