菜鸟学JAVA之——异常

异常

异常分为错误(Error)与异常(Exception)

一、异常体系结构

java.lang.Throwable

  • java.lang.Error:一般不编写针对性的代码进行处理
  • java.lang.Exception:可以进行异常的处理
    • 编译时异常(checked)(也称“受检异常”)
      • IOException
      • ClassNotFoundException
    • 运行时异常(unchecked)(也称“非受检异常”)
      • NullPointerException
      • ArrayIndexOutOfBoundsException
      • ClassCastException
      • NumberFormatException
      • InputMismatchException
      • ArithmeticException

二、Java异常处理的方式

  • 方式一:try-catch-finally

    举个例子:小孩在放羊,这时狼来了,小孩发现这个狼只是个狼崽子,他自己就有能力解决。羊就该吃草还吃草,相当于代码正常执行

  • 方式二:throws + 异常类型

    这个就相当于来了一只老狼,小孩处理不了了,需要在山下喊人,让成人来解决,大人如果不能解决就会找警察。这种处理方式就相当于方式二

三、异常的处理:抓抛模型

  • 过程一:“抛”:程序在正常执行的过程中,一旦出现了异常,就会在异常代码处生成一个对应异常类的对象。并将此对象抛出。一旦抛出对象后,其后的代码就不再执行。

    关于异常对象的产生:① 系统自动生成的异常对象(在产生异常的代码出生成一个异常对象)

    ​ ② 手动的生成一个异常对象,并抛出(throw)

        • public class StudentTest {
                      
                      
              public static void main(String[] args) {
                      
                      
                  Student s = new Student();
                  s.regist(-1001);
              }
          }
          
          class Student {
                      
                      
              private int id;
              
              public void regist(int id) {
                      
                      
                  if(id > 0) {
                      
                      
                      this.id = id;
                  } else {
                      
                      
                      //System.out.println("您输入的数据非法!");//这里如果输入的数据非法,他还会返回一个id的默认值,还是会出来一个结果。按理说输入非法应该报错
                      //手动抛出一个异常对象,一般这个对象都是运行时异常,或编译时异常。如果是编译时异常就需要捕获处理
                      //手动抛出运行时异常,编译可以通过,可以不用处理
                      //throw new RunTimeException("您输入的数据非法!");
                      
                      //手动抛出编译时异常,编译都不会通过,这时就需要处理了
                      try {
                      
                      
                      	throw new Exception("您输入的数据非法!");//手动抛出异常
                      } catch(Exception e) {
                      
                      
                          System.out.println(e.getMessage());
                      }
                      
                  }
              }
              @Override
              public String toString() {
                      
                      
                  return "Student " + "[id = " + id + "]";
              }
          }
          
          
          
  • 过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throws

四、异常处理方式一:try-catch-finally 的使用

try {
    
    
    //可能出现异常的代码
}catch(异常类型1 变量名1){
    
    
    //处理异常的方式1
}catch(异常类型2 变量名2){
    
    
    //处理异常的方式2
}catch(异常类型3 变量名3){
    
    
    //处理异常的方式3
}
...
finally{
    
    
    //一定会执行的代码
}

说明:

  • finally是可选的。

  • 使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配

  • 一旦try中的异常对象匹配到某个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没有写finally的情况)。继续执行其后的代码。

  • catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。

    catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错

  • 常用的异常对象的处理的方式:① getMessage()(返回值类型是String) ② printStackTrace()(void,无返回值,其抛出的异常信息包含getMessage()抛出的,所以比较常用)

  • 在try结构中声明的变量,在出了try结构以后,就不能再被调用

  • try-catch-finally结构可以嵌套。

体会1:使用try-catch-finally处理编译时异常,使得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。

体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。

​ 针对编译时异常,我们说一定要考虑异常的处理。

五、finally

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

    public void testMethod() {
          
          
        int num = method();
        System.out.println(num);
    }
    
    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("我一定会被执行!");
            //return 3;
        }
    }
    

    如果不注释①号代码,则输出语句为:我一定被执行!/n 2

    如果注释掉:输出:我一定被执行!/n 1

    如果给finally里面加一个语句return 3; 则不管有没有异常都输出:输出:我一定被执行!/n 3

    来个情景对话方便理解:如果程序存在异常,那么程序执行到catch代码块中。先执行e.printStackTrace();然后准备返回2,finally说等等,我还没执行,我先执行。catch的return说,好,那你先执行,执行完了给我说一声。然后finally就先执行第一句代码,然后直接返回3,并没有catch中return返回的机会。

    不管是遇到了一个return还是制造的异常,都先执行finally的语句

  • 像数据库链接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要手动的进行资源的释放。此时的资源释放,就需要声明在finally中。

六、异常处理方式二:throws + 异常类型

  • "throws + 异常类型"写在方法的声明处。指明此方法执行时,可能会抛出的异常类型。

    一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后的异常类型时,就会被抛出。异常代码后续的代码,就不再执行!

  • 体会:try-catch-finally:真正的将异常处理掉了

    throws的方式只是将异常抛给了方法的调用者。并没有真正将异常处理掉。(最多抛到main方法里,你就需要用try-catch-finally将他处理了)

    归根结底,最后都是需要用try-catch(-finally)来解决

  • 在开发过程中如何选择使用try-catch-finally还是throws?

    • 如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着如果子类重写的方法中有异常,必须使用try-catch-finally方式处理。
    • 执行的方法a中,先后又调用了另外的几个方法,这几个方法时递进关系执行的(比如:方法三要用到方法二得到的结果,方法二又需要方法一得到的结果)。我们建议这几个方法使用throws的方式进行处理。而执行的方法a可以考虑使用try-catch-finally方法进行处理。

七、如何自定义异常类?

  • 继承于先有的异常结构:RunTimeException、Excetion

  • 提供全局常量:serialVersionUID

  • 提供重载的构造器

    public class MyException extends RunTimeException {
          
          
        static final long serialVersionUID = -4321532432453543543L;
        
        public MyException() {
          
          
            
        }
        
        public MyException(String msg) {
          
          
            super(msg);
        }
    }
    

八、throw和throws的区别:

throw表示抛出一个异常类的对象,生成异常对象的过程。声明在方法体内

throws属于异常处理的一种方式,声明在方法的声明处。

Guess you like

Origin blog.csdn.net/ysf15609260848/article/details/104267023