2、异常处理
出现了Error
会造成程序直接无法运行,所以捕获Error的异常的意义不大,毕竟你程序都直接挂了,捕捉了也没有很大的意义,你需要做的就是在开发环境提前观察、发现。
所以我们在编程的时候不用刻意预处理Error
错误,但是出现了就必须解决。
异常的处理分两种:
- 抛出
- 捕获
# 异常抛出
异常的显式抛出使用throws
关键字处理。
# throws关键字
throws 表示此方法不处理异常,而交给方法调用者进行处理。
简单地说就是 : 我这段代码很有可能会抛出错误,所以我用了 throws
把错误抛出,你要调用我的方法,你也要抛出错误。
语法:
public 返回值类型 方法名称(参数列表,,,)throws 异常类1,异常类2{
}
# throw关键字
throw
表示直接抛出一个异常,作用于方法函数内,如果异常没有捕获,则立马返回,把异常抛出给调用者。异常发生的时候,会立即返回,所以下面的代码就不会执行了。
接着上一章的例子,五菱宏光生产商知道我可能会把轮胎安装错误,于是它显式的告诉我:更换轮胎 有可能会出错,你必须要注意错误。
但是我并不在意,还是一意孤行,有错误就直接抛出吧。
throws、throw 是一种消极的异常处理
eg:
public class WuLingHongGuang {
private String[] weels = {"左1", "左2", "后1", "后2", "备胎1"};
//throws 关键字,表示更换轮胎方法可能会抛出异常
void changeWeel(int index, String weelName) throws Exception {
if (index > weels.length) {
throw new Exception("ERROR!!!轮胎安装位置错误了!"); //throw 关键字,位置错误 直接抛出异常
}else {
weels[index] = weelName;
}
}
}
class HaC {
public static void main(String[] args) throws Exception { //addWeel() 会抛出异常,所以调用者也要处理这个异常,抛出 or 捕获
WuLingHongGuang wuLingHongGuang = new WuLingHongGuang();
wuLingHongGuang.changeWeel(10, "备胎2");
System.out.println("更换成功"); //抛出了异常,就直接返回了,下面的代码不会执行
}
}
输出:
Exception in thread "main" java.lang.Exception: ERROR!!!轮胎安装位置错误了!
at com.hac.异常.WuLingHongGuang.changeWeel(WuLingHongGuang.java:13)
at com.hac.异常.HaC.main(WuLingHongGuang.java:27)
可以看到,void addWeel(int index, String weelName) throws Exception
方法表示它可能会抛出错误,而在方法函数内 有一个判断,如果位置错误,就直接抛出 throw new Exception("ERROR!!!轮胎安装位置错误了!")
,这可以很友好的把错误直接打印在控制台。
异常抛出后 ,System.out.println("更换成功");
就不会执行了。
还有就是调用者Hac的main方法也需要抛出Exception,因为在这里Exception它是一个父类异常,它包含了CheckedException
和unCheckedException
,但是它并不知道是哪一个,所以一律需要处理,不然就不能通过编译。
可以看到,如果main方法不加throws Exception
,会报错:
# 异常捕获
throw 只是抛出了异常,抛出给了调用者,调用者又简单的抛出了,但是调用者能否直接处理这个错误呢?抛出者能否直接处理这个异常而不是抛出呢?
答案是可以的,我们可以捕获这个异常。
异常捕获的关键字是 try-catch-finally
# try ... catch...finally 关键字
使用try来捕获有可能出错的代码,然后在catch里面进行处理。
语法:
try{
//TODO
}catch(IOException e){
//TODO 可能出错的代码
}catch(Exception e){
//TODO
}finally{
}
注意点:
- finally可以不写,无论是否报错,finally最后一定会执行
- try不能单独存在,要么接catch,要么接finally
- 存在多个
catch
的时候,catch
的顺序非常重要:异常的子类必须写在前面,因为只会执行一个catch。
我知道五菱宏光更换轮胎可能会出错,为了以防万一,我决定把错误捕捉,防止错误影响我的其他操作:
eg:
public class WuLingHongGuang {
private String[] weels = {"左1", "左2", "后1", "后2", "备胎1"};
void changeWeel(int index, String weelName) throws Exception { //throws关键字,表示方法可能会抛出异常
if (index > weels.length) {
throw new Exception("ERROR!!!轮胎安装位置错误了!"); //throw 关键字,直接抛出异常
}else {
weels[index] = weelName;
}
}
}
class HaC {
public static void main(String[] args) {
WuLingHongGuang wuLingHongGuang = new WuLingHongGuang();
try {
wuLingHongGuang.changeWeel(10, "备胎2"); //捕获异常
System.out.println("更换成功"); //报错并不会执行
} catch (Exception e) {
e.printStackTrace(); //把错误打印出来
}finally {
System.out.println("更换完毕...");
}
}
}
输出:
java.lang.Exception: ERROR!!!轮胎安装位置错误了!
at com.hac.异常.WuLingHongGuang.changeWeel(WuLingHongGuang.java:13)
at com.hac.异常.HaC.main(WuLingHongGuang.java:28)
更换完毕...
printStackTrace()
表示打印方法的调用栈(方法之间的调用关系),对于调试错误非常有用,可以直接定位到出错的行数,上面输出的错误就是通过e.printStackTrace();
输出的。
try ... catch...finally 是一种主动的异常处理方式
# RuntimeException和Exception的区别
有一天,五菱宏光的厂商告诉我,五菱宏光更换轮胎换错了也不是什么大事,你不用处理了,交给车的轮轴处理吧,反正也能转动(JVM),这种就是非检查异常 unCheckedException
,而不是用户(程序员)处理。
对应代码就是抛出的异常改一下,改成RuntimeException,因为RuntimeException属于 unCheckedException
,我可以处理,也可以不处理:
public class WuLingHongGuang {
private String[] weels = {"左1", "左2", "后1", "后2", "备胎1"};
void changeWeel(int index, String weelName) throws RuntimeException { //throws 关键字,表示方法可能会抛出异常
if (index > weels.length) {
throw new RuntimeException("ERROR!!!轮胎安装位置错误了!"); //throw 关键字,直接抛出异常
}else {
weels[index] = weelName;
}
}
}
class HaC {
public static void main(String[] args) {
WuLingHongGuang wuLingHongGuang = new WuLingHongGuang();
wuLingHongGuang.changeWeel(10, "备胎2");//我不处理
System.out.println("更换成功");
}
}
当然我们这里可以写更准确的异常,这里应该是 ArrayIndexOutOfBoundsException
(数组越界),它们的关系是这样的:
Exception
│
├─ RuntimeException
│ │
│ ├─ ArrayIndexOutOfBoundsException
│ │
│ ├─ NullPointerException
更加准确的写法(但是RuntimeException也已经足够了):
void changeWeel(int index, String weelName) throws ArrayIndexOutOfBoundsException {
if (index > weels.length) {
throw new ArrayIndexOutOfBoundsException("ERROR!!!轮胎安装位置错误了!");
}else {
weels[index] = weelName;
}
}
两者区别:
非RuntimeException要求程序员写try-catch处理。如果不处理,程序将出现编译错误,是必须要处理才能运行的。
RuntimeException 不用try catch捕捉,但是万一出错将会导致程序运行中断。
所以说,只要某个方法throws 错误,我们应该积极处理。
# NullPointerException空指针异常
空指针是最常见的异常之一,表示该引用为null
,即未赋值,未分配内存地址。一个为空的值去操作其他方法,就会出错,因为不知道你指向哪个对象。
eg:
class HaC {
public static void main(String[] args) {
WuLingHongGuang wuLingHongGuang = new WuLingHongGuang();
wuLingHongGuang = null; //把wuLingHongGuang 赋值 为null
wuLingHongGuang.changeWeel(10, "备胎2"); // null调用其他就会报错
System.out.println("更换成功");
}
}
输出
Exception in thread "main" java.lang.NullPointerException
at com.hac.异常.HaC.main(WuLingHongGuang.java:28)