在这里说说一些容易忽略的知识点。
- 对异常来说最重要的就是异常的类名。要做到见名知义。比如NullPointerException,IllegalStateException等,它们都只是简单地继承了RuntimeException。
public class NullPointerException extends RuntimeException {
private static final long serialVersionUID = 5162710183389028792L;
/**
* Constructs a {@code NullPointerException} with no detail message.
*/
public NullPointerException() {
super();
}
/**
* Constructs a {@code NullPointerException} with the specified
* detail message.
*
* @param s the detail message.
*/
public NullPointerException(String s) {
super(s);
}
}
- printStackTrace()方法所提供人信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。
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("#############################");
}
}
运行结果:
f
main
invoke0
invoke
invoke
invoke
main
#############################
f
g
main
invoke0
invoke
invoke
invoke
main
#############################
f
g
h
main
invoke0
invoke
invoke
invoke
main
#############################
- fillInStackTrack()用来更新栈信息,fillInStackTrack()方法将返回一个Throwable对象。通过下面的例子会直观一些。
public class Rethrowing {
public static void f() throws Exception{
System.out.println("f方法里的原始异常");
throw new Exception("从f方法里面抛出的异常");
}
public static void g() throws Exception{
try {
f();
} catch (Exception e) {
System.out.println("g方法里面的catch代码块");
e.printStackTrace(System.out);
throw e;
}
}
public static void h()throws Exception{
try {
f();
} catch (Exception e) {
System.out.println("h方法里面的代码块");
e.printStackTrace(System.out);
throw (Exception) e.fillInStackTrace();
}
}
public static void main(String[] args){
try {
g();
} catch (Exception e) {
System.out.println("main方法中调用g方法");
e.printStackTrace(System.out);
}
System.out.println("#####################");
try {
h();
} catch (Exception e) {
System.out.println("main方法中调用h方法");
e.printStackTrace(System.out);
}
}
}
运行结果:
f方法里的原始异常
g方法里面的catch代码块
java.lang.Exception: 从f方法里面抛出的异常
at exception.Rethrowing.f(Rethrowing.java:9)
at exception.Rethrowing.g(Rethrowing.java:13)
at exception.Rethrowing.main(Rethrowing.java:31)
main方法中调用g方法
java.lang.Exception: 从f方法里面抛出的异常
at exception.Rethrowing.f(Rethrowing.java:9)
at exception.Rethrowing.g(Rethrowing.java:13)
at exception.Rethrowing.main(Rethrowing.java:31)
#####################
f方法里的原始异常
h方法里面的代码块
java.lang.Exception: 从f方法里面抛出的异常
at exception.Rethrowing.f(Rethrowing.java:9)
at exception.Rethrowing.h(Rethrowing.java:22)
at exception.Rethrowing.main(Rethrowing.java:38)
main方法中调用h方法
java.lang.Exception: 从f方法里面抛出的异常
at exception.Rethrowing.h(Rethrowing.java:26)
at exception.Rethrowing.main(Rethrowing.java:38)
4.异常链,Throwable的子类在构造器中都可以一个cause对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得在当前位置创建并抛出新的异常,也能通过这个异常链追踪最初发生的位置。需要注意的是,在Throwabe的子类中,只有三种基本的异常类提供了带cause参数构造器它们是Error,Exception和RuntimeException。如果要把其它类型的异常链接起来,应该使用initCause()方法而不是构造器。比如说:
new RuntimeException的子类(RuntimeException的其它子类的对象);
Exception的子类的对象.initCause(RuntimeException的子类对象);
5.在try块里有return,finally是会执行的。那如果finally也有return呢,会返回哪个呢?
public class FinallyTest {
public static void main(String[] args){
System.out.println(testFinally());
}
private static String testFinally(){
try{
return "try";
}finally {
return "finally";
}
}
}
运行结果:
finally
这种结构会导致代码可读性变差。
5.异常的不足就是会导致异常丢失。异常作为程序出错的标志决不应该被忽略。然而某些使用finally子句就会发生这种情况。
public class LostException {
class VeryImportantException extends Exception{
@Override
public String toString() {
return "very important exception";
}
}
class HoHumException extends Exception{
@Override
public String toString() {
return "hohum exception";
}
}
void f() throws VeryImportantException{
throw new VeryImportantException();
}
void dispose() throws HoHumException{
throw new HoHumException();
}
public static void main(String[] args){
try{
LostException lost=new LostException();
try{
lost.f();
}finally {
lost.dispose();
}
}catch (Exception e){
System.out.println(e);
}
}
}
运行结果:
hohum exception
从输出结果中看到VeryImportantException不见了,它被finally子句中的HoHumException取代。这是相当严重的缺陷。
6.异常的限制,当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。这些限制很有用,因为这意味着,当基类使用的代码应用到派生类对象的时候,一样能够工作,当然这是面向对象的基本概念。异常也不例外。
public class ExceptionTest {
class BaseException extends Exception{};
class Foul extends BaseException{};
class Strike extends BaseException{};
abstract class Inning{
public Inning () throws BaseException{}
public void event () throws BaseException{}
public abstract void atBat() throws Strike,Foul;
public void walk(){};
}
class StormException extends Exception{};
class RainedOut extends StormException{};
class PopFoul extends Foul{};
interface Storm{
public void event() throws RainedOut;
void rainHard() throws RainedOut;
}
public class StormyInning extends Inning implements Storm{
//因为Inning的构造器抛出异常,所以它的继承类必须处理父类的异常,当然也可以抛出自己想要处理的异常
public StormyInning() throws BaseException,Foul{
}
//下面这个方法不能通过编译,因为在Inning方法中并没有抛出异常
// @Override
// public void walk() throws PopFoul{
//
// }
//接口不能基类中的方法添加异常,所以下面也不能编译
// @Override
// public void event() throws RainedOut{
//
// }
@Override
public void event() {
}
//重写的方法可以抛出继承的异常
@Override
public void atBat() throws PopFoul {
}
@Override
public void rainHard() throws RainedOut {
}
}
}
在Inning类中。可以看到构造器和event方法都声明将抛出异常,但实际上并没有抛出,这种方式使你强制用户去捕获可能在覆盖的event版本中增加的异常。
7.被检查异常并不总是好的,它有时候会使问题变得复杂,因为它强制你在还没有准备好处理问题的时候加上catch子句。