一.异常的定义
异常是程序在运行时发生的不正常情况。
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.在开发阶段捕获并显示所有异常信息,发布阶段要移除部分代码,以避免“过于专业”的异常信息困扰用户,特别地,系统发布之后,不要将服务端异常的详细信息发给客户端,以免被黑客利用。
其他知识点:嵌套异常捕获,父类子类异常处理的关系