Java异常处理

1. 异常

  • 异常的基本概念:在程序运行过程中出现的错误,称为异常

  • 异常的作用:增强程序的健壮性

  • 所有异常都是发生在运行阶段的。

  • 异常在java中以类的形式存在,每一个异常类都可以创建异常对象。

2. 异常的分类

为何要对异常分类?

假设java中没有对异常进行划分,没有分为:编译时异常和运行时异常,
                所有的异常都需要在编写程序阶段对其进行预处理,将是怎样的效果呢?
                        首先,如果这样的话,程序肯定是绝对的安全的。
                        但是程序员编写程序太累,代码到处都是处理异常
                        的代码。
复制代码

2.1 异常的层次结构

异常结构图.png

2.2 异常的分类

异常主要分为:Error错误、编译时异常(Exception类)、运行时异常(RuntimeException)

  • Error错误:如果应用程序出现了 Error,那么将无法恢复,只能重新启动应用程序,最典型的 Error 的异常是:OutOfMemoryError。

  • 编译时异常:出现了这种异常必须处理,不处理 java程序将无法编译通过。

  • 运行时异常:此种异常可以不用显示的处理,例如被0 除异常,java没有要求我们一定要处理 。(system.out.println(10 / 0))

3. 对于异常的处理方法

3.1 使用throws 关键字上抛(上报)异常类

  1. 在声明的位置上继续使用:throws "异常类",将异常抛给方法的调用者(JVM)
  2. 上抛类似于推卸责任。(继续把异常传递给调用者)
  3. 一般不建议在main方法使用throws,异常如果发生了,一定会抛给JVM,JVM将终止程序

注意:throws后面可以写多个类,用“,”隔开

    public class ExceptionTest05 {

        public static void main(String[] args) throws ClassNotFoundException {
            doSome();

        }

        public static void doSome() throws ClassNotFoundException{
            System.out.println("doSome!!!!");
        }
    }
    
复制代码

3.2 使用try..catch 捕捉异常类

捕捉等于把异常拦下并真正的解决了,调用者是不知道的

try..catch 语法:

    try {

        调用方法
        //以上代码出现异常,直接进入catch语句块中执行
        //catch是捕捉异常之后走的分支,进行处理异常操作

    } catch (异常类名 变量名) {

        异常之后的报错
    }
    
复制代码

try..catch 和 throws 联合使用

public static void main(String[] args){
    try {
        doSome();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

public static void doSome() throws ClassNotFoundException{
    System.out.println("doSome!!!!");
}
    
复制代码

3.3 两种异常处理方法的注意

  1. 只要异常没有捕捉,采用上报的方式,此方法的后续代码不会执行

  2. try语句块中的某一行出现异常,该行后面的代码不会执行,try...catch捕捉异常之后,后面的代码可以执行

3.3.1 上报和捕捉如何选择

如果希望调用者来处理选择上报,其他情况使用捕捉的方式。

4. 深入try..catch

4.1 catch语句

  1. catch()内中的类型可以是具体的异常类型,

  2. catch可以写多个。建议catch的时候,精确地一个一个处理。这样有利于程序的调试

  3. catch写多个时,从上到下,异常类型必须遵守从小到大:

     try {
             //创建输入流
             FileInputStream fis = new FileInputStream("F:\\Java教程\\day08作业.txt");
             //读文件
             fis.read();
         } catch (FileNotFoundException e) {
         
             System.out.println("文件不存在!");
             
         } catch (IOException e) {
         
             System.out.println("读文件报错了!");
             
         }
        
       
    复制代码
  4. JDK8新特性: catch( )内可使用 “|” 逻辑或 并列多个异常类,表示发生这几个异常类中的某个类时,执行以catch语句中的代码

       try {
                 //创建输入流
                 FileInputStream fis = new FileInputStream("F:\\Java教程\\day08作业.txt");
                 
                 //进行数学运算
                 System.out.println(100 / 0);  //这个异常是运行时异常,可以处理也可以不处理
                 
            } catch (FileNotFoundException | ArithmeticException | NumberFormatException e) {
            
              //JDK8新特性,使用逻辑或,表示存在这几个异常类时,执行以下代码
         
              System.out.println("文件不存在?数学异常?空指针不存在?都有可能!");
            }
    复制代码

4.2 try..catch语句中的finally子句

  1. 在finally子句中的代码是最后执行的,并且一定会执行,即使try语句出现了异常。

  2. finally子句必须和try一起出现,不能单独编写。

  3. finally语句通常使用在:

  • 通常在finally语句块中完成资源的释放/关闭,因为finally必定会执行
    复制代码

示例:

public class ExceptionTest10 {
    public static void main(String[] args) {
        FileInputStream fis = null;//声明位置放到这,finally才能用到
        try {
            //创建输入流对象
            fis = new FileInputStream("F:\\Java教程\\day08作业.txt");

            String s = null;
            //空指针异常
            s.toString();
            System.out.println("hello world");

            //流需要关闭,因为流占用资源
            //放在这里流可能关不了
            //fis.close();
            
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (NullPointerException e) {
            e.printStackTrace();
        }finally {
            System.out.println("hello 浩克");
            //流放在这里比较保险
            if (fis != null) {
                try {
                
                    //close方法有异常,采用捕捉的方式
                    fis.close();
                    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        System.out.println("hello kity");
    }
}
复制代码

4.3 try finally 联合使用,try不能单独使用

  public class ExceptionTest11 {
     public static void main(String[] args) 
     
         try {
             System.out.println("try....");
             return;
             
         } finally {
             System.out.println("");
             //还是会输出 finally...
         }
     }
 }
复制代码

4.4 只有退出JVM,finally语句才不会执行

public class ExceptionTest12 {
    public static void main(String[] args) {
        try {
           
           System.exit(0);//退出JVM,finally语句的代码不执行
        } finally {
            System.out.println("finally...");
        }

    }
}
复制代码

4.5 finally面试题

public class ExceptionTest13 {
    public static void main(String[] args) {
        int result = m();
        System.out.println(result); //100
    }

    /*
        方法体中的代码必须遵循自上而下的顺序依次逐行执行
        return语句一旦执行,整个方法必须结束
    */

    public static int m () {
        int i = 100;
        try {
            return i; //return语句是一定最后执行的
        } finally {
            i++;
        }
    }
}

/*
反编译之后的效果
public static int m(){
    int i = 100;
    int j = i;
    i++;
    return j;
}
*/
复制代码

5. 异常对象有两个重要的方法

  • 获取异常简单的描述信息:

    String msg = exception.getMessage( );
    复制代码
  • 打印异常追踪的堆栈信息(推荐使用):

    exception.printStackTrace( );
    复制代码
  • 异常追踪信息怎么看?

    从上往下逐行看,看自己编写的代码,SUN公司编写的不需要查看
    复制代码

方法使用示例:

public class ExceptionTest08 {
    public static void main(String[] args) {
        NullPointerException e = new NullPointerException("空指针异常");
        
        //获取异常简单描述信息,这个信息是构造方法上面的String参数
        String msg = e.getMessage();
        
        System.out.println(msg);

        //打印异常堆栈信息
        //java后台打印异常信息追踪的时候,采用了异步线程的方式打印的
        e.printStackTrace(); 

        System.out.println("hello world");
    }
}
复制代码

6. 自定义异常(重点)

  1. 自定义异常的原因:SUN提供的异常是不够用,需要自定义异常类

  2. Java中如何自定义异常类:

       第一步,编写一个类继承Exception或者RuntimeException
    
       第二步,提供两个构造方法,无参和有String参数的
       
    复制代码

自定义异常类示例:

1.png

2.png

1.png

7. 关于方法重写不能抛出更多异常

重写之后的方法不能比重写之前抛出更多(更宽泛)的异常,只能更少

class Animal {
    public void doSome(){

    }

    public void doOther() throws Exception {

    }
}

class Cat extends Animal{
    //编译报错
    /*public void doSome() throws Exception {

    }*/

    //编译正常
  /*  public void doOther(){

    }*/

    //编译正常
    /*public void doOther() throws Exception {

    }*/

    //编译正常
    /*public void doOther() throws NullPointerException {

    }*/

}
复制代码

8. 末尾总结

总结异常中的关键字:

    异常捕捉:
         try
         catch
         finally

 throws 在方法声明位置上使用,表示上报异常信息给调用者
 throw  手动抛出异常!
复制代码