Java学习笔记——异常exception

概念

Java中的异常指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止。它并不是语法错误或者逻辑错误。
Java程序在执行过程中所发生的异常事件可分为两类:

  • Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。比如:StackOverflowError(栈溢出)和OOM(内存溢出)。一般不编写针对性的代码进行处理。
  • Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。例如:
    空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界等。Exception又分为两大类:运行时异常[程序运行时,发生的异常]和编译时异常[编程时,编译器检查出的异常]。

异常的体系结构

在这里插入图片描述
Error和Exception的共同父类是Throwable类。
广义上的异常分为Error和Exception,而我们常说的是狭义上的异常Exception
广义上的异常分为检查异常(checked exception)非检查异常(unchecked exception),其中的检查和非检查是对于javac而言的。

  • 检查异常就是编译器要求必须处理(catch或抛出)的异常,编译器在编译时会对这类异常进行检查,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。除了运行时异常和Error的都是检查异常(也即编译异常都是检查异常),如SQLException , IOException,ClassNotFoundException 等。
  • 非检查异常即编译器不要求强制处置的异常,虽然可能出现错误,但是编译器不会在编译的时候检查。 包括RuntimeException与其子类,以及错误(Error),对于这类异常,我们一般都进行修改代码来进行处理,因为这样的异常发生的原因多半是代码写的有问题。当然也可以进行catch或抛出,或干脆不处理

运行时异常(RuntimeException)

运行时异常编译器检查不出来。一般是指编程时的逻辑错误,是程序员应该避免其出现的java.lang.RuntimeException类及它的子类都是运行时异常,常见的运行时异常包括:

  1. NullPointerException 空指针异常,当应用程序试图在需要对象的地方使用 null 时,抛出该异常
package com.dhw.exception;

public class NullPointerExceptionTest {
    //当应用程序试图在需要对象的地方使用 null 时,抛出该异常
    public static void main(String[] args) {
        String str = null;
        System.out.println(fun(str));
    }
    static int fun(String str){
        return str.length();
    }
}
  1. ArithmeticException 数学运算异常,当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例,注意:浮点类型(float和double)除以0时不会抛出异常,因为Java的浮点运算是基于 IEEE-754 标准
package com.dhw.exception;

public class ArithmeticExceptionTest {
    //当出现异常的运算条件时,抛出此异常。例如,一个整数除以零时,抛出此类的一个实例
    //注意:浮点类型(float和double)除以0时不会抛出异常,因为Java的浮点运算是基于 IEEE-754 标准
    public static void main(String[] args) {
        int num1 = 1;
        double num2 = 2.0;
        float num3 = 3.0f;
        System.out.println(num3 / 0);
        System.out.println(num2 / 0);
        System.out.println(num1 / 0);
    }
}

  1. ArrayIndexOutOfBoundsException 数组下标越界异常,非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引
  2. ClassCastException 类型转换异常,当试图将对象强制转换为不是其运行类型或其父类的类型时,抛出该异常。
package com.dhw.exception;
//当试图将对象强制转换为不是其运行类型或其父类的类型时,抛出该异常。
public class ClassCastExceptionTest {
    public static void main(String[] args) {
        A i1 = new C();
        B i2 = (B)i1;   // 正确,因为对象实例是C,编译类型是A,
                        // 这么向下转型后相当于把编译类型变为B,运行类型还是C,而B是C的父类,还是一个向上转型,
                        // 只是把他的编译类型向下转了一级
        C i3 = (C)i2;

        A i4 = new B();
        C i5 = (C) i4;// 抛出异常,运行类型是B,不能把他强转为C类型,因为C类型不是B类或其父类
    }
}
class A{}
class B extends A{}
class C extends B{}

  1. NumberFormatException 数字格式不正确异常,当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常
package com.dhw.exception;

public class NumberFormatExceptionTest {
    //当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常
    public static void main(String[] args) {
        System.out.println(Boolean.parseBoolean("abc"));//输出false
        String name = "abc";
        //将 String 转成 int
        //使用异常我们可以确保输入是数字
        int num = Integer.parseInt(name);//抛出 NumberFormatException
        System.out.println(num);//
    }
}

编译时异常

RuntimeException以外的异常就是编译时异常,类型上都属于Exception类及其子类。必须进行处理,否则程序就不能编译通过。常见的编译时异常包括:

  • SQLException//操作数据库时,查询表可能发生异常
  • IOException//操作文件时,发生的异常
  • FileNotFoundException//当操作一个不存在的文件时,发生异常
  • ClassNotFoundException//加载类,而该类不存在时,异常
  • EOFException//操作文件,到文件未尾,发生异常
  • lllegalArguementException //参数异常

异常处理

Java中可以使用异常处理机制来解决异常的问题,防止程序因异常而退出,从而保证程序的健壮性
处理异常主要有以下两种方式:

  • 1、使用try…catch…finally语句块捕获异常并处理
  • 2、在函数签名中使用throws 声明,将异常抛出,交给函数调用者caller去解决,最顶级的处理者是jvm,它处理的方式是打印异常信息并退出程序

try-catch处理方式

语法:

        try{
            //可能产生异常的代码
        }
        catch( ExceptionName1 e ){
             //当产生ExceptionName1型异常时的处置措施
        }
        catch( ExceptionName2 e ){
            //当产生ExceptionName2型异常时的处置措施
        }
        ......
        [ finally{
             //无论是否发生异常,都无条件执行的语句,比如关闭连接,释放资源等
             //没有finally块,语法也能通过
        } ]

try-catch处理规则:

  1. 如果没有出现异常,则执行try块中所有语句,不执行catch块中语句,如果有finally,最后还需要执行finally里面的语句
  2. 如果出现异常,则try块中异常发生后,try块剩下的语句不再执行。将执行catch块中的语句,如果有finally,最后还需要执行finally里面的语句
try-catch-finally处理异常中关于return的问题(重要,难点)
package com.dhw.exception;

public class TryCatchFinallyDetail {
    public static void main(String[] args) {
        System.out.println(fun());
    }
    static int fun(){
        int i = 1;
        i++;
        try {
            String[] names = new String[3];
            if (names[1].equals("tom")){//空指针异常 抛出NullPointerException
                System.out.println(names[1]);
            }else {
                names[1]="tom";}
            return 1;
        } catch (ArrayIndexOutOfBoundsException e) {
            return 2;
        } catch (NullPointerException e) {
            return i++;//i=2 在return前会使用一个临时变量保存return的值 temp = i (i = 2)
                       //return前必须执行finally块
                       //执行完finally块中的语句后再返回保存的临时变量temp=2
        } finally {
            ++i; //i=4
            System.out.println("i=" +i);//i=4
        }
    }
}

使用try-catch-finally处理异常时,若出现异常,且catch块中有return语句,会

  1. 先执行return语句中的运算,再将运算完后的值保存在一个临时变量temp中
  2. 随后执行finally块中的语句,若finally块中有return语句,则在finally块中return
  3. 若finally块中没有return语句,则返回到catch块中返回,return的值为临时变量temp的值

注意:若finally块和catch块中都无return语句,会出现error提示,过不了编译(throw语句和return类似处理)

throws处理方式

概念:
如果一个方法中可能产生某种异常,但是并不能确定如何处理这种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者(也是方法)负责处理。
在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。

public void method() throws ArithmeticException, NullPointerException{
	//函数体
}

细节:

  1. 对于运行时异常,程序中如果没有处理,默认就是 throws
  2. 子类重写的方法,所抛出的异常类型要么和父类抛出的异常一致,要么为父类抛出的异常类型的子类型
  3. 如果有 try-catch,相当于处理异常,就可以不必 throws
  4. 如果方法抛出了编译异常,则该方法的调用者必须进行处理,否则报错,因为编译异常是必须要处理的
package com.dhw.exception;

import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class ThrowsTest {
    public static void main(String[] args) throws FileNotFoundException {
        //如果方法抛出了编译异常,则该方法的调用者必须进行处理,否则报错,因为编译异常是必须要处理的
        fun2();
    }
    public static void fun1(){
        int num1 = 0;
        int num2 = 1;
        int num3 = num2/num1;
        //对于运行时异常,程序中如果没有处理,默认就是 throws
        //一直throws到JVM中,JVM的处理方式就是打印错误并终止程序
    }
    public static void fun2() throws FileNotFoundException {
        fun1();
        FileInputStream fis = new FileInputStream("d://aa.txt");
    }
}

自定义异常

在Java中还可以自己设计异常类,用于描述程序中的错误信息。只需自定义一个异常类,继承Exception或RuntimeException即可。如果继承Exception,属于编译异常;如果继承RuntimeException,属于运行异常(一般来说,继承RuntimeException)

package com.dhw.exception;

public class CustomException {
    public static void main(String[] args) {
        int age = 180;
        if(!(age >= 18 && age <= 120)) {
            //使用throw生成一个异常,这个异常是RuntimeException,默认作抛出处理
            //这里我们可以通过构造器,设置信息

            throw new AgeException("年龄需要在 18~120 之间");
            //throw new AgeException();

            //抛出的Excption类型的异常是编译异常
            //throw new Exception();
        }
        System.out.println("你的年龄范围正确.");
    }
}
class AgeException extends RuntimeException {
    public AgeException(String message) {//构造器
        super(message);
    }
    public AgeException(){}
}

throws和throw的区别

图源自韩顺平

图源自韩顺平《循序渐进学Java》

代码示例(重要)

package com.dhw.exception;

public class ExceptionExercise {
    static void funA(){
        try {
            System.out.println("funA");
            //手动抛出这个异常,抛出异常后方法会在这里终止,但终止之前要执行finally语句
            throw new RuntimeException("funA create an exception!");
            //System.out.println("test");
        }finally {
            System.out.println("use funA finally");
        }
    }
    static void funB(){
        try {
            System.out.println("funB");
        }finally {
            System.out.println("use funB finally");
        }
    }

    public static void main(String[] args) {
        try {
            funA();
        }catch (Exception e){//捕获到手动throw出的异常,进行处理,将其信息打印出来
            System.out.println(e.getMessage());
        }
        funB();
    }
}

可以用throw来手动制造一个异常。

其他Java笔记:Java学习笔记——内部类(InnerClass)浅析 Java学习笔记——多态(polymorphic)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值