从头开始学java-错误处理
一.异常的定义
异常是程序在运行时发生的不正常情况。
java的面向对象实在是忒牛了!不正常情况也被封装成了一个对象。
异常有两种,一种严重的,用Error类进行描述;一种是不是太严重的,用Exception类进行描述。对于Error,就已经是无药可救的错误了,所以我们不进行处理,直接交给JVM处理。我们所做的异常处理是针对Exception进行处理。
一个异常如下:
class ExceptionDemo1 { int div(int a, int b) { return a / b; } } public class ExceptionTest1 { public static void main(String[] args) { ExceptionDemo1 demo1 = new ExceptionDemo1(); int result = demo1.div(2,0); System.out.println(result); } }结果:
一个异常,JVM默认的异常处理机制是停止运行报错。如果不想停机,那么我们就需要自己对异常进行处理,而不是给JVM处理,因为JVM的处理有点儿“狠”。
那么这样一个“低级的”错误,为什么不用一个if语句直接判断然后输出“除数不能为0呢”?
原因有以下几个:
1.曾经看过一份数据,一个程序中有一半以上是为了处理程序的异常的!这么多的处理条件,如果都和功能代码写在一起,那么代码必定会难于理解,而用异常处理机制会将异常处理的代码封装起来,使程序更容易理解。
2.异常处理机制提供抛出异常的机制,如果一段代码中的异常处理不了,可以将这个异常抛出,让调用模块处理异常。这样在我们移植代码时会省去不少麻烦。
3.异常处理有很大一部分是为了我们调试方便,我们用异常处理这个机制可以提供异常的一些详细信息,便于调试。
4.异常毕竟是少数情况,大部分的错误判断语句都是用不上的,有了异常,我们直接用一些java为我们提供的异常类,也可以让我们少写一些代码。
综上,异常处理机制比错误判断要好得多。使用异常处理机制的代码更加健壮,程序更稳定,安全。
下面就看一下怎么使用异常处理:
二.异常处理三部曲
异常处理三部曲,try,catch,finally
try
{
被检测的代码;
}
catch(异常类 异常变量)
{
处理异常的代码;
}
finally
{
一定会执行的语句;
}
一个异常处理的例子:
class ExceptionDemo1 { int div(int a, int b) { return a / b; } } public class ExceptionTest1 { public static void main(String[] args) { ExceptionDemo1 demo1 = new ExceptionDemo1(); //异常处理第一步,try被检测的代码 try { int result = demo1.div(2,0); System.out.println(result); } //异常处理第二部,catch,通过基类Exception的多态性接收子类的各种异常 catch(Exception e) { //打印异常的一些信息 System.out.println(e.getMessage()); //打印异常的名称及信息 System.out.println(e.toString()); //打印异常的详细信息 e.printStackTrace(); //终止程序 return; } //异常处理第三步,finally中的内容一定会执行,不管怎么样,即使异常处理终止了程序,finally中的代码也会执行 finally { System.out.println("finally!"); } //写在这里的代码,如果异常处理终止了程序,这里的代码是不会执行的。 } }结果:
要点:
1.try块中只要发生了异常,就抛出一个异常,发生异常的语句下面的语句都不运行了。
2.catch块接受try块中产生的异常,如果没有catch那么虚拟机会接收这个异常(虚拟机的处理方式很残暴,直接罢工)。catch块中根据异常,写出处理异常的代码。
3.finally块中的代码,一定会执行。有时异常处理会终止程序,那么下面的程序将不被执行,一些资源释放的代码就不能执行,将会引起资源不足。所以将这些代码放在finally块中,即使程序终止,finally块中的代码也会执行。
4.异常处理是在异常的时候才会执行catch块中的内容,在try块中没有抛出异常的时候,程序正常运行,但是finally块中的内容也会执行。
5.所有的异常都继承Exception类,包括以后我们自己定义的异常。
三.声明异常&抛出异常
class ExceptionDemo2 { //同throws关键字声明一个异常 int div(int a, int b) throws Exception { return a / b; } } public class ExceptionTest2 { public static void main(String[] args) { ExceptionDemo2 demo2 = new ExceptionDemo2(); //未处理异常 int result = demo2.div(4, 1); } }结果:
class ExceptionDemo2 { //同throws关键字声明一个异常 int div(int a, int b) throws Exception { return a / b; } } public class ExceptionTest2 { public static void main(String[] args) { ExceptionDemo2 demo2 = new ExceptionDemo2(); //通过try捕获异常 try { int result = demo2.div(4, 0); System.out.println(result); } catch(Exception e) { e.printStackTrace(); } } }
class ExceptionDemo2 { //同throws关键字声明一个异常 int div(int a, int b) throws Exception { return a / b; } } public class ExceptionTest2 { //继续用throws关键字声明异常,即抛出去,供下一个调用它的函数处理,最终到虚拟机 public static void main(String[] args) throws Exception { ExceptionDemo2 demo2 = new ExceptionDemo2(); int result = demo2.div(4, 0); System.out.println(result); } }
要点:
四.多异常处理
class ExceptionDemo2 { //同throws关键字声明两个异常,一个越界异常,一个除零异常 int div(int a, int b) throws ArithmeticException,ArrayIndexOutOfBoundsException { int[] arr = new int[a]; System.out.println(arr[100]); return a / b; } } public class ExceptionTest2 { //继续用throws关键字声明异常,即抛出去,供下一个调用它的函数处理,最终到虚拟机 public static void main(String[] args) { ExceptionDemo2 demo2 = new ExceptionDemo2(); try { int result = demo2.div(4, 0); System.out.println(result); } //捕获ArithmeticException异常 catch(ArithmeticException e) { //异常处理代码 e.printStackTrace(); } //捕获ArrayIndexOutOfBoundsException异常 catch(ArrayIndexOutOfBoundsException e) { //异常处理代码 e.printStackTrace(); } } }
要点:
五.自定义异常
class FushuException extends Exception { //无参数构造函数,没有异常信息 FushuException() { } //有参数构造函数,参数为异常信息,又throw时赋值 FushuException(String msg) { super(msg); } } class ExceptionDemo { //注意用throw抛出异常的时候,也要注意在函数后用throws声明异常 public static void div(int a, int b) throws FushuException { //判断抛出异常的条件 if (b < 0) //注意new throw new FushuException("除数小于0"); System.out.println(a / b); } } public class ExceptionTest3 { public static void main(String[] args) { try { ExceptionDemo.div(2, -1); } catch(FushuException e) { e.printStackTrace(); } } }
六.Runtime异常
class RunTime { //runtime异常可以不声明异常 public static void div(int a, int b) { if (b == 0) //抛出一个Runtime异常 throw new ArithmeticException(); System.out.println(a / b); } } public class ExceptionTest4 { public static void main(String[] args) { //runtime异常可以不进行异常处理 RunTime.div(3, 0); } }
要点:
七.关于异常要注意的
2.在底层组件中捕获JVM 抛出的“只有程序员能看懂的”异常,转换为中间层的业务逻辑异常,再由界面层捕获以提供有意义的信息。
3.自身能够处理的异常,不要再向外界抛出。
4.尽可能地在靠近异常发生的地方捕获并处理异常。
5.尽可能地捕获最具体的异常类型,不要在中间层用catch(Exception)“吃掉”所有异常
6.在开发阶段捕获并显示所有异常信息,发布阶段要移除部分代码,以避免“过于专业”的异常信息困扰用户,特别地,系统发布之后,不要将服务端异常的详细信息发给客户端,以免被黑客利用。
其他知识点:嵌套异常捕获,父类子类异常处理的关系