Java异常和错误、异常的产生过程解析、throw、Objects.requireNonNull、处理异常的方法(throws、try-catch)

一、Java异常和错误

java.lang.Throwable:类是 Java 语言中所有错误或异常的超类。
在这里插入图片描述

1. Exception分两种异常(编译器异常和运行时异常)

  • 编译期异常(checked异常),进行编译(写代码的时候就报错了)java程序出现的问题, 在编译时期,就会检查,如果没有处理异常,则编译失败。(如日期格式化异常)

  • RuntimeException(运行期异常),java程序运行过程中出现的问题,在运行时期,检查异常;在编译时期,运行异常不会被编译器检测(不报错)。(如数学异 常)

2.异常和错误的区别

  • 异常,就相当于程序得了一个小毛病(感冒,发烧),把异常处理掉,程序可以继续执行(try,catch后还可以执行之后的代码)(吃点药,继续革命工作)
  • Error:错误 错误就相当于程序得了一个无法治愈的毛病(非典,艾滋).必须修改源代码,程序才能继续执行

捕获异常:try-catch(若try中出错,catch可打印信息,而后面的代码还是可以运行的)
详细看一下代码:

异常是xxxException,而错误的是:xxxError

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

public class Debug_Test {
    public static void main(String[] args) throws ParseException {
        show03();
    }

    private static void show03() {
        /*
            Error:错误
            OutOfMemoryError: Java heap space
            内存溢出的错误,创建的数组太大了,超出了给JVM分配的内存
         */
        int []arr=new int[1024*1024*1024];
        //必须修改代码,创建的数组小一点
        System.out.println("后续代码!");//这里输出不了
        //Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
    }

    private static void show02() {
        //RuntimeException:运行期异常,java程序运行过程中出现的问题
        int []arr={1,2,3};
        //这里没有快捷键按哦,编译时异常就可以快捷键
        try {
            System.out.println(arr[3]);//运行时才报错,编译的时候没事
        }catch (Exception e){
            //异常的处理逻辑
            System.out.println(e);
        }
        System.out.println("这里可以输出吗?");//可以输出的,try-catch后面都是可以输出的
    }

    private static void show01() /*throws ParseException*/ {
        //Exception:编译期异常,进行编译(写代码)java程序出现的问题
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd");//用来格式化日期
        //1.直接throws,抛给虚拟机
//        Date date = sdf.parse("2000-0223");
//        System.out.println(date);
//        System.out.println("我输出不了");//若出错,这里代码运行不了,所以还是try-catch好
        //结果Exception in thread "main" java.text.ParseException: Unparseable date: "2000-0223"


        //2.try-catch方法
        Date date = null;//这里要抛出异常(不然写代码的时候就变红,执行不了)/*throws(记得主方法也要抛出异常)ParseException,或者try-catch(这个后面(catch下面)的代码还可以执行)
        try {
            date = sdf.parse("2000-0223");
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println(date);//try-catch(若try中出错,catch可打印信息,而后面的代码还是可以运行的)
        System.out.println("还可以运行");//有异常提示,但后面的代码还是可以执行的
        // 运行结果:java.text.ParseException: Unparseable date: "2000-0223"
        //null
        //at java.base/java.text.DateFormat.parse(DateFormat.java:388)
        //还可以运行

    }
}

二、异常的产生过程解析

在这里插入图片描述
为什么运行时的异常可以不处理呢( 即不捕获也不声明抛出)?:

因为默认给虚拟机处理,终止程序,什么时候不抛出运行时异常了,在来继续执行程序,还是jvm会调用printStackTrace输出红色提示。

三、throw

throw关键字
作用:
可以使用throw关键字在指定的方法中抛出指定的异常
使用格式:
throw new xxxException(“异常产生的原因(这里相当于上面(二)的提示内容)”);

  1. 创建一个异常对象。封装一些提示信息(信息可以自己编写)。

  2. 需要将这个异常对象告知给调用者。怎么告知呢?怎么将这个异常对象传递到调用者处呢?通过关键字throw 就可以完成。throw 异常对象。 throw用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。

    注意:
    1.throw关键字必须写在方法的内部
    2.throw关键字后边new的对象必须是Exception或者Exception的子类对象
    3.throw关键字抛出指定的异常对象,我们就必须处理这个异常对象
    throw关键字后边创建的是RuntimeException或者是 RuntimeException的子类对象,我们可以不处理,默认交给JVM处理(打印异常对象,中断程序)
    throw关键字后边创建的是编译异常(写代码的时候报错),我们就必须处理这个异常,要么throws,要么try…catch

注意:

  • 常见运行时异常:

NullPointerException是一个运行期异常,我们不用处理,默认交给JVM处理
ArrayIndexOutOfBoundsException是一个运行期异常,我们不用处理,默认交给JVM处理
IndexOutOfBoundsException (ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException)

  • 常见编译异常:

FileNotFoundException
IOException

public class Demo03Throw {
    public static void main(String[] args) {
        //int[] arr = null;
        int[] arr = new int[3];
        int e = getElement(arr,3);
        System.out.println(e);
    }
    /*
        定义一个方法,获取数组指定索引处的元素
        参数:
            int[] arr
            int index
        以后(工作中)我们首先必须对方法传递过来的参数进行合法性校验
        如果参数不合法,那么我们就必须使用抛出异常的方式,告知方法的调用者,传递的参数有问题
        注意:
            NullPointerException是一个运行期异常,我们不用处理,默认交给JVM处理
            ArrayIndexOutOfBoundsException是一个运行期异常,我们不用处理,默认交给JVM处理
     */
    public static int getElement(int[] arr,int index){
        /*
            我们可以对传递过来的参数数组,进行合法性校验
            如果数组arr的值是null
            那么我们就抛出空指针异常,告知方法的调用者"传递的数组的值是null"
         */
        if(arr == null){
            throw new NullPointerException("传递的数组的值是null");//这里只是抛出异常而已,程序还是执行不了
        }

        /*
            我们可以对传递过来的参数index进行合法性校验
            如果index的范围不在数组的索引范围内
            那么我们就抛出数组索引越界异常,告知方法的调用者"传递的索引超出了数组的使用范围"
         */
        if(index<0 || index>arr.length-1){
            throw new ArrayIndexOutOfBoundsException("传递的索引超出了数组的使用范围");
        }

        int ele = arr[index];
        return ele;
    }
}

Objects.requireNonNull

简化一点你写的代码而已

Objects.requireNonNull(obj,"传递的对象的值是null");

可代替写这个非空判断而已:`

if(obj == null){
            throw new NullPointerException("传递的对象的值是null");
        }`
import java.util.Objects;

/*
    Obects类中的静态方法
    public static <T> T requireNonNull(T obj):查看指定引用对象不是null。
    源码:
        public static <T> T requireNonNull(T obj) {
            if (obj == null)
                throw new NullPointerException();
            return obj;
        }
 */
public class Demo04Objects {
    public static void main(String[] args) {
        method(null);
    }

    public static void method(Object obj){
        //对传递过来的参数进行合法性判断,判断是否为null
        /*if(obj == null){
            throw new NullPointerException("传递的对象的值是null");
        }*/

        //Objects.requireNonNull(obj);
        Objects.requireNonNull(obj,"传递的对象的值是null");
	
    }
}

四、处理异常的方法(throws、try-catch)

1.throws

throw如果抛出一个编译异常,则可以用throws抛出,否则写代码的时候会变红**

throw和throws是不一样的

throws关键字:异常处理的第一种方式,交给别人处理
作用:
当方法内部抛出异常对象的时候,那么我们就必须处理这个异常对象
可以使用throws关键字处理异常对象,会把异常对象声明抛出给方法的调用者处理(自己不处理,给别人处理),最终交给JVM处理–>中断处理
使用格式:在方法声明时使用
修饰符 返回值类型 方法名(参数列表) throws AAAExcepiton,BBBExcepiton…{
throw new AAAExcepiton(“产生原因”);
throw new BBBExcepiton(“产生原因”);

}
注意:
1.throws关键字必须写在方法声明处
2.throws关键字后边声明的异常必须是Exception或者是Exception的子类
3.方法内部如果抛出了多个异常对象,那么throws后边必须也声明多个异常
如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可(子也可以写在前面也可以省略)
4.调用了一个声明抛出异常的方法,我们就必须的处理声明的异常
要么继续使用throws声明抛出,交给方法的调用者处理,最终交给JVM
要么try…catch自己处理异常

FileNotFoundException extends IOException extends Excepiton(所以可以直接写Exception就可以)
如果抛出的多个异常对象有子父类关系,那么直接声明父类异常即可

import java.io.FileNotFoundException;
import java.io.IOException;
public class Demo05Throws {
    /*
        
     */
    //public static void main(String[] args) throws FileNotFoundException,IOException {
    //public static void main(String[] args) throws IOException {
    public static void main(String[] args) throws Exception {
        readFile("c:\\a.tx");

        System.out.println("后续代码");若路径对这句话可以输出;若路径错了,则这句话不会输出,只会打印异常提示
    }

    /*
        定义一个方法,对传递的文件路径进行合法性判断
        如果路径不是"c:\\a.txt",那么我们就抛出文件找不到异常对象,告知方法的调用者
        注意:
            FileNotFoundException是编译异常,抛出了编译异常,就必须处理这个异常
            可以使用throws继续声明抛出FileNotFoundException这个异常对象,让方法的调用者处理
     */
    public static void readFile(String fileName) throws FileNotFoundException,IOException{
        if(!fileName.equals("c:\\a.txt")){
            throw new FileNotFoundException("传递的文件路径不是c:\\a.txt");
        }

        /*
            如果传递的路径,不是.txt结尾
            那么我们就抛出IO异常对象,告知方法的调用者,文件的后缀名不对

         */
        if(!fileName.endsWith(".txt")){
            throw new IOException("文件的后缀名不对");
        }

        System.out.println("路径没有问题,读取文件");//若路径对这句话可以输出;若路径错了,则这句话不会输出,只会打印异常提示
    }
}

2.try-catch

重点:
try-catch的区别是什么
1.throws如果出错,后序代码执行不了,因为它是最终交给JVM处理–>中断处理。而try-catch就算出错,还是可以执行后面的代码的
2.throws最终是交给jvm处理,而try-catch是自己处理。

try…catch:异常处理的第二种方式,自己处理异常
格式:
try{
可能产生异常的代码
}catch(需要定义一个异常的变量,用来接收try中抛出的异常对象){
异常的处理逻辑,异常异常对象之后,怎么处理异常对象
一般在工作中,会把异常的信息记录到一个日志中
}

catch(异常类名 变量名){

}
catch可以多个

注意:

  1. try中可能会抛出多个异常对象,那么就可以使用多个catch来处理这些异常对象
  2. 如果try中产生了异常,那么就会执行catch中的异常处理逻辑,执行完毕catch中的处理逻辑,继续执行try…catch之后的代码
  3. 如果try中没有产生异常,那么就不会执行catch中异常的处理逻辑,执行完try中的代码,继续执行try…catch之后的代码
    4.如果try中的第一句出错,那么try中的第二句不会执行的,但try-catch后的代码可执行(特殊看文末)。
import java.io.IOException;
public class Demo01TryCatch {
    public static void main(String[] args) {
        try{
            //可能产生异常的代码
            readFile("d:\\a.tx");
            System.out.println("上一句出错就不输出");
            
        }catch (IOException e){//try中抛出什么异常对象(看下面具体代码throw了什么类型的异常哦),catch就定义什么异常变量,用来接收这个异常对象
            //异常的处理逻辑,异常异常对象之后,怎么处理异常对象
            System.out.println("catch - 传递的文件后缀不是.txt");
        }
        System.out.println("后续代码");
    }

    /*
       如果传递的路径,不是.txt结尾
       那么我们就抛出IO异常对象,告知方法的调用者,文件的后缀名不对

    */
    //这里只是main不抛给虚拟机处理用try而已,这里用throws的;这里也可以直接在这个方法用try-catch就不用throws了
    public static void readFile(String fileName) throws IOException {
        if(!fileName.endsWith(".txt")){
            throw new IOException("文件的后缀名不对");
        }
        System.out.println("路径没有问题,读取文件");
    }
}

关于输出结果:
1.若你传的路径不是txt结尾(出错),则会输出:

catch - 传递的文件后缀不是.
txt 后续代码

而throws的最后一句路径没有问题,读取文件不会输出。

2.若你传的路径符合txt,则会输出(先是你自定义的方法,再到你try那里):

路径没有问题,读取文件
后续代码

就是try…catch后面的代码一定会执行。而throws后面的代码只有对的情况才会执行,错误不执行

特别注意:并不是try-catch里面代码全部执行了,try{
//执行可能产生异常的代码
sout(“出错就不输出这条语句了”)
},就是若出现异常,则try中后面的语句不会执行,有点像throws一样。所以就出现了finally语句

4.2.1Throwable类中定义了3个异常处理的方法(要在catch里写

String getMessage() 返回此 throwable 的简短描述。
String toString() 返回此 throwable 的详细消息字符串(重写了tostring了哦)。
void printStackTrace() JVM打印异常对象,默认此方法,打印的异常信息是最全面的(会打印错误原因、内容、位置)

代码复制上面一份:

import java.io.IOException;
public class Demo01TryCatch {
    public static void main(String[] args) {
        try{
            //可能产生异常的代码
            readFile("d:\\a.tx");
            
        }catch (IOException e){//try中抛出什么异常对象(看下面具体代码throw了什么类型的异常哦),catch就定义什么异常变量,用来接收这个异常对象
            //异常的处理逻辑,异常异常对象之后,怎么处理异常对象
            //System.out.println("catch - 传递的文件后缀不是.txt");
            //System.out.println(e.getMessage());//文件的后缀名不对
            //System.out.println(e.toString());//重写Object类的toString java.io.IOException: 文件的后缀名不对
            //System.out.println(e);//java.io.IOException: 文件的后缀名不对

            /*
                java.io.IOException: 文件的后缀名不对
                    at com.itheima.demo02.Exception.Demo01TryCatch.readFile(Demo01TryCatch.java:55)
                    at com.itheima.demo02.Exception.Demo01TryCatch.main(Demo01TryCatch.java:27)
             */
            e.printStackTrace();//不用sout的,因为返回值为void
        }
        System.out.println("后续代码");
    }

    /*
       如果传递的路径,不是.txt结尾
       那么我们就抛出IO异常对象,告知方法的调用者,文件的后缀名不对

    */
    //这里只是main不抛给虚拟机处理用try而已,这里还是得用throws的
    public static void readFile(String fileName) throws IOException {
        if(!fileName.endsWith(".txt")){
            throw new IOException("文件的后缀名不对");
        }
        System.out.println("路径没有问题,读取文件");
    }
}

4.2.2Finally

finally{
无论是否出现异常都会执行
}
注意:
1.finally不能单独使用,必须和try一起使用
2.finally一般用于资源释放(资源回收),无论程序是否出现异常,最后都要资源释放(IO)

(个人感觉finally就像try-catch后面的代码,就算出现异常也会执行的,可能起个finally给人看代码好懂一些把, 但他们还是有点区别的:如果不想执行后面的代码,可以在catch里面return;就结束了,就不执行try-catch后面的代码了;但如果有finally,finally有return也会被输出的)

4.2.3throws和try-catch区别:

  1. 用throws处理异常,若出现异常,控制台打印红色的提示信息(jvm默认采用printStackTrace方法)

  2. throws不处理,是交给别人处理;而cat-catch是自己处理。

  3. 而用try_catch处理异常,控制台不会打印红色(除非你也是使用e.printStackTrace(),若sout是黑色的,还可以在catch不写东西,则说明都不输出哦。

  4. throws如果出错,后序代码执行不了,因为它是最终交给JVM处理(进行中断处理)。而try-catch就算出错,还是可以执行后面的代码的(如果不想执行后面的代码,可以在catch里面return;就结束了,就不执行try-catch后面的代码了;但如果有finally,finally有return也会被输出的)
    throws最终是交给jvm处理,而try-catch是自己处理。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值