Bootstrap

Java学习之异常抛出

异常抛出

1. 异常简介

import java.util.ArrayList;

public class DemoException1 {
    
    public static void main(String[] args) {
        /*TODO
            异常:
                需求:要买菜做饭
                1.先制定计划做哪些菜 但是计划里面有问题 比如需要做红烧肉,但是菜单里面写的是排骨  -> 编译时期的异常
                2.计划没问题 菜买回来了 开始做饭,明明要做红烧肉,但是用的是炖锅 -> 表示为运行时期异常
                3.用铁锅做红烧肉,但是肉放多了,整个锅放了一头猪 装不下 锅太小 -> ERROR (系统性错误无法处理)
               在Java中异常顶级父类为 Throwable 其子类分为三类
                    1.ERROR  程序运行时,发生的系统性错误
                    2.RuntimeException   程序运行时的一些代码 逻辑错误
                    3.编译异常.. 写代码时期可以避免发生
         */

//        ArrayList<String> list = new ArrayList<>();
//        list.add(1);

//        System.out.println(1/0);

        // 堆内存空间溢出
        // java.lang.OutOfMemoryError: Java heap space
        String guo = "";
        while (true){
            guo += (guo+"一头猪");
        }

    }


}

2. 异常种类概述

public class DemoException2 {

    public static void main(String[] args) {
        /*TODO
            异常种类:
                1.编译时期异常
                    ① 代码语法错误 代码的位置存在问题
                    ② 代码中的一些类型,存在不匹配
                 编译时期的异常,可以通过当前IDEA开发工具来展示
                2.运行时期的异常
                    ① ArithmeticException 除0错误  / by zero
                    ② ArrayIndexOutOfBoundsException 数组访问越界
                    ③ ConcurrentModificationException 迭代器的数据长度和数组的长度不一致导致的
                    ④ ClassCastException 类型转换错误 一般发生在对象强制类型转换的情况下
                    ⑤ NullPointerException 空指针异常 对对象进行调用时,其内存地址指向为空
                    注意:特点属于 RuntimeException 下的一个子类
                        在运行时会报错,报错后整个程序停止运行
                    如果当程序出现问题,需要对当前的错误进行捕获处理
                        比如对于 NullPointerException 异常,如果出现,那么可以对其进行重新创建对象
                        继续通过对象的方法进行调用

         */
//        System.out.println(1/0);

//        int[] ints = new int[3];
//        System.out.println(ints[3]);

        Exp exp = null;
//        exp.printInfo();
        // TODO 如果当exp的变量,指向的是为null,那么可以调用其 new 方法重新创建一个对象,赋予给变量
        if (exp == null) {
            exp = new Exp();
        }
        exp.printInfo();
        // TODO 上述的方式属于,问题错误没有发生,但是可以提前进行预防
        //      对于已经发生的错误,如果要进行处理就需要使用 try .. catch 方式进行捕获



    }

//    new Student();
}

class Exp {
    public void printInfo() {
        System.out.println("这是Exp类中的printInfo方法...");
    }
}

3. Try/Catch

public class DemoTryCatch {
    
    public static void main(String[] args) {
        /*
            TODO 异常捕获
                try
                    ...
                catch (异常的类型 变量名)=> 抓,捕获
                    ...
                Java是一个面向对象的语言,所有的异常类 NullPointerException 也是会有一个对象
                    于是需要使用变量调用对象
         */

        /*
            TODO
                int[] ints = new int[3];
                System.out.println(ints[3]);
                对于代码如果发生错误,那么错误的内容包含有三个方面:
                Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
                    at com.shujia.day11.Demo05TryCatch.main(Demo05TryCatch.java:17)
                异常类型:
                    ArrayIndexOutOfBoundsException
                异常提示信息:
                    3
                错误发生位置:
                     at com.shujia.day11.Demo05TryCatch.main(Demo05TryCatch.java:17)
                对于排除问题的思路:
                    ① 获取完整的日志信息 错误的位置不一定就发生在代码指定的位置
                    ② 找到错误的类型 根据类型可以确定当前问题发生的范围,
                            同时也可以根据类型将错误捕获,让程序继续运行
                    ③ 根据错误的提示信息,寻找线索 提示信息中一般都会打印一些数据
                    ④ 根据代码指定的位置,对其进行debug,重新运行,查看每个变量中的数据是否符合要求
                    ⑤ 如果因为自身对程序的了解不够,导致程序问题寻找不到,那么尝试换种方式

         */

        Exp exp = getExp();

        try {
            exp.printInfo();
            // 也可以使用它们的父类Exception
        }catch (NullPointerException exception){
            // 如果发生错误了,就可以捕获到错误的对象 对该对象调用其方法
//            System.out.println("错误提示信息:"+ exception.getMessage());
            System.err.println("错误提示信息:"+ exception.getMessage());
            // 打印整个的错误提示信息
            exception.printStackTrace();
            // 如何将日志信息写入到文件中进行保存?
        }

        // TODO 当程序被捕获时,那么可以继续运行代码
        System.out.println("程序执行到这里了....");


        // TODO 对于多行代码中可能会出现有多个错误,但是也不确定
        try {
            System.out.println(3/0);
            int[] ints = new int[2];
            System.out.println(ints[2]);
            // 对于错误类型有两种  ArrayIndexOutOfBoundsException 或者 ArithmeticException
        }catch (ArrayIndexOutOfBoundsException | ArithmeticException exception){
            System.out.println("发生错误了...");
            System.err.println(exception.getMessage());
            exception.printStackTrace();
        }


        /*
            TODO 对于多种错误类型,可以直接使用父类 Exception 表示
                 由于异常也是一个具体的类,也符合对象的多态特性
                    于是子类异常对象可以指向父类 Exception变量 在调用时,
                        比如 printStackTrace 方法是调用其具体对象的
         */

        try {
            System.out.println(3/0);
            int[] ints = new int[2];
            System.out.println(ints[2]);
        }catch (Exception exception){
//            exception.getMessage();
            System.out.println("捕获到了一个异常,但是暂时不知道是什么");
            exception.printStackTrace();
        }


        /*
            给定的异常太过于具体没有捕获到
                TODO 需求:当不管代码是否执行正常,都要执行某些代码..
                    比如:如果对Mysql数据库进行操作,操作过程中代码出错了
                        但是Mysql的连接需要进行关闭,否则对于Mysql数据库来说,有一个连接被占用了
             final格式:
                try{
                    可能发生错误的代码
                }catch(){
                    当指定的错误类型,被获取到后的操作
                }finally{
                    不管代码是否有错误,都会执行..
                }

         */
        try {
            String s = null;
            s.split("");
            System.out.println(3/0);
            int[] ints = new int[2];
            System.out.println(ints[2]);
        }catch (ArrayIndexOutOfBoundsException | ArithmeticException exception){
            System.out.println("捕获到了一个异常,但是暂时不知道是什么");
            exception.printStackTrace();
        }finally {
            System.out.println("不管程序是否有错误,该代码块中得代码都会被执行...");
        }

        System.out.println("该行被打印表示程序正常执行完成...");



    }

    // 假设这是另外一个Jar包或者其他程序员开发的代码 不能进行改变
    public static Exp getExp() {
        return null;
    }
}

4. 自定义异常

public class DemoMyException {
    
    // TODO 对于main方法 也可以进行异常抛出,但是一旦遇到异常,程序终止
    public static void main(String[] args) throws MyDefineException {
        /*
            TODO 如果想要定义一个自定义的错误类型
                 throw:通过该关键字可以 抛出一个异常的对象,让其产生错误
         */

        try {
            /*
                com.shujia.day11.MyDefineException: 这是自定义的异常
                       at com.shujia.day11.Demo06MyException.main(Demo06MyException.java:11)
             */
            throw new MyDefineException("这是自定义的异常");
        } catch (MyDefineException exception) {
            System.out.println(exception.getMessage());
            exception.printStackTrace();
        }

        /*
            TODO 当在fun函数中定义了一个错误的发生位置,那么对于main方法中,并没有提示其错误
                 那么如何让main方法知道fun函数中可能会发生错误?

                可以使用throws对异常进行抛出,意思表达为当程序中出现问题,自己不处理交给调用方进行处理
                    由于在main方法中进行调用,那么就需要在main中进行处理
         */
//        System.out.println(3/0);

//        try {
//            fun(3, 0);  // 经过捕获之后就没有异常了
//        }catch (MyDefineException exception){
//            exception.printStackTrace();
//        }
        fun(2, 0);
        System.out.println("程序是否能打印改行?");

    }


//    public static void fun(int a, int b) {
// TODO 方法内不处理,交给调用方进行处理
    public static void fun(int a, int b) throws MyDefineException,NullPointerException {

        if (b == 0) {
            // 直接处理由于有 throw 执行到该处时肯定会发生错误,程序退出...
            throw new MyDefineException("除数不能为零,抛出异常,请检查逻辑");
        } else {
            System.out.println(a / b);
        }

        // TODO 方法内自行处理
//        if (b == 0) {
//            try {
//                throw new MyDefineException("除数不能为零,抛出异常,请检查逻辑");
//            } catch (MyDefineException exception) {
//                exception.printStackTrace();
//            }
//        } else {
//            System.out.println(a / b);
//        }


    }
}

class MyDefineException extends Exception {
    // 如果当遇到自定义的类型时,想要去打印自定义的信息怎么做
    //   打印信息 可以使用 getMessage()

    public MyDefineException(String message) {
        super(message);
    }
}

5. 异常总结

public class DemoExceptionTxt {
    
    public static void main(String[] args) {
        /*TODO
            异常操作的总结:
                1.对于可能会发生错误的代码处理方式
                    ① 使用 try catch 进行捕获异常 程序不会终止
                           如果有必须要执行的代码,可以放到 finally 代码块中
                    ② 使用 throws 对可能发生的异常进行抛出,交给调用方进行处理
                            如果调用方也不处理,那么遇到错误,程序会终止
                2.如果想定义错误
                    1.构建一个自定义异常类继承于 Exception 或者其子类
                    2. 通过 throw 方法触发 其异常类的对象
               finally的作用
                    用于释放资源,在IO流操作和数据库操作中会见到: 比如对Mysql连接进行关闭操作
               TODO throws和throw的区别:
                throws
                    用在方法声明后面,跟的是异常类名
                    可以跟多个异常类名,用逗号隔开
                    表示抛出异常,由该方法的调用者来处理
                    throws表示出现异常的一种可能性,并不一定会发生这些异常
                throw
                    用在方法体内,跟的是异常对象名
                    只能抛出一个异常对象名
                    表示抛出异常,由方法体内的语句处理
                    throw则是抛出了异常,执行throw则一定抛出了某种异常
                异常可以用埋雷、排雷的方式理解
         */

        /*
            如果catch里面有return语句,请问finally的代码还会执行吗?如果会,请问是在return前还是return后。
                finally代码是必须要执行的,是在return之前,函数执行完成之前执行
         */
        System.out.println(fun());
    }
    
    public static int fun(){
        try {
            System.out.println("return之前...");
            return 0;
//            System.out.println("return之后...");
        }catch (Exception exception){
            System.out.println("异常被捕获...");
        }finally {
            System.out.println("finally代码被执行...");
        }
        return 1;
    }
}
;