异常

一,异常概述

异常介绍:

  • 异常:代表程序出现的问题
  • 误区:不是让我们以后不出异常,而是程序出了异常之后,该怎么处理。

异常的继承体系:

image

  • Error:代表的系统级别错误(属于严重问题)

    系统一旦出现问题,sun公司会把这些错误封装成Error对象。

    Error是给sun公司自己用的,跟我们没啥关系,不需要管。

  • Exception:异常,代表程序可能出现的问题。

    我们通常会用Exception以及他的子类来封装程序出现的问题。

  • RuntimeException 以及它的子类:运行时出现的异常(如数组越界,空指针异常)

    编译时异常:在编译阶段,必须手动处理,否则代码报错。目的是提醒程序员检查本地信息。

  • 其他异常:叫做编译时异常,编译阶段就会出现异常提示的。(如日期解析异常)

    运行时异常:在编译阶段不需要处理,是代码运行时出现的异常。目的是:由于参数错误而导致的问题。

异常的作用:

  1. 异常是用来查询bug的关键参考信息。
  2. 异常可以作为方法内部的一种特殊返回值,以便通知调用者底层的执行情况。

二,异常的处理方式

2.1 JVM默认的处理方式

  • 把异常的名称,异常原因以及异常出现的位置等信息输出在控制台。
  • 程序停止执行,异常下面的代码不会再执行。

2.2 自己处理(捕获异常

捕获异常的目的:不让程序停止。

格式:

try{
    可能出现异常的代码;
} catch(异常类名 变量名){
    异常的处理代码;
}finally{
    //不管程序是否跳转到 catch 块中,finally 块中的代码都会被执行。
    //finally可以不写,但是一般情况下都建议编写,用来释放资源或者关闭流等操作
    System.out.println("finally 块中的代码已经执行完毕");
}

目的:当代码出现异常时,可以让程序继续往下执行。

举例:

try{
    //可能出现异常的代码
	System.out.println(2/0);//这里出现了异常,程序就会在这里创建一个ArithmeticException对象
    					//new ArithmeticException();
    					//拿着这个对象到catch的小括号中进行对比,看小括号中的变量能否接收这个对象
    					//如果能接收,就表示这个异常被捕获(抓住),执行catch里面对应的代码
    					//当catch里面所有的代码执行完毕,继续执行try...catch体系下面其他代码
}catch(ArithmeticException e){
    //如果出现了ArithmeticException异常,我该如何处理
	System.out.println("算数异常");
}finally{
    System.out.println("finally 块中的代码已经执行完毕");
}

使用情况:

  1. 如果try中没有遇到问题,那么就不会执行catch中的代码,而是执行try中代码。

  2. 如果try中遇到多个问题,会写多个catch与之对应

    如果要捕获多个异常,这些异常中如果存在继承关系,那么父类一定要写在最下面。

    在jdk7以后可以在catch中的同时捕获多个异常,中间用 |隔开

    catch(ArrayIndexOutOfBoundsException | ArithmeticException e )表示如果出现了A异常或B异常,采用同一种处理方案。

    public class Text {
        public static void main(String[] args){
            int[] arr={1,2,3,4,5,6};
            try{
                System.out.println(arr[10]);//如果没有写多个catch
                System.out.println(2/0);
                String s=null;
                System.out.println(s.equals("abc"));
            }catch (ArrayIndexOutOfBoundsException e){
                System.out.println("索引越界");
            }catch (ArithmeticException e){
                System.out.println("算数异常");
            }catch (NullPointerException e){
                System.out.println("空指针异常");
            }finally{
                System.out.println("finally 块中的代码已经执行完毕");
            }
            System.out.println("看看我执行了么");
        }
    }
    

    上面这么多catch,只能输出第一个捕获到异常的catch中的代码。

    建议按照try中代码顺序依次写catch捕获对应的异常,增强可读性。

  3. 如果try中遇到的问题没有被捕获,即catch中写的依次与实际引发的异常不一样,这样就会无法捕获到异常。

    此时相当于try catch代码白写了,最终采用JVM进行默认处理。

  4. 如果try中遇到了异常,那么try下面的其他代码就不会执行了

    public class Text {
        public static void main(String[] args){
            int[] arr={1,2,3,4,5,6};
            try{
                System.out.println(arr[10]);
                System.out.println("这里不执行了");//这段代码就不会执行
            }catch (ArrayIndexOutOfBoundsException e) {
                System.out.println("索引越界");
            }finally{
                System.out.println("finally 块中的代码已经执行完毕");
            }
            System.out.println("看看我执行了么");
        }
    }
    
    

异常中常用方法(Throwable的成员方法):

方法名 说明
public String getMessage() 返回异常的简短描述
public String toString() 返回异常的详细描述
public void printStackTrace() 把异常的错误信息输出在控制台

第三个方法虽然会把异常的所有信息打印在控制台和JVM默认处理方法一样,但是!!不会停止运行下面的代码

第三个方法包含了上面两个方法的信息,是最常用的。

第三个方法在底层利用System.err.println进行输出,在控制太显示红色字体

2.3 交给调用者处理(抛出异常

抛出异常的目的:告诉方法调用者这个方法出错了!

方法抛出异常要写两个部分:

  1. throws

    写在方法定义处,表示声明一个异常。告诉调用者,使用本方法可能有哪些异常。

    如果是编译时异常:必须要写

    如果是运行时异常:可以省略不写

    public void 方法() throws 异常类名1,异常类名2,....{
        
    }
    
  2. throw

    写在方法内,结束方法。手动抛出异常对象,交给调用者。方法中下面的代码不再执行了。

    public void 方法(){
        throw new NullPointerException();
        System.out.println("这部分代码不会执行");
    }
    
  3. 完整写法

    public void 方法() throws 异常类名1,异常类名2,...{//如果是runtime异常可以不写
        throw new NullPointerException();
        System.out.println("这部分代码不会执行");
    }
    

举例:

//写一个获取数组最大元素的方法
public int getMax(int[] arr){
    if(arr==null){
        //此时抛出异常
        throw new NullPointerException;
    }
    if(arr.length==0){
        throw new ArrayIndexOutOfBoundsException();
    }
    int max=arr[0];
    for(int i=1;i<arr.length;i++){
        if(arr[i]>max){
            max=arr[i]
        }
    }
    return max;
}
public class Text {
    public static void main(String[] args){
        int[] arr=null;
        int max=0
        try{
            max=getMax(arr);//将调用方法的代码用try进行捕捉,要不然下面的代码将无法执行。
        }catch(NullPointerException e){//空指针异常
            e.printStackTrace();
        }catch(ArrayIndexOutOfBoundsException e){//索引越界异常
            e.printStackTrace();
        }
    }
}

三,自定义异常

步骤:

  1. 定义异常类

  2. 写继承关系

    如果是运行时异常:继承RuntimeException

    如果是编译时异常:继承Exception,如果方法中new出编译时异常,我们需要在方法外部throws抛出。

  3. 空参构造

  4. 带参构造

自定义异常的意义:让控制台的报错信息更加见名知意。

可以在Catch中捕捉哪些java中没有定义的比如说名字规范等情况。

自定义异常的规范写法:

public class NameFormatException{}
//NameFormat:异常名字
//Exception:表示这是一个异常类
//使用大驼峰命名法

举例:

//自定义运行时异常
public class NameFormatException extends RuntimeException{//姓名格式异常(自定义的)(idea快捷键alt+insert自动补齐即可)
    public NameFormatException() {
    }
    public NameFormatException(String message) {//传入异常的信息
        super(message);
    }
}
//自定义编译时异常
public class NameFormatException extends Exception{
    public NameFormatException(String message) {
        super(message);
    }
    public NameFormatException() {

    }
}

import Exception.NameFormatException;
public class ExceptionExample {
    public static void main(String[] args) {
        try{
            saveName("");
        }catch(NameFormatException e){
            e.printStackTrace();
            System.out.println("Error saving name");
        }
    }
    
    public static void saveName(String name) throws NameFormatException{
        if(name.isEmpty()){
            throw new NameFormatException("名字不能为空");
        }else{
            System.out.println("Name saved");
        }
    }
    
}
posted @ 2024-06-23 13:45  wdadwa  阅读(16)  评论(0)    收藏  举报