实际工作中,我们写的程序可能会遇到各种各样的问题。比如用户的输入不符合我们的要求;打开某个文件时这个文件可能不存在或者格式不对;读取数据库时数据库可能是空的;程序运行中内存或者硬盘可能满了,甚至Jvm也会报错等等。
遇到这些突发情况的时候我们的程序将会无法执行跑不下去。这种情况我们就叫做异常(Exception)。
异常的突然出现使我们的程序无法按照正常的流程执行,为了让我们的程序正常被执行,我们面对可能会发生的这些异常必须在代码中给出合理的处理,这就是异常处理机制。
要理解我们的Java异常处理是如何工作的,需要至少掌握以下三种类型。
检查性异常
最具代表性的异常,指用户错误或问题引起的异常,这种异常是我们写程序的时候无法被预见的。例如我们的程序需要打开某一个文件,而这个文件实际在磁盘中不存在时一个异常就发生了。
运行时异常
运行时异常是可能在我们写程序的时候被避免的异常,与检查性异常相反,运行时异常可以在编译的时候被忽略,因为语法上并没有任何问题。
ArrayIndexOutOfBoundsException(数组索引越界)
NullPointerException(空指针)
ArithmeticException(算数异常)
MissingResourceException(资源丢失)
ClassNotFoundException(不存在的类)
运行时异常通常是我们的程序产生了逻辑错误导致的,我们需要尽可能从逻辑的角度避免这类异常的发生。
错误(Error)
错误不是异常,而是程序脱离控制的问题。错误通常在代码中被忽略。比如栈溢出时,一个错误就发生了,这种错误在编译时是无法检测到的。
OutOfMemoryError(内存不足)
NoClassDefFoundError(类定义错误)
LinkageError(链接错误)
错误一般是由于JVM生成并抛出的,大多数错误与我们代码编写者执行的操作无关。
Java中,异常被当做对象处理。Java定义了一个基类java.lang.Throwable为所有异常的超类,超类往下又统分为Exception类和Error类。
Java给了我们五个关键字用来处理异常:
try
指定监控区域。
catch
捕获异常。
finally
善后处理。
throw
主动抛出异常,一般在方法中使用。
throws
同throw一样,主动抛出异常,区别是抛到方法外。
示例代码:
public class Demo20 { public static void main(String[] args) { int a = 1; int b = 0; try { //异常监视 System.out.println(a / b); } catch (Exception e) { //异常处理 System.out.println("被除数“b”不能为0"); } finally { System.out.println("finally常用来执行一些线程的关闭。"); } } }
运行结果:
示例代码:
public class Demo20 { public static void main(String[] args) { int a = 1; int b = 0; System.out.println(divide(a, b)); } public static int divide(int a, int b) { if (b == 0) { try { //直接在方法中抛出异常并处理。 throw new ArithmeticException(); } catch (ArithmeticException e) { System.out.println("被除数“b”不能为0"); return 0; } } else { return a / b; } } }
运行结果:
示例代码:
public class Demo20 { public static void main(String[] args) { int a = 1; int b = 0; try { System.out.println(divide(a, b)); } catch (ArithmeticException e) { System.out.println("被除数“b”不能为0"); } } //将异常往方法外抛出,在主程序进行处理。 public static int divide(int a, int b) throws ArithmeticException { return a / b; } }
运行结果:
通过以上示例代码我们可以发现:
只要是处理异常基本都会用到try和catch语句,但finally并不是必须的。
不管try和catch代码块有没有捕获到异常,finally总会执行。
一个try代码块后面可以跟数个catch代码块,根据捕获到的异常不同,采取不同的应对方式。
当我们使用多个catch代码块的时候范围必须从小到大捕获异常,因为程序是从上往下执行的,如果在之前的catch代码块捕获到了异常则后面的catch代码块不会执行。
因为Java将异常和错误当做类处理,所以catch捕获的时候实际上是实例化了一个当前异常的对象”e“。
throws往方法外抛异常是当前方法如果产生对应的异常才会抛,如果没有异常则不会抛出异常。
throw和throws不同,不管是否产生异常,throw是只要执行到这个代码语句就一定会抛一个异常!
热知识:在IDEA中使用Ctrl+Alt+T可以快速生成代码块。
因为所有的异常都属于Exception类,我们自定义异常实际就是继承Exception类。
异常类示例代码:
/** * 自定义一个异常,当变量 a > 10 时抛出。 */ public class MyException extends Exception { //私有字段 private int detail; //构造方法 public MyException(int a) { this.detail = a; } //Alt+Ins自动生成一个ToString框架用来打印异常信息 @Override public String toString() { return "MyException{" + "detail=" + detail + '}'; } }
测试示例代码:
/** * 自定义一个异常,当变量 a > 10 时抛出。 */ public class Application { public static void main(String[] args) { int a = 11; //捕获并处理异常 try { //调用抛异常的方法。 new Application().test(a); } catch (MyException e) { //直接打印异常信息到控制台 e.printStackTrace(); } } //写一个可能会抛出异常的方法,抛到主程序去处理。 public int test(int a) throws MyException { if (a > 10) { //主动抛出异常 throw new MyException(a); } //因为执行到异常后方法会“终止”所以这里就不写else了 return a; } }
测试结果:
因为catch捕获到之后没有对异常进行其他处理,只是单纯的打印了异常信息,所以看起来跟没处理一样,实际是跟直接抛是不同的哦~