JavaSE基础七----<异常>常见的异常,异常处理机制,自定义异常

Posted 小智RE0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JavaSE基础七----<异常>常见的异常,异常处理机制,自定义异常相关的知识,希望对你有一定的参考价值。


广义上讲,所有不正常的状况都可以归类为异常;

实际上,在Java语言中的异常,指的是在程序执行时出现的异常;
可分为两类:

  • Error: Java虚拟机无法解决的严重问题。如:JVM系统内部错误、内存耗尽;一般不编写针对性的代码进行处理。
  • Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理;使得程序可以继续运行下去;

常见的异常


  • 1.数组下标越界 java.lang.ArrayIndexOutOfBoundsException
//1.数组下标越界 java.lang.ArrayIndexOutOfBoundsException
//数组长度为10;这里并没有a[10]这个下标索引;
        int[] a=new int[10];
        a[10]=13;
  • 2.空指针异常 java.lang.NullPointerException
//2.空指针异常  java.lang.NullPointerException
//字符串s1存入了null
        String s1=null;
        s1.concat("123");
  • 3.数据类型转换异常 java.lang.ClassCastException
 //3.数据类型转换异常  java.lang.ClassCastException
        //例如向下转型
        Object o="123";
        Integer s2=(Integer)o;
  • 4.数据格式化异常 java.lang.NumberFormatException
//4.数据格式化异常 java.lang.NumberFormatException
        //例如给Integer中传入一个内容非数字的字符串"我"
        new Integer("我");
  • 5.算术异常 java.lang.ArithmeticException
//5.算术异常 java.lang.ArithmeticException
        //例如用5除以0
        int a=5,b=0;
        System.out.println(a/b);

异常的体系


Throwable类有两个直接子类:Exception类、Error类。

Error表示错误,往往程序中并不处理。

Exception表示异常,是所有异常类的父类,是程序员所关心的。

在这里插入图片描述

那么接下来就是重点学习Exception:

异常分为运行期异常和编译期异常两种

  • 运行期异常:程序运行时抛除的异常,所有RuntimeException子类都是运行期异常;
    (即所有直接或者间接继承RuntimeException的异常类);在编译期间不强制要求处理;
    例如:数组下标越界,空指针异常;

  • 编译期异常(Checked Exception):除去运行期之外的异常都是编译期异常,也称为检测异常;在编译期间强制要求处理;否则代码不能编译;
    例如:IOException , SQLException;

在这里插入图片描述


异常处理


在这里插入图片描述
Java的异常处理是通过5个关键字来实现的:try、catch、finally、throw、throws

在这里插入图片描述


try&catch


写个简单的异常处理:
仅包含try代码块和catch捕获:

try代码块中写入可能会出现异常的代码;
catch中写入需要捕获的异常类型(仅捕获指定类型的),以及处理异常的代码;
例如算术异常:

public class Demo003 {
    public static void main(String[] args) {
        //在try代码块中写入可能会出现异常的代码
        //在catch中写入需要捕获的异常类型(仅捕获指定类型的),以及处理异常的代码
        try{
            //先写个算术异常
            int a=5;
            int b=0;
            System.out.println(a/b);

            //由于前面出现异常;不会进入下一个代码
            System.out.println("下一个代码");
        }
        //捕获算术异常
        catch (ArithmeticException a){
            System.out.println("除数不能为0");
        }
        //后面的程序
        //处理完异常后,继续运行后面的程序
        System.out.println("后面的程序");
    }
}

输出:

除数不能为0
后面的程序

出现两个异常时;

public class Demo003 {
    public static void main(String[] args) {
        //在try代码块中写入可能会出现异常的代码
        //在catch中写入需要捕获的异常类型(仅捕获指定类型的),以及处理异常的代码
        try{
            //写第二个异常:数组越界异常
            int[] array={0,1,3,4};
            array[5]=0;
            //发现后面的代码块不运行了;

            //先写个算术异常
            int a=5;
            int b=0;
            System.out.println(a/b);

            //由于前面出现异常;不会进入下一个代码
            System.out.println("下一个代码");
        }
        //负责捕获数组越界异常;
        catch(ArrayIndexOutOfBoundsException array){
            System.out.println("数组下标越界了");
        }
        //负责捕获算术异常
        catch (ArithmeticException a){
            System.out.println("除数不能为0");
        }
        //后面的程序
        //处理完异常后,继续运行后面的程序
        System.out.println("后面的程序");
    }
}

发现处理完数组下标越界异常后,不再执行算术异常代码以及下一个代码了;处理完数组下标越界异常后直接进入运行之后的程序;

数组下标越界了
后面的程序

如果一开始不确定代码会出现什么异常,可以直接写个捕获Exception类型的异常;

catch(Exception e){
            System.out.println("代码有异常");
        }

注意:一个try代码块中可以写多个可出现异常的代码;可以对应多个catch代码块,异常类型越大的放在子类的下面.


异常处理中的几个常用方法:


在catch语句处理时,通常需要两种处理;

处理1:给用户的提示;

处理2:程序开发人员需要看到的信息;打印输出异常信息;记录日志,后续会学到通过一些日志组件将异常信息写到文件中去;

  • public String getMessage( )
    返回此throwable的详细消息字符串。
public class Demo004 {
    public static void main(String[] args) {
        //在try代码块中写入可能会出现异常的代码
        //在catch中写入需要捕获的异常类型(仅捕获指定类型的),以及处理异常的代码
        try{
            //写个算术异常
            int a=5;
            int b=0;
            System.out.println(a/b);
        }
        //如果一开始不确定代码会出现什么异常,可以直接写个捕获Exception类型的异常;
        catch(Exception e){
            //给用户的提示
            System.out.println("提示:代码有异常");
            //程序开发人员需要看到的信息
            //public String getMessage()
            // 返回此throwable的详细消息字符串。 
            System.out.println(e.getMessage());
        }
        //后面的程序
        //处理完异常后,继续运行后面的程序
        System.out.println("后面的程序");
    }
}

输出:

提示:代码有异常
/ by zero
后面的程序
  • public void printStackTrace()
    打印输出异常信息到控制台;
public class Demo004 {
    public static void main(String[] args) {
        //在try代码块中写入可能会出现异常的代码
        //在catch中写入需要捕获的异常类型(仅捕获指定类型的),以及处理异常的代码
        try{
            //写个算术异常
            int a=5;
            int b=0;
            System.out.println(a/b);
        }
        //如果一开始不确定代码会出现什么异常,可以直接写个捕获Exception类型的异常;
        catch(Exception e){
            //给用户的提示
            System.out.println("提示:代码有异常");
            //程序开发人员需要看到的信息
            //public void printStackTrace()
            //打印输出异常信息
           e.printStackTrace();
        }
        //后面的程序
        //处理完异常后,继续运行后面的程序
        System.out.println("后面的程序");
    }
}

输出:

提示:代码有异常
后面的程序
java.lang.ArithmeticException: / by zero
	at com.ff.javaException01.Demo004.main(Demo004.java:15)
  • public void printStackTrace(java.io.PrintWriter s)
    将异常信息的日志文件打印到文件中去;
    (在之后的IO流中会讲解到)
public class Demo004 {
    public static void main(String[] args) throws FileNotFoundException {
        //在try代码块中写入可能会出现异常的代码
        //在catch中写入需要捕获的异常类型(仅捕获指定类型的),以及处理异常的代码
        try{
            //写个算术异常
            int a=5;
            int b=0;
            System.out.println(a/b);
        }
        //如果一开始不确定代码会出现什么异常,可以直接写个捕获Exception类型的异常;
        catch(Exception e){
            //给用户的提示
            System.out.println("提示:代码有异常");
            
            //public void printStackTrace(java.io.PrintWriter s)
            //将异常信息的日志文件打印到文件中去;
            PrintWriter p=new PrintWriter("F:\\\\exception.txt");
            e.printStackTrace(p);
            p.flush();
            p.close();//关闭 流

        }
        //后面的程序
        //处理完异常后,继续运行后面的程序
        System.out.println("后面的程序");
    }
}

我们会发现控制台并没有变化,实际上它将异常信息存储到了文件中;

在这里插入图片描述

这些方法来自Throwable类;而不是Exception类;


finally块


无论程序是否有异常;finally块中的内容总是会执行的,只能有一个finally语句;

  • (1)可以不写catch语句;只写try和finally;程序异常时,执行finally后抛出异常信息,后面的程序不再执行;
public class Demo005 {
    public static void main(String[] args) {
        //可以不写catch语句
        //只写try和finally;
        // 程序异常时,执行finally后,后面的程序不再执行
        try{
            //写第二个异常:数组越界异常
            int[] array={0,1,3,4};
            array[5]=0;
        }
       finally {
            System.out.println("finally块中一定执行");
        }
        //后面的程序;不会执行
        System.out.println("后面的程序");
    }
}

输出:

finally块中一定执行
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 5
	at com.ff.javaException01.Demo005.main(Demo005.java:13)


  • (2)在try语句和catch语句中写return,finally块中的内容也会执行,且优先执行输出;
    例如:
    首先给该测试方法,输入a=5,b=0;即出现算术异常的状况;输出时发现:优先执行finally块内容,再返回catch中的-1;
public class Demo006 {
    public static void main(String[] args) {
        Demo006 d=new Demo006();
        System.out.println(d.test(5,0));
    }
    //写个方法
    public int test(int a,int b){
        try{
            return a/b;
        }
        catch (ArithmeticException num){
            //程序异常时,返回-1;
            return -1;
        }
        finally{
            System.out.println("finally语句执行");
        }
    }
}

输出时发现:优先执行finally块内容,再返回catch中的-1;

finally语句执行
-1

给该测试方法,输入a=5,b=1;即正常的状况;输出时发现:优先执行finally块内容,再返回try中的计算得到5;
finally语句执行
5


  • (3)在try语句和catch语句中写return,finally块中也写return;不论程序是否异常,直接优先执行finally块中的内容,并且返回值为finally块中的return内容;不执行输出try或者catch中的return;
    在上个程序中的finally块中加入返回值return;
finally{
            System.out.println("finally语句执行");
            return 100;
        }

输出:

finally语句执行
100


throws 声明异常


throws声明方法中可能会出现的某种异常;

例如: public void test throws 异常1,异常2,异常3{......}

(1)throws声明异常大多数是编译期(检查期)的异常;如果是声明的是运行期异常,在方法调用处可处理,也可不处理; 如果声明的是编译期异常,在方法调用处,要么声明给调用它的上一级;要么try catch处理;
(2)对于编译期的异常可以throws声明,也可以直接try, catch直接处理;
(3)在调用声明了异常的方法时,可以选择try,catch处理异常,或者选择throws声明给调用它的上级;
(4)在实际应用中,不要在main方法中进行throws声明,没有实际效果;
(5) 一个方法中可以声明多个异常;

import java.io.FileNotFoundException;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
//throws关键字
//throws声明方法中可能会出现的某种异常,作为方法的声明,调用时需要注意
public class Demo007throw {
    public static void main(String[] args) {
       //实际中;在main方法中就要处理异常了;而不是再次throws声明;
       //这里就需要处理异常了;
        Demo007throw d=new Demo007throw();
        try {
            d.test1();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
    //方法1,调用方法2;它进行了声明,传给了调用它的方法;
    public void test1() throws FileNotFoundException, UnsupportedEncodingException {
        test2();
    }
    //方法2中声明异常,
    public void  test2() throws FileNotFoundException, UnsupportedEncodingException {
        //两个异常代码:
        PrintWriter p=new PrintWriter("W:\\\\exception.txt");
        "我".getBytes("utf-8");
    }
}

(6)在抽象方法中也可以使用throws声明,即便它没有方法体;在子类继承抽象类时,重写声明了抽象方法;那么这时,子类重写的方法中声明的异常类型需要小于等于父类的异常类型
例如:
父类

public abstract class Demo008 {
    //抽象方法,这里声明Exception异常
    public abstract void test3() throws Exception;
}

子类:

//子类
public class Demo008extend extends Demo008{

    //重写test3方法
    //且ArrayIndexOutOfBoundsException小于父类中的Exception
    @Override
    public void test3() throws ArrayIndexOutOfBoundsException {
        int[]a= {0,1,2,3};
        a[2]=2;
    }
}

throw 抛出异常


•throw关键字用于显式抛出异常,抛出的是一个异常类的实例化对象.

•在异常处理中,try语句要捕获的是一个异常对象,那么此异常对象也可以自己抛出。

​ 如: throw new RunTimeException( );

public class Demo001 {
    public static void main(String[] args) {
        test4(0);
    }
    public static void test4(int a) {
        if (a==0) {
            throw new RuntimeException("a不能为0");
            //异常被抛出,后面的代码不再执行
        }
        if(a==1){
            a+=2;
            System.out.println(a);
        }else
            System.out.println(a);
    }
}

这里传入a=0;抛出异常;

Exception in thread "main" java.lang.RuntimeException: a不能为0
	at com.ff.javaException01.throw01.Demo001.test4(Demo001.java:12)
	at com.ff.javaException01.throw01.Demo001.main(Demo001.java:8)

对于这样不做处理的运行期异常;输出的是由虚拟机在运行时出现的异常,抛出此类的对象;

public class Demo02 {
    public static void main(String[] args) {
        System.out.println(10/0);
    }
}
//由虚拟机在运行时出现的异常,抛出此类的对象;
/*
    Exception in thread "main" java.lang.ArithmeticException: / by zero
        at com.ff.day10.javaexception.start04throw.Demo02.main(Demo02.java:8)
*/

throws与throw的区别


•throw用于方法体中,用来抛出一个实际的异常对象,使用throw后,要么使用try ,catch捕获异常,要么使用throws声明异常

•throws用于方法声明,用来声明该方法可能发生的异常类型,可以是多个异常类型,用来强制调用该方法时处理这些异常

•抽象方法也可以使用throws


自定义异常


自定义异常就是自己定义的异常类,也就是API中的标准异常类的直接或间接的子类;
实际应用中根据需要,进行自定义;使得异常提示更精确;

  • 自定义异常类中往往不写其他方法,只重载需要使用的构造方法;

  • 继承Exception,在方法中使用throw抛出后,必须在方法中try-catch或throws抛出;

例如自定义一个成绩输入异常类;

//成绩输入异常类
public class GradeException extends Exception{
    //构造方法
    public GradeException() {
    }

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

使用它:

public class Demo002 {
    public static void main(String[] args) {
        Demo002 d=new Demo002();
        //使用try,catch处理异常
        try {
            d.grade(200);
        } catch (GradeException e) {
            e.printStackTrace();
        }
    }
    //一个判断成绩的方法;声明了异常
    public String grade(int num) throws GradeException {
        if(num>150){
            //抛出成绩输入异常
            throw new GradeException("成绩输入异常!");
        }
        if(num==100){
            return "好!";
        }else
            return "不行!";
    }
}

输出:

com.ff.javaException01.throw01.GradeException: 成绩输入异常!
	at com.以上是关于JavaSE基础七----<异常>常见的异常,异常处理机制,自定义异常的主要内容,如果未能解决你的问题,请参考以下文章

JavaSE基础——异常机制

JavaSE系列Java异常处理机制

JavaSE(基础篇)——异常机制

python基础七--异常处理

javase基础11

JavaSE知识-19(异常&IO(File类))