Java编程思想(九) —— 通过异常处理错误(1)
前阵子看到一个编程的学习方法——橡皮鸭调试法,就是拿一只小黄鸭。
面对面,跟他讲解你的编程思路,如果你没有橡皮鸭或者是一个能听你讲java,c,cpp,前端,io,系统内核,汇编,数据结构,计算机网络的女朋友,那么写博客也是你自己梳理好思路的一个好方法。
书中原话:java的基本理念是结构不佳的代码不能运行。
其实我之前也搞不懂为什么要弄个这样的东西出来,其实跟书上讲的一样,“异常”有“对此感到意外”的意思,问题出现了,我们不知道怎么处理,但又不能置之不理,可以停下来,看是否有其他地方处理这样的问题。这样能降低代码的复杂度,如果不使用异常,那你在程序中就要检查并处理这个错误,使用异常之后,就不用在方法下面进行检查,异常机制能够捕获错误。
所谓的“描述正常执行过程中做了什么事”和“出了问题怎么办”代码分离。
举个简单的例子:
public class Box { public static void main(String[] args) { int i[] = new int[4]; Box.change(i, 5, 1); } static void change(int[] i ,int index,int value){ i[index] = value; } }
这个传参如果超越数组边界的话怎么办,这是错误的啊,所以你用在方法中处理好,添加:
static void change(int[] i ,int index,int value){ if(index>i.length){ System.out.println("IndexOutOfBound"); return; } i[index] = value; }
这里的处理比较简单,如果这个程序里面有好多这样的问题,那你岂不是要在每一处都写上这个解决方法,这样就太麻烦了,异常就是这样用的。
if(index>i.length){ throw new IndexOutOfBoundsException(); }
交给后面再去处理。
1)捕获异常
方法内部抛出异常,方法将在抛出异常的过程中结束,如果不希望方法就此结束,可以用try块来捕获异常。这样就不用每个方法都去加错误检查的代码,把他们一起捕获,对于相同的异常一次性解决。
try{ }catch(xx){ }catch(xx){ }
一旦有同类型的异常,便进入catch语句里面处理。
2)创建自定义异常
public class MyException { public void f(){ System.out.println("f() throw simple exception!"); try { throw new SimpleException(); } catch (SimpleException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { MyException me = new MyException(); me.f(); } }
这个例子有很多东西可以讲,首先,如果用了
throw new SimpleException();那么,就要像上面一样加上try和catch。
或者,直接在方法中抛出:
public void f() throws SimpleException{ System.out.println("f() throw simple exception!"); throw new SimpleException(); }但是这样直接抛出之后,
me.f();就有问题了,以为异常抛出了,要么你在main方法中继续抛出:
public static void main(String[] args) throws SimpleException { MyException me = new MyException(); me.f(); }
要么你在main方法中捕获异常进行处理:
public static void main(String[] args) { MyException me = new MyException(); try { me.f(); } catch (SimpleException e) { System.err.println("catch it!"); } }err输出为标准错误流,out输出为标准输出,err的话在控制台会以红色字体显示,比较醒目。
但你会发现输出结果是catch it! 然后才是f() throw simple exception。这个后面再提。
try { me.f(); } catch (SimpleException e) { e.printStackTrace(System.out); } result: son.SimpleException at son.MyException.f(MyException.java:8) at son.MyException.main(MyException.java:13)printStackTrace(),栈跟踪,打印的是从方法调用处直到异常抛出处,默认无参数的话会输出到标准错误流。
3)记录日志
其实大型的网站,每天都有记录日志,哪里有错误再去日志里找,究竟发生了什么问题。
public class LoggingException extends Exception{ private static Logger logger = Logger.getLogger("LoggingException"); public LoggingException(){ StringWriter sw = new StringWriter(); printStackTrace(new PrintWriter(sw)); logger.severe(sw.toString()); } public static void main(String[] args) { try { throw new LoggingException(); } catch (LoggingException e) { System.err.println("catch "+e); } } } result: 九月 10, 2014 7:35:50 下午 son.LoggingException <init> 严重: son.LoggingException at son.LoggingException.main(LoggingException.java:16) catch son.LoggingExceptionnew PrintWriter(sw) 将输出抽取为String。
PrintWriter的话作为参数传进去,后面IO的时候再了解。
Logger,A Logger object is used to log messages for a specific system
or application component.
serve方法,Log a SEVERE message。
4)异常说明
Java鼓励人们把方法可能会抛出的异常告知使用此方法的客户端程序员。如果有源代码,那么客户端程序员可以通过查找throw语句获知抛出的异常。然而源代码一般不提供。
java提供了相应的语法,书上写得很有趣,说以礼貌的方式告诉客户端程序员某个方法可能会抛出的异常类型,他们可以进行相应的处理,这就是异常说明。
其实就是上面提到的不用try,catch而使用到的throws。
5)printStackTrace
我之前翻译成栈跟踪了,书上写的是栈轨迹,其实方法返回的是栈轨迹元素组成的数组,0为栈顶元素,先进后出,这是栈的特性。
public class TestElement { static void first(){ try { throw new Exception(); } catch (Exception e) { // 栈元素 拿到方法名 for(StackTraceElement s : e.getStackTrace()){ System.out.println(s.getMethodName()); } } } static void second(){first();} static void third(){second();} public static void main(String[] args) { first(); System.out.println("////////////////"); second(); System.out.println("////////////////"); third(); } } result: first main //////////////// first second main //////////////// first second third main
整个栈元素的信息都可以逐一显示。
这篇先讲解基本的异常概念,理清一下为什么使用异常的思路,下一篇,讲到try,catch嵌套这更有深度的内容的时候,更加精彩!
郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。