通过异常处理错误
1、概念
2、基本异常
异常处理程序将程序从错误状态中恢复,以使程序要么换一种方式运行,要么继续运行下去。
在没有其它办法的情况下,异常允许我们强制程序停止运行,并告诉我们出现了什么问题。理想状态下,还可以强制程序处理问题,并返回到稳定状态的。
异常参数
关键字throw:
3、捕获异常
监控区域是一个可能产生异常的代码,并且后面跟着处理这些异常的代码。
try块:
如果在方法内部抛出了异常,那么这个方法就此结束。如果不希望这个方法结束,那么可以在方法内设置一个特殊的块来捕获异常,即try块。为什么叫try呢,因为在这个块里“尝试”各种可能产生异常的方法进行调用,所以是try。
异常处理程序catch:
try { // Code that might generate exceptions } catch(Type1 id1)|{ // Handle exceptions of Type1 } catch(Type2 id2) { // Handle exceptions of Type2 } catch(Type3 id3) { // Handle exceptions of Type3 }
终止与恢复:
4、创建自定义异常
package exception; class SimpleException extends Exception {} 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 MyException extends Exception { public MyException() {} public MyException(String msg) { super(msg); }
} public class FullConstructors { public static void f() throws MyException { System.out.println("Throwing MyException from f()"); throw new MyException(); } public static void g() throws MyException { System.out.println("Throwing MyException 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); } } }
import java.util.logging.*; import java.io.*; 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()); } } public class LoggingExceptions { public static void main(String[] args) { try { throw new LoggingException(); } catch(LoggingException e) { System.err.println("Caught " + e); } try { throw new LoggingException(); } catch(LoggingException e) { System.err.println("Caught " + e); } } }
输出结果:
当然,不能指望每个程序员把记录日志的程序的基础设施都构建在异常里,所以更常见的情形是需要捕获和记录他人编写的异常,因此需要在异常处理程序中生成日志消息,如下:
import java.util.logging.*; import java.io.*; 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); } } }
输出结果:
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(); } } 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 h()", 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()); } } }
输出结果:
5、异常说明
6、捕获所有异常
String getLocalizedMessage( )
获取详细信息(抛出异常对象所带的参数),或者用本地语言表示的详细信息。
返回对Throwable的简单描述,要是有详细信息的话,也会把它包含在内。
void printStackTrace(PrintStream)
void printStackTrace(java.io.PrintWriter)
用于在Throwable对象的内部记录栈帧的当前状态。
public class ExceptionMethods { public static void main(String[] args) { try { throw new Exception("My Exception"); } catch (Exception e) { System.out.println("Caught Exception"); System.out.println("getMessage():" + e.getMessage()); System.out.println("getLocalizedMessage():" + e.getLocalizedMessage()); System.out.println("toString():" + e); System.out.println("printStackTrace():"); e.printStackTrace(System.out); } } }
输出:
public class WhoCalled { static void f() { // Generate an exception to fill in the stack trace 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(); } }
输出:
重新抛出异常
package exception; //: exceptions/Rethrowing.java //Demonstrating fillInStackTrace() public class Rethrowing { public static void f() throws Exception { System.out.println("originating the exception in f()"); 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); } } }
输出结果:
class OneException extends Exception { public OneException(String s) { super(s); } } class TwoException extends Exception { public TwoException(String s) { super(s); } } public class RethrowNew { public static void f() throws OneException { System.out.println("originating the exception in f()"); 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 outer try, e.printStackTrace()"); e.printStackTrace(System.out); } } }
输出结果:
异常链
在捕获一个异常后抛出另一个异常,并希望把原始异常的信息保存下来,这被称为异常链。
package exception; //: exceptions/DynamicFields.java //A Class that dynamically adds fields to itself. //Demonstrates exception chaining. 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.println(df); try { df.setField("d", "A value for d"); df.setField("number", 47); df.setField("number2", 48); System.out.println(df); df.setField("d", "A new value for d"); df.setField("number3", 11); System.out.println("df: " + df); System.out.println("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); } } }
7、Java标准异常
特例RuntimeException
比如nullPointerException,空指针异常。
运行时产生的异常,不需要在异常说明中声明方法将抛出RuntimeException类型的异常。它们被称为“不受检查的异常”。这种异常属于错误,会被自动捕获,而不用程序员自己写代码捕获。
package exception; public class NeverCaught { static void f() { throw new RuntimeException("From f()"); } static void g() { f(); } public static void main(String[] args) { g(); } }
8、使用finally进行清理
class ThreeException extends Exception { } public class FinallyWorks { static int count = 0; public static void main(String[] args) { while (true) { try { // Post-increment is zero first time: if (count++ == 0) throw new ThreeException(); System.out.println("No exception"); } catch (ThreeException e) { System.out.println("ThreeException"); } finally { System.out.println("In finally clause"); if (count == 2) break; // out of "while" } } } }
class FourException extends Exception { } public class AlwaysFinally { public static void main(String[] args) { System.out.println("Entering first try block"); try { System.out.println("Entering second try block"); try { throw new FourException(); } finally { System.out.println("finally in 2nd try block"); } } catch (FourException e) { System.out.println("Caught FourException in 1st try block"); } finally { System.out.println("finally in 1st try block"); } } }
package exception; public class MultipleReturns { public static void f(int i) { System.out.println("Initialization that requires cleanup"); 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; System.out.println("End"); return; } finally { System.out.println("Performing cleanup"); } } public static void main(String[] args) { for (int i = 1; i <= 4; i++) f(i); } }
class VeryImportantException extends Exception { public String toString() { return "A very important exception!"; } } class HoHumException extends Exception { public String toString() { return "A trivial exception"; } } public class LostMessage { void f() throws VeryImportantException { throw new VeryImportantException(); } void dispose() throws HoHumException { throw new HoHumException(); } public static void main(String[] args) { try { LostMessage lm = new LostMessage(); try { lm.f(); } finally { lm.dispose(); } } catch (Exception e) { System.out.println(e); } } }
public class ExceptionSilencer { public static void main(String[] args) { try { throw new RuntimeException(); } finally { // Using 'return' inside the finally block // will silence any thrown exception. return; } } }
9、异常的限制
当覆盖 方法时,只能抛出在基类方法的异常说明里列出的那些异常。这个限制意味着,当基类代码运用到派生类时,依旧有用。
package exception; //: exceptions/StormyInning.java //Overridden methods may throw only the exceptions //specified in their base-class versions, or exceptions //derived from the base-class exceptions. 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"); } } } /// :~
当处理派生类对象时,编译器只会强制要求捕获派生类该方法产生的异常。如果向上转型为基类,编译器会要求捕获基类方法产生的异常。很智能的。
异常说明本身并不属于方法类型的范畴中,因此不参与重载的判断。
基于特定方法的“异常说明的接口”不是变大了而是变小了,小于等于基类异常说明表——这恰好和类接口在继承时的情形相反。
10、构造器
11、异常的匹配
package exception; class Annoyance extends Exception { } class Sneeze extends Annoyance { } public class Human { public static void main(String[] args) { // Catch the exact type: try { throw new Sneeze(); } catch (Sneeze s) { System.out.println("Caught Sneeze"); } catch (Annoyance a) { System.out.println("Caught Annoyance"); } // Catch the base type: try { throw new Sneeze(); } catch (Annoyance a) { System.out.println("Caught Annoyance"); } } }
输出: