七千字带你了解异常处理下

简介: 七千字带你了解异常处理下

一、异常的处理

在Java中,异常处理主要的5个关键字:throw、try、catch、final、throws。

3.1 异常的捕获

使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。Catch 语句包含要捕获异常类型的声明,使用 try/catch 的语法如下:

try
{
   // 程序代码
}catch(ExceptionName e1)
{
   //Catch 块
}

具体实例:

public static void main(String[] args) {
        try{
            int[] arr = {1,2,3};
            arr[3] = 1;
        }catch(ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();//打印异常信息
            System.out.println("数组越界异常!");
        }
        System.out.println("out");
    }

在这里插入图片描述
我们可以发现系统处理完异常后,程序正常进行到结束.

  1. try块内抛出异常位置之后的代码将不会被执行
  2. 如果抛出异常类型与catch时异常类型不匹配,即异常不会被成功捕获,也就不会被处理,继续往外抛,直到JVM收到后中断程序----异常是按照类型来捕获的

多重捕获块:

 public static void main(String[] args) {
        try{
            int arr[] = {0,1,2};
            arr[3] = 0;
            System.out.println(2/arr[0]);
        }catch(ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();//打印异常信息
            System.out.println("数组越界异常!");
        }catch(ArithmeticException e) {
            e.printStackTrace();//打印异常信息
            System.out.println("算术异常!");
        }
        System.out.println("out");
    }

在这里插入图片描述

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

捕获多个异常:

try{

}catch(ArrayIndexOutOfBoundsException | NullPointerException e) {

}

如果异常之间具有父子关系,一定是子类异常在前catch,父类异常在后catch,否则编译报错.

public static void main(String[] args) {
        try{
            
        }catch(Exception e) {
            
        }catch(ArrayIndexOutOfBoundsException e) {
            
        }
    }

在这里插入图片描述
也可以一个catch捕获,不建议,代码可阅读性差:

public static void main(String[] args) {
        try{

        }catch(Exception e) {

        }
    }

在这里插入图片描述
阅读时不知道这里的异常到底是什么异常.

3.2 异常的抛出

在编写程序时,如果程序中出现错误,此时就需要将错误的信息告知给调用者,比如:参数检测。在Java中,可以借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者。具体语法如下:
throw关键字:

public static int fun(int[] arr,int index) {
        if(index < 0 || index >= arr.length) {
            throw new ArrayIndexOutOfBoundsException("数组越界!");
        }
        if(null == arr) {
            throw new NullPointerException("数组为空!");
        }
        return arr[index];
    }
    public static void main(String[] args) {
        int[] arr = {0,1,2};
        System.out.println(fun(arr, 3));
    }

在这里插入图片描述
我们可以自己指定异常信息.

  1. throw必须写在方法体内部
  2. 抛出的对象必须是Exception 或者 Exception 的子类对象
  3. 如果抛出的是 RunTimeException 或者 RunTimeException 的子类,则可以不用处理,直接交给JVM来处理
  4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译
  5. 异常一旦抛出,其后的代码就不会执行

throws关键字:
下面方法的声明抛出一个 RemoteException 异常:

 public void deposit(double amount) throws RemoteException
  {
    throw new RemoteException();
  }

一个方法可以声明抛出多个异常,多个异常之间用逗号隔开。例如,下面的方法声明抛出 RemoteException 和 InsufficientFundsException:

 public void withdraw(double amount) throws RemoteException,
                              InsufficientFundsException
   {

   }
  1. throws必须跟在方法的参数列表之后
  2. 声明的异常必须是 Exception 或者 Exception 的子类
  3. 方法内部如果抛出了多个异常,throws之后必须跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型

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

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

3.3 finally的使用

在写程序时,有些特定的代码,不论程序是否发生异常,都需要执行,比如程序中打开的资源:网络连接、数据库连接、IO流等,在程序正常或者异常退出时,必须要对资源进进行回收。另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这个问题的。

 public static void main(String[] args) {
        try{
            int[] arr = {1,2,3};
            arr[3] = 1;
        }catch(ArrayIndexOutOfBoundsException e) {
            e.printStackTrace();
        }finally {
            System.out.println("finally已执行!");
        }
    }

在这里插入图片描述
相信大家有一个疑问,try-catch后面的代码会执行,为什么还要使用finally?

public static int get() {
        Scanner scanner = null;
        int n = 0;
        try{
            scanner = new Scanner(System.in);
            n = scanner.nextInt();
            return n;
        }catch(InputMismatchException e) {
            e.printStackTrace();
        }finally {
            System.out.println("finally!");
        }
        System.out.println("try-catch执行完毕!");
        if(null != scanner) {
            scanner.close();
        }
        return n;
    }
    public static void main(String[] args) {
        int n = get();
    }

在这里插入图片描述
当我们输入的值为整型类型时,就会出现类型泄露.
在这里插入图片描述
这里执行到第二步程序就终止了,没有对scanner进行释放,只有在finally中加上才能实现程序的安全性.
finally的危险性:

public static int func1() {
        try{
            return 1;
        }finally {
            return 2;
        }
    }
    public static void main(String[] args) {
        System.out.println(func1());
    }

这里输出的是1还是2?

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

finally 执行的时机是在方法返回之前(try 或者 catch 中如果有 return 会在这个 return 之前执行 finally). 但是如果finally 中也存在 return 语句, 那么就会执行 finally 中的 return, 从而不会执行到 try 中原有的 return.

3.4 JVM处理异常

如果我们一直将异常向上抛,抛给主方法还没有处理,系统将会抛给JVM处理,异常终止程序.try中的异常如果没有catch捕捉处理也是一样的.

public static void func2() {
        int[] arr = {1,2,3};
        arr[3] = 1;
    }
    public static void main(String[] args) {
        func2();
        System.out.println("main未处理异常!");
    }

在这里插入图片描述

1.程序先执行 try 中的代码
2.如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
3.如果找到匹配的异常类型, 就会执行 catch 中的代码
4.如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
5.无论是否找到匹配的异常类型, finally 中的代码都会被执行到(在该方法结束之前执行).
6.如果上层调用者也没有处理的了异常, 就继续向上传递.
7.一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

二、自定义异常类

在 Java 中你可以自定义异常。编写自己的异常类时需要记住下面的几点:

1.所有异常都必须是 Throwable 的子类。
2.如果希望写一个检查性异常类,则需要继承 Exception 类。
3.如果你想写一个运行时异常类,那么需要继承 RuntimeException 类。

可以像下面这样定义自己的异常类:

class MyException extends Exception{
}

实现一个登录功能,自定义异常类

public class NameException extends Exception{
    public NameException(String message) {
        super(message);
    }
}
public class PassException extends Exception{
    public PassException(String message) {
        super(message);
    }
}
1.自定义异常通常会继承自 Exception 或者 RuntimeException
2.继承自 Exception 的异常默认是受查异常
3.继承自 RuntimeException 的异常默认是非受查异常.
private String Name = "zd";
    private String Word = "000000";
    public static void login(String Name,String Word)throws NameException,WordException {
        if(!Name.equals(Name)) {
            throw new UserNameException("用户名错误!");
        }
        if(!Word.equals(Word)) {
            throw new PassWordException("密码错误!");
        }
        System.out.println("登录成功!");
    }
    public static void main(String[] args) {
        try {
            login("zd","000000");
        }catch (NameException e) {
            e.printStackTrace();
        }catch (WordException e) {
            e.printStackTrace();
        }
    }
目录
相关文章
|
4月前
|
前端开发 Java 应用服务中间件
一文讲明SpringMVC 【爆肝整理一万五千字】
文章提供了一份全面的SpringMVC教程,涵盖了SpringMVC的简介、创建实例、注解、获取请求参数的不同方式、域对象共享数据、视图渲染、RESTful风格、拦截器使用、异常处理以及文件上传下载等内容,并包含了相应的代码示例和测试结果。
一文讲明SpringMVC 【爆肝整理一万五千字】
|
存储 Java 编译器
类和对象(两万字超详解析)(一)
类和对象(两万字超详解析)
89 0
|
Java 编译器
类和对象(两万字超详解析)(二)
类和对象(两万字超详解析)
56 0
|
存储 Java 数据库
类和对象(两万字超详解析)(三)
类和对象(两万字超详解析)
68 0
|
Java 编译器
类和对象(两万字超详解析)(四)
类和对象(两万字超详解析)
77 0
|
存储 缓存 算法
JVM专题整理,三万字分析(一)
JVM专题整理,三万字分析
151 1
|
Arthas 存储 缓存
JVM专题整理,三万字分析(三)
JVM专题整理,三万字分析
153 0
|
存储 Java 索引
1760字,让你拿捏 [‘列表‘]
1760字,让你拿捏 [‘列表‘]
|
运维 监控 算法
JVM专题整理,三万字分析(二)
JVM专题整理,三万字分析
193 0
|
存储 安全 Java
Java多线程基础——两万字详解
进程简单来说就是正在运行的程序,是可以通过双击执行的.exe文件,打开我们电脑的任务管理器,可以看到我们的电脑正在执行的进程,目前我们的电脑都是多进程模式。
126 0
Java多线程基础——两万字详解