java中Debug调试、异常的概念、异常体系、处理异常的关键字try/catch/finally/throw/throws、多个异常使用捕获并处理的方式、继承关系中处理异常、自定义异常类

Debug调试

bug指程序当中遇到的一些错误异常,Debug指调试bug,找到bug对其解决;debug可以让代码逐行执行,查看代码执行的过程,调试程序中出现的bug或异常。

public class DebugClass {
    
    
    public static void main(String[] args) {
    
    
        // 鼠标单击行号的右边可以打断点,打完断点后,鼠标右击弹框选择调试Debug,点击完Debug后程序就停留在断点的第一行,此时需要点击控制台箭头进行调试,其中各个按钮的意思:
        // F8: 每点击一次,执行一步
        // F7: 进入到方法中
        // shift + F8:跳出方法
        // Ctrl + F5: 重新执行debug调试
        // Ctrl + shift + F5:退出debug调试模式
        int a = 1;
        int b = 2;
        int c = a + b;
        System.out.println(c); // 3
    }
}

异常的概念:

在程序中,异常指程序在执行过程中,出现的非正常的情况,会导致jvm的非正常停止。

在java中异常本身就是一个类,产生异常就是创建异常对象并抛出一个异常对象。java处理异常的方式是中断处理。

异常不是语法错误,语法错误编译不通过。

异常体系:

异常机制其实是帮助我们找到程序中的问题,异常的根类是:java.lang.Throwable,其下有两个子类:java.lang.Error(不能被处理)与java.lang.Exception(异常,处理后程序可以继续执行 )

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;

// Throwable类:是java语言中所有错误或异常的超类
// Exception: 编译期异常(写代码时就会报的异常),它下面有个类:RuntimeException:运行期异常,指java程序运行过程中出现的问题。我们把异常处理掉程序可以继续运行。
// Error: 错误:一个程序不被允许的错误,想要程序运行,就必须得解决掉这个错误。
public class ExceptionClass {
    
    
    public static void main(String[] args) /*throws ParseException*/ {
    
    
        // 一、异常处理:
        // 把字符串格式的日期解析为Date的日期;parse这个方法本身会抛异常,需要处理,这里有两种处理方式:
        // 1.选中parse后Alt+回车,选择添加异常方法签名,此时main方法后面会新增throws ParseException将异常抛出,交给虚拟机处理
        // 2.选中parse后Alt+回车,选中带有try/catch的一项,处理后程序可以正常执行。
        SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd");
        try {
    
    
            // try中写可能出现异常的代码,运行期间报错也不会导致程序停止。
            Date dt = sd.parse("2000-12-12");
        } catch (ParseException e) {
    
    
            throw new RuntimeException(e);
        }

        // 二、错误处理:
        int[] arr = new int[100000000];
        // int[] arr2 = new int[100000000000]; // 过大的整数: 100000000000,超出int范围报错

        // 三、异常过程的解析:异常的产生过程解析(分析异常的怎么产生,如何处理异常),见下面数组索引越界异常图:
    }
}

数组索引越界异常如:
请添加图片描述
处理异常:

java中处理异常会用到这几个关键字:try、catch、finally、throw、throws

throw 抛出异常:

import java.util.Objects;

// throw: 将指定的异常在指定的方法中抛出,使用格式:throw new xxxException()
// 注意:1.throw关键字必须写在方法的内部 2.后面new对象必须是Exception或者Exception的子类对象
// 3.抛出指定的异常对象后,需要对异常对象处理(方式一:throw关键字创建的是RuntimeException或者是RuntimeException的子类时,可以不处理,默认给jvm处理;方式二:如果创建的是编译异常,那么必须通过throws或try/catch处理)
public class ThrowClass {
    
    
    public static void main(String[] args) {
    
    
        // 调用方法测试:
        int[] arr1 = new int[]{
    
    1,2,3};
        System.out.println(arr1[1]); // 2
        System.out.println(arr1[3]); // ArrayIndexOutOfBoundsException属于运行期异常,无需处理

        int[] arr2 = null;
        System.out.println(arr2[1]); // NullPointerException属于运行期异常,无需处理
    }
    // 定义一个获取数据元素的方法:
    public static int getArrayElement(int[] arr, int index) {
    
    
        if (arr == null) {
    
    
            throw new NullPointerException("数组不能为空!");
        }
        // java中Objects对象有一个静态方法,可以对对象是否为空进行判断
        Objects.requireNonNull(arr);
        Objects.requireNonNull(arr, "数组不能为空!");
        if (index < 0 || index > arr.length -1) {
    
    
            throw new ArrayIndexOutOfBoundsException("索引大于数组的长度!");
        }
        return arr[index];
    };
}

throws 申明异常:

import java.io.FileNotFoundException;
import java.io.IOException;

import static java.lang.System.err;

// throws:异常处理的第一种方式,将异常交给其它方法处理
// 作用:当方法内部抛出异常对象时,此时可以使用throws申明异常对象并抛出异常给方法的调用者处理,最终由jvm处理,中断处理。
// 使用格式:在方法申明时使用:修饰符 返回值类型 方法名(参数列表) throws aaaException,aaaException,... {throw new aaaException();throw new bbbException();}
// 注意:1.throws关键字必须写在方法申明处 2.后面申明的异常必须是Exception或Exception的子类 3.如果抛出对个异常对象有子父类关系,那么直接申明父类异常即可 4.调用了一个申明抛出异常的方法,我们就要处理申明的异常(要么继续使用throws抛出,要么try/catch处理异常)
public class ThrowsClass {
    
    
    public static void main(String[] args) /* throws FileNotFoundException,IOException 1.继续抛出异常 */ {
    
    
        readFile("C:\\b.txt");
        // 异常后面的代码都不会执行了:下面代码不会 被执行,如果想要执行,那么就得使用try/catch捕获异常并处理
    }
    // 定义一个文件路径异常的方法:
    public static void readFile(String path) throws FileNotFoundException,IOException {
    
    
        if (!path.equals("C:\\a.txt")) {
    
    
            throw new FileNotFoundException("路径不对!");
        };
        if (!path.endsWith(".txt")) {
    
    
            throw new IOException("路径不对!");
        };
        System.out.println("操作文件!");
    };
}

try/catch捕获异常:

import java.io.IOException;

// try/catch:异常处理的第二种方式,try中书写可能会抛出异常的代码,在catch中处理异常的情况,比如将异常记录在日志中
// 注意:try中可能抛出多个异常,此时可以使用多个catch处理异常;try中产生异常后会执行catch中的代码,执行完catch中的代码后会继续执行catch后面的代码
// try {
    
    
//  执行可能抛出异常的代码块
// } catch (异常类名 变量名) {
    
    
//  处理异常1:
// } catch (异常类名 变量名) {
    
    
//  处理异常2:
// }
public class TryCatchClass {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            readFile("C:\\a.txtb"); // 鼠标移动到这里:Alt+回车 选择try/catch环绕即可自动使用try/catch处理
        } catch (IOException err) {
    
     // 抛出什么类型的异常就定义什么类型的异常变量
            System.out.println(err); // 文件类型错误!
        };
        System.out.println("代码继续执行了"); // 代码继续执行了
    }
    // 定义一个会抛出异常的方法:
    public static void readFile(String path) throws IOException {
    
    
        if (!path.endsWith(".txt")) {
    
    
            throw new IOException("文件类型错误!");
        };
    };
}

Throwable处理异常:

它下面有三个最常用的方法:String getMessage()返回此throwable的简短描述;String toString()返回此throwable详细信息字符串;void printStackTrace()jvm默认打印异常方法,此方法返回的信息最全面

import java.io.IOException;
public class ThrowableClass {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            readFile("C:\\a.txtb");
        } catch (IOException err) {
    
    
            System.out.println(err);

            // Throwable常用三方法:
            System.out.println(err.getMessage()); // 文件类型错误!
            System.out.println(err.toString()); //java.io.IOException: 文件类型错误!
            err.printStackTrace(); // java.io.IOException: 文件类型错误! at TryCatchClass.readFile(TryCatchClass.java:28) at TryCatchClass.main(TryCatchClass.java:15)
        };
        System.out.println("代码继续执行了");
    }
    // 定义一个会抛出异常的方法:
    public static void readFile(String path) throws IOException {
    
    
        if (!path.endsWith(".txt")) {
    
    
            throw new IOException("文件类型错误!");
        };
    };
}

finally代码块:

程序无论进入try或catch最后都会进入到finall代码块,当try中抛出异常后,try中异常后面的代码可以放到finall代码块中执行,解决try中因抛出异常不继续执行异常后面代码的情况,finally代码块和try/catch搭配使用,还有一点,finally中如果使用return,那么整体将返回finally中return后面的内容,应避免这种情况出现如:

import java.io.IOException;

import static java.lang.System.err;

// 注意:finally不能单独使用,必须和try/catch一起使用; finally一般用于资源释放(资源回收)
public class FinallyClass {
    
    
    public static void main(String[] args) {
    
    
        try {
    
    
            readFile("C:\\a.txtb");
            System.out.println("这里的代码在抛出异常时不会执行,想要执行就要讲此代码写在finally代码块中:"); // 没有执行此行代码
        } catch (IOException err) {
    
    
            System.out.println(err);
        } finally {
    
    
            System.out.println("无论是否抛出异常,这里的代码都会执行"); // 无论是否抛出异常,这里的代码都会执行
        };
        System.out.println("代码继续执行了");

        
        // 避免在finally中返回内容:
        System.out.println(getResult()); // 2
    }
    // 定义一个会抛出异常的方法:
    public static void readFile(String path) throws IOException {
    
    
        if (!path.endsWith(".txt")) {
    
    
            throw new IOException("文件类型错误!");
        };
    };

    // 避免在finally中返回内容:
    public static String getResult(){
    
    
        String a = "1";
        try {
    
    
            return a;
        } catch (Exception err) {
    
    
            System.out.println(err);
        } finally {
    
    
            a = "2";
            return a;
        }
    };
}

多个异常使用捕获并处理:

多个异常使用捕获处理异常的方式有:1.多个异常分别处理 2.多个异常一次捕获,多次处理 3.多个异常一次捕获一次处理。

注意:运行期间的异常,可以不用处理,直接丢给虚拟机处理,但是这样会阻止程序继续执行。

import java.util.List;

// 多个异常使用捕获处理的额方式:
// 1.多个异常分别处理  2.多个异常一次捕获,多次处理  3.多个异常一次捕获一次处理
public class MultExceptionClass {
    
    
    public static void main(String[] args) {
    
    
        // 1.多个异常分别处理:
        try {
    
    
            int[] arr1 = {
    
    1,2,3};
            System.out.println(arr1[5]);
        } catch (ArrayIndexOutOfBoundsException err) {
    
    
            System.out.println("1数组索引越界异常:" + err); // 1数组索引越界异常:java.lang.ArrayIndexOutOfBoundsException: 5
        }
        try {
    
    
            List<Integer> arr2 = List.of(1,2,3);
            System.out.println(arr2.get(5));
        } catch (IndexOutOfBoundsException err) {
    
    
            System.out.println("2数组索引越界异常:" + err); // 2数组索引越界异常:java.lang.IndexOutOfBoundsException: Index 5 out-of-bounds for length 3
        }

        // 2.多个异常一次捕获,多次处理:  注意:catch里边定义的异常变量,如果有子父类关系,那么子类的异常变量必须写在上边,否则会报错(ArrayIndexOutOfBoundsException继承了IndexOutOfBoundsException,如果这两个处理异常的顺序调换位置就会报错)
        try {
    
    
            int[] arr3 = {
    
    1,2,3};
            System.out.println(arr3[5]);
            List<Integer> arr4 = List.of(1,2,3);
            System.out.println(arr4.get(5));
            // 因为ArrayIndexOutOfBoundsException是IndexOutOfBoundsException的子类,这里需要将子类异常处理写在最前面,否则会报错,报错原因:父类IndexOutOfBoundsException接收了try中子类的报错(多态),后面的子类没有用到。
        } catch (ArrayIndexOutOfBoundsException err) {
    
    
            System.out.println("3数组索引越界异常:" + err); // 3数组索引越界异常:java.lang.ArrayIndexOutOfBoundsException: 5
        } catch (IndexOutOfBoundsException err) {
    
    
            System.out.println("4数组索引越界异常:" + err); // 因为第一个异常抛出后后面代码不会执行了,因此这里没有打印出异常,需要打印的话可以将第一个异常注释后在测试
        }

        // 3.多个异常一次捕获一次处理(利用多态的原理)
        try {
    
    
            int[] arr5 = {
    
    1,2,3};
            System.out.println(arr5[5]);
            List<Integer> arr6 = List.of(1,2,3);
            System.out.println(arr6.get(5));
            // 因为ArrayIndexOutOfBoundsException是IndexOutOfBoundsException的子类, 父类IndexOutOfBoundsException接收了try中子类的报错(多态)
        } catch (IndexOutOfBoundsException err) {
    
     // 这里建议使用Exception,可以处理多种类型异常
            System.out.println("5数组索引越界异常:" + err); // 5数组索引越界异常:java.lang.ArrayIndexOutOfBoundsException: 5
        }
    }
}

继承关系中处理异常

// 子父类异常处理情况:
// 1.如果父类抛出多个异常,子类重写父类方法时可以抛出和父类相同的异常或者子类抛出和父类异常的子类或者不抛出异常
// 2.如果父类没有抛出异常时,子类重写父类方法时也不可以抛出异常(不能声明),此时子类有异常只能通过try/catch捕获处理
public class ExceptionInextendsClass {
    
    
    public void me1() throws NullPointerException,ClassCastException{
    
    }
    public void me2() throws IndexOutOfBoundsException{
    
    }
    public void me3() throws IndexOutOfBoundsException{
    
    }
    public void me4() {
    
    }
}

class Zi extends ExceptionInextendsClass {
    
    
    // 子类可以抛出父类所有已有的异常:
    @Override
    public void me1() throws NullPointerException,ClassCastException {
    
    }

    // 子类可以抛出父类已有异常的子类:
    @Override
    public void me2() throws ArrayIndexOutOfBoundsException {
    
    }

    // 子类可以不抛出异常:
    @Override
    public void me3() {
    
    }

    // 父类没有抛出异常时,子类也不能抛出异常,只能使用try/catch捕获处理:
    @Override
    public void me4() {
    
    
        try {
    
    
            throw new Exception("生成异常");
        } catch (Exception e) {
    
    
            throw new RuntimeException(e);
        }
    }
}

自定义异常类

尽管java提供了很多的异常类,但是任然不够我们使用,此时我们可以自定义异常类来实现我们的需求。

定义自定义异常类(注册异常类):

// 自定义异常类:自定异常类需要继承Exception或RuntimeException才可以,其格式: public class XXXException extends Exception或RuntimeException {
    
    
    // 空参数构造方法
    // 异常信息的构造方法
// }
// 注意:1.自定义异常类必须以xxxException命名,说明该类是一个异常类  2.继承Exception说明是编译期异常,如果方法抛出异常,那么必须throws或try/catch处理 3.继承RuntimeException说明是运行期异常,无法处理,交给虚拟机处理(中断处理)
public class RegisterException extends Exception {
    
    
    // 添加一个空参数构造方法:
    public RegisterException () {
    
    
        // 调用了父类的空参数构造方法
        super();
    }
    // 添加一个带有异常信息的构造方法:
    public RegisterException (String msg) {
    
    
        // 调用父类带有异常信息的构造方法
        super(msg);
    }
}

定义一个注册类,用于测试自定义异常类:

import java.util.Scanner;

// 场景:模拟注册业务,如果用户已经存在,则抛出异常,否则提示注册成功。
public class RegisterClass {
    
    
    // 1.定义一个已有的用户数组,实际开发中这里的数据从数据库中获取
    static String[] userList = new String[]{
    
    "user01","user02"};

    public static void main(String[] args) /*抛出异常处理:throws RegisterException*/ {
    
    
        // 2.获取用户输入的用户名并存到变量中,实际开发中由前端传入
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名:");
        String inputUserName = sc.next();
        // 4.调用校验用户名是否存在的方法做校验:
        isRegister(inputUserName);
    }

    // 3.定义一个校验用户名是否已存在的方法:
    public static void isRegister (String user) /*抛出异常处理:throws RegisterException*/ {
    
    
        // 遍历已存在的用户名和当前传入的用户名做比较:
        for (String s : userList) {
    
    
            if (s.equals(user)) {
    
    
                // 抛出自定义异常(并使用try/catch处理异常,当然运行期异常也可以不用自己处理,交给jvm处理):
                try {
    
    
                    throw new RegisterException("用户名已存在!");
                } catch (RegisterException e) {
    
    
                    e.printStackTrace();
                    return; // 如果异常,阻止程序继续执行
                }
            };
        }
        System.out.println("注册成功!");
    };
}

提示:本文图片等素材来源于网络,若有侵权,请发邮件至邮箱:[email protected]联系笔者删除。
笔者:苦海

猜你喜欢

转载自blog.csdn.net/weixin_46758988/article/details/128309708