JavaSE-第六章

发布于 2022-03-21  150 次阅读


第六章 异常

6.1 什么是异常

public class Demo {
    public static void main(String[] args) {
        int i = 10;
        int n = 0;
        //使用除法
        int res = i/n;
        System.out.println(res);
    }
}
  • 错误的原因就是因为被0除
  • 这种信息被java的运行环境报出来了,这就是java的异常体系
  • 注意:java的异常都是"类"

6.2 异常的分类

6.2.1 异常分类图

6.2.2 异常分类

  • Error错误:如果程序出现Error,那么将无法恢复,只能重新启动应用程序,例如:OutOfMemeoryError
  • 一般性异常(受控异常;编译期异常):这种异常出现时,必须显示的处理,因为,不处理程序无法运行
  • 运行期异常(非受控异常):这种异常不需要显示处理,只在运行期间出现

6.2.3 try、catch、finally

  • 异常的捕获和处理采用try-catch完成,具体的格式:
try{
    
}catch(OneException e){
    
}catch(TwoException e){
    
}finally{
    
}
  • try中包含任何可能产生的代码
  • try后面可以是一个catch或者多个catch;catch就是捕获异常的
    • 如果try代码块中出现了异常,那么,立刻跳转到catch语句块中
    • 在try出现异常的代码中以下的代码不会执行了
  • finally表示,无论上面的代码是否有异常出现都必须执行
  • try可以直接使用finally
try{
    
}finally{
    
}

6.2.3.1 案例

public class Demo {
    public static void main(String[] args) {
        int i = 10;
        int n = 0;
        try {
            //业务代码
            //因为,这一行代码被0除,所以,下一行代码不会被执行
            int res = i/n;
            //永远不会执行
            System.out.println(res);
        }catch(ArithmeticException e){
            //e表示ArithmeticException类的局部变量
            System.out.println("不能被0除");
        }
    }
}

6.2.3.2 可以使用两种方式输出异常信息

  • 获取异常的描述信息
public class Demo {
    public static void main(String[] args) {
        int i = 10;
        int n = 0;
        try {
            //业务代码
            //因为,这一行代码被0除,所以,下一行代码不会被执行
            int res = i/n;
            //永远不会执行
            System.out.println(res);
        }catch(ArithmeticException e){
            //e表示ArithmeticException类的局部变量
            String message = e.getMessage();
            System.out.println("异常的描述信息:"+message);
        }
    }
}
  • 获取异常堆栈信息(适合程序调试阶段)
public class Demo {
    public static void main(String[] args) {
        int i = 10;
        int n = 0;
        try {
            //业务代码
            //因为,这一行代码被0除,所以,下一行代码不会被执行
            int res = i/n;
            //永远不会执行
            System.out.println(res);
        }catch(ArithmeticException e){
            //e表示ArithmeticException类的局部变量
            e.printStackTrace();
        }
    }
}

6.2.3.3 受控异常

public class Demo {
    public static void main(String[] args) {
        try{
            FileInputStream fileInputStream = new FileInputStream();
        }catch (FileNotFoundException e){//必须显示的处理,因为,这是一个编译期异常
            e.printStackTrace();
        }
    }
}

6.2.3.4 finally关键字

  • 特点:无论执行是否正常,都必须执行的代码段
import java.io.FileInputStream;
import java.io.FileNotFoundException;

public class Demo {
    public static void main(String[] args) {
        try{
            FileInputStream fileInputStream = new FileInputStream("D:/a/a.txt");
        }catch (FileNotFoundException e){//必须显示的处理,因为,这是一个编译期异常
            e.printStackTrace();
        }finally {
            System.out.println("是必须执行的代码");
        }
    }
}
  • 使用场景:释放资源
    • 例如:关闭数据库连接
    • 例如:关闭数据流
  • 深入finally:在return前执行finally代码块,但是,返回的是10,因为a变量已经在弹栈过程中了
public class Demo {
    public static void main(String[] args) {
        int result = method();
        System.out.println(result);
    }
    
    public static int method(){
        int a = 10;
        try{
            return a;
        }finally {
            System.out.println("在return前执行");
            a = 100;
        }
    }
}
  • 深入finally;返回100
public class Demo {
    public static void main(String[] args) {
        int result = method();
        System.out.println(result);
    }
    
    public static int method(){
        int a = 10;
        try{
            a = 200;
        }finally {
            System.out.println("在return前执行");
            a = 100;
        }
        return a;
    }
}

6.2.3.5 final、finally、finalize的区别?

  • final:
    • 修饰类不能被继承
    • 修饰参数:
      • 基本类型:值不能修改
      • 引用类型:地址(对象)不能修改
      • static final修饰常量
  • finally:代码无论如何都必须执行
  • finalize():垃圾回收前调用的方法

6.3 如何声明异常

  • 在方法中可以使用throws声明异常,如果声明的异常为受控异常,那么,在调用方法的时候必须处理

6.3.1 声明异常;处理异常

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

public class Demo {
    public static void main(String[] args) {
        //调用方法
        try {
            method();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
    }

    public static void method() throws FileNotFoundException{//声明异常
        FileInputStream fileInputStream = new FileInputStream("d:/a.txt");
    }
}

6.3.2 还是使用声明异常(不推荐)

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

public class Demo {
    //可以在此声明异常,但是,这样就把异常处理交给java虚拟机处理了
    public static void main(String[] args) throws FileNotFoundException {
        //调用方法
        method();
    }

    public static void method() throws FileNotFoundException{//声明异常
        FileInputStream fileInputStream = new FileInputStream("d:/a.txt");
    }
}

6.3.3 声明非受控异常

public class Demo {
    public static void main(String[] args) {
        //可以不编写try-catch,因为是个非受控异常
        //method();

        //建议编写异常处理
        try{
            method();
        }catch (ArithmeticException e){
            e.printStackTrace();
        }
    }

    //非受控异常
    public static void method() throws ArithmeticException{//声明异常
        int i = 10,n = 0;
        int r = i/0;
        System.out.println(r);
    }
}

6.4 手动抛出异常

  • 使用throw关键字抛出异常
    • 相当于return,以下的代码不被执行
    • 可以抛出受控异常或非受控异常

6.4.1 自定义一般性异常

public class Demo {
    public static void main(String[] args) {
        //测试
        method(1,0);
    }

    public static void method(int i,int n) {
        try{
            if (n==0){
                //手动抛出
                throw new MyException("不能为0");
            }
            if (!(n > 0 && n<=100)){
                //手动抛出
                throw new MyException("必须在1-100之间");
            }
            int result = i / n;
            System.out.println("------------>>"+result);

        }catch(Exception e){
            e.printStackTrace();
        }

    }
}

//自定义异常类
class MyException extends Exception{
    public MyException() {
        super();
    }

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

    public MyException(String message, Throwable cause) {
        super(message, cause);
    }

    public MyException(Throwable cause) {
        super(cause);
    }

    protected MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

6.4.2 自定义运行期异常

public class Demo {
    public static void main(String[] args) {
        //测试
        method(1,0);
    }
    //手动抛出异常的测试方法
    public static void method(int i,int n) {
        if (n==0){
            //手动抛出
            throw new MyException("不能为0");
        }
        if (!(n > 0 && n<=100)){
            //手动抛出
            throw new MyException("必须在1-100之间");
        }
        int result = i / n;
        System.out.println("------------>>"+result);
    }
}

//自定义异常类
class MyException extends RuntimeException{
    public MyException() {
        super();
    }

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

    public MyException(String message, Throwable cause) {
        super(message, cause);
    }

    public MyException(Throwable cause) {
        super(cause);
    }

    protected MyException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
        super(message, cause, enableSuppression, writableStackTrace);
    }
}

6.4.3 throws和throw的区别?

  • throws:声明异常(不处理,谁使用谁处理)
  • throw:抛出异常(抛出自定义异常)

6.5 异常的捕获顺序

  • 捕获的顺序:从小到大
  • 案例:
public static void main(String[] args) {
    try {
        FileInputStream fileInputStream = new FileInputStream("");
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e){
        e.printStackTrace();
    } catch (Exception e){
        e.printStackTrace();
    }
}

6.6 方法覆盖(重写)与异常

  • 子类不能抛出比父类更多的异常,可以抛出父类异常的子异常

6.6.1 自定义异常类和接口

//自定义异常类
class UserNotFound extends IOException{}
class PasswordException extends UserNotFound{}
class RegisterException extends Exception {}

//创建接口
interface UserDao{
    //编写一个方法
    void show() throws UserNotFound;
}

6.6.2 第一种情况(正确),实现类抛出的异常与接口中抛出的异常相同

class UserDaoImpl implements UserDao{

    @Override
    public void show() throws UserNotFound {

    }
}

6.6.3 第二种情况(正确),可以抛出父类异常的子异常

class UserDaoImpl implements UserDao{

    @Override
    public void show() throws UserNotFound,PasswordException {

    }
}

6.6.4 第三种情况(错误),不能抛出比父类更多的异常

class UserDaoImpl implements UserDao{

    @Override
    public void show() throws UserNotFound,RegisterException {

    }
}

6.7 总结

6.7.1 受控异常、非受控异常区别:

  • 受控异常:必须显示的处理
  • 非受控异常:可以不用显示处理,运行时不需要处理,直接由jvm捕获;

6.7.2 异常的关键字:

  • try把有可能出现问题的代码编写在此
  • catch异常捕获
  • finally代码无论如何都必须执行
  • throws声明异常,但不处理(一般都是底层代码)
  • throws手动抛出异常(自定义异常)

6.7.3 方法覆盖与异常

  • 子类不能抛出比父类更多的异常,可以抛出父类异常的子异常

我从未觉得繁琐,说浪漫些,我很爱你。