JavaSE学习笔记 - 异常处理机制

  • 程序在执行的过程中出现非正常的情况,最终导致 JVM 非正常停止。异常在 Java 中以类的形式存在,每一个异常类都可以创建对象,在产生异常的时候就是创建了一个异常类的对象,然后将异常对象抛出。
public class Main {
    public static void main(String[] args) {
        NullPointerException exception = new NullPointerException("空指针异常");
        System.out.println(exception);//java.lang.NullPointerException: 空指针异常
    }
}

异常继承结构图

在这里插入图片描述
编译时异常和运行时异常都发生在运行阶段

异常的分类

  • 大类参考上面异常继承结构图

RuntimeException

  • 错误的强制类型转换
  • 空指针异常
  • 数组越界异常
  • 这种异常是可以通过程序检查来避免的

  • 除了 RuntimeException,其余的所有异常都是检查型异常,在编译时期就要对这种异常进行处理,否则编译器就会报错。对这种异常处理有两种方式,一种是使用 throws 异常将异常抛出,抛给调用者,然后调用者也可以选择抛出或者直接处理这个异常,这种方式如果在某一个地方发生了异常,那么程序将终止,不会在继续向下执行。另外一种方式就是使用 try...catch...finally 来处理这个异常,finally 关闭资源以后,程序可以继续向下执行。
  • 在异常发生的时候,如果一直上抛给调用者,那么最终给抛给 main 方法,main 方法继续上抛异常的话,那么这个异常就会抛给 JVM,然后 JVM 就会终止程序的运行。
  • 异常机制可以提高程序的健壮性

编译时异常

public class Main {
    public static void main(String[] args) throws ParseException {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        //date = sdf.parse("2020-1-1 00:00:00");//Wed Jan 01 00:00:00 CST 2020
        date = sdf.parse("2020-1-1");//java.text.ParseException: Unparseable date: "2020-1-1"
        //这里格式不对,所以程序中断抛出异常,程序不再往下进行
        //如果不想让程序中断,那么就可以对这个异常进行捕获处
        System.out.println(date);
    }
}

//捕获异常
public class Main {
    public static void main(String[] args) {
        Date date = new Date();
        try {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
            date = sdf.parse("2020-1-1");//java.text.ParseException: Unparseable date: "2020-1-1"
        } catch (ParseException e) {
            e.printStackTrace();//java.text.ParseException: Unparseable date: "2020-1-1"
        }
        //这里对异常进行了捕获,所以程序在发生异常的时候并没有中断程序
        System.out.println(date);//Fri Jul 31 15:28:21 CST 2020
    }
}

运行时异常

public class Main {
    public static void main(String[] args) {
        String str = "abc";
        System.out.println(str.charAt(3));
        //StringIndexOutOfBoundsException: String index out of range: 3
        //运行时异常要进行捕获处理
        System.out.println(str);
    }
}

//捕获异常
public class Main {
    public static void main(String[] args) {
        try {
            System.out.println(3 / 0);
        } catch (Exception e) {
            //java.lang.ArithmeticException: / by zero
            //e.printStackTrace();
            System.out.println(e.getMessage());/// by zero
        }
        System.out.println(3);
    }
}

异常的产生

public class Main {
    public static void main(String[] args) throws ParseException {
        Date date = new Date();
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        //date = sdf.parse("2020-1-1 00:00:00");//Wed Jan 01 00:00:00 CST 2020
        date = sdf.parse("2020-1-1");//java.text.ParseException: Unparseable date: "2020-1-1"
        //这里格式不对,所以程序中断抛出异常,程序不再往下进行
        //如果不想让程序中断,那么就可以对这个异常进行捕获处
        System.out.println(date);
    }
}

//parse源码
public Date parse(String source) throws ParseException {
    ParsePosition pos = new ParsePosition(0);
    Date result = parse(source, pos);
    if (pos.index == 0)
        throw new ParseException("Unparseable date: \"" + source + "\"" ,
           pos.errorIndex);
    return result;
}
  • 通过上面的源码我们可以看出,如果日期的格式不正确那么 JVM 就会检测出来这个异常,此时 JVM 会创建一个异常类对象,这个异常类对象包含了异常产生的原因,位置和内容,使用 throw 手动抛出异常,然后使用红色的字体打印在控制台,然后 JVM 终止当前的程序。

手动抛出异常

public class Main {
    public static void main(String[] args) throws ParseException {
        //Exception in thread "main" java.lang.RuntimeException: 除数不能为 0
        //System.out.println(div(1, 0)); //程序中断,往下执行
        try {
            System.out.println(div(1, 0));
        } catch (Exception e) {
            System.out.println(e.getMessage());//除数不能为 0
        }
        System.out.println(div(1, 1));//1
    }
    public static int div(int a, int b) {
        if(b == 0) throw new RuntimeException("除数不能为 0");
        return a / b;
    }
}

自定义异常类

public class NullPointerException extends RuntimeException {
    private static final long serialVersionUID = 5162710183389028792L;

    /**
     * Constructs a {@code NullPointerException} with no detail message.
     */
    public NullPointerException() {
        super();
    }

    /**
     * Constructs a {@code NullPointerException} with the specified
     * detail message.
     *
     * @param   s   the detail message.
     */
    public NullPointerException(String s) {
        super(s);
    }
}
  • Java 中提供的异常类不能够处理所有的异常,通过查看源码,我们可以发现所有的异常类中都有两个方法,这个一无参构造方法,一个是有参构造方法。自定义异常类必须继承 \(Exception\) 或者 \(RuntimeException\),如果是发生概率小的异常可以继承 \(RuntimeException\),表示运行时异常;否则继承 \(Exception\),编译时异常,在编译阶段就要对异常进行处理。
public class MyException extends Exception {
    public MyException() {
    }

    public MyException(String message) {
        super(message);
    }
}
public class Main {
    public static void main(String[] args) throws MyException {
        try {
            System.out.println(div(1, 0));
        } catch (Exception e) {
            System.out.println(e.getMessage());//除数不能为 0
        }
        System.out.println(div(1, 0));//自己不处理,抛给虚拟机
        //Exception in thread "main" cn.edu.zut.MyException: 除数不能为 0
    }

    public static int div(int a, int b) throws MyException {
        //自定义的异常在使用的时候是要抛给调用者去处理的
        //所以这里不进行处理
        //MyException exception = new MyException("除数不能为 0");
        //if(b == 0) exception;
        if(b == 0) throw new MyException("除数不能为 0");
        return a / b;
    }
}

在继承机制中,子类的方法抛出的异常不能比父类方法抛出的异常还要多

final finally finalize

  • final:关键字,最终的,不变的
  • finally:关键字,在异常机制中,必然会执行,在发生异常的时候程序会终止,那么如果使用流的时候可能会打开一些资源,那么这些资源将不会被处理,如果使用 finally,不管程序是否发生异常,程序都会进入 finally 执行程序,此时在 finally 中就可以关闭一些资源,避免空间的浪费。(如果在进入 finally 程序代码块之前退出了 JVM 虚拟机,那么 finally 中的代码将不再执行。)
  • finalize:Object 类中的一个方法,GC 负责调用这个方法进行垃圾回收。
public class Main {
    public static void main(String[] args) {
        System.out.println(print());//100
    }
    public static int print() {
        int x = 100;
        try {
            return x;
        } finally {
            x ++;
        }
    }
}
  • 我们知道 finally 中的程序一定会被执行,但是 Java 中又规定 Java 方法体中的代码必须自上而下的执行,return 一旦执行,整个方法必须结束。在 IDEA 中我们可以直接查看 .class 文件,然后查看其执行过程。
public class Main {
    public Main() {
    }

    public static void main(String[] args) {
        System.out.println(print());
    }

    public static int print() {
        byte x = 100;

        byte var1;
        try {
            var1 = x;
        } finally {
            int var5 = x + 1;
        }

        return var1;
    }
}

posted @ 2020-08-27 12:52  菜鸭丶  阅读(180)  评论(0编辑  收藏  举报