Java的基本概念是结构不佳的代码不能运行余下的问题必须在运行期间解决,这就须要错误源能经过某种方式,把适当的信息传递给某个接收者——该接收者将知道如何正确处理这里问题。java
使用异常所带来的另外一个至关明显的好处,它每每可以下降错误处理代码的复杂度。程序员
异常情形是指阻止当前方法或做用域继续执行的问题。把异常情形与普通问题相区分很重要,普通问题是指,在当前环境下能获得足够的信息,总能处理这个错误。而对于异常情形,就不能继续下去了,由于在当前环境下没法得到必要的信息来解决问题。你所能作的就是从当前环境跳出,而且把问题提交给上一级环境。这就是抛出异常所发生的事情。
当抛出异常后,有几件事就会发生。首先,同Java中其余对象的建立同样,将使用new在堆上建立异常对象,而后,当前的执行路径被终止,而且从当前环境中弹出堆异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方来继承执行程序。这个恰当的地方就是异常处理程序,它的任务就是将程序从错误状态中恢复,以使程序能要么换一种方式运行,要么继承运行下去。编程
咱们总用new在堆上建立异常对象,这也伴随着存储控件的分配和构造器的调用。全部标准异常类都有两个构造器:数组
使用new建立了异常对象以后,此对象的引用将传给throw。
能够简单把异常处理当作一种不一样的返回机制。抛出异常的方法从当前的做用域退出。将返回一个异常对象,而后退出方法做用域。安全
异常如何被捕获,必须首先理解监控区域。app
异常处理机制,能够把全部动做都放在try块中,而后再一个地方处理捕获的异常就能够了。ide
抛出的异常必须在某处获得处理。这个地点就是异常处理程序。异常处理程序紧跟在try块后,以关键字catch表示。
当异常抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。ui
异常处理理论有两种基本模型:this
恢复模型会致使耦合:恢复性的处理程序须要了解异常抛出的地点,着势必要包含依赖于抛出位置的非通用性代码。这增长了代码编写和维护的困难。设计
要本身定义异常,必须从已有的异常类继承,最好选择意思相近的异常类继承。
public class InheritingExceptions { public void f() throws SimpleException { System.out.println("Throw SimpleException from f()"); throw new SimpleException(); } public static void main(String[] args) { InheritingExceptions sed = new InheritingExceptions(); try { sed.f(); } catch (SimpleException e) { System.out.println("Caught it!"); } } } class SimpleException extends Exception { }
编译器建立了默认构造器,它将自动调用基类的默认构造器。
public class FullConstructors { public static void f() throws MyException{ System.out.println("Throwing MyExcetion from f()"); throw new MyException(); } public static void g() throws MyException{ System.out.println("Throwing MyExcetion from g()"); throw new MyException("Originated in g()"); } public static void main(String[] args){ try{ f(); } catch (MyException e){ e.printStackTrace(System.out); } try{ g(); } catch (MyException e){ e.printStackTrace(System.out); } } } class MyException extends Exception{ public MyException(){} public MyException(String msg){super(msg);} }
使用基本日志记录功能
import java.io.PrintWriter; import java.io.StringWriter; import java.util.logging.Logger; public class LoggingExceptions { public static void main(String[] args){ try{ throw new LoggingException(); } catch (LoggingException e){ System.out.println("Caught "+e); } try{ throw new LoggingException(); } catch (LoggingException e){ System.out.println("Caught "+e); } } } class LoggingException extends Exception{ private static Logger logger= Logger.getLogger("LoggingException "); public LoggingException(){ StringWriter trace=new StringWriter(); printStackTrace(new PrintWriter(trace)); logger.severe(trace.toString()); } }
须要捕获或记录他人编写的异常,必须在异常处理程序中生成日志消息
import java.io.PrintWriter; import java.io.StringWriter; import java.util.logging.Logger; public class LoggingExceptions2 { private static Logger logger=Logger.getLogger("LoggingExceptions2"); static void logException(Exception e){ StringWriter trace=new StringWriter(); e.printStackTrace(new PrintWriter(trace)); logger.severe(trace.toString()); } public static void main(String[] args){ try{ throw new NullPointerException(); } catch(NullPointerException e){ logException(e); } } }
更进一步自定义异常,加入额外的构造器和成员
public class ExtraFeatures { public static void f() throws MyException2{ System.out.println("Throwing MyException2 from f()"); throw new MyException2(); } public static void g() throws MyException2{ System.out.println("Throwing MyException2 from g()"); throw new MyException2("Originated in g()"); } public static void h() throws MyException2{ System.out.println("Throwing MyException2 from h()"); throw new MyException2("Originated in g()",47); } public static void main(String[] args){ try{ f(); } catch (MyException2 e){ e.printStackTrace(System.out); } try{ g(); } catch (MyException2 e){ e.printStackTrace(System.out); } try{ h(); } catch (MyException2 e){ e.printStackTrace(System.out); System.out.println("e.val()="+e.val()); } } } class MyException2 extends Exception{ private int x; public MyException2(){} public MyException2(String msg){super(msg);} public MyException2(String msg,int x){super(msg);this.x=x;} public int val(){return x;} public String getMessage(){ return "Detail message:"+x+" "+super.getMessage(); } }
异常也是一种对象,全部能够继续修改异常类,得到更强大的功能。
Java提供语法,使能够告知客户端程序员某个方法可能会抛出的异常类型,而后客户端程序员就能够进行相应的处理。这就是异常说明,它属于方法说明的一部分,紧跟在形式参数以后。
异常说明使用了附加的关键字throws,后面接一个潜在的异常类型的列表,因此方法定义可能看起来像这样:
void f() throws TooBig,TooSmall { }
若是方法里的代码产生了异常却没有处理,编译器会发现这个问题而且提醒你,要么处理这个异常,要么就在异常说明中代表此方法将产生异常。
这种编译时被强制检查的异常称为被检查的异常。
gillInStackTrace()用于在Throwable对象的内部记录栈帧的当前状态。这在程序从新抛出错误或异常时颇有用。
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(); } }
从新抛出异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch子句将被忽略。异常对象的全部信息都得以保持,因此高一级环境中捕获此异常的处理程序能够从这个异常对象中获得全部信息。
public class Rethrowing { public static void f() throws Exception{ System.out.println("originating the "); throw new Exception("thrown from f()"); } public static void g() throws Exception{ try{ f(); } catch (Exception e){ System.out.println("Inside g(),e.printStackTrace()"); e.printStackTrace(System.out); throw e; } } public static void h() throws Exception{ try{ f(); }catch (Exception e){ System.out.println("Inside h(),e.printStackTrace()"); e.printStackTrace(System.out); throw (Exception) e.fillInStackTrace(); } } public static void main(String[] args){ try{ g(); }catch (Exception e){ System.out.println("main: printStackTrace()"); e.printStackTrace(System.out); } try{ h(); }catch (Exception e){ System.out.println("main: printStackTrace()"); e.printStackTrace(System.out); } } }
有可能在捕获异常以后抛出另外一种异常。这么作的话,获得的效果相似于使用fillInStackTrace(),有关原来异常发生点的信息会丢失,剩下的是于新的跑出点有关的信息:
public class RethrowNew { public static void f() throws OneException{ System.out.println("originating the exception"); throw new OneException("thrown from f()"); } public static void main(String[] args){ try{ try{ f(); }catch (OneException e){ System.out.println("Caught in inner try,e.printStackTrace"); e.printStackTrace(System.out); throw new TwoException("from inner try"); } }catch (TwoException e){ System.out.println("Caught in inner try,e.printStackTrace"); e.printStackTrace(System.out); } } } class OneException extends Exception{ public OneException(String s){super(s);} } class TwoException extends Exception{ public TwoException(String s){super(s);} }
异常都是用new在堆上建立的对象,全部垃圾回收器会自动把它们清理掉。
想在捕获一个异常后抛出另外一个异常,而且但愿把原始异常信息保持下来,这被称为异常链。如今全部Throwable的子类在构造器中均可以接受一个causr对象做为参数。这个cause就用来表示原始异常,这样经过把原始异常传递给新的异常,使得即便在当前位置建立并抛出了新的异常,也能经过这个异常链最终到最初发生的位置。
class DynamicFieldsException extends Exception {} public class DynamicFields { private Object[][] fields; public DynamicFields(int initialSize) { fields = new Object[initialSize][2]; for(int i = 0; i < initialSize; i++) fields[i] = new Object[] { null, null }; } public String toString() { StringBuilder result = new StringBuilder(); for(Object[] obj : fields) { result.append(obj[0]); result.append(": "); result.append(obj[1]); result.append("\n"); } return result.toString(); } private int hasField(String id) { for(int i = 0; i < fields.length; i++) if(id.equals(fields[i][0])) return i; return -1; } private int getFieldNumber(String id) throws NoSuchFieldException { int fieldNum = hasField(id); if(fieldNum == -1) throw new NoSuchFieldException(); return fieldNum; } private int makeField(String id) { for(int i = 0; i < fields.length; i++) if(fields[i][0] == null) { fields[i][0] = id; return i; } // No empty fields. Add one: Object[][] tmp = new Object[fields.length + 1][2]; for(int i = 0; i < fields.length; i++) tmp[i] = fields[i]; for(int i = fields.length; i < tmp.length; i++) tmp[i] = new Object[] { null, null }; fields = tmp; // Recursive call with expanded fields: return makeField(id); } public Object getField(String id) throws NoSuchFieldException { return fields[getFieldNumber(id)][1]; } public Object setField(String id, Object value) throws DynamicFieldsException { if(value == null) { // Most exceptions don't have a "cause" constructor. // In these cases you must use initCause(), // available in all Throwable subclasses. DynamicFieldsException dfe = new DynamicFieldsException(); dfe.initCause(new NullPointerException()); throw dfe; } int fieldNumber = hasField(id); if(fieldNumber == -1) fieldNumber = makeField(id); Object result = null; try { result = getField(id); // Get old value } catch(NoSuchFieldException e) { // Use constructor that takes "cause": throw new RuntimeException(e); } fields[fieldNumber][1] = value; return result; } public static void main(String[] args) { DynamicFields df = new DynamicFields(3); System.out.print(df); try { df.setField("d", "A value for d"); df.setField("number", 47); df.setField("number2", 48); System.out.print(df); df.setField("d", "A new value for d"); df.setField("number3", 11); System.out.print("df: " + df); System.out.print("df.getField(\"d\") : " + df.getField("d")); Object field = df.setField("d", null); // Exception } catch(NoSuchFieldException e) { e.printStackTrace(System.out); } catch(DynamicFieldsException e) { e.printStackTrace(System.out); } } }
Throwable这个Java类被用来表示任何能够做为异常抛出的类。Throwable对象可分为两中类型:Error用来表示编译时和系统错误。Exception是能够被抛出的基本类型。
RuntimeException类型的异常也许会穿越全部执行路径直达main()方法,而不会被捕获。
public class NeverCaught { static void f(){ throw new RuntimeException("From f()"); } static void g(){ f(); } public static void main(String[] args){ g(); } }
RuntimeException表明的是编程错误:
做为程序员,应该在代码中检查的错误
应该把异常机制用来处理一些烦人的运行时错误,这些错误每每是由代码控制能力以外的因素致使的。
public class FinallyWorks { static int count=0; public static void main(String[] args){ while(true){ try{ if(count++==0) throw new ThreeException(); System.out.println("No exception"); } catch (ThreeException e){ System.out.println("ThreeException"); } finally { System.out.println("finally cluse "+count); if(count==2)break; } } } } class ThreeException extends Exception{}
当要把除内存以外的资源恢复到初始状态时,就要用到finally子句
public class WithFinally { static Switch sw = new Switch(); public static void main(String[] args) { try { sw.on(); // Code that can throw exceptions... OnOffSwitch.f(); } catch(OnOffException1 e) { System.out.println("OnOffException1"); } catch(OnOffException2 e) { System.out.println("OnOffException2"); } finally { sw.off(); } } } class Switch { private boolean state = false; public boolean read() { return state; } public void on() { state = true; System.out.print(this); } public void off() { state = false; System.out.print(this); } public String toString() { return state ? "on" : "off"; } } class OnOffSwitch { private static Switch sw = new Switch(); public static void f() throws OnOffException1,OnOffException2 {} public static void main(String[] args) { try { sw.on(); // Code that can throw exceptions... f(); sw.off(); } catch(OnOffException1 e) { System.out.println("OnOffException1"); sw.off(); } catch(OnOffException2 e) { System.out.println("OnOffException2"); sw.off(); } } } class OnOffException1 extends Exception {} class OnOffException2 extends Exception {}
当设计break和continue语句时,finally也会获得执行
由于finally子句总会执行,因此在一个方法中,能够从多个点返回,而且能够保证重要的清理工做仍旧会执行:
public class MultipleReturns { public static void f(int i){ System.out.println("Initialization that requires"); try{ System.out.println("Point 1"); if (i==1)return; System.out.println("Point 2"); if (i==2)return; System.out.println("Point 3"); if (i==3)return; } finally { System.out.println("end"); } } public static void main(String[] args){ for(int i=1;i<=3;i++) f(i); } }
public class LoseMessage { void f() throws VeryImportantException { throw new VeryImportantException(); } void dispose() throws HoHumException { throw new HoHumException(); } public static void main(String[] args) { try { LoseMessage lm = new LoseMessage(); try { lm.f(); } finally { lm.dispose(); } } catch(Exception e) { System.out.println(e); } } } class VeryImportantException extends Exception { public String toString() { return "A very important exception!"; } } class HoHumException extends Exception { public String toString() { return "A trivial exception"; } }
前一个异常尚未处理就抛出了下一个异常。
当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。
class BaseballException extends Exception {} class Foul extends BaseballException {} class Strike extends BaseballException {} abstract class Inning { public Inning() throws BaseballException {} public void event() throws BaseballException { // Doesn't actually have to throw anything } public abstract void atBat() throws Strike, Foul; public void walk() {} // Throws no checked exceptions } class StormException extends Exception {} class RainedOut extends StormException {} class PopFoul extends Foul {} interface Storm { public void event() throws RainedOut; public void rainHard() throws RainedOut; } public class StormyInning extends Inning implements Storm { // OK to add new exceptions for constructors, but you // must deal with the base constructor exceptions: public StormyInning() throws RainedOut, BaseballException {} public StormyInning(String s) throws Foul, BaseballException {} // Regular methods must conform to base class: //! void walk() throws PopFoul {} //Compile error // Interface CANNOT add exceptions to existing // methods from the base class: //! public void event() throws RainedOut {} // If the method doesn't already exist in the // base class, the exception is OK: public void rainHard() throws RainedOut {} // You can choose to not throw any exceptions, // even if the base version does: public void event() {} // Overridden methods can throw inherited exceptions: public void atBat() throws PopFoul {} public static void main(String[] args) { try { StormyInning si = new StormyInning(); si.atBat(); } catch(PopFoul e) { System.out.println("Pop foul"); } catch(RainedOut e) { System.out.println("Rained out"); } catch(BaseballException e) { System.out.println("Generic baseball exception"); } // Strike not thrown in derived version. try { // What happens if you upcast? Inning i = new StormyInning(); i.atBat(); // You must catch the exceptions from the // base-class version of the method: } catch(Strike e) { System.out.println("Strike"); } catch(Foul e) { System.out.println("Foul"); } catch(RainedOut e) { System.out.println("Rained out"); } catch(BaseballException e) { System.out.println("Generic baseball exception"); } } }
派生类构造器不能捕获基类构造器抛出的异常。
派生类能够不抛出任何异常,即便它是基类定义的异常。
若是处理的恰好是派生类对象的话,编译器只会强制要求你捕获这个类所抛出的异常,若是将它向上转型成基类,那么编译器就会要求你捕获基类的异常。
在继承中,基类的方法必须出如今派生类里,在继承和覆盖的过程当中,某个特定的方法异常说明接口不是变大而是变小——这刚好和类接口在继承时的情形相反。
通常在构造器中,会把对象设置成安全的初始状态。若是在构造器内抛出异常,这些清理行为也许就不能正常工做了。就算使用finally每次都执行了清理代码,若是构造器在器执行的中途异常,那么某些对象就没有被建立成功,而这些部分在finally子句中却要被清理。
import java.io.BufferedReader; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; public class InputFile { private BufferedReader in; public InputFile(String fname) throws Exception { try { in = new BufferedReader(new FileReader(fname)); // Other code that might throw exceptions } catch (FileNotFoundException e) { System.out.println("Could not open " + fname); // Wasn't open, so don't close it throw e; } catch (Exception e) { // All other exceptions must close it try { in.close(); } catch (IOException e2) { System.out.println("in.close() unsuccessful"); } throw e; // Rethrow } finally { // Don't close it here!!! } } public String getLine() { String s; try { s = in.readLine(); } catch (IOException e) { throw new RuntimeException("readLine() failed"); } return s; } public void dispose() { try { in.close(); System.out.println("dispose() successful"); } catch (IOException e2) { throw new RuntimeException("in.close() failed"); } } }
对于构造阶段可能会抛出的异常,而且要求清理的类,最安全的时使用嵌套try子句:
public class Cleanup { public static void main(String[] args) { try { InputFile in = new InputFile("Cleanup.java"); try { String s; int i = 1; while ((s = in.getLine()) != null) ; // Perform line-by-line processing here... } catch (Exception e) { System.out.println("Caught Exception in main"); e.printStackTrace(System.out); } finally { in.dispose(); } } catch (Exception e) { System.out.println("InputFile construction failed"); } } }
对InputFile对象的构造在其本身的try语句块中有效,若是构造失败,将进入外部的catch子句,而dispose()方法不会被调用。若是构形成功,咱们确定要确保对象可以被清理,所以在构造器以后当即建立一个新的try语句。执行清理的finally与内部热try语句块相关联。这种方式中,finally子句在构造失败时不会执行的,而是构形成功时才会被执行。
这种通用的清理惯用法在构造器不抛出任何异常时也应该运用。基本规则是:在建立须要清理的对象以后,当即进入一个try-finally语句中:
class NeedsCleanup { // Construction can't fail private static long counter = 1; private final long id = counter++; public void dispose() { System.out.println("NeedsCleanup " + id + " disposed"); } } class ConstructionException extends Exception { } class NeedsCleanup2 extends NeedsCleanup { // Construction can fail: public NeedsCleanup2() throws ConstructionException { } } public class CleanupIdiom { public static void main(String[] args) { // Section 1: NeedsCleanup nc1 = new NeedsCleanup(); try { // ... } finally { nc1.dispose(); } // Section 2: // If construction cannot fail you can group objects: NeedsCleanup nc2 = new NeedsCleanup(); NeedsCleanup nc3 = new NeedsCleanup(); try { // ... } finally { nc3.dispose(); // Reverse order of construction nc2.dispose(); } // Section 3: // If construction can fail you must guard each one: try { NeedsCleanup2 nc4 = new NeedsCleanup2(); try { NeedsCleanup2 nc5 = new NeedsCleanup2(); try { // ... } finally { nc5.dispose(); } } catch (ConstructionException e) { // nc5 constructor System.out.println(e); } finally { nc4.dispose(); } } catch (ConstructionException e) { // nc4 constructor System.out.println(e); } } }
Section1至关简单:遵循了在可去除对象以后紧跟try—finally原则。若是对象构造不能失败,就不须要任何catch。在Section2中,为了构造和清理,能够看到具备不能失败的构造器对象能够群组在一块儿。
Section3展现了如何处理那些具备能够失败的构造器,且须要清理的对象。
抛出异常的时候,异常处理系统会按照代码的书写顺序找出最近的处理程序。找到匹配的处理程序以后,它就认为异常将获得处理,而后就不在继续查找。
若是把捕获基类的catch子句放在最前面,以此想把派生类的异常全屏蔽,就像这样:
try { throw new Sneeze(); } catch(Annoance a){} catch(Sneeze s){}
这样编译器就会发现Sneeze的catch子句永远也得不到执行,所以它会向你报错。
异常处理系统就像一个活门,使你能放弃程序的正常执行序列。开发异常处理的初衷是为了方便处理错误。
异常处理的一个重要原则:只有在你知道如何处理的状况下才捕获异常。就是把错误的代码同错误发生地点相分离。
被检查的异常:由于它们强制你在可能还没准备好处理错误的时候被迫加上catch子句,这就致使了吞食则有害的问题。
应在下列状况下使用异常: