第七篇 Java异常处理

目录

一、异常概述

1.Error

2.Exception

✒️编译时异常(checked)

✒️运行时异常(unchecked)

二、异常处理(Exception Handing)

1.异常的处理方式一:try-catch-finally

2.异常的处理方式二:throws+异常类型

三、手动抛出异常throw

四、自定义异常类

1.如何自定义一个异常类

2.自定义并使用异常类demo1

五、练习

1.throw和throws的区别?

2.总结编程练习


参考书籍:《Java核心技术(卷I)基础知识(原书第9版)》、《Java编程思想(第4版)》

学习视频:尚硅谷Java入门视频教程(B站,主讲:宋红康老师)  

一、异常概述

        (从C到Java的理解)在C中,调用一些函数时,如:open、read…,通常会返回某个特殊值或者标志,我们需要对该返回值进行检查,以判断执行过程是否发生错误。多次的if判断语句会使代码可读性变差,难以维护。对于构造大型、健壮、可维护的程序而言,这种错误处理已经成为主要障碍。

        在《Java编程思想(第4版)》中所述,Java的基本理念是:结构不佳的代码不能运行,它的异常处理机制建立在C++的基础之上(虽然看上去更像Object Pascal)。异常处理是Java中唯一正式的错误报告机制,异常处理的目的在于通过使用少于目前数量的代码来简化大型程序。也就是说,按照写C的习惯,可以理解为,Java异常处理就是C的函数执行错误判断。

        在Java中,将程序执行中发生的不正确情况称为"异常"(注意:程序语法错误和逻辑错误不是异常)Java在执行过程的异常可分为两类:①Error、②Exception 下图是Java异常类的体系

1.Error

        Java虚拟机无法解决的严重问题。 如: JVM系统内部错误、 资源耗尽等严重情况。 比如:栈溢出(java.lang.StackOverflowError)、堆溢出(OOM-OutOfMemoryError)。一般不编写代码进行处理。

public class ErrorTest {
    public static void main(String[] args) {
        /* 栈溢出 java.lang.StackOverflowError */
        main(args);
        /* 堆溢出 java.lang.OutOfMemoryError */
        int[] arr = new int[1024*1024*1024];
    }
}

2.Exception

        其它因编程错误或偶然的外在因素导致的一般性问题, 可以使用针对性的代码进行处理。 例如:空指针访问、读取文件不存在、网络连接中断、数组角标越界等。 分类:①编译时异常(受检异常 - checked)、运行时异常(非受检异常 - unchecked 也就是 RuntimeException)

✒️编译时异常(checked)

        例如:IOException、FileNotFoundException、ClassNotFoundException

package com.exception.java;

import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.FileInputStream;

public class ErrorTest {
    /* 编译时异常 */
    @Test
    public void test1(){
        File file = new File("test.txt");//文件不存在编译时出现异常
        FileInputStream fis = new FileInputStream(file);
        int data = fis.read();
        while (data != -1){
            System.out.println(data);
            data = fis.read();
        }
        fis.close();
    }
}

✒️运行时异常(unchecked)

        例如:NullPointerExcepition、ArrayIndexOutOfBoundsException、ClassCastException、 NumberFormatException、InputMismatchException、ArithmeticException

package com.exception.java;

import org.junit.jupiter.api.Test;
import java.util.Date;
import java.util.Scanner;

public class ExceptionTest {
    /***********运行时异常*************/
    /* NullPointerException(空指针异常) */
    @Test
    public void test1(){
        int []arr = null;
        System.out.println(arr[1]);
    }
    /* ArrayIndexOutOfBoundsExcepiton */
    /* StringIndexOutOfBoundsException */
    @Test
    public void test2(){
        int []arr = new int[10];
        System.out.println(arr[10]);
        String str = "abcd";
        System.out.println(str.charAt(4));
    }
    /* ClassCastException(类型转换异常) */
    @Test
    public void test3(){
        Object obj = new Date();
        String str = (String)obj;
    }
    /* NumberFormatException */
    @Test
    public void test4(){
        String str = "abc";
        int num = Integer.parseInt(str);
        System.out.println(num);
    }
    /* InputMismatchException */
    @Test
    public void test5(){
        Scanner scaner = new Scanner(System.in);
        int data = scaner.nextInt();
        System.out.println(data);
        scaner.close();
    }
}

二、异常处理(Exception Handing)

1.异常的处理方式一:try-catch-finally

package com.exception.java;

import org.junit.jupiter.api.Test;
public class ExceptionHanding {
    @Test
    /* NumberFormatException */
    public void test1(){
        try {
            String str = "abc";
            int num = Integer.parseInt(str);
            System.out.println("----Test Line 1-----");	//一旦异常,后面代码不执行
        }catch (NumberFormatException e){		//先捕获NumberFormatException
            System.out.println(e.getMessage());
        }catch (IllegalArgumentException e){	//再捕获IllegalAugumantException
            System.out.println(e.getMessage());	//顺序不可调换
        }finally {
            System.out.println("-----Test Line 2----");//一定会执行的代码放finally里
        }
        System.out.println("-----Test Line 3------");//处理完异常继续执行后续代码
    }
}

📓如果在try语句块中任何代码抛出了一个再catch子句中声明的异常类,那么:

(1)程序将跳过try语句块的其余代码

(2)程序将执行对应异常类的catch子句的代码

如果try语句块中的任何代码没有抛出异常,那么程序将跳过catch子句。

如果方法中的任何代码抛出了一个在catch子句中没有声明的异常类型,那么这个方法会立即退出

(希望调用者为这种类型的异常设计catch子句)

              —— 来自于《Java核心技术(卷I)基础知识(原书第9版)》

📓catch多个异常时,异常类型没有父子类关系,无catch顺序;否则需要先catch子类异常,再catch父类异常,不然编译会报错。

📓catch中两个常用的方法:①String getMessage( ) 、②void printStackTrace( )

📓不管是否有异常被捕获,finally语句中的代码一定会被执行。数据库连接、I/O流、Socket等资源JVM无法自动回收,必须手动进行资源释放,这部分代码通常放置于finally中。

📓另外,还需要注意以下两点 

(1)try-catch-finall处理编译时异常,使程序编译不报错,但是运行时仍可能出错。相当于使用try-catch-finally将一个编译时异常延迟到运行时出现。

(2)实际开发中,由于运行时异常比较常见,通常不针对运行时异常使用try-catch-finall处理。而编译时异常,则一定要考虑异常的处理。

package com.exception.java;

import org.junit.jupiter.api.Test;
public class ExceptionHanding {
    @Test
    /* finally中的代码一定会被执行 */
    /* 无论catch完异常return或者又出现异常,还是没有异常return的情况,finally都会执行 */
    public void test2(){
        int retn = test2();
        System.out.println(retn);
    }
    public int test1(){
        try {
            int a = 10;
            int b = 0;  //ArithmeticException算数异常
            System.out.println(a/b);
            return 1;
        }catch(ArithmeticException e){
            System.out.println(e.getMessage());
            int[] arr = new int[10];    //ArrayIndexOutOfBoundsException
            System.out.println(arr[15]);
            return 2;
        }finally {
            System.out.println("finally");
            return 3;
        }
    }
}

PS:IDEA处理异常快捷键Ctrl + Alt + T

2.异常的处理方式二:throws+异常类型

📓throws将异常抛出给方法的调用者,并没有真正处理异常("谁调谁处理")

📓子类重写的方法抛出的异常不大于(小于)父类被重写方法抛出的异常类型

package com.exception.java;

import org.junit.jupiter.api.Test;
import java.io.FileNotFoundException;
import java.io.IOException;

public class ThrowsTest {
    @Test
    /* 子类重写父类方法时,抛出异常需小于父类方法 */
    public static void main(String[] args) {
        ThrowsTest test = new ThrowsTest();
        test.method2(new subClass());
    }
    public static void method2(superClass s){
        try {
            s.method1();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /* 子类重写父类的method1(),抛出异常必须小于父类抛出的,小到可以没有异常 */
    /* 如果子类抛出异常类型大于父类,在method2中无法catch */
    /* 如果父类方法没有抛出异常,子类重写该方法是不允许抛出异常 */
    static class subClass extends superClass{
        public void method1() throws FileNotFoundException{
            System.out.println("subClass");
        }
    }
    static class superClass{
        public void method1()throws IOException{
            System.out.println("superClass");
        }
    }
}

三、手动抛出异常throw

package com.exception.java;

import org.junit.jupiter.api.Test;
import java.util.Scanner;

public class ThrowTest {
    public static void main(String[] args) {
        try{
            Student stu = new Student();
            Scanner scan = new Scanner(System.in);
            stu.register(scan.nextInt());
            System.out.println(stu.id);
        }catch(Exception e) {
            System.out.println(e.getMessage());
        }
    }
    /* 类(结构体) */
    static class Student{
        private int id; //成员变量(结构体成员)
        /* 方法(函数指针)*/
        public void register(int id)throws Exception{
            if(id > 0){
                this.id = id;
            }else{
                /* 手动抛出throw编译时异常Exception,处理方式是throws*/
                throw new Exception("Illegal ID");
            }
        }
    }
}

四、自定义异常类

1.如何自定义一个异常类

(1)定义一个继承于现有的异常结构(RuntimeException、Exception)的类

(2)提供全局常量serialVersionUID

(3)提供重载构造器

2.自定义并使用异常类demo1

package com.exception.java;
import java.util.Scanner;

public class ThrowTest {
    public static void main(String[] args) {
        try{
            Student stu = new Student();
            Scanner scan = new Scanner(System.in);
            stu.register(scan.nextInt());
            System.out.println(stu.id);
        }catch(MyException e) {
            System.out.println(e.getMessage());
        }
    }
    static class Student{
        private int id;
        public void register(int id)throws MyException{
            if(id > 0){
                this.id = id;
            }else{
                /* 手动抛出throw编译时异常Exception,处理方式是throws*/
                throw new MyException("Illegal ID");
            }
        }
    }
    /* 自定义异常类MyException继承于RuntimeException */
    public static class MyException extends RuntimeException{
        static final long serialVersionUID = -7034897193246939L;
        public MyException(){

        }
        public MyException(String msg){
            super(msg);
        }
    }
}

五、练习

1.throw和throws的区别?

✒️答:throw语句用来明确地抛出一个"异常"。

        throws用来标明一个成员函数可能抛出的各种"异常"。

        Finally为确保一段代码不管发生什么"异常"都被执行一段代码。

        一般我们在一个成员函数调用的外面写一个try语句,在这个成员函数内部写另一个try语句保护其他代码。每当遇到一个try语句,"异常"的框架就放到堆栈上面,直到所有的try语句都完成。如果下一级的try语句没有对某种"异常"进行处理,堆栈就会展开,直到遇到有处理这种"异常"的try语句。

2.总结编程练习

        编写应用程序Summary.java, 接收命令行的两个参数, 要求不能输入负数, 计算两数相除。对数据类型不一致(NumberFormatException)、 缺少命令行参数(ArrayIndexOutOfBoundsException)、被除数为零(ArithmeticException)及输入负数(DevideBynegativeNumber 自定义的异常)进行异常处理。提示:

​(1)在主类(Summary)中定义异常方法(Divede)完成两数相除功能。 ​

(2)在main()方法中使用异常处理语句进行异常处理。 ​

(3)在程序中,自定义对应输入负数的异常类(DevideBynegativeNumber)。 ​

(4)运行时接受参数 java Summary 20 10 //args[0]=“20” args[1]=“10” ​

(5)Interger类的static方法parseInt(String s)将s转换成对应的int值。 ​

如: int a=Interger.parseInt(“314”); //a=314;

public class Summary {
    public static void main(String[] args) {
        try{
            int data1 = Integer.parseInt(args[0]);
            int data2 = Integer.parseInt(args[1]);
            int result = Divede(data1,data2);
            System.out.println(result);
        }catch(DivedeByNegativeNumException e){
            System.out.println(e.getMessage());
        }catch(NumberFormatException e){
            e.printStackTrace();
        }catch(ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
        }catch(ArithmeticException e){
            e.printStackTrace();
        }
    }
    public static int Divede(int data1,int data2) throws DivedeByNegativeNumException{
        if(data1 < 0 || data2 < 0){
            throw new DivedeByNegativeNumException("Divede by negative number");
        }
        return data1 / data2;
    }
    /* 自定义异常类DivedeByNegativeNumException */
    public static class DivedeByNegativeNumException extends Exception {
        static final long serialVersionUID = -70397193246939L;
        public DivedeByNegativeNumException(){}
        public DivedeByNegativeNumException(String msg){
            super(msg);
        }
    }
}

 

 说明:由于笔者水平有限,文中难以避免有所错漏,敬请各读者斧正

版权声明:转载请附上原文出处链接及本声明。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

AF_INET6

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

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

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

打赏作者

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

抵扣说明:

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

余额充值