830 likes | 965 Views
Exception Handling. 提要. 简介 Java 异常处理 Manchester University, Kung-Kiu Lau, CS3101 源自 http://www.cs.man.ac.uk/~kung-kiu/cs3101/ Java Exception 机制的不当使用示例 C++ 异常处理 http://www.cs.unm.edu/~ingber/SNL/CS162/PowerPoint/Session12and13.ppt Eiffel 异常处理 小结. 简介. 问题 何谓“异常( Exception )” ?
E N D
提要 • 简介 • Java 异常处理 • Manchester University, Kung-Kiu Lau, CS3101 源自 http://www.cs.man.ac.uk/~kung-kiu/cs3101/ • Java Exception机制的不当使用示例 • C++ 异常处理 • http://www.cs.unm.edu/~ingber/SNL/CS162/PowerPoint/Session12and13.ppt • Eiffel 异常处理 • 小结 Institute of Computer Software Nanjing University
简介 • 问题 • 何谓“异常(Exception)”? • 一种情况要异常到什么程度才算“异常”? • 为什么要引入异常处理机制? • Robustness readability? • 如何进行异常处理? • Mandatory or Optional • 不同的认识,不同的答案 • Java/C++/C# • Eiffel Institute of Computer Software Nanjing University
Java Exception Mechanisms • Java 对 Exception 的界定 较宽 • 因而 需 认真区分 不同类型的 Exceptions • Java的Exception机制回顾 • try/catch/finally • 自定义Exceptions • Java Exception与Design by Contract • Exception的转换 Institute of Computer Software Nanjing University
Understanding Java Exceptions • 这部分使用Manchester University, Kung-Kiu Lau, CS3101的PPT Slides 源自 http://www.cs.man.ac.uk/~kung-kiu/cs3101/ Institute of Computer Software Nanjing University
What are Exceptions? Many “exceptional” things can happen during the running of a program, e.g.: • User mis-types input checked • Web page not available • File not found • Array index out of bounds unchecked • Method called on a null object • Divide by zero • Out of memory sys errors • Bug in the actual language implementation Exceptions are unexpected conditions in programs.
The World Program System We can distinguish 3 categories: • checked exceptions — Problems to do with the program's interaction with “the world”. • unchecked exceptions — Problems within the program itself (i.e. violations of the contract, or bugs). The world is unpredictable, so we would expect these things to happen in production code, and so need to handle them. • system errors— Problems with the underlying system. These are outside our control. These should be removed by testing, and not occur in production code. Checked Exceptions Unchecked Exceptions System Errors
The World Checked Exceptions Program Unchecked Exceptions System Errors System Checked vs Unchecked Exceptions an important distinction, which the Java Exception class hierarchy does make, but in a rather confusing way it's normal to let these just crash the program so we can debug it. we would normally check for these, and deal with them when they occur. Exception handling is the business of handling these things appropriately.
The World Checked Exceptions Program Unchecked Exceptions System System Errors Exception Hierarchy in Java Throwable Error Exception . . . RunTimeException ExceptionRunTimeException RunTimeException Error
What do we want of exceptions? Ideally, a language (and its implementation) should: • Restrict the set of possible exceptions to “reasonable” ones • Indicate where they happened, and distinguish between them e.g. no pointers to deallocated memory • Allow exceptions to be dealt with in a different place in the code from where they occur and not map them all to “bus error” etc. so we throw exceptions where they occur, and catch them where we want to deal with them. so normal case code can be written cleanly without having to worry about them Ideally, we don't want non-fatal exceptions to be thrown too far — this breaks up the modularity of the program and makes it hard to reason about.
Exceptions in Java — a reminder In Java, the basic exception handling construct is to: • trya block of code which normally executes ok • catchany exceptions that it generates, and • finally do anything we want to do irrespective of what happened before. If a thrown exception is not caught, it propagates out to the caller and so on until main. If it is never caught, it terminates the program. If a method can generate (checked) exceptions but does not handle them, it has to explicitly declare that it throws them so that clients know what to expect.
// Do this regardless public class Etest { finally { public static void main(String args[]){ System.out.println( "That's all, folks" ); } // What we expect to happen try { int x = Integer.parseInt(args[0]); int y = Integer.parseInt(args[1]); System.out.println( x + "/" + y + " = " + x/y ); } // Things which can go wrong catch (IndexOutOfBoundsException e) { System.out.println( "Usage: Etest <int> <int>" ); } catch (NumberFormatException e) { System.out.println( e.getMessage() + " is not a number" ); } } // main } // Etest Example
// Do this regardless public class Etest { finally { public static void main(String args[]){ System.out.println( "That's all, folks" ); } // What we expect to happen try { int x = Integer.parseInt(args[0]); int y = Integer.parseInt(args[1]); System.out.println( x + "/" + y + " = " + x/y ); } // Things which can go wrong catch (IndexOutOfBoundsException e) { System.out.println( "Usage: Etest <int> <int>" ); } catch (NumberFormatException e) { System.out.println( e.getMessage() + " is not a number" ); } } // main } // Etest > java Etest 99 42 99/42 = 2 That's all, folks
// Do this regardless public class Etest { finally { public static void main(String args[]){ System.out.println( "That's all, folks" ); } // What we expect to happen try { int x = Integer.parseInt(args[0]); int y = Integer.parseInt(args[1]); System.out.println( x + "/" + y + " = " + x/y ); } // Things which can go wrong catch (IndexOutOfBoundsException e) { System.out.println( "Usage: Etest <int> <int>" ); } catch (NumberFormatException e) { System.out.println( e.getMessage() + " is not a number" ); } } // main } // Etest > java Etest 99 Usage: Etest <int> <int> That's all, folks
// Do this regardless public class Etest { finally { public static void main(String args[]){ System.out.println( "That's all, folks" ); } // What we expect to happen try { int x = Integer.parseInt(args[0]); int y = Integer.parseInt(args[1]); System.out.println( x + "/" + y + " = " + x/y ); } // Things which can go wrong catch (IndexOutOfBoundsException e) { System.out.println( "Usage: Etest <int> <int>" ); } catch (NumberFormatException e) { System.out.println( e.getMessage() + " is not a number" ); } } // main } // Etest > java Etest 99 fred fred is not a number That's all, folks
// Do this regardless public class Etest { finally { public static void main(String args[]){ System.out.println( "That's all, folks" ); } // What we expect to happen try { int x = Integer.parseInt(args[0]); int y = Integer.parseInt(args[1]); System.out.println( x + "/" + y + " = " + x/y ); } // Things which can go wrong catch (IndexOutOfBoundsException e) { System.out.println( "Usage: Etest <int> <int>" ); } catch (NumberFormatException e) { System.out.println( e.getMessage() + " is not a number" ); } } // main } // Etest > java Etest fred fred is not a number That's all, folks
// Do this regardless public class Etest { finally { public static void main(String args[]){ System.out.println( "That's all, folks" ); } // What we expect to happen try { int x = Integer.parseInt(args[0]); int y = Integer.parseInt(args[1]); System.out.println( x + "/" + y + " = " + x/y ); } // Things which can go wrong catch (IndexOutOfBoundsException e) { System.out.println( "Usage: Etest <int> <int>" ); } catch (NumberFormatException e) { System.out.println( e.getMessage() + " is not a number" ); } } // main } // Etest > java Etest 99 0 That's all, folks java.lang.ArithmeticException: / by zero at Etest.main(Etest.java:8)
Socket s; InputStream in; ... try { s = new Socket(...); ... in = s.getInputStream(); } catch (IOException e) {...} finally { finally { if (in != null) in.close()); try { if (in != null) in.close()); s.close(); s.close(); } } catch (IOException e){} } Using finally for Cleanup Finalizers aren't much good for releasing resources To get guaranteed cleanup of network connections etc. use finally clauses, e.g.: because we don't know when (or even if) they will be called But there's a snag — the call to in.close() may itself throw an exception So we actually need a try...catch block within the finally clause e.g. if the network goes down at the wrong moment
class UnhappyWidgetException extends Exception { public UnhappyWidgetException (Widget w) { super(w.getProblem()); } Creating and Throwing Your Own Exceptions You can create your own exception classes by inheriting from the standard ones. E.g. When the relevant problem is detected, you create an exception and throw it. The constructor should call the superclass constructor with a string to indicate what went wrong
If it's not handled within the same method, you need to declare that the method throws the exception E.g. and likewise for methods which call that method, and methods which call those methods ... public void widgetTest(Widget wig) throws UnhappyWidgetException { if (wig.isHappy()) {...} } else throw(new UnhappyWidgetException(wig)); Note the freedom which GC gives us: whenever we need a new thingy (UnhappyWidgetException) we just make one we don't have to worry about when we will be able to reclaim the memory.
Throwable Error Exception . . . RunTimeException The Throwable Class Hierarchy The standard API defines many different exception types • basic ones in java.lang • others in other packages, especially java.io Top level class is notException but Throwable.
Throwable Error Exception . . . RunTimeException Problems with the underlying Java platform, e.g. VirtualMachineError. User code should never explicitly generate Errors. This is not always followed in the Java API, and you may occasionally need to say catch (Throwable e) to detect some bizarre error condition In general, you want to catch specific sorts of (checked) exceptions, and saying catch (Exception e) is bad style. never mind Throwable!
Throwable Error Exception . . . RunTimeException Unchecked exceptions are subclasses of RuntimeException It would be much clearer if this was called UncheckedException These include our old friends NullPointerException and ArrayIndexOutOfBoundsException. Virtually any method can in principle throw one or more of these, so there's no requirement to declare them in throwsclauses.
Throwable Error Exception . . . RunTimeException All other exceptions are checked exceptions It would be nice if there was a class CheckedException. The most common ones are IOExceptions such as FileNotFoundException. If you write code which can throw one of these, you must either catch it or declare that the method throwsit. The compiler goes to enormous lengths to enforce this, and related constraints, e.g. that all overridden versions of a method have consistent throws clauses.
Exception Handling and DbC Exceptions are about dealing with things going wrong at runtime. DbC is about statically defining the conditions under which code is supposed to operate. (The two are nicely complementary.) • Unchecked exceptions are “what happens when the contract is broken” • Checked exceptions are expected to happen from time to time so are not contract violations. e.g. if a precondition that an array has at least one element is broken, an ArrayIndexOutOfBoundsExceptionwill probably occur.
So if we're going to use DbC,we ought to structure our code into: • code which deals with The World • code which is insulated from The World. Methods doing this will have no (or weak) preconditions, and will deal with problems via exception handling. This can have strong preconditions and won't throw exceptions (apart from unchecked ones during debugging). E.g. an application involving form-filling should have • code which validates the user input for type-correctness etc. • code which relies on the input being correct to process it If these two sorts of operations are mixed together, the application will probably be harder to maintain and more error-prone.
Client Black box op Converting Exceptions Good OO design decouples things. E.g. in most patterns there's a Clientclass which delegates operations to some “black box” in the pattern. some application-specific code some application-independent library What happens if the black box throws a (checked) exception? may have its own exception types, but they need to be application-specific. exceptions thrown from within the library will be application-independent. The trick is to convert from the former to the latter.
try {... obj.delegateOp(...); ... } catch (ApplicationIndependentException e){ throw new ApplicationSpecificException(e); } ApplicationSpecificException will therefore have a constructor which • takes an ApplicationIndependentException • extracts the relevant information and re-casts it in application-specific terms.
Example Consider a compiler for an oo language which has an application-independentHierarchyclass which represents a hierarchy of arbitrary key-value pairs. In such a compiler, it's useful to have an explicit representation of the inheritance hierarchy of the source program as a tree (or graph). If something goes wrong in building or using a Hierarchy an InvalidHierarchyException is thrown. The key is the class name and the value is the definition of the class. • If this occurs while the class hierarchy is being built, it means the user has mis-spelt a class name or whatever. e.g. a node has no parent, or there's a cycle The InvalidHierarchyExcetpion is converted to a (checked) IllegalInheritanceException resulting in a suitable error message being displayed to the user. • If it happens later, an (unchecked) CompilerBrokenExpection is thrown instead, because it means there's a bug in the compiler.
Summary • Exception handling is an important, highly integrated, part of the Java system, one of the Really Neat Things About Java. • Unchecked exceptions routinely occur during debugging, and make that process much easier. By product shipping time, they should no longer occur. • Checked exceptions happen when the program's interaction with the world departs from the normal case. Make sure you handle them appropriately. In particular, think hard about where to handle an exception, so as to simplify the normal-case code, but not to do the handling so far away that modularity is compromised. • Application-independent exceptions need to be converted to application-specific ones.
Java Exception • Java Exception机制的不当使用: • 引自网络论坛: http://www.chinaunix.net/forum/viewtopic.php?t=136123 Institute of Computer Software Nanjing University
1 OutputStreamWriter out = ... 2 java.sql.Connection conn = ... 3 try { // ⑸ 4 Statement stat = conn.createStatement(); 5 ResultSet rs = stat.executeQuery( 6 "select uid, name from user"); 7 while (rs.next()) 8 { 9 out.println("ID:" + rs.getString("uid") // ⑹ 10 ",姓名:" + rs.getString("name")); 11 } 12 conn.close(); // ⑶ 13 out.close(); 14 } 15 catch(Exception ex) // ⑵ 16 { 17 ex.printStackTrace(); //⑴,⑷ 18 } Institute of Computer Software Nanjing University
问题 • 丢弃异常 • 不指定具体的异常 • 占用资源不释放 • 不说明异常的详细信息 • 过于庞大的try块 • 输出数据不完整 Institute of Computer Software Nanjing University
finally { if (conn != null) { try { conn.close(); } catch(SQLException sqlex2) { System.err(this.getClass().getName() + ".mymethod - 不能关闭数据库连接: " + sqlex2.toString()); } } if (out != null) { try { out.close(); } catch(IOException ioex2) { System.err(this.getClass().getName() + ".mymethod - 不能关闭输出文件" + ioex2.toString()); } } } OutputStreamWriter out = ... java.sql.Connection conn = ... try { Statement stat = conn.createStatement(); ResultSet rs = stat.executeQuery( "select uid, name from user"); while (rs.next()) { out.println("ID:" + rs.getString("uid") + ",姓名: " + rs.getString("name")); } } catch(SQLException sqlex) { out.println("警告:数据不完整"); throw new ApplicationException( "读取数据时出现SQL错误", sqlex); } catch(IOException ioex) { throw new ApplicationException( "写入数据时出现IO错误", ioex); } Institute of Computer Software Nanjing University
C++ Exception简介 • 这部分使用Jeanine Ingber的PPT Slides • 源自http://www.cs.unm.edu/~ingber/SNL/CS162/PowerPoint/Session12and13.ppt • 该连接似乎实效了 Institute of Computer Software Nanjing University
What is an Exception? • Any event outside the bounds of routine behavior. • Some typical examples: • Error conditions (ie iterator/offset out of bounds) • Unexpected data format • Memory management failures
Handling Problems • Return a special error value T* error = “some error code”; if(iter < 0 || iter >= maxSize) return *error; return mat[iter]; But what happens here? x = v[2] + v[4]; //not safe!
Handling Problems • Just Die if(iter < 0 || iter >= maxSize) exit(1); return mat[iter]; • Die Gracefully assert(iter < 0 && iter >= maxSize) return mat[iter]; Output when assert fails: Assert.C:7: failed assertion `iter < 0 && iter >= 5' Abort
Why Use Exception handling? • Many times you do not know what should be done if an error occurs. • Solution: Send notification and let the caller of your routine take responsibility.
Exception Handling • In C++, Exception Handling allows the programmer to notify the user when there is a problem. • The problem can be handled in the notifying routine, or passed along to the caller.
Exception Handling Facility • The exception handling facility consists of two primary components: • Recognizing a problem and throwing an exception. (try - throw) • Eventual handling of the exception. (catch)
Simplistic overview: int main(int argc, char* argv[]) { string filename; try { if(argc <= 1) throw “no command line arguments”; fin.open(argv[1]); } catch(string s) { cout << “enter input filename “; cin >> filename; fin.open(filename.c_str()); } //process data
Throwing an Exception • An exception is thrown using a throw statement. • Example: • throw 42; • throw “Panic message”; • throw IteratorOverflow(iter); • IteratorOverflow e(iter); throw e;
throw Statement • An exception (ie expression in a throw statement) is an object of some type. • Typically an exception is a class object of an explicitly provided exceptionclass or exceptionclasshierarchy. if(iter < 0 || iter >= maxSize) throw IteratorOverflow(iter, maxSize); //class constructor return mat[iter];
class IteratorOverflow{ public: IteratorOverflow(int index, int max) : offset(index), bound(max) {} int getOffset() {return offset;} int getBound() {return maxSize;} void reset(){offset=0;} void message(ostream& os = cerr) { os << “Error: offset “ << offset << “ exceeds maximum bound “ << bound; } private: int offset, bound; };
Nothing Exceptional About Exception Classes • An exception class hierarchy provides: • Storage for data that we wish to carry across function calls. • Methods for displaying data and messages. • An exception object calls its message method to display information from within a catch block.
catch Blocks • A program catches exceptions by their type using a sequence of catch blocks. • A catch block consists of three parts: • the keyword catch • the declaration of a single type within parentheses • a statement block
Example: catch(IteratorOverflow &iof) { iof.message(); //message writes to cerr iof.message(log); //log is iostream object iof.reset() //or something … } continue; • The type of the exception object that was thrown is compared against the exception declaration of each catch block in turn. If the types match, the statement block is executed. • The above represents a completehandling of the exception. Normal program execution continues with the first statement following the catch block.
Re-throw Expression It is often the case that we can not completely handle an exception. If this is the case, we can re-throw the exception. catch(IteratorOverflow &iof) { iof.message(); //message writes to cerr iof.message(log); //log is iostream object throw; } • A re-throw can occur only within a catch block • The search for a matching catch block continues up the line • If complete handling in some catch block does not occur, the program aborts.
Catch all Block catch(…) //catch any and all exceptions { cerr << “unknow exception thrown. GoodBye! “; //… exit(1); }