java Exception 异常

什么是异常

  • 程序运行时发生的不被期望的事件,阻止了程序按照预期正常执行

  • 常见的错误分类

  • 编译错误

  • 运行时错误

  • 逻辑错误

java中的异常类

  • java.lang 包下的 Throwable 类,这个类是所有异常类的父类,
  • Throwable 类有 2个子类
    • Error 出错后,程序无法处理 ,如 OutOfMemoryError
    • Exception 出错后 ,程序可处理 , 如 ArrayIndexOutOfBoundException
  • 异常两大类
    • 可查异常(编译器要求必须要处理的异常,运行之前就报错)
      • RuntimeException 及其子类除外 ,其它的Exception类及其子类 ,如 ClassNotFoundException ,NoSuchMethodException ⽅法不存在
    • 不可查异常(编译器不要求强制处理的异常,运行时报错)
      • 运行时异常 RunTimeException 及其子类 和 Error类 ,如ArrayIndexOutOfBoundException ,NullPointerException

所以 ,Exception 类可分为 可查异常 和不可查异常

  • Throwable 常用方法
    • public String getMessage()
      • 获取异常的详细信息
    • public Throwable getCause()
      • 获取异常原因
    • public void printStackTrace()
      • 打印错误的堆栈信息 ,错误原因和位置
    • public StackTraceElement [] getStackTrace()
      • 堆栈层次的数组,下标为0的元素代表栈顶,最后⼀个元素代表⽅法调⽤堆栈的栈底

public StackTraceElement [] getStackTrace()

返回一个表示该线程堆栈转储的堆栈跟踪元素数组。如果该线程尚未启动或已经终止,则该方法将返回一个零长度数组。如果返回的数组不是零长度的,则其第一个元素代表堆栈顶,它是该序列中最新的方法调用。最后一个元素代表堆栈底,是该序列中最旧的方法调用。

捕获异常之 Try Catch

  • 格式

    try{
    // 可能发⽣异常的代码
    }catch(AExceptionName e){
    //出异常的时候处理
    }catch(BExceptionName e){
    //可以有多个catch
    }finally{
    
    }
    
    • catch 不能独立存在 ,必须依赖于try
    • finally 可有可无,而且finally里的代码 一定会执行
    • finally 代表结束 之后不可再跟 catch模块
    • try 代码块包裹可能发生异常的代码,将异常捕获,但不会终止整个方法的执行
    • 如果异常不用try 捕获,那么会终止整个方法 ,并报错打印堆栈信息
    • 对于多个catch模块,当代码中发⽣异常,异常被抛给第⼀个 catch 块, 如果不匹配则继续往下⼀个catch进⾏传递
    • try,catch和finally块有两种可能的组合:try-catch-finally或try-finally或try-catch。
    • 总结 ;try 必须存在,后可接finally,catch, 或者三个都有
    • try 不可单独存在
    • try-catch里面定义的变量,只能在try - catch内部使用 , finally 不可访问;
    • finally 最初用于资源的关闭
    • finally 代码块里面有异常的话 ,finally里的语句执行到异常处终止,终止整个方法并直接报错
    • catch 代码块里有异常的话,也会遇到异常语句终止方法,报错,但会执行 finally 语句

异常处理之 throws/throw

  • throws 声明异常 往外抛出 ,异常不可无限向上抛出,最终必须由调用者处理

    public class Main {
     public static void readChar() throws
    IOException,RemoteException {
     int input = System.in.read(); 
     }
    }
    
    • throws⼦句放在⽅法参数列表的右括号之后,⼀个⽅法可以声明抛出多个异常,

      多个异常之间⽤逗号隔开

      try catch中捕获了异常,捕获⾃⼰处理然后继续往外⾯抛异常

      throw 语句

      throw new ExceptionName(“异常信息”);

      eg:throw new IOException(“File not found”);

      public class ThrowMain {
      
          public static void main(String[] args) {
      
              try {
                  divide(2,0);
              } catch (Exception e) {
                  System.out.println("main");
                  System.out.println(e.getMessage());
              }
      
          }
      
          public static int divide(int num1,int num2) throws  Exception{
              try {
                  return num1 / num2;
              }catch (Exception e){
                  System.out.println("出异常!");
                  throw new Exception("参数异常!!!");
              }
             
          }
      }
      
      
      
      • throw 和 throws 搭配 一般用于自定义异常
      • 单独使用throw 编译出错, 报错信息: java: 未报告的异常错误java.lang.Exception; 必须对其进行捕获或声明以 便抛出
      • throw之后的语句不会执行, 程序在此中断

      总结:当抛出⼀个被检查的异常,我们必须使⽤try-catch块来处理它,或者在⽅法声明中使⽤ throws⼦句继续往外抛

java 自定义异常

  • 为什么要使⽤⾃定义异常

    • 当前JDK内置的异常不满⾜需求,项⽬会出现特有异常
    • ⾃定义异常可以让业务更清晰
  • 如何进⾏⾃定义异常

    • 异常都是继承⾃Exception类,所以我们要⾃定义的异常也需要继承这个基类。

    • 
      //我们自定义一个NoException 异常类
      public class NoException  extends Exception {
          /**
           * 自定义异常
           *
           */
          
          private String msg;
      
          public NoException(){
              super();
          }
          public NoException( String msg){
              //异常信息
              super(msg);
              this.msg=msg;
          }
      
      
          public String getMsg() {
              return msg;
          }
      
          public void setMsg(String msg) {
              this.msg = msg;
          }
      }
      

      这里记录以下为啥还需要自定义一个msg

      Exception 类中并没有定义 message属性 ,在Throwable类中定义了相关属性 ,但没有设置set方法

    • 使用

    •   public static void main(String[] args) {
              try {
                  func();
              } catch (NoException e) {
                  System.out.println( e.getMsg());
              }
          }
          public  static void func()throws  NoException{
              throw new NoException("asdd");
          }
      
  • 对于不可查异常 和 error ,若使用try -catch 或者 throws 处理 ,编译不会出错 ,程序在运行时会自动抛出异常

异常屏蔽

  • 什么是异常屏蔽
  • 如果catch代码块中捕获了异常,但触发了新的异常,那么finally捕获并且重抛的异常会是后一个异常,这样原本的异常就会被忽略掉。

  • 如果finally代码块中也抛出了异常,那么会是这个异常向上传递,try中的异常也就被“屏蔽”了。

这样的情况对bug调试很不利。

public class ExceptionShield {
    public static void main(String[] args) {
        testExceptionShield();
    }
    private static void testExceptionShield() {
        try {
            double a = 1 / 0;
            System.out.println(a);
        } catch (Exception e) {
            int[] a = {1, 2};
            System.out.println(a[2]);
        } finally {
            System.out.println("finally");
        }
    }
}


//运行结果

finally
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
	at com.zzz.exception.ExceptionShield.testExceptionShield(ExceptionShield.java:15)
	at com.zzz.exception.ExceptionShield.main(ExceptionShield.java:6)

可以看到,try内的原始错误应该为 java.lang.ArithmeticException: / by zero 异常,但却被catch内的新异常给“屏蔽”了。

  • 解决方法 Suppressed()

    • java7 中 增加了addSuppressed方法把这些被抑制的方法记录下来。被抑制的异常会出现在抛出的异常的堆栈信息中,也可以通过getSuppressed方法来获取这些异常。这样做的好处是不会丢失任何异常,方便开发人员进行调试。

          private static void testExceptionShield() {
              try {
                  double a = 1 / 0;
                  System.out.println(a);
              } catch (Exception e) {
                  try {
                      int[] a = {1, 2};
                      System.out.println(a[2]);
                  } catch (Exception exception) {
                      exception.addSuppressed(e);
                      throw exception; //抛出
                  }
              } finally {
                  System.out.println("finally");
              }
          }
      
      
      //运行结果
      
      Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 2
      	at com.zzz.exception.ExceptionShield.testExceptionShield(ExceptionShield.java:16)
      	at com.zzz.exception.ExceptionShield.main(ExceptionShield.java:5)
      	Suppressed: java.lang.ArithmeticException: / by zero
      		at com.zzz.exception.ExceptionShield.testExceptionShield(ExceptionShield.java:11)
      		... 1 more
      
      

try - with - resources

  • 作用
  • Java7 中专门构造了这个try-with-resource的语法糖,它的主要目的是精简资源打开关闭的用法,

不用手动finally ,同 时它还会在字节码层面上自动使用Suppressed异常功能,

  • 这里的resources可以是文件资源 ,也可以是创建的对象 :(所有实现了 java.lang.AutoCloseable 接口(其中,它包括实现了 java.io.Closeable 的所有对象),可以使用作为资源。)
  • 语法
// try 后放资源,就可自动关闭
try(;;){  //多个资源以分号隔开  
    
}catch(){
    
}

// eg:

public class Main2 {    
    public static void main(String[] args) {
        try(ResourceSome some = new ResourceSome();
             ResourceOther other = new ResourceOther()) {
            some.doSome();
            other.doOther();
        } catch(Exception ex) {
            ex.printStackTrace();
        }
    }
}
 //  实现 AutoCloseable 接口
class ResourceSome implements AutoCloseable { 
    void doSome() {
        System.out.println("do something");
    }
    @Override
    public void close() throws Exception {
        System.out.println("some resource is closed");
    }
}

class ResourceOther implements AutoCloseable {
    void doOther() {
        System.out.println("do other things");
    }
    @Override
    public void close() throws Exception {
        System.out.println("other resource is closed");
    }
}


  • 资源关闭顺序 ,遵从 后开先闭原则
  • try结束后自动调用的close方法,这个动作会早于finally和catch里调用的方法
  • 不管是否出现异常,try()里的实例都会被调用close方法
  • 凡是实现了AutoCloseable接口的类,在try()里声明该类实例的时候,在try结束后,close方法都会被调用
  • try 块发生异常,然后自动调用 close 方法,如果 close 也发生异常,catch 块只会捕捉 try 块抛出的异常,close 方法的异常会在catch 中被压制,但是你可以在catch块中,用 Throwable.getSuppressed 方法来获取到压制异常的数组。
  • try - with 结构中 ,catch 不能访问 try里声明的变量
  • try-with-recourse 中,try 块中抛出的异常,在 e.getMessage() 可以获得,而调用 close() 方法抛出的异常在e.getSuppressed() 获得。
package Exception;

public class TryWith {
    public static void main(String[] args) {
        
        try( Resource r=new Resource()){
            int a= 2/0;
        } catch (Exception e) {
            System.out.println(e.getMessage());
            Throwable [] th = e.getSuppressed();
            for (int i = 0; i < th.length; i++) {
                System.out.println(th[i].getMessage());
            }
        }
    }
}
class Resource implements AutoCloseable{

    @Override
    public void close() throws Exception {
        System.out.println("资源关闭");
        throw new ArrayIndexOutOfBoundsException("数组溢出!");

    }
}
  • 为啥要在 try 里声明变量呢? try 外面声明 就不可用 ;

  • 防止声明的变量引用改变,导致资源释放不成功;

  • 为此 ,可以加 final 修饰变量

  • jdk 7 和 8中

    •   public static void main(String[] args) {
             final Resource r=new Resource();
              try(Resource ri= r){
                  int a= 2/0;
              } catch (Exception e) {
                  System.out.println(e.getMessage());
              }
        }
      
  • jdk 9 以后 可以省略 final 和 try() 直接使用 变量引用

    // 我用的 jdk 11 测试的  
    public static void main(String[] args) {
          final Resource r=new Resource();
            Resource r1=new Resource();
            try(r ; r1){
                int a= 2/0;
            } catch (Exception e) {
                System.out.println(e.getMessage());
    

面试 try -catch -finally里加return ,throw 的问题

throw 和 return都会终止方法;

  • finally覆盖catch
    • 如果finally有return会覆盖catch里的throw,同样如果finally里有throw会覆盖catch里的return。
    • 如果catch里和finally都有return, finally中的return会覆盖catch中的。throw也是如此。
  • catch有return而finally没有:
    • 当 try 中抛出异常且catch 中有 return 语句,finally 中没有 return 语句, java 先执行 catch 中非 return 语句,再执行 finally 语句,最后执行 catch 中 return 语句。
  • try有return语句,后续还有return语句,分为以下三种情况:
    • 如果finally中有return语句,则会将try中的return语句”覆盖“掉,直接执行finally中的return语句,得到返回值,这样便无法得到try之前保留好的返回值。
    • 如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后,会接着执行try中的return语句,返回之前保留的值。
    • 如果finally中没有return语句,但是改变了要返回的值,这里有点类似与引用传递和值传递的区别,分以下两种情况,:
      • 如果return的数据是基本数据类型或文本字符串,则在finally中对该基本数据的改变不起作用,try中的return语句依然会返回进入finally块之前保留的值。
      • 如果return的数据是引用数据类型,而在finally中对该引用数据类型的属性值的改变起作用,try中的return语句返回的就是在finally中改变后的该属性的值。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值