在這里說說一些容易忽略的知識點。
- 對異常來說最重要的就是異常的類名眼五。要做到見名知義。比如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就用來表示原始異常田藐,這樣通過把原始異常傳遞給新的異常,使得在當前位置創(chuàng)建并拋出新的異常吱七,也能通過這個異常鏈追蹤最初發(fā)生的位置汽久。需要注意的是,在Throwabe的子類中踊餐,只有三種基本的異常類提供了帶cause參數構造器它們是Error景醇,Exception和RuntimeException。如果要把其它類型的異常鏈接起來吝岭,應該使用initCause()方法而不是構造器三痰。比如說:
new RuntimeException的子類(RuntimeException的其它子類的對象);
Exception的子類的對象.initCause(RuntimeException的子類對象);
5.在try塊里有return吧寺,finally是會執(zhí)行的。那如果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子句就會發(fā)生這種情況。
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子句姨拥。