博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

JavaEE - 07异常处理

Posted on 2020-12-07 21:50  Kingdomer  阅读(152)  评论(0编辑  收藏  举报

JavaEE - 07异常处理

(1)异常概述
  • 异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
    • 比如说,代码中少了一个分号,那么运行出来结果是提示是错误 java.lang.Error;
    • 如果用System.out.println(11/0),那么用0做了除数,会抛出 java.lang.ArithmeticException 的异常。
  • 异常发生的原因有很多,通常包含以下几大类:
    • 用户输入了非法数据。
    • 要打开的文件不存在。
    • 网络通信时连接中断,或者JVM内存溢出。
  • 这些异常有的是因为用户错误引起,有的是程序错误引起的,还有其它一些是因为物理错误引起的。
  • 异常:在java语言中,将程序执行中发生的不正常情况称为"异常"。开发过程中的语法错误和逻辑错误不是异常。
  • Java程序在执行过程中所发生的异常事件分为两类:
    • ErrorJava虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。StackOverflowError和OOM。一般不编写针对性的代码进行处理。
    • Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行处理。如:空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界。
  • 对于这些错误,一般有两种解决方法:
    • 一是遇到错误就终止程序的运行。
    • 另一种是在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理。
  • 捕获错误最理想的是在编译期间,但是有些错误只有在运行时才会发生。如:除数为0、数组下标越界等。
    • 分类: 编译时异常和运行时异常。
    • 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
    • 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
  • 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
  • Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。

 

public class ErrorTest {
    public static void main(String[] args) {
        // 栈溢出: Exception in thread "main" java.lang.StackOverflowError
//        main(args);  
        
        // 堆溢出: Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
//        Integer[] arr = new Integer[1024*1024*1024]; 
    }
}

 

(2)异常体系结构

  • Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。 
  • Error 用来指示运行时环境发生的错误。例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
  • 所有的异常类是从 java.lang.Exception 类继承的子类。
    • 异常类有两个主要的子类:IOException 类和 RuntimeException 类。
  • java.lang.Throwable
    • java.lang.Error: 一般不编写针对性的代码进行处理。
    • java.lang.Exception: 可以进行异常的处理
      • 编译时异常(checked) 
        • IOException
          • EOFException  
          • FileNotFoundException
          • MalformedURLException
          • UnknownHostException
        • ClassNotFoundException
        • CloneNotSupportedException
      • 运行时异常(unchecked)
        • RuntimeException
          • NullPointerException
          • ArrayIndexOutOfBoundsException
          • ClassCastException
          • NumberFormatException
          • InputMismatchException
          • ArithmaticException
          • illegalArgumentException
          • illegalStateException
          • NoSuchElementException

(3)异常处理机制: try-catch-finally

 

  • 异常处理机制:将可能异常的程序代码集中在一起,与正常的程序代码分开。
    • 抛: 程序在正常执行的过程中,一旦出现异常,就会在异常代码处生成一个对应异常类的对象,并将此处对象抛出。一旦抛出对象,其后的代码将不再执行。
    • 抓: 异常的处理方式 try-catch-finally
  • try-catch-finally
    • finally是可选的。
    • 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。
    • 一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常处理。一旦处理完成。就会跳出当前try-catch结构(在没有写finally的情况)。继续执行其后的代码。
    • catch中的异常类型如果没有子父类关系,则声明顺序无所谓;如果有子父类关系,子类一定声明在父类上面,否则报错。
    • 常用的异常对象处理的方式:  String getMessage() /  printStackTrace()
    • 在try结构中声明的变量,在出了try结构以后,就不能再被调用。
    • try-catch-finally 结构可以嵌套
    • try-catch-finally处理编译时异常,使程序在编译时不再报错,但运行时仍可能报错。将一个编译时出现的异常,延迟到运行时出现。
    • 由于运行时异常比较常见,通常就不针对运行时异常编写try-catch-finally了。
    • 像数据库连接、输入输出流、网络编程Socket等资源,JVM不会自动回收,需要手动释放资源,就需要声明在finally中。

  • try{
  •     // 可能出现异常的代码  
  • }catch(异常类型1 变量名1){
  •     // 处理异常的方式1
  • }catch(异常类型2 变量名2){
  •     // 处理异常的方式2
  • }finally{
  •     // 一定会执行的代码
  • }
    @Test
    public void test1(){
        String str = "abc";
        try{
            int num = Integer.parseInt(str);
            System.out.println("hello--1");
        }catch (NumberFormatException e){
            System.out.println("出现数值转换异常了");  // 出现数值转换异常了
//            e.getMessage();
            e.printStackTrace();
        }catch (NullPointerException e){
            System.out.println("空指针异常");
        }
        System.out.println("hello--2");              // hello--2
    }

finally中声明的是一定会被执行的代码。即使catch中又出现异常了、try中有return语句、catch中有return语句等情况。

    public int method(){
        try{
            int[] arr = new int[10];
            System.out.println(arr[10]);
            return 1;
        }catch (ArrayIndexOutOfBoundsException e){
            e.printStackTrace();
            return 2;
        }finally {
            System.out.println("一定执行的代码");
        }
    }

    @Test
    public void test1(){
        int i = method();
        System.out.println(i);
    }
一定执行的代码
2
java.lang.ArrayIndexOutOfBoundsException: 10
    at FinallyTest.method(FinallyTest.java:14)
    at FinallyTest.test1(FinallyTest.java:26)
    ......

 

 

    @Test
    public void test1(){
        try{
            int a = 10;
            int b = 0;
            System.out.println(a /b);   // 触发异常
        }catch (ArithmeticException e){
            int[] arr = new int[10];
            System.out.println(arr[10]);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            System.out.println("必须要执行");
        }
    }
//执行结果:-------------------
必须要执行

java.lang.ArrayIndexOutOfBoundsException: 10

    at FinallyTest.test1(FinallyTest.java:25)

 

 

public class ReturnExeceptionDemo {
    public static void main(String[] args) {
        try{
            methodA();
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
        methodB();
    }

    static void methodA(){
        try{
            System.out.println("进入方法A");
            throw new RuntimeException("制造异常");
        } finally {
            System.out.println("调用方法A的finally");
        }
    }

    static void methodB(){
        try{
            System.out.println("进入方法B");
            return;
        } finally {
            System.out.println("调用方法B的finally");
        }
    }
}

 执行结果:

进入方法A
调用方法A的finally
制造异常
进入方法B
调用方法B的finally
  • 如果try中有return语句, 那么finally中的代码还是会执⾏。因为return表示的是整个方法体返回, finally中的语句会在return之前执⾏。
    • 但是return前执行的finally块内,对数据的修改效果对于引用类型和值类型会所不同:
    public static int f(){
        int ret = 0;
        try{
            return ret;
        }finally {
            ++ret;
            System.out.println("finally 执行");    // finally 执行 0
        }
    }

    public static int[] f2(){
        int[] ret = new int[]{0};
        try{
            return ret;
        }finally {
            ret[0]++;
            System.out.println("finally 执行");  // finally执行  1
        }
    }

    @Test
    public void test5(){
        int aa = f();
        System.out.println(aa);
        int[] a = f2();
        System.out.println(a[0]);
    }

 

    public int getNum() {
        try{
            int a = 1 / 0;           // 3: 代码走到这一行时,会遇到一个MathExeception异常,这样retrun 1 代码就不会执行了
            return 1;
        }catch (Exception e){        // 5: 捕获异常
            return 2;                // 6: 走到这一行,异常机制:如果在catch中遇到了return 或者 异常,能使改方法终止的代码,有finally就必须先去执行finally的代码然后再返回值。
        }finally {
            return 3;                // 8: 走到这一行, 因为是return语句,这时方法就直接结束了,第6行的返回值不能被真正返回。如果finally仅仅是处理释放资源类的操作,那么就会返回2。
        }
    }

    @Test
    public void test5(){
        System.out.println(getNum());  // 3
    }

 

 

 

(4)异常处理机制: throws + 异常类型

  • throws + 异常类型 写在方法的声明处指明此方法执行时,可能会抛出的异常类型。
    • 一旦方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。
    • 异常代码之后的代码,就不再执行。
  • try-catch-finally: 真正的将异常给处理掉了。throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉。
  • 如何选择 try-catch-finally 还是 throws?
    • 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
    • 执行的方法A中,先后调用了另外几个方法,这几个方法是递进关系执行的。建议这几个方法使用throws 的方式进行处理。而执行的方法A考虑使用try-catch-finally方式进行处理。

 

public class ThrowsTest {
    public void method1 () throws FileNotFoundException, IOException {
        File file = new File("hello.txt");
        FileInputStream fis = new FileInputStream(file);

        int data = fis.read();
        while(data != -1){
            System.out.println((char)data);
            data = fis.read();
        }
        fis.close();
    }

    public void method2() throws IOException{
        method1();
    }

    public static void main(String[] args) {
        try{
            new ThrowsTest().method2();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

(5)手动抛出异常: throw

  • 异常对象的产生: 系统自动生成的异常对象。
  • 手动的生成一个异常对象,并抛出(throw)

class Student {
    private int id;

    public void regist(int id) throws Exception {
        if(id > 0){
            this.id =id;
        }else {
//            System.out.println("你输入的数据有误");
// 手动抛出异常对象 throw new RuntimeException("你输入的数据有误"); } } @Override public String toString() { return "Student{" + "id=" + id + '}'; } } public class ThrowTest { public static void main(String[] args) { Student s = new Student(); try{ s.regist(1001); System.out.println(s); }catch (Exception e){ System.out.println(e.getMessage()); } } }

 

throw 和 throws的异同

throw:  生成一个异常对象,并抛出。使用在方法内部,手动抛出一个异常对象。

throws: 处理异常的方式。使用在方法声明处的末尾。 声明这个方法会抛出某种类型的异常,让使用者知道需要捕获的异常类型,try-catch-finally 进行处理。 出现异常的一种可能性,不一定发生。

 

(6)用户自定义异常类

如何自定义异常?
  • 继承于现有的异常结构: RuntimeException、Exception
  • 提供全局常量: serialVersionUID
 
public class MyException extends RuntimeException {
    static final long serialVersionUID = 2044963742759499859L;

    public MyException(){}

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

 

public class MyExceptionTest {
    public static void main(String[] args) {
        try{
            int i = Integer.parseInt(args[0]);
            int j = Integer.parseInt(args[1]);

            double result = test(i,j);
            System.out.println(result);
        }catch (NumberFormatException e){
            System.out.println("数据类型不一致");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("缺少命令行参数");
        }catch (ArithmeticException e){
            System.out.println("除数为0");
        }catch (MyException e){
            System.out.println(e.getMessage());
        }
    }

    static double test(int i, int j){
        return i / j;
    }
}

 

 
E:\JavaProgramLearn\JavaEE\07Exception\src>javac MyExceptionTest.java -encoding UTF-8

E:\JavaProgramLearn\JavaEE\07Exception\src>java MyExceptionTest 4 2
2.0
E:\JavaProgramLearn\JavaEE\07Exception\src>java MyExceptionTest "4" "2a"
数据类型不一致
E:\JavaProgramLearn\JavaEE\07Exception\src>java MyExceptionTest
缺少命令行参数
E:\JavaProgramLearn\JavaEE\07Exception\src>java MyExceptionTest "3" 0
除数为0