Java之异常处理笔记
Java值异常处理学习笔记
1、异常概述
异常:尽管代码写的很完美,但是在系统运行过程中任然会出现一些问题,例如用户的输出格式错误、文件不存在、网络中断、物理限制等等。将程序中不正常的情况称为异常。异常分为两类:Error和Exception两种。
Error:
Java
虚拟机无法解决的严重问题,不能通过编写代码来解决问题,例如栈溢出等等。Exception:其它编程错误或者偶然因素导致的问题,可是使用针对性的代码进行解决。
在Java中异常对象都派生于Throwable
这个类,如果Java中内置的异常不满足需求,可以创建自定义异常类,下面是异常的结构图。
非检查型异常:Error、
RunTimeException
(运行时异常,运行时才会报错),例如空指针、数组越界。检查型异常:其他异常情况(编译的时候就会报错),例如I/O流,编译的时候会检查这个文件是否存在,可以对异常进行处理。
常见异常:
- 运行时异常:
NullPointerException
、ArrayIndexOutOfBoundsException
、ClassCastException
、NumberFormatException
等等。 - 编译时异常:
ClassNotFoundException
、IOException
等
2、异常处理机制
通常在写程序时,我们都需要检测某个数据是否为null、格式是否正确等这些因素,我们可以通过if-else来先检测这些数据,但是这样的代码过于臃肿,不建议使用,所以采用Java中的异常处理机制,将可能出现错误的程序集中在一个,然后进行统一处理。
异常处理:抓抛模型
抛:在程序运行过程中,如果出现异常就会在代码异常后面生成一个对应异常类的对象并将对象抛出,其后面的代码就不在继续执行。
抓:就是异常处理的方式,①:try-catch-finally。②:throws
2.1、try-catch 捕获异常
捕获格式
try{ // 可能出现异常的代码}catch (异常类型1 e1){ // 进行异常处理 }catch (异常类型2 e2){ }
注意事项:
- 如果产生的异常在catch中没有匹配到任何一个,程序还是会终止,输出错误信息。
- 异常一旦匹配到对应异常,就不在继续匹配,就跳出try-catch。
- 范围小的异常应该写在上面,大的范围写在下面,先匹配小的异常,再匹配大的异常(子类放在父类前面),如果顺序放反会报错。
- 异常块中声明的变量,在外面不能使用,可以先在外面声明,然后再到块中使用。
- try-catch可以嵌套使用。
异常对象的常用信息
try{ String s = "abc"; int num = Integer.parseInt(s); // abc不能转换成数字,就会产生异常 System.out.println("产生异常后,在代码块中的后面程序就不在执行");}catch (NumberFormatException e){ // 进行异常的处理 System.out.println(e.getMessage()); // 异常的信息 e.printStackTrace(); // 输出异常的栈轨迹信息}
其它异常捕获格式:可以在一个catch中捕获多个异常,这就需要这些异常的处理方式都一样。
try{ String s = "abc"; int num = Integer.parseInt(s); // abc不能转换成数字,就会产生异常 System.out.println("产生异常后,在代码块中的后面程序就不在执行");}catch (NumberFormatException | NullPointerException e){ // 进行异常的处理 System.out.println(e.getMessage()); // 异常的信息 e.printStackTrace(); // 输出异常的栈轨迹信息}catch (Exception e){ e.printStackTrace();}
只有当捕获的异常类型彼此间不存在子类关系时才能使用这种方式,使用这种方式的e是一个final修饰的变量,不能修改。而且这样代码看起来更加简单、高效,生成的字节码只包含对应公共catch的一个代码块。
2.2、finally 语句
finally: 异常处理中,一定会执行的语句块,即使异常没有被处理。
try{ String s = "abc"; int num = Integer.parseInt(s); // abc不能转换成数字,就会产生异常 System.out.println("产生异常后,在代码块中的后面程序就不在执行");}catch (NumberFormatException | NullPointerException e){ // 进行异常的处理 System.out.println(e.getMessage()); // 异常的信息 e.printStackTrace(); // 输出异常的栈轨迹信息}catch (Exception e){ e.printStackTrace();}finally { System.out.println("我一定会执行");}
catch处理语句中出现异常:
存在return:
经典面试题:
public int method(){ try{ int num = 0; num++; return num; }catch (ArithmeticException e){ e.printStackTrace(); return 0; }finally { return 100; } // 返回100,即使没有异常,但是返回之前也会执行finally中的代码,提前返回100}
public int method(){ try{ int num = 0; num++; num = num / 0; return num; }catch (ArithmeticException e){ e.printStackTrace(); return 0; }finally { return 100; } // 返回100,除以异常后返回0之前会执行finally的代码}
public int method(){ try{ int num = 0; num++; num = num / 0; return num; }catch (ArithmeticException e){ System.out.println(1 / 0); return 0; }finally { return 100; } // 返回100,即使在处理异常时又出现异常,也会在程序停止前执行finally中的代码}
finally应用场景:
- 当资源需要手动关闭时,可以在finally中进行关闭
public static void main(String[] args) { FileInputStream fis = null; try{ File file = new File("hello.txt"); fis = new FileInputStream(file); int read = fis.read(); while (read != -1){ System.out.print((char) read); read = fis.read(); } } catch (Exception e){ e.printStackTrace(); } finally { // 使用finally来手动关闭资源 if (fis != null){ try { fis.close(); } catch (IOException e) { e.printStackTrace(); } } }}
2.3、throws 抛出异常
格式:使用 throws 关键字,作用于方法后面。
public static void method1() throws 异常类型1,异常类型2{ }
使用了throws关键字的方法,指明这个方法可能抛出异常,一旦方法执行时产生异常就匹配对应throws后面的异常类型,如果匹配到就生成一个对应异常对象,并且方法就立刻停止,后面的程序就不在继续执行。
也可以中上一层的代码中,通过try-catch-finally进行捕获处理,这样就不需要继续向上抛出异常对象。main中的异常必须捕获处理,不能交给虚拟机处理,可能出现意想不到的错误。throws只是将异常抛给方法的调用者,并没有处理。
2.4、子类重写异常
子类重写父类时,子类重写方法抛出的异常必须小于或等于父类中被重写方法中抛出的异常。
2.5、手动抛出异常( throw)
前面两种方式是程序中遇到错误自动抛出一个异常对象,我们也可以通过 throw 手动抛出一个异常,是自己手动产生一个异常对象。
throw只能抛出异常体系中的类,不能是其他类。
2.6、try-catch-Resources
在Java7
之前,开启的资源需要通过finally语句块去手动关闭,不然会产生资源泄露。但是在Java7
之后,try-catch-Resources可以自动关闭开启的资源。
格式:
public static void main(String[] args) { File file = new File("hello.txt"); // try(开启需要关闭的资源,可以有多个) try ( FileInputStream fis = new FileInputStream(file) ){ int read = fis.read(); while (read!=-1){ System.out.print((char) read); read = fis.read(); } }catch (IOException e){ e.printStackTrace(); }}
无论try-catch块如何退出,最后都会执行fis.close()
语句,就是资源关闭的语句。
3、自定义异常
自定义异常步骤:
- 继承现有的异常父类:
RunTimeException
(运行时异常)、Exception。 - 提供一个
serialVersionUID
。 - 提过无参和有参构造器。
// 自定义数据格式异常public class DataFormatException extends RuntimeException{ static final long serialVersionUID = -7034898730745766939L; public DataFormatException() { } public DataFormatException(String message) { super(message); }}
使用自定义异常:
public static void main(String[] args) { int age = -9; if (age>0){ age++; }else { throw new DataFormatException("数据错误"); }}
4、异常使用规则
- 异常处理不能代替简单的测试:只在异常的情况下使用异常。
- 不要过分细化异常:不要将每个出现异常的代码都是用try-catch来包裹,通常使用一个大的try-catch来包裹。
- 充分利用异常的层次性:不要直接捕获大的异常,应该捕获一个适合的子类异常或者创建自己的异常。
- 不要压制异常:不出现异常时正常执行,但是一旦出现异常一定要展示出来。
- 检查异常时,苛刻比放任更好:如果方法返回一个虚拟值,不需要的值,可以直接抛出异常。
- 多向上传递异常:高成的方法更加明确出现异常时的处理方式(早抛出,晚捕获)。