此篇是 Java基础-异常处理(一)的续篇,未看过的朋友请先看上篇。
finally子句
当你希望在try块中无论异常是否被捕获,都希望最后都执行一些代码时,可在末尾添加finally子句实现这些代码,因为无论如何finally子句最终都会被执行 (即使之前的块有return语句)。
语法如下:
try
{
try-statements;
}
catch(Exception e) //catch statements can leave out
{
handle e;
}
finally
{
finally-statements;
}
blew-statements;
可能出现的三种执行情况
1.try块中没有出现异常,则执行finally块的代码,然后再执行下面的代码(blew-statements);
2.try块中抛出异常,并被catch块中捕获并执行catch块的代码后,再执行finally块的代码,然后再执行下面的代码(blew-statements);
3.try块中抛出异常,并未被catch块中捕获,则会执行finally块的代码,然后终止程序并将异常传递给调用者。
注意:使用finally子句可以省略catch块;CPP中经常使用finally清理开辟的内存,但在JAVA中有内存自动回收的机制,所以谨慎或不用finally子句。
从异常中获取信息
异常对象包含关于异常有价值的信息。可以使用下面的实例方法获取有关的异常信息。
例子
/**
* @author Created by MadJieJie on 2017/1/14-12:40.
* @brief
* @attention
*/
public class TestFinallyStatements
{
public static void main(String[] args)
{
try{
int num = 1/0;
}
catch ( Exception e )
{
System.out.println("e.getMessage():"+e.getMessage());
System.out.println("e.toString():"+e.toString());
System.out.println("e.printStackTrace():");
e.printStackTrace();
System.out.println("e.getStackTrace():");
StackTraceElement[] element = e.getStackTrace();
for(int i=0;i<element.length;i++)
{
System.out.print("method "+element[i].getMethodName());
System.out.print("("+element[i].getClassName()+":");
System.out.println(element[i].getLineNumber() + ")");
}
}
}
}
输出
java.lang.ArithmeticException: / by zero
e.getMessage():/ by zero
at TestFinallyStatements.main(TestFinallyStatements.java:13)
e.toString():java.lang.ArithmeticException: / by zero
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
e.printStackTrace():
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
e.getStackTrace():
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
method main(TestFinallyStatements:13)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
method invoke0(sun.reflect.NativeMethodAccessorImpl:-2)
method invoke(sun.reflect.NativeMethodAccessorImpl:62)
method invoke(sun.reflect.DelegatingMethodAccessorImpl:43)
method invoke(java.lang.reflect.Method:497)
method main(com.intellij.rt.execution.application.AppMain:147)
何时使用异常
异常处理将错误处理代码从正常的程序设计任务中分离出来,使得程序更容易读和更改。但是,使用异常处理是需要高昂的代价,由于异常处理需要初始化新的异常对象,需要从调用栈返回,而且还需要沿着调用方法链来传播异常以便能找到它的异常处理代码(catch块),所有不要轻易使用。
那应该什么时候使用try-catch呢?当处理不可预料的错误状况时使用它。不要使用try-catch块处理简单的和可预料的错误情况。
简单的例子
try
{
System.out.print(str.tostring);
}
catch( NullPointerException e )
{
System.out.print(" str point null object ");
}
应修改成
if( str != null )
{
System.out.print(str.tostring);
}
else
{
System.out.print(" str point null object ");
}
重新抛出异常
Java允许异常处理器重新抛出异常,如果异常处理器不能处理一个异常或只是简单地希望它的调用者注意到该异常。
try
{
statements;
}
catch( Exception e)
{
handle e;
throw e;
}
语句 throw e; 重新抛出异常给调用者,一边调用者其他处理器获得异常e的机会
链式异常
有时候,可能需要同原始异常一起抛出一个新的异常(带有附加信息),这称为链式异常。
这样让我们更加清晰问题的出处,Java库中很多都采用了链式异常。
例子
/**
* @author Created by MadJieJie on 2017/1/14-21:38.
* @brief
* @attention
*/
public class ChainedExceptionDemo
{
public static void main(String[] args)
{
try{
First();
}
catch( Exception e )
{
e.printStackTrace();
}
}
public static void First() throws Exception
{
try
{
Second();
}
catch( Exception e )
{
throw new Exception("First Exception",e); //notice:the cause(e) will be save and print it.
}
}
public static void Second() throws Exception
{
throw new Exception("Second Exception");
}
}
输出
java.lang.Exception: First Exception
at ChainedExceptionDemo.First(ChainedExceptionDemo.java:28)
at ChainedExceptionDemo.main(ChainedExceptionDemo.java:11)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.Exception: Second Exception
at ChainedExceptionDemo.Second(ChainedExceptionDemo.java:34)
at ChainedExceptionDemo.First(ChainedExceptionDemo.java:24)
... 6 more
注意:First()方法中抛出的异常实例的使用了装饰模式设计,将异常对象(第二个参数)添加了cause-message(第一个参数:导致异常的原因),可以用getCause()方法调出自己描述异常对象的描述信息(第一个参数)。
下面是流程分析:
1.从main方法开始,main方法调用one(),one()调用two()方法,two()方法抛出异常。
2.该异常被one()方法中的catch块捕获,并被包装成一个新异常。
3.该新异常被抛出后被main()方法中的catch块捕获。
创建自定义异常类
Java提供相当多的异常类,尽量不要使用自己创建的异常类。
如果遇到一个不能预定义异常类恰当描述问题,可以通过派生Exception或其子类来创建自己的异常类。
UserDefinedException.java 自定义类
/**
* @author Created by MadJieJie on 2017/1/14-22:14.
* @brief
* @attention
*/
public class UserDefinedException extends Exception
{
private double radius;
public UserDefinedException(double radius)
{
super("Invalid radius " + radius);
this.radius = radius;
}
public double getRadius()
{
return radius;
}
}
Circle.java 测试
/**
* @author Created by MadJieJie on 2017/1/14-22:26.
* @brief
* @attention
*/
public class Circle
{
public static void main ( String[] args )
{
Circle circle = new Circle(-0.5);
}
private double radius;
public Circle ( double radius )
{
try
{
if(radius>=0)
{
this.radius = radius;
}
else
{
throw new UserDefinedException(radius);
}
} catch( UserDefinedException e )
{
e.printStackTrace();
}
}
}
输出
UserDefinedException: Invalid radius -0.5
at Circle.<init>(Circle.java:27)
at Circle.main(Circle.java:11)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)