概述
说起异常,我就想起了Bug,也就是常说的信春哥,无Bug,什么是Bug呢?我理解的Bug就是没有按照自己原先假想的逻辑去执行,这其中包括了两个方面,一方面是代码语法问题,一方面是逻辑问题,就比如正常逻辑买了东西要付款,但是买家买了东西却没有付款,直接拿东西走了,这是逻辑问题,或者是本来数组存储3个元素,你却存了4个,这时候也出现了Bug,程序报错了,这种Bug就是异常。
异常也是Java类的一种,用new创建对象,他们的结构是这样的:
-
Throwable
error
Exception
恩,就是这样,异常分为错误和异常,感觉有点绕口,但就是这样,像error这种错误是在编译期间就会报出来的,如果你使用的是IDE,所以我们只需要关注Exception异常就可以了。
获取异常和处理异常
获取和处理异常可能就是异常的全部,我们的一个原则就是:
发生异常就得让程序员知道
获取异常
怎么获取异常呢?Java给出了try关键字用来解决,我们只需要将可能出现异常的代码放在try中就可以了,就像这样:
try{...}
java给出了尽量优雅的解决方案来处理异常,它不希望处理异常的代码和本身的业务代码有过多的混合,获取到异常就该处理异常,这时候就应该这样写了:
try{...}catch(Type1 type1){...}catch(Type2 type2){...}
catch中的type代表了在try中可能出现的异常类型
catch如何匹配异常?
按顺序从第一个到最后一个,如果发现异常匹配,就停止匹配,和switch模式不一样
基类包含子类异常,如果第一个是Exception,那么后面的都不用匹配了
那么catch中怎么处理这个异常呢?
不作处理(会出现“吞食”)
打印到控制台
写入日志
继续向上抛
在try块中抛出异常,我们在catch中会匹配到相应的异常类型,这时候我们就会拿到对应的异常对象的引用,我们调用throwalbe的方法用于处理:
public class ExceptionMethods {public static void main(String[] args) {try {throw new Exception("My Exception");} catch (Exception e) {System.out.println("Caught Exception");System.out.println(e.getMessage());System.out.println(e.getLocalizedMessage());System.out.println(e);System.out.println(e.toString());e.printStackTrace();e.printStackTrace(System.out);}}
}
我们一般使用e.printStackTrace();打出异常栈信息即可
或者是写入Log,使用logger.error
每个人处理处理异常的方式可能不尽相同
我们除了可以使用try来捕获异常,我们也可以将这个异常抛出去,我们可以把异常比作现在这种制度,地球村出了点问题,村长衡量了一下,觉得这事情自己处理不了,就交给乡长了,这种把问题交给上层或者是Java编程思想中说的跳出当前环境,交给一个更大的环境去处理,这也是Java异常处理的一种思路。
if(t == null)throw new NullPointerException();
其实异常最重要的就是异常的名称,现在Java正在扩充异常的种类,但是很多异常可能不是我们想要的,那么我们就可以自定义异常:
class SystemErrException extends Exception{}
这就是一个异常类,我们继承了Exception,这个SysteErrException就可以用于我们处理异常的时候了,当然我们可以给他加点参数:
class SystemErrException extends Exception{public SystemErrException(){ }public SystemErrException (String message){super(message);}
}
或者是这样:
class SystemErrException extends Exception{private int i;public SystemErrException(){ }public SystemErrException (String message){super(message);}public SystemErrException (String message, int i){super(message);this.i = i;}
}
这只不过是多加了几个参数,但是这些一般是没什么必要的。
throws
说到这,就得说另一个关键字throws,看清楚,不是throw,而是throws,这可能也是面试新手的时候会问的一个问题,throws是异常中的一个申明,在IO学习中会非常常见,它是一个声明,编译器检查到说你这段代码可能会发生什么异常,你要声明一下,这时候你就要在方法上声明:
public void inputFile() throws IOException{....
}
finally
finally用处就和他的意思相符,表示最终的含义,也就是finally里面的代码一定会得到执行:
案例一
public class FinallyException {static int count = 0;public static void main(String[] args) {while (true){try {if (count++ == 0){throw new ThreeException();}System.out.println("no Exception");}catch (ThreeException e){System.out.println("ThreeException");}finally {System.out.println("in finally cause");if(count == 2)break;}}}
}class ThreeException extends Exception{}
执行结果:
ThreeException
in finally cause
no Exception
in finally cause
案例二:return案例
public class MultipleReturns {public static void f(int i){System.out.println("start.......");try {System.out.println("1");if(i &#61;&#61; 1)return;System.out.println("2");if (i &#61;&#61; 2)return;System.out.println("3");if(i &#61;&#61; 3)return;System.out.println("else");return;}finally {System.out.println("end");}}public static void main(String[] args) {for (int i &#61; 1; i<4; i&#43;&#43;){f(i);}}
}
执行结果&#xff1a;
start.......
1
end
start.......
1
2
end
start.......
1
2
3
end
最佳实践
我们可能遇到这种情况&#xff0c;打开一个文件&#xff0c;读取文件&#xff0c;关闭文件&#xff0c;正常逻辑是这样的&#xff0c;但是这样会有几个问题&#xff1a;
在打开文件的过程中出现异常&#xff0c;但是还要关闭文件
就像这样&#xff1a;
try{A a &#61; new A();...}catch(){...}finally{a.close();}
A在创建过程中会出现异常&#xff0c;但是还是会执行a的方法&#xff0c;比如关闭文件
所以我们应该这么做&#xff1a;
try{A a &#61; new A();try{...}finally{a.close();}}catch(){...}
异常栈
一般报错都会报一大堆错误&#xff0c;大家无从看起&#xff0c;其实异常报错也是有规律的&#xff0c;这就是栈结构&#xff0c;先进后出&#xff0c;举个栗子&#xff1a;
public class WhoCalled {static void f() {try {throw new Exception();} catch (Exception e) {for (StackTraceElement ste : e.getStackTrace()){System.out.println(ste.getMethodName());}}}static void g(){f();}static void h(){g();}public static void main(String[] args) {f();System.out.println("---------------------------");g();System.out.println("---------------------------");h();System.out.println("---------------------------");}
}
运行结果&#xff1a;
f
main
---------------------------
f
g
main
---------------------------
f
g
h
main
---------------------------
可以看到异常信息都是从内到外的&#xff0c;按我的理解查看异常的时候要从第一条异常信息看起&#xff0c;因为那是异常发生的源头。
总结
异常总的来说还是很重要的&#xff0c;也是保障程序健壮性很重要的一栏&#xff0c;并且可以达到立竿见影的效果&#xff0c;这里只是基本总结了异常的一些常见问题&#xff0c;很多还得在自己运用的过程中去探索。