Java-异常

目录

异常

异常概述

异常概述小结:

JVM的默认处理方案

异常处理

异常处理之try…catch…

Throwable的成员方法

编译时异常和运行时异常的区别

异常处理之throws

自定义异常


异常

异常概述

先到程序中演示:

/*
    异常
 */
public class ExceptionDemo01 {
    public static void main(String[] args) {
        method();
    }
    //1.定义method方法,在里面定义域一个静态数组,并输出了索引
    public static void method(){
        int[] arr = {1,2,3};
        System.out.println(arr[1]);//2,没问题
        //2.但是当我们输出arr[3]就会报错
        System.out.println(arr[3]);//ArrayIndexOutOfBoundsException(数组索引越界异常)
        //3.ArrayIndexOutOfBoundsException这个就是异常,也就是程序出现了不正常的情况,我们到帮助文档查看一下
    }
}

到帮助文档中查看此异常:

这个类的继承体系有点复杂,但是它最顶层的是Object,所以我们也可以说Object是异常的老祖宗类。但是所有异常的老祖宗不应该是Object,因为Object是所有类的祖宗类,所以这一次我们要看Throwable类。

结构就不解释了继续往下看:

可以看到:

Throwable类是Java语言中所有错误和异常的超类。

也就是Thrwable类是所有错误和异常的祖宗类。

再往下看:

两个子类的实例Error异常通常用于表示发生了异常情况。

我们先来看Error:

由图所述:

我们程序如果出现了Error的情况,一般来说java程序是无能为力的,例如:硬件层面问题,内存资源不足等等。所以一般针对Error问题我们一般是不处理的。

再来看异常:

他有很多子类,我们跟多的使用的是它的子类。

再往下看:

由图所述:

异常及其子类是Throwable一种形式,它指示合理的应用程序可能想要捕获的条件。也就是说异常才是我们要捕获的内容。

异常和任何不属于RuntimeException子类的子类都是已检查的异常 。注意:检查异常指的是编译期间就必须处理的,否则程序就不能通过编译,程序就不能正常运行了。

这里又说了异常类不属于任何RuntimeException子类的子类,都是一检查异常,那么RuntimeException又是什么呢?

我们继续跟进:

图中所述:

     RuntimeException是在Java虚拟机的正常操作期间可以抛出的那些异常的超类。RuntimeException及其子类是未经检查的异常 。也就是说RuntimeException及其子类是未经检查的异常,也就是说RuntimeException及其子类是非受检异常,在编译期间是不需要检查的,出现问题后需要我们回来修改代码的。

而我们的代码中的:ArrayIndexOutOfBoundsException就是RuntimeException系列的异常,一开始我们的代码通过索引3去访问,编译并不会给我们提示错误,当我们运行报错之后,我们回来修改我们代码的值这就是我们的RuntimeException系列的异常。

异常概述小结:

异常:就是程序出现了不正常的情况。

异常体系:

Error:严重问题,不需要处理

Exception:称为异常类,他表示程序本身可以处理的问题。

RuntimeException:在编译期间是不检查的,出现问题后,需要我们回来修改代码。

非RuntimeException:编译期间就必须处理的,否则程序不能通过编译,就更不能正常运行了。

JVM的默认处理方案

(java虚拟机针对程序出现问题后的默认处理方案)

程序演示:

public class ExceptionDemo02 {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }
    public static void method(){
        int[] arr ={1,2,3};
//        System.out.println(arr[1]);//此时程序无问题,得到:开始 2 结束
        System.out.println(arr[3]);
        System.out.println();
        /*
        运行结果:
            开始
            Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
                at com.itheima01.ExceptionDemo02.method(ExceptionDemo02.java:12)
                at com.itheima01.ExceptionDemo02.main(ExceptionDemo02.java:6)
        拆解输出结果:
            Exception in thread "main":线程"main"异常
            ArrayIndexOutOfBoundsException:异常的类名
            Index 3 out of bounds for length 3:异常原因:索引3越界了
            at com.itheima01.ExceptionDemo02.method(ExceptionDemo02.java:12):异常出现位置:第16行
            at com.itheima01.ExceptionDemo02.main(ExceptionDemo02.java:6):同理
            但是结束两个自并未打印到控制台,也就是说,java虚拟机他把这个信息对应的异常问题输出在控制台后就结束了程序的运行.
         */
    }
}

小结:

如果程序出现了问题,我们没有做任何处理,最终JVM会做默认的处理:

  1. 把异常的名称,异常原因以及出现的位置等信息输出在

了控制台。

  1. 程序终止运行。

异常处理

为什么要学习自己处理异常呢?原因很简单因为java虚拟机的默认处理方案会让程序在出现问题的地方直接结束掉,而在实际开发中,程序某一个地方出问题他不应该影响后面的执行,所以我们要自己来处理异常。那么我们自己如何来处理异常呢?

两种方案:

  1. 使用try…catch…
  2. throws

异常处理之try…catch…

执行流程:

程序从try里面的代码开始执行;

出现异常,会自动生成一个异常类对象,该异常对象被提交给java运行时系统;

当java运行时系统接收到异常对象时,会到catch中去找匹配的异常类,找到后进行异常的处理;

执行完毕后,程序还可以继续往下执行。

程序演示:

/*
    try{
        可能出现异常的代码
    }catch(异常类名 变量名){
        异常的处理代码
    }
 */
public class ExceptionDemo {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        int[] arr = {1, 2, 3};
//        System.out.println(arr[1]);//此时程序无问题,得到:开始 2 结束
        try {
            System.out.println(arr[3]);//这里等于new ArrayIndexOutOfBoundsException();
        } catch (ArrayIndexOutOfBoundsException e) {
//            System.out.println("你访问数组所以不存在");//在实际开发中如果出现异常是应该给出页面提示,只是现在我能力不足
            //既然e是个对象那么就可以调方法
            e.printStackTrace();//这个方法会让程序执行完毕后再来汇报错误信息,并不会在程序有问题的时候终止程序
        }
    }
}

Throwable的成员方法

Throwable是所有异常和错误的祖宗类,只要属于这个体系的都可以使用它的成员方法。

看一下成员方法:

程序演示:

/*
    public String getMessage();返回此throwable的详细消息字符串
    public String toString();返回此可抛出的简短描述
    public void printStackTrace();把异常的错误信息输出在控制台
 */
public class ExceptionDemo02 {
    public static void main(String[] args) {
        System.out.println("开始");
        method();
        System.out.println("结束");
    }

    public static void method() {
        int[] arr = {1, 2, 3};
        try {
            System.out.println(arr[3]);//1.当程序这里出现问题后就会在我们运行的时候产生一个异常对象new ArrayIndexOutOfBoundsException();
        } catch (ArrayIndexOutOfBoundsException e) {//2.这个对象将会和catch里面匹配,发现匹配上了,就把上面的异常对象赋值给e,e就是对象了 就可以调方法了
//            e.printStackTrace();

            //3.public String getMessage();返回此throwable的详细消息字符串
//            System.out.println(e.getMessage());//Index 3 out of bounds for length 3(这是异常原因)

            //4.public String toString();返回此可抛出的简短描述
//            System.out.println(e.toString());//java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3(异常原因以及类名)

            //5.public void printStackTrace();把异常的错误信息输出在控制台
            e.printStackTrace();//这一个方法在控制台输出的信息是最全的,所以一般来说我们会调用这个方法把我们的异常错误信息输出在了控制台
        }
    }
                /*
            跟进getMessage方法可以看到它返回了一个detailMessage;

            public class Throwable implements Serializable {
                private String detailMessage;
                    public Throwable(String message){//构造方法
                        detailMessage = message;
                    }
                        public String getMessage() {
                            return detailMessage;
                        }
            }
             */
}

编译时异常和运行时异常的区别

Java中的异常被分为两大类:编译时异常运行时异常,也被称为受检异常非受检异常

所有的RuntimeException类及其子类被称为运行时异常,其他的异常都是编译时异常。

  1. 编译时异常:必须显示处理,否则程序就会发生错误,无法通过编译(受检异常)。
  2. 运行时异常:无需显示处理,也可以和编译时异常一样处理(非受检异常)。

程序演示:

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

/*
    Java中的异常被分为两大类:编译时异常和运行时异常,也被称为受检异常和非受检异常。
    所有的RuntimeException类及其子类被称为运行时异常,其他的异常都是编译时异常。

    	编译时异常:必须显示处理,否则程序就会发生错误,无法通过编译(受检异常)。
    	运行时异常:无需显示处理,也可以和编译时异常一样处理(非受检异常)。

 */
public class ExceptionDemo03 {
    public static void main(String[] args) {
        //3.调用方法并执行
//        method();//4.报错ArrayIndexOutOfBoundsException

        //12.注释掉method方法并调用method2方法
        method2();//13.结果:Sun Aug 09 00:00:00 CST 2048,这里不会出问题,为什么呢?
        /*
        14.因为编译时异常不代表一定出现异常,我们try中的字符串对应和pattern这里匹配就不会出问题
        除非不匹配,但是它是有可能有问题这个方法会告诉我们它可能有问题
         */

    }

    //8.编译时异常
    public static void method2() {
        String s = "2048-08-09";
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        //11.处理
        try {
            Date parse = simpleDateFormat.parse(s);//9.未报告的异常错误java.text.ParseException; 必须对其进行捕获或声明以便抛出
            System.out.println(parse);
        } catch (ParseException p){
            p.printStackTrace();
        }

        /*
        10.到帮助文档中查看ParseException类信息
        父类是Exception,说明他不是运行时异常,既然不是运行时异常他就是编译时异常
        而编译时异常我们必须显示处理,否则程序就会发生错误,无法通过编译.
        怎么处理呢?我们通过try...catch...处理
         */


    }

    //1.运行时异常
    public static void method() {
        int[] arr = {1, 2, 3};
//        System.out.println(arr[3]);//2.索引越界但是编译不报错,也就是说这是一个非受检异常,也就是运行时异常
        //5.这时我们没就要回来修该代码,我们可以使用try...catch...来修改代码
        try {
            System.out.println(arr[3]);//6.就是我们做不做这个try...catch...我们也必须回来修改这个代码,因为它是运行时异常.
        } catch (ArrayIndexOutOfBoundsException a) {
            a.printStackTrace();
        }
        /*7.我们怎么知道它是运行时异常呢?很简单到帮助文档中搜索异常类名即可
        它的父类是IndexOutOfBoundsException,爷爷类是RuntimeException
        所以他就是运行时异常.
         */
    }
}

异常处理之throws

虽然我们通过try…catch…可以对异常进行处理,但是并不是所有的情况我们都有权限进行异常的处理。

也就是说有些时候可能出现的异常是我们处理不了的,这个时候怎么办呢?

这个时候java就提供了throws的处理方案

     格式:

throws 异常类名;

注意:这个格式是跟在方法的括号后面的。

程序演示:

我们一般默认选第一个:

抛完之后可以看见,这个问题还是要处理:

所以就是只是把异常抛出去了,我现在处理不了你,谁用他就谁处理了:于是我们在调用方法这里也选择Alt+Enter:

先择第二个try catch来处理,不要选第一个怕这个异常给抛了,不然的话这个异常只是抛掉了,并未处理

然后右键运行就没有问题了。

所以不管编译时异常还是运行时异常都可以在方法后面通过使用throws来处理,但是这种抛出,它并没有做实际的处理,真正的处理还得通过try catch来实现。只不过我们在这里处理不了才把他抛出去。留给调用者来处理。

小结:

  1. 编译时异常必须要进行处理,两种处理方案:try…catch…或者throws,如果采用throws这种方案,将来谁调用谁还得来进行处理。
  2. 运行时异常可以不处理,出现问题后,需要我们回来修改代码。

自定义异常

为什么要学习自定义异常?

虽然java提供了很多异常类供我们使用,但是在实际开发中他并不能满足我们所有的需求,比如说学生的考试成绩只能在0-100之间,这个java它是并不知道的。所以我们需要自己来写一个异常类来实现我们的需求。

     我们如何让自己写的异常类是异常体系的一员呢?

打开idea界面按下Ctrl+N

然后输入RuntimeException :

可以看到RuntimeException继承自Exception。我再按下Ctrl+N,搜索ParseException:

可以看到ParseException也是继承自Exception:

也就是说我们的类它继承自Exception或者类继承自RuntimeException他就变成了异常体系的一员,那么这里我们采用类来继承Exception来实现我们的自定义异常类。

格式:

程序演示:

public class ScoreException extends Exception{//1.新建一个异常类让他继承自Exception
    //2.给出无参和带参构造方法
    public ScoreException() {
    }

    public ScoreException(String message) {
        //3.带参构造方法,通过super将message传给它的父类
        super(message);
    }
}
class Teacher {
    //5.抛出的异常对象继承自Exception,所以说,在方法这需要用throws把异常类给抛出来
    public void checkScore(int score) throws ScoreException {
        if (score < 0 || score > 100) {
            //4.这里要产生一个异常对象,如果满足if条件我们就将这个异常对象抛出去
//           throw new ScoreException();

            //10.注释掉throw new ScoreException,并修改
            throw new ScoreException("你给的分数有误,分数应该在0到100之间");
           } else {
            System.out.println("分数正常");
        }
    }
}
import java.util.Scanner;
//6.测试
class TeacherTest {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        System.out.print("请输入分数:");
        int i = scanner.nextInt();

        Teacher teacher = new Teacher();
        /*
        7.这里会报错,因为checkScore方法有抛出一个异常,
        而这个异常继承自Exception,所以这是一个编译时异常,编译时异常我们必须要显示处理
         */
//        teacher.checkScore(i);
        //8.按下Alt+Enter,选用trycatch方式处理
        try {
            teacher.checkScore(i);
        } catch (ScoreException e) {
            e.printStackTrace();
            /*
            9.当我们输入的分数满足if条件的时候就会报错
            com.itheima04.ScoreException
                at com.itheima04.Teacher.checkScore(Teacher.java:8)
                at com.itheima04.TeacherTest.main(TeacherTest.java:19)
                显示了异常的位置,但是没有异常的原因,所以这时候我们就不能在方法中throw new 这里使用无参构造方法了
             */

            /*
            11.修改完之后右键执行:当我们输入的分数满足if条件的时候就会报错:
            com.itheima04.ScoreException: 你给的分数有误,分数应该在0到100之间
                at com.itheima04.Teacher.checkScore(Teacher.java:11)
                at com.itheima04.TeacherTest.main(TeacherTest.java:19)
                这一次就有了报错原因,虽然原因是我们自己给的.
             */
        }
    }
}

小结:

throws和throw的区别:

throws:

  1. 用在方法声明后面,跟的是异常类名。
  2. 表示抛出异常,由该方法的调用者来处理。
  3. 表示出现异常的一种可能性,并不一定会发生这些异常。

throw:

  1. 用在方法体内,跟的异常对象名。
  2. 表示抛出异常,由方法体内的语句处理。
  3. 执行throw一定是抛出了某种异常。

解释:

就相当于方法声明后面跟的throws表示这个方法有可能会出异常,但不是一定会出现异常,因为在这个方法里面他有可能是执行else里面的内容。

而当程序走到if里面的throw这里,那么程序它一定是抛出了异常;因为,只有当分数出现问题的时候程序才会走到这里。

给个赞!

--欢迎大家跟我一起交流java知识

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

贰陆.256

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值