Java的”异常“超详细讲解

Java的”异常“超详细讲解前言:​ 我们在写代码的时候,经常会遇到错误,并且编译器会给出相关提示 如:

这里就会显示 ArrayIndexOutOfBoundsException *(数组越界)*错误。并且会提示报错的行号。

同时我们发现 ArrayIndexOutOfBoundsException 是可以点击进去的,点击进去之后会发现是一个JDK自带的类

由此我们可以知道:

在Java中,不同类型的异常,都有与其对应的类来进行描述,也可以说异常也是一种类

但是我们在日常编写项目时,错误是时常发生的。我们作为程序员当然不希望一个项目时不时的崩溃。

在此之前,我们通常对代码可能出现的潜在问题用 条件判断来进行处理:,下面我以一个简略的游戏进行演示:

可以看到当条件一旦增加,代码就会非常臃肿,并且难以后续对功能进行增加和修改。

所以我们必须要学会如何使用,以及处理异常的方式!!

一、异常的分类: Throwable:

是异常体系的顶层类,其派生出两个重要的子类, Error 和 Exception

Error:

**指的是Java虚拟机无法解决的严重问题,**比如:JVM的内部错误、资源耗尽等,

典型代表:StackOverflowError(栈溢出错误)和OutOfMemoryError

Exception:

异常产生后程序员可以通过代码进行处理,使程序继续执行。而异常又分为运行时异常和编译时异常;

编译时异常:

编译无法通过,编译器会进行提醒。也称为受检查异常(Checked Exception)运行时异常:

可以通过编译,但是运行后会报错。也称为非受检查异常(Unchecked Exception)

常见的 NullPointerException(空指针异常)、ArrayIndexOutOfBoundsException(数组越界异常)都属于运行时异常。二、异常的处理:1.异常的抛出:在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者

关键字:throw

语法:throw new 异常类型(要告知的信息);

代码案例:

代码语言:javascript复制public static void func(int[] arrays) {

//如果传递的是一个空数组,抛出异常

if(arrays == null) {

//这里的NullPointerException是Java自带的一个类

throw new NullPointerException("你传递了一个空指针");

}

}

public static void main(String[] args) {

int[] arrays = null;

func(arrays);

}运行结果:

注意事项

异常抛出 必须写在方法内部!!!(由方法来实现抛出)抛出对象必须是Exception或是其子类默认情况下:RuntimeException及其子类可以通过JVM处理异常一旦抛出,其后的代码就不会执行我们可以看到,在IDEA中,如果异常后还有代码,会直接提示编译错误(因为无法被执行!)

2. 异常声明: 关键字: throws

语法格式:

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

}

如果我们在使用方法是,有编译时异常(编译器警告),可以使用throws来声明这个异常可能会存在,这样就可以通过编译时异常了。

下面我以clone方法为例子:如果不对main函数进行异常声明,会提示clone编译错误(无法确定能否被克隆)

我们在main函数(使用者)后加入声明:throws CloneNotSupportedException

发现编译错误消失,可以正常运行:

代码语言:javascript复制class Student implements Cloneable{

public String name;

public int age;

//构造方法:

public Student(String name, int age) {

this.name = name;

this.age = age;

}

@Override

protected Object clone() throws CloneNotSupportedException {

return super.clone();

}

}

public class CloneTest {

public static void main(String[] args) throws CloneNotSupportedException {

Student s1 = new Student("张三",18);

Student s2 = (Student) s1.clone();

}

}注意事项

throws必须跟在方法的参数列表之后

声明的异常必须是 Exception 或者 Exception 的子类

方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型

具有父子关系,直接声明父类即可。

调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出

3.异常捕捉: 关键字:try - catch

语法格式:

​ try{

​ // 将可能出现异常的代码放在这里

​ }catch(要捕获的异常类型 e){

​ //异常处理内容

​ }[catch(异常类型 e){

​ // 对异常进行处理

​ }finally{

​ // 此处代码一定会被执行到

​ }]

解释:

try { }:

内部放的是可能出现异常的代码块(也有可能没有异常)

如果出现异常,则异常后续的代码均不会被执行,直接跳转到对应异常类的的catch内容 catch( ){ }

()小括号内是异常类类名 + 实例化的对象名(这里通常习惯用e表示){ } 花括号内表示的对应的异常处理内容可以存在多个catch也可以一个catch内写多个异常类型,只需要一个实例化对象名,中间用 | 隔开

(不推荐用此写法,无法判断异常类型!!)

finally{ }

finally{}内的代码块,无论异常发生与否,始终会被执行

通常被用于资源的关闭,对资源进行回收。

注意事项:

try块内抛出异常位置之后的代码将不会被执行

如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到

JVM收到后中断程序----异常是按照类型来捕获的

try中可能会抛出多个不同的异常对象,则必须用多个catch来捕获----即多种异常,多次捕获

如果异常存在父子类关系,则必须子类在前(catch),父类在后(catch)

因为如果父类在前,子类内容无法被执行,肯定会被父类捕获(catch)

这里就是因为父类 RuntimeException在前,后续的InputMismatchException无法被执行,出现编译错误。 子类在前则只会抛出子类的异常,而不会抛出父类的异常。

finally中的代码一定会执行的,且是最后执行。一般在finally中进行一些资源清理的扫尾工作代码语言:javascript复制 public static int func3() {

Scanner scanner = new Scanner(System.in);

try{

int a = scanner.nextInt();

return a;

} catch (InputMismatchException e) {

//InputMismatchException是错误输入类型异常

// (这里我想要输入的是int,但是输入String就会报此错误)

e.printStackTrace();

System.out.println("发生了InputMismatchException异常");

}finally {

scanner.close();

System.out.println("Scanner资源已经关闭!!!");

return 100;

}

}

public static void main(String[] args) {

int ret = func3();

System.out.println(ret);

}注意这里,try语句中已经有了return 语句了, 正常情况下输出的结果是你输入的数;

我们进行输入测试:

我们会发现最终return的值是100,即为finally return的值,

这说明:finally不仅一定会执行,而且可以在return之后最后执行。

三、自定义异常类:既然异常是一个类,那我们肯定也能通过定义类,来定义一个自定义的异常类型。

具体如何定义呢 ?,我们可以仿照已知的异常类,这里是NullPointerException为例子:

其定义方法为:

要继承RuntimeException类(异常父类) 或者是 Exception类。重写构造方法并调用父类的构造,用来传递发生错误的字段下面我以一个简单的登录代码来进行演示:

首先是 登录功能类:

代码语言:javascript复制public class Login {

//这里是字段:

public String userName;

public String passWord;

//进行简单的逻辑判断

public void login(String userName,String passWord) {

//如果输入的信息不匹配该类的成员信息,抛出自定义异常!(这里只抛出,不解决)

if(!this.userName.equals(userName)) {

throw new UserNameException("账户名输入错误!!!");

}

if(!this.passWord.equals(passWord)) {

throw new PassWordException("密码输入错误!!!");

}

}

//构造函数

public Login(String userName, String passWord) {

this.userName = userName;

this.passWord = passWord;

}

}自定义类:

首先是用户名错误的异常类:

代码语言:javascript复制public class UserNameException extends RuntimeException {

public UserNameException() {

super();

}

public UserNameException(String userName) {

super(userName);

}

}其次是密码错误类:

代码语言:javascript复制public class PassWordException extends RuntimeException{

public PassWordException() {

}

public PassWordException(String message) {

super(message);

}

}在主函数中实例化:

代码语言:javascript复制public class Main {

public static void main(String[] args) {

try {

Login login = new Login("admin","123123");

login.login("admin","123");

}catch (UserNameException e) {

//打印函数栈信息

e.printStackTrace();

}catch (PassWordException e) {

//打印函数栈信息

e.printStackTrace();

}finally {

System.out.println("finally被执行,程序继续...");

}

}

}输出结果:显示了自定义的异常类型 PassWordException

四、 总结:异常的处理流程:

Back to top: