在日常的程序开发中难免会出现遗漏并且就算代码没有问题可是由于程序运行环境的内存不够了,磁盘满了,网络连接问题等这些非正常的情况在java中都称之为异常。在java中对异常的处理有统一的异常处理机制,今天我来简单的介绍一下java中的异常处理。
首先我们来看一个在我们平时开发中比较常见的一个异常:NullPointerException。
public class TestNullPointerException { public static void main(String[] args) { String test = null; System.out.println(test.length()); System.out.println("end"); } }
上面的代码很简单,在执行到test.length()这行代码时会报NullPointerException,这时java虚拟机在发现test变量的值为null后代码没有办法执行就会启动异常处理机制,首先java虚拟机会创建一个异常的对象也就是类java.lang.NullPointerException的对象,然后在代码中查看有没有处理这个异常对象,如果没有java虚拟机就会使用默认的异常处理方式,也就是打印异常栈信息到控制台并退出程序。
在java中除了NullPointerException类还有很多异常类,所有的异常类都有一个共同的父类Throwable。Throwable类有两个子类分别是Error和Exception。其中Error类表示系统错误或资源耗尽,在我们的应用代码里无法处理这类异常比如内存溢出。Exception表示的是应用程序出现异常,它有很多的子类其中当然也包括NullPointerException。这里我要着重介绍的一个异常类叫做RuntimeException,它的名字叫做运行期异常,这里需要说明的是异常都是在代码运行时产生的只是有一些异常强制要求程序员在编写业务代码的时候处理否则代码在编译的时候会报错,这类异常叫做受检异常,相反未受检异常则没有这个要求。RuntimeException就是一个未受检异常,而Exception的其他子类包括Exception自己都是受检异常。
在java中对异常的处理的关键字有:try、catch、throws、throw、finally。首先我们来看一个try-catch的例子:
public class TestTryCatch { public static void main(String[] args){ try { String test = null; System.out.println(test.length()); }catch (NullPointerException e) { System.out.println("打印 NullPointerException"); } catch (Exception e) { System.out.println("打印 Exception:"+e.getMessage()); } System.out.println("end"); } }
程序输出:
打印 NullPointerException
end
其中try的代码块中放的是可能出现异常的代码,catch语句可以有多个来捕获不同的异常但是需要注意的是要把最具体的异常子类放在最前面因为如果父类异常类放在了子类的前面那个java会默认进入父类的catch代码块这是因为catch异常的匹配原则是进入第一个匹配的catch块。
throw关键字表示的就是抛出异常,它会触发java的异常处理机制。
public class TestThrow { public static void main(String[] args) { for (int i = 0; i <10; i++) { System.out.println("i="+i); if(i==5){ throw new RuntimeException("测试throw"); } } } }
控制台输出:
i=0 i=1 Exception in thread "main" java.lang.RuntimeException: 测试throw at com.java.exception.TestThrow.main(TestThrow.java:8) i=2 i=3 i=4 i=5
当我们的代码中throw出异常对象时会导致程序的退出。
说完了throw下面我要介绍一个和throw很像的关键字叫throws。这个关键字是用来声明一个方法可能抛出的异常,可以声明多个异常用“,”隔开。其中对于未受检异常是不要求使用throws声明但是对于受检异常则要求必须使用throws进行声明。
public class TestThrows { public void testThrows() throws Exception{ String str = "hello world"; System.out.println(str); throw new Exception(); } public void testNoThrows() { String str = "hello world"; System.out.println(str); throw new RuntimeException(); } }
上面代码中Exception是受检异常所以必须声明抛出而RuntimeException是未受检异常所以不要求声明抛出。
在学习java中的io流时我们知道流在使用完毕后是需要关闭的。但是前面我们学习到当程序运行出现异常时java的异常处理机制会终止程序的运行这时如果没有关闭流的话会导致资源的浪费,这时finally关键字就起到了作用。finally内的代码无论有无异常发生都会执行。
public class TestFinally { public static void main(String[] args) { try { String test = null; System.out.println(test.length()); }finally { System.out.println("finally run"); } System.out.println("end"); } }
上面代码执行的结果为:
finally run Exception in thread "main" java.lang.NullPointerException at com.java.exception.TestFinally.main(TestFinally.java:7)
finally语句还有一些细节,在try语句或catch语句块中ruturn语句的执行顺序是return语句在finally语句执行之后执行但是finally语句不能改变返回值。在finally代码块中如果包含return语句那么finally代码块中的return值会覆盖函数本身的return值并且如果在finally代码块中抛出了异常会覆盖方法本身的异常,所以在finally代码块中我们不建议使用return语句和抛出异常。