你之所以能优于别人,正是因为你坚持了别人所不能坚持的。
本文相关代码在我的Github,欢迎Star~
https://github.com/zhangzhibo1014/DaBoJava异常指不期而至的各种状况,异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
比如说,你的代码少了一个分号,那么运行出来结果是提示是错误 java.lang.Error
;如果你用 System.out.println(11/0)
,那么你是因为你用0做了除数,会抛出 java.lang.ArithmeticException
的异常。
要理解Java异常处理是如何工作的,你需要掌握以下三种类型的异常:
异常都是从 Throwable
类派生出来的,而 Throwable
类是直接从 Object
类继承而来。
所有的异常类是从 java.lang.Exception
类继承的子类。而 Exception
异常下又主要分为两大类异常,一个是派生于 RuntimeExcption
的异常,一个是除了 RuntimeExcption
体系之外的其他异常。
RuntimeExcption
异常(运行时异常)通常有以下几种:
null
异常 | 描述 |
---|---|
ArithmeticException | 当出现异常的运算条件时,抛出此异常。例如,一个整数"除以零"时,抛出此类的一个实例。 |
ArrayIndexOutOfBoundsException | 用非法索引访问数组时抛出的异常。如果索引为负或大于等于数组大小,则该索引为非法索引。 |
ArrayStoreException | 试图将错误类型的对象存储到一个对象数组时抛出的异常。 |
ClassCastException | 当试图将对象强制转换为不是实例的子类时,抛出该异常。 |
IllegalArgumentException | 抛出的异常表明向方法传递了一个不合法或不正确的参数。 |
IllegalMonitorStateException | 抛出的异常表明某一线程已经试图等待对象的监视器,或者试图通知其他正在等待对象的监视器而本身没有指定监视器的线程。 |
IllegalStateException | 在非法或不适当的时间调用方法时产生的信号。换句话说,即 Java 环境或 Java 应用程序没有处于请求操作所要求的适当状态下。 |
IllegalThreadStateException | 线程没有处于请求操作所要求的适当状态时抛出的异常。 |
IndexOutOfBoundsException | 指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。 |
NegativeArraySizeException | 如果应用程序试图创建大小为负的数组,则抛出该异常。 |
NullPointerException | 当应用程序试图在需要对象的地方使用 null 时,抛出该异常 |
NumberFormatException | 当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。 |
SecurityException | 由安全管理器抛出的异常,指示存在安全侵犯。 |
StringIndexOutOfBoundsException | 此异常由 String 方法抛出,指示索引或者为负,或者超出字符串的大小。 |
UnsupportedOperationException | 当不支持请求的操作时,抛出该异常。 |
一般来说, RuntimeException
都是代码逻辑出现问题。
非 RuntimeException
(受检异常,Checked Exception)一般有:
异常 | 描述 |
---|---|
ClassNotFoundException | 应用程序试图加载类时,找不到相应的类,抛出该异常。 |
CloneNotSupportedException | 当调用 Object 类中的 clone 方法克隆对象,但该对象的类无法实现 Cloneable 接口时,抛出该异常。 |
IllegalAccessException | 拒绝访问一个类的时候,抛出该异常。 |
InstantiationException | 当试图使用 Class 类中的 newInstance 方法创建一个类的实例,而指定的类对象因为是一个接口或是一个抽象类而无法实例化时,抛出该异常。 |
InterruptedException | 一个线程被另一个线程中断,抛出该异常。 |
NoSuchFieldException | 请求的变量不存在 |
NoSuchMethodException | 请求的方法不存在 |
受检异常是编译器要求必须处理的异常,必须使用 try catch
处理,或者使用 throw
抛出,交给上层调用者处理。
当程序运行时数据出现错误或者我们不希望发生的情况出现的话,可以通过抛出异常来处理。
异常抛出语法:
throw new 异常类(); //抛出异常 public static void fun1() { Integer a = 1; Integer b = null; // 如果 a 或者 b 为 null 则抛出异常 if (a == null || b == null) { throw new NullPointerException(); } else { System.out.println(a+b); } }
throws
用于声明异常,表示该方法可能会抛出的异常。如果声明的异常中包括 checked
异常(受检异常),那么调用者必须捕获处理该异常或者使用 throws
继续向上抛出。 throws
位于方法体前,多个异常之间使用 ,
分割。
public static void main(String[] args) throws FileNotFoundException { fun2(); } //声明异常 public static void fun2() throws FileNotFoundException { new FileInputStream("a.txt"); }
throw
用于抛出异常对象,后面跟的是异常对象; throw
用在函数内。 throws
用于抛出异常类,后面跟的异常类名,可以跟多个,用逗号隔开。 throws
用在函数上 通常抛出异常后,还需要将异常捕获。使用 try
和 catch
语句块来捕获异常,有时候还会用到 finally
。
对于上述三个关键词所构成的语句块, try
语句块是必不可少的, catch
和 finally
语句块可以根据情况选择其一或者全选。你可以把可能发生错误或出现问题的语句放到 try
语句块中,将异常发生后要执行的语句放到 catch
语句块中,而 finally
语句块里面放置的语句,不管异常是否发生,它们都会被执行。
捕获异常对于系统而言,其开销非常大,所以应尽量减少该语句块中放置的语句。
public class Demo1 { public static void main(String[] args) { try{ //要检查的程序语句 System.out.println("I am try block."); Class<?> tempClass = Class.forName(""); // 声明一个空的Class对象用于引发“类未发现异常” System.out.println("Bye! Try block."); }catch (ClassNotFoundException e) { //异常发生时的处理语句 System.out.println("I am catch block."); e.printStackTrace(); //printStackTrace()的意义在于在命令行打印异常信息在程序中出错的位置及原因 System.out.println("Goodbye! Catch block."); }finally { //一定会执行的语句 System.out.println("I am finally block."); } } }
catch
不能独立于 try
存在。 try/catch
后面添加 finally
块并非强制性要求的。 try
代码后不能既没 catch
块也没 finally
块。 try, catch, finally
块之间不能添加任何代码。 finally
很有用,主要用户关闭资源。无论是否发生异常,资源都必须进行关闭 在一段代码中,可能会由于各种原因抛出多种不同的异常,而对于不同的异常,我们希望用不同的方式来处理它们,而不是笼统的使用同一个方式处理,在这种情况下,可以使用异常匹配,当匹配到对应的异常后,后面的异常将不再进行匹配。
public class Demo2 { public static void main(String[] args) { try{ new FileInputStream(""); } catch (FileNotFoundException e) { System.out.println("IO 异常"); } catch (Exception e) { System.out.println("发生异常"); } } }
在处理异常时,并不要求抛出的异常同 catch
所声明的异常完全匹配,子类的对象也可以匹配父类的处理程序。比如异常 A 继承于异常 B,那么在处理多个异常时,一定要将异常 A 放在异常 B 之前捕获,如果将异常 B 放在异常 A 之前,那么将永远匹配到异常 B,异常 A 将永远不可能执行,并且编译器将会报错。
自定义一个异常类非常简单,只需要让它继承 Exception 或其子类就行。在自定义异常类的时候,建议同时提供无参构造方法和带字符串参数的构造方法,后者可以为你在调试时提供更加详细的信息。
在 Java
中你可以自定义异常。编写自己的异常类时需要记住下面的几点。
Throwable Exception RuntimeException
public class Demo3 { public static void main(String[] args) { int number = 5; for(int i = 4; i > -1; i--) { if (i == 0) { throw new MyException("这是一个异常"); } System.out.println(number / i); } } } //自定义异常类 ,继承于ArithmeticException class MyException extends ArithmeticException { //实现无参构造器 public MyException() { } //实现参数构造器,可将提示信息作为异常结果输出 public MyException(String msg) { super(msg); } }
当异常抛出后,我们可以通过异常堆栈追踪程序的运行轨迹,以便我们更好的 DEBUG
。
public class Demo4 { public static void method1() { method2(); } public static void method2() { throw new NullPointerException(); } public static void main(String[] args) { try{ method1(); } catch (Exception e) { // 打印堆栈轨迹 e.printStackTrace(); } } } 打印结果: java.lang.NullPointerException at Demo4.method2(Demo4.java:10) at Demo4.method1(Demo4.java:6) at Demo4.main(Demo4.java:15)
通过上面的异常堆栈轨迹,在对比我们方法的调用过程,可以得出异常信息中首先打印的是距离抛出异常最近的语句,接着是调用该方法的方法,一直到最开始被调用的方法。 从下往上看 ,就可以得出程序运行的轨迹。
try
异常的捕获、抛出和异常处理是维持代码健壮性的重要条件。灵活使用异常及处理,不仅能最大限度的避免出错,也能增加软件的容错机制。
相关代码记录于 GitHub 中,欢迎各位伙伴 Star !
有任何疑问 微信搜一搜 [程序猿大博] 与我联系~
如果觉得对您有所帮助,请 点赞 , 收藏 ,如有不足,请评论或私信指正!谢谢~