编译时异常和运行时异常,它们两个有什么区别呢?我们先写个代码,再画个图,就恍然大悟了。
一、代码
1)编译时异常
//编译时异常
String time = "2030年1月1日";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日");
Date date = sdf.parse(time);
System.out.println(date);
parse()
解析方法会有一条红色波浪线
想要让代码成功运行,我们需要用鼠标点一下方法,alt + 回车 ,然后选择第一个,这才可以。
我们是这样的呢?其实就是因为 parse()
底层有 ParseException
,这个异常就是编译时期异常。
编译时期异常在编译阶段,必须要手动处理,否则代码报错。
2)运行时异常
在之前我们遇到的索引越界,就是一个标准的运行时期异常。
int[] arr = {1,2,3,4,5};
System.out.println(arr[10]); // ArrayIndexOutOfBoundsException
运行时异常在编译阶段是不会出现问题的,只有当真正运行的时候才会出现问题。
运行时异常在编译阶段是不需要处理的,是代码运行时出现的异常。
二、画图
一开始我们写的是Java文件,如果我想要运行代码,首先通过 javac
进行编译,编译成字节码文件,这个阶段叫做编译阶段。
在这个阶段中要处理的异常叫做:编译时异常。
例如我们刚刚书写的 ParseException
(日期解析日常),你不处理,代码就报错,这就是它最大的特点。
编译完后,需要通过 java命令
去运行代码,那么在运行时出现的异常就是:运行时异常。
它是 RuntimeException本身及其子类
,运行时异常它在编译阶段不需要处理,而是代码运行时出现的异常。
例如 ArrayIndexOutOfBoundsException
(数组索引越界异常),就算代码真的有问题了真的越界了,编译阶段也不会出现问题,只有代码运行的时候才会出现的异常。
三、扩展
你觉得Java为什么这么设计呢?为什么要分编译时异常
和运行时异常
呢?为什么不能把所有的异常都归为一类呢?
其实是有原因的,在编译的时候Java是不会运行代码的,只会检查语法是否错误,或者做一些性能的优化。
例如定义变量的类型错了,这个就是语法错误,在编译的时候Java会检查。
再比如说字符串拼接的优化机制:在编译之后,等号的右边就变成了最终的结果 "abc"
。
但是如果遇到索引越界,在编译的时候它是不知道的。
例如以下代码,先获取一个随机数,再根据随机数创建相应长度的数组,获取 10索引
上的元素,此时只有当代码真正运行了,才能确认代码的长度,才能确定索引是否超出范围。
因此像这种运行时发生的异常,是不能放到编译时的,只能在运行的时候才能确定它是否真的超出范围。
因此,索引越界异常是属于运行时发生的异常。
那这样问题又来了,那为什么不把所有的异常都放在下面的运行时呢?
因为编译型异常更多的在于提醒程序员检查本地信息,告诉程序员:如果有问题就会出现异常。
例如:日期解析日常,parse()
方法底层会涉及到本地时区的相关信息。
再比如说,后面我们会用 IO
去读取本地文件的数据,而这个文件 a.txt
也是在本地操作系统中的,因此在这个代码中,它也会有编译时异常,用来提醒程序员检查本地的 a.txt
是否真的存在。
而下面运行时期异常,它的核心不在于提醒,它就是由于代码出错而导致程序出现的问题。
四、这两种异常在开发中都包含哪些呢?
我们可以打开一下 API帮助文档
看一下。
搜索 Exception
,这里显示的所有的都是异常,其中 RuntimeException
就是运行时异常。
而除了 RuntimeException
以外,所有的都是编译时异常。
点进 RuntimeException
看一下,可以发现它也有很多子类。
RuntimeException
所有的子类都是运行时异常,都是由于我们在代码中参数写错了而出现的异常。
以 IndexOutOfBoundsException
为例,点进去看一下,可以发现它下面还有两个子类
五、总结
运行时异常和编译时异常的区别?
编译时异常,在语法中,它是直接继承 Exception
:除了 RuntimeException和它的子类
,其他都是编译时异常。编译阶段需要进行处理,作用在于提醒程序员。
运行时异常:RuntimeException本身和所有子类
都是运行时异常。在编译阶段是不会报错的,是程序运行时出现的异常。
这种异常一般是由于程序员代码写错了,参数传递错误代理的问题。