Java学习笔记(六)


学习内容来自于mooc中华东师范大学课程Java核心技术。

Java异常分类

Java 异常是指程序执行中发生的不正常行为,语法错误和逻辑错误不算异常

Throwable是所有错误的祖先
Java异常分为两大类:Error 和 Exception

Error

系统内部错误或资源耗尽,不常见,可以不处理。

Exception

程序有关的异常。分成两大类:RuntimeException、非RuntimeException

  • RuntimeException:程序自身的错误,例如空指针、数组越界。这个又叫运行时异常
  • 非RuntimeException:外界相关的错误,例如打开不存在的文件、加载不存在的类。这个又叫编译时异常

Checked和Unchecked

Checked和Unchecked也是一种错误的分类方法。

  • Unchecked Exception: 编译器不会辅助检查的,需要自己注意的异常。包括Error和RuntimeException子类。如 a/b,b 可能为0,而编译器不会辅助检查。以预防为主。
  • Checked Exception:非RuntimeException的子类,编译器会辅助检查。程序员必需处理,以发生后处理为主。
    这个是

Java异常分类重点:

  • 异常是程序不正常的行为或者状态
  • Java异常分为 Exception(程序相关)和 Error(系统相关)
  • Java程序相关异常又分为 unchecked(编译器不辅助检查)和 checked(编译器辅助检查)

Java异常处理

异常处理的目的:

  • 允许用户及时保存结果
  • 抓住异常,分析异常行程原因
  • 控制程序返回到安全状态

try-catch-finally机制

有三种结构:

  • try…catch(catch可以有多个,下同)
  • try…catch…finally
  • try…finally 。应用场景:执行一段代码,不管是否发生异常,都必须执行某个业务逻辑。但是如果,执行过程中发生异常,在 finally 执行结束后会直接退出程序

try必须有,catch和finally至少有一个

每种结构体的内容为:

  • try:正常的业务逻辑内容
  • catch:当 try 发生异常时,执行 catch;否则,不执行
  • finally:当 try 或 catch 发生异常后,必须执行 finally

例子:

public static void main(String[] args) {
        try {
            // 都正确,不会执行catch
            int a = 5/2;
            System.out.println(a);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        finally {
            System.out.println("Section 1 is over.");
        }

        try {
            // 不正确,会执行catch
            int a = 5/0;
            // 出现异常,不执行
            System.out.println(a);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        finally {
            System.out.println("Section 2 is over.");
        }

        try {
            // 不正确,会执行catch
            int a = 5/0;
            // 出现异常,不执行
            System.out.println(a);
        }
        catch(Exception e) {
            e.printStackTrace();
            // 会执行,在finally之后报错
            int a = 5/0;
        }
        finally {
            System.out.println("Section 3 is over.");
        }
    }

执行结果:

2
Section 1 is over.
java.lang.ArithmeticException: / by zero
        at courseTest.TryDemo.main(TryDemo.java:20)
Section 2 is over.
java.lang.ArithmeticException: / by zero
        at courseTest.TryDemo.main(TryDemo.java:33)
Section 3 is over.
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at courseTest.TryDemo.main(TryDemo.java:40)

注意:

  • 异常发生时,在 try 语句块中异常后面的代码不会执行
  • 异常未发生,则不会进入 catch 块
  • 不管是否发生异常,finally 块一定执行

多个catch

catch块可以有多个,每个拥有不同的入口形参。当发生的异常和某一个catch形参类型一致,那么将会执行该块内容;若异常和所有的catch形参都不同,那么将会跳过catch部分。
同样的,catch块执行后,将不会跳回到try发生异常的位置,而是去执行下一部分。

catch块的匹配机制和 if 语句相同,都是从上到下匹配。一般将小的异常类型(精准,如具体的异常子类)写在前面,大二宽泛的异常写在后面。
如果不按照这个规则写的话,因为精准的小的异常在大而宽泛的异常之后,该catch块并不会执行,会编译报错Unreachable catch block for ArithmeticException. It is already handled by the catch block for ExceptionJava(553648315)

例子:

public class MultiCatchDemo {
    public static void main(String[] args) {
        try {
            int a = 5/0;
            System.out.println("a is: " + a);
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        // 这样是不对的,ArithmeticException是小异常,应该卸载Exception前面
        catch(ArithmeticException e) {
            e.printStackTrace();
        }
        finally {
            System.out.println("Section 1 is over");
        }
    }
}

更改为:

 public static void main(String[] args) {
        try {
            int a = 5/0;
            System.out.println("a is: " + a);
        }
        catch(ArithmeticException e) {
            e.printStackTrace();
        }
        catch(Exception e) {
            e.printStackTrace();
        }
        finally {
            System.out.println("Section 1 is over");
        }
    }

注意,在try-catch-finally机制中,若有finally,则finally一定被执行。

同时,try-catch-finally每个模块也可能发生异常,可以再其中再套用try-catch-finally,和 if语句 套用类似。

throws

在方法中,有时候对于可能存在异常的语句并不进行处理,而是用throws来声明异常。

对于有throws异常(Checked Exception)的方法,调用时要么处理这些异常(try-catch),要么再次向外部throws,直至到 main 函数为止。而 main 函数会把异常传给 JVM,JVM 遇到异常直接输出异常信息,然后中断程序

比如:

  • try… finally 语句中,若 try 过程中出现异常,那么会在 finally 执行结束后退出程序,并打印错误信息。就是这个语句块把错误 throws 给 main 方法,然后 throws 给了 JVM

注意:

  • throws 抛出的异常可以是方法实际产生的异常类型,也可以是它的父类
  • throws 可以抛出多个异常,异常类型之间用 , 隔开
  • 对于编译异常,程序中必须处理,可以用 try-catch 或者是 throws
  • 对于运行时异常,程序中如果没有处理,默认就是 throws 的方式处理
  • 在 throws 过程中,如果有 try-catch 捕捉了异常,就算是处理异常
  • throws 抛出的类型若是运行时异常,那么它的外部可以不进行 throws(不报编译错误),JVM 会自行处理

例子:

public class Throws {
    public static void main(String[] args) {
        try {
            // 调用该divide方法应该承担异常处理的任务
            int result = new Test().divide(3, 1);
            System.out.println("the first result is " + result);
        }
        // 异常处理
        catch(ArithmeticException e) {
            e.printStackTrace();
        }
        // 未处理异常,整个程序向外爆发异常
        int result = new Test().divide(3, 0);
        System.out.println("the second result is " + result);
    }
}
class Test {
    // ArithmeticException is a RuntimeException, not checked Exception
    public int divide(int x, int y) throws ArithmeticException {
        int result = x/y;
        return result;
    }
}

执行结果:

the first result is 3
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at courseTest.Test.divide(Throws.java:19)
        at courseTest.Throws.main(Throws.java:12)

例子2:

	// public 是方法修饰词,代表所有类都可调用该方法
	// static 也是方法修饰词,代表该方法是静态方法,可通过类名调用,不可有非静态内容
	// MyException 是自定义异常,下一小节会介绍
	public static void testException() throws MyException {
        throw new MyException("10001", "The reason of myException");
    }

    public static void main(String[] args) throws MyException  {
        MyExceptionTest.testException();

        // 如果main函数未加throws,需要用try-catch来捕捉处理异常,否则报错
        // try {
        //     MyExceptionTest.testException();
        // }
        // catch(MyException e) {
        //     e.printStackTrace();
        // }
    }

异常相关的继承问题

  • 一个方法被覆盖,覆盖它的方法必须抛出相同的异常,或者异常的父类
  • 如果父类抛出多个异常,子类可以抛出父类异常的子集,不可以有新的异常

例子:

public class Father {
	public void f1() throws ArithmeticException {
	
	}
}

public class Son extends Father {
	// 会报错,因为未抛出相同的异常,或者是异常的子类
	// public void f1() throws Exception {
	//
	// }
	
	// 正确
	public void f1() throws ArithmeticException {
	
	}
}

异常处理重点:

  • try-catch-finally
  • 使用throw抛出异常
  • 子类声明的异常不能超过父类异常的范围

自定义异常

Exception 是所有异常的父类。继承自 java.lang.Throwable,同时有一个 兄弟类 Error。其中 Error 是系统层面的错误,无需程序处理。程序只需要处理Exception。

自定义异常,需要继承Exception类或其子类。

  • 继承自Exception,就变成Checked Exception,编译工具会辅助检查
  • 继承自RuntimeException,就变成Unchecked Exception

重点在构造函数

  • 调用父类Exception的message构造函数
  • 可以定义自己的成员变量,不局限于message

在程序中使用throws主动抛出异常
同时需要注意,在方法头部声明中使用throws抛出异常;在方法内部程序中使用throw抛出异常。

例子:

// 自定义异常类
public class MyException extends Exception{
    
    private String returnCode; // 异常对应的返回码
    private String returnMsg; // 异常对应的描述信息

    // 无参构造函数
    public MyException() {
        super(); // 父类的构造函数
    }

    public MyException(String returnMsg) {
        super(returnMsg); // 父类的有参构造函数
        this.returnMsg = returnMsg;
    }

    public MyException(String returnCode, String returnMsg) {
        super();
        this.returnCode = returnCode;
        this.returnMsg = returnMsg;
    }

    public String getReturnCode() {
        return returnCode;
    }

    public String getReturnMsg() {
        return returnMsg;
    }
}

测试一下:

public class MyExceptionTest {
    // 头部使用throws,代表方法可能抛出异常
    public static void testException() throws MyException {
        throw new MyException("10001", "The reason of myException");
    }

    public static void main(String[] args) {
        // main未加throws,使用下行语句会报错
        // MyExceptionTest.testException();

        // 如果main函数未加throws,需要用try-catch来捕捉处理异常,否则报错
        // 报错原因是因为 MyException 继承了 Exception,是非RuntimeException异常,也就是Checked Exception
        // 表示编译器会辅助检查是否有错

        try {
            MyExceptionTest.testException();
        }
        catch(MyException e) {
            e.printStackTrace();
            System.out.println("returnCode:" + e.getReturnCode());
            System.out.println("returnMsg:" + e.getReturnMsg());
        }
    }
}

输出结果:

courseTest.MyException
        at courseTest.MyExceptionTest.testException(MyExceptionTest.java:6)
        at courseTest.MyExceptionTest.main(MyExceptionTest.java:18)
returnCode:10001
returnMsg:The reason of myException

例子2:

public class Student {
    
    public int divide(int x, int y) {
        return x/y;
    }

    public static void main(String[] args) throws DivideByMinusException {
        Student newton = new Student();
        newton.divide2(5, 0);
        newton.divide5(5, -2);
    }

    public int divide2(int x, int y) {
        int result;
        try {
            result = x/y;
            System.out.println("result is " + result);
        }
        catch (ArithmeticException e) {
            System.out.println(e.getMessage());
            return 0;
        }
        catch(Exception e) {
            e.printStackTrace();
            return 0;
        }
        return result;
    }

    // ArithmeticException is unchecked exception
    public int divide3(int x, int y) throws ArithmeticException {
        return x/y;
    }

    public int divide4(int x, int y) {
        // try {
        //     
        //     return divide3(x, y);
        // }
        // catch (ArithmeticException e) {
        //     e.printStackTrace();
        //     return 0;
        // }

        // 调用divide3(其throws异常),不过ArithmeticException是编译器不管的,此处也不会报错
        // 如果调用divide5,则需要在头部throws相同的异常,或者是使用上方try-catch
        return divide3(x, y);
    }

    public int divide5(int x, int y) throws DivideByMinusException {
        try {
            if (y < 0) {
                // 将该异常返回给调用方
                throw new DivideByMinusException("the divisor is negetive", y);
            }
            return divide3(x, y);
        }
        catch(ArithmeticException e) {
            e.printStackTrace();
            return 0;
        }
    }
}

输出结果:

/ by zero
Exception in thread "main" courseTest.DivideByMinusException: the divisor is negetive
        at courseTest.Student.divide5(Student.java:56)
        at courseTest.Student.main(Student.java:12)

关于自定义异常的重点:

  • 自定义异常继承自Exception或者RuntimeException
  • 需要使用父类的构造函数,可以自定义异常信息
  • 使用throw抛出异常

throw 和 throws 的区别

意义位置后面跟的东西
throws异常处理的一种方式,抛出异常方法声明处异常类型
throw手动生成异常对象的关键字方法体中异常对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

三更鬼

谢谢老板!

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

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

打赏作者

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

抵扣说明:

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

余额充值