Summary Java provides a rich exception handling framework, but many programmers have discovered that this rich exception handling framework is much easier to use by using class EXCEPTIONS. This paper discusses the risk of generating, capturing and ignoring the risks of the class Exceptions, and is recommended for the general complex abnormalities in complex software projects.
In the nearest project, there is a piece of code to achieve the source code. Because it has many different calls, it is likely to produce six different exceptions. The original programmer announced after trying to simplify the code (or saved the program): The program still produces 6 different exceptions. This makes the code call must be packaged in a TRY / CATCH block that can capture Exception. The programmer determines: because the code is used to clear the source code, failure is not important, so the catch block is empty, as if the system is closed. Obviously, this is not the best programming solution, but it seems that there is a big mistake, except for a small logic problem in the third line of the source code:
Listing 1. Original Cleanup Code
Private void cleanupconnections () throws exceptionone, exceptiontwo {
For (int i = 0; i Connection [i] .release (); // throws ExceptionOne, ExceptionTWO Connection [I] = NULL; } Connections = NULL; } Protected Abstract Void Cleanupfiles () THROWS Exceptionthree, ExceptionFour Protected Abstract Void Removelisteners () THROWS ExceptionFive, ExceptionSIX Public void cleanureauverything () throws exception { CleanupConnections (); Cleanupfiles (); REMOVELISTENERS (); } Public void done () { Try { Dostuff (); Cleanupeverything (); Domorestuff (); } Catch (Exception E) {} } At another part of the code, the Connections is not initialized until the first connection is created. But if the connection is not found, the connection arrangement is NULL. So in some cases, the call to Connections [i] .Release () will generate NullPointerException. This is also a relatively easy solution. Just add a check of Connections! = Null. However, there is no reporting exception. CleanupConnections () produces an exception, and cleanurerything () produces an exception, and finally the exception is captured by Done (). Done () method does not do anything, or even don't record it. Since CleanupeveryThing () can only be called by done (), it is not seen. So the code has not been resolved. Therefore, in this failure case, there is no CleanupFiles () and Removelisteners () method (so their resources are not released), and Domorestuff () is not called, so that the final processing in done () is not completed. Worse, there is no call when the system is turned off, and the program only calls Done () to complete each transaction. So the resources in each business are missing. This problem is undoubtedly a major problem: the error has no report, the resource is missing. However, the code itself seems unpleasant, and from the code of the code, this problem is difficult to go back. However, using several simple guidelines, you can find and resolve the problem: • Do not ignore the exception · Do not capture the class Exception · Do not generate the most obvious one of the code that exceeded unusually unusually liSTING 1 is: program The error is completely ignored. A non-expected exception is produced (abnormal is inherently non-expected), and the rag is prepared to handle the exception. An even no report because the code assumption: the expected exception will not appear. In most cases, it should be recorded at least in most cases. Several record packs (see "Logging Exceptions") can log system errors and exceptions, and do not affect the performance of the system. Most recording systems also allow print stack paths to provide valuable information about abnormal locations and causes. Finally, because records usually write into the file, we can review and analyze an abnormal record. To view an example of a record stack path, see the Listing 11 of the supplementary column. Recording exception is not important in certain special circumstances. Such as the finally clause is like this. The exception in the Finally clause is in Listing 2, some data is read from the file. Whether it is necessary to read data files, a close () method can be packaged in the finally clause. But if the file is closed, we also take it no way: Listing 2 Public void loadfile (string filename) throws oException { InputStream in = NULL; Try { IN = New fileInputstream (filename); Readsomedata (in); } Finally { IF (in! = NULL) { Try { In.Close (); } Catch (IOException IoE) { // ignored } } } } Note LoadFile () still reports ioException to the modified method, as long as the actual data load failed is the issue of I / O (input / output). Also note: Even if you ignore the exception of Close (), the code will still use the comment to make it clearly marked, so that people who are engaged in this code know this. You can apply the same program to clear all I / O traffic, turn off the plugin and JDBC connections. An important reason for ignoring an abnormality is to ensure that only one method is wrapped in the ignored TRY / CATCH block (such a method can still be called in the package block) and only capture a particular exception. This special situation is essentially different from the capture of class Exception. In other cases, a status should be recorded (at least), it is best to use the stack path record. Doing not capturing class Exception typically in a complex software, the given source code execution will produce a series of exceptions. Dynamic loading classes and initializations will produce several different exceptions, including ClassNotFoundException, InstantiationException, IllegaCcessException, and ClassCastException. Busy programmers often do not add four different CATCH blocks to the TRY block, but to call the method to call the package in the TRY / CATCH block that can capture the class Exception (shown in Listing 3). Although this doesn't look good, it still has some unexpected negative impact. For example: if classname () is null, class.Forname () will give NullPointerexception, which can be captured. In this case, the block will capture it does not want to capture an exception, because NullPointerexception is the subset of RuntimeException, while the RuntimeException is also the subset of Exception. Such CATCH (Exception E) will capture all subsets of RuntimeException, including NullPointRexception, IndexOutOfboundSexception, and ArrayStoreException. In fact, programmers are not willing to capture those abnormalities. In Listing 3, Null ClassName will result in NullPointersException, which indicates that the class name of the call method is invalid: LISTING 3 Public SomeInterface BuildInstance (String ClassName) { SomeInterface Impl = NULL; Try { Class Clazz = Class.Forname (classname); Impl = (SomeInterface) Clazz.newinstance (); } Catch (Exception E) { Log. Error ("Error Creating Class:" ClassName); } Return IMPL; } Another consequence of the class Catch clause is that the record is limited because Catch does not know the captured special exception. Some of the programmers always add an exception type check (such as Listing 4), which is against the original intention of using the Catch block: Listing 4 Catch (Exception E) { IF (E InstanceOf classnotfoundexception) { Log.Error ("Invalid Class Name:" ClassName "," E.TOString ()); Else { Log.Error ("Cannot Create Class:" ClassName "," E.TOSTRING ()); } } Listing 5 provides a special case that may be interested in some programmers. He doesn't need InstanceOf operator because it can capture special exceptions. Each check-over an exception (InstantiationException, IllegalaccessException) is captured and processed. Special circumstances that generate ClassCastException (class is fully loaded, but do not execute SomeInterface interface) can also be verified by checking the anomaly. Listing 5 Public SomeInterface BuildInstance (String ClassName) { SomeInterface Impl = NULL; Try { Class Clazz = Class.Forname (classname); Impl = (SomeInterface) Clazz.newinstance (); } Catch (classnotfoundexception e) { Log.Error ("Invalid Class Name:" ClassName "," E.toTOString ()); } Catch (InstantiationExcection E) { Log.Error ("Cannot Create Class:" ClassName "," E.TOSTRING ()); } Catch (ILLEGALACCESSEXCEE) { Log.Error ("Cannot Create Class:" ClassName "," E.TOSTRING ()); } Catch (ClassCastException E) { Log.Error ("Invalid Class Type," ClassName "Does Not Implement" SomeInterface.class.getName ()); } Return IMPL; } In some cases, it is again given the known exception (or create new exception) than processing the exception in the method. This makes the calling method to handle exceptions by putting an exception in a known context. The following Listing 6 provides alternative versions of the buildinterface () method, and if there is a problem in the loading and initialization class, ClassNotFoundException will be given. In this example, the calling method does receive an appropriate initialized object or exception. Therefore, the calling method does not need to check if the return object is empty. Note: This example uses Java 1.4 methods: Create a new exception, which is packaged within another exception to save the original stack path information. Otherwise, the stack path display method buildinstance () creates an exception, and it should actually be newInstance () generate potential exception: Listing 6 Public SomeInterface BuildInstance (String ClassName) throws classnotfoundexception { Try { Class Clazz = Class.Forname (classname); Return (SomeInterface) Clazz.newinstance (); } Catch (classnotfoundexception e) { Log.Error ("Invalid Class Name:" ClassName "," E.toTOString ()); Throw e; } Catch (InstantiationExcection E) { Throw New ClassNotFoundException ("Cannot Create Class: ClassName, E); } Catch (ILLEGALACCESSEXCEE) { Throw New ClassNotFoundException ("Cannot Create Class: ClassName, E); } Catch (ClassCastException E) { Throw New ClassNotFoundException (ClassName "Does Not Implement" SomeInterface.class.getName (), E); } } In some cases, the code can restore certain error conditions. In this case, it is important to capture special exceptions, so that the code can calculate if the condition can be restored. Use this idea to analyze the sample examples in Listing 6. In Listing 7, the code returns a default object for invalid ClassName, but gives an exception for illegal operations, such as invalid data type conversion or security violation. Note: ILLEGALCLASSEXCEPTION is a domain anomaly class, here it is mentioned just to demonstrate. Listing 7 Public SomeInterface BuildInstance (String ClassName) Throws IllegalClassexception { SomeInterface Impl = NULL; Try { Class Clazz = Class.Forname (classname); Return (SomeInterface) Clazz.newinstance (); } Catch (classnotfoundexception e) { Log.warn ("Invalid Class Name: ClassName ", Using Default "); } Catch (InstantiationExcection E) { Log.warn ("Invalid Class Name: ClassName ", Using Default "); } Catch (ILLEGALACCESSEXCEE) { Throw New IllegalClassexception ("Cannot Create Class: ClassName, E); } Catch (ClassCastException E) { Throw new IllegalClassexception (ClassName "Does Not Implement" SomeInterface.class.getName (), E); IF (IMPL == NULL) { Impl = new defaultImctation (); } Return IMPL; } When you should capture the class Exception When you capture the class Exception and you can capture the class Exception. These situations are very special, but it is important for large fault tolerant systems. In Listing 8, the request always reads and processes from the request queue. However, if any exception occurs during the request processing (either BadRequestexception either any subclass of RuntimeException, including nullpointerException, you can capture the exception outside the process. So any error can cause the process loop to stop and no saving commands can be performed. This means that the way the error is handled in the request processing is not good: Listing 8 Public void processallRequests () { Request req = null; Try { While (true) { Req = getNextRequest (); IF (Req! = null) { ProcessRequest (Req); // Throws BadRequestexception } Else { // Request Queue Is Empty, Must Be Done Break; } } } Catch (BadRequestexception E) { Log.Error ("Invalid Request:" Req, E); } } We can use a better way to complete the request processing: Do two perfect changes to the logic, such as Listing 9. First, try the TRY / CATCH to the request processing loop. Thus, any errors within the processing cycle can be captured and processed, and they do not cause cyclic rupture. Therefore, the cycle continues to process the request, even if a single request processing fails. Second, modify the TRY / CATCH block so that he captures the class Exception so that any exception can capture in the loop and the request can continue: Listing 9 Public void processallRequests () { While (true) { Request req = null; Try { Req = getNextRequest (); IF (Req! = null) { ProcessRequest (Req); // Throws BadRequestexception } Else { // Request Queue Is Empty, Must Be Done Break; } } Catch (Exception E) { Log.Error ("Error Processing Request: Req, E); } } } Capture the class Exception sounds like this in violation of the principle of this part of the beginning. However, specific analysis is specifically analyzed. In this example, the capture class Exception is to prevent a single exception from stopping the entire system. In the case where the request, transaction, or event is handled in the loop, the loop needs to continue, even if an abnormality occurs during the process. In Listing 9, the TRY / CATCH block in the processing cycle can be seen as the highest level exception processor, which requires capture and record any exceptions on the code at this level. In this way, a game will not be ignored and lost, but an exception does not interrupt the remainder of the request that needs to be processed. Each large-scale complex system has a maximum level of anomalous processor (perhaps one of the subsystems, depending on how the system performs processing). The highest level of abnormal processors are unintentionally solved the root cause of abnormalities, but it should not be able to capture and record problems without stopping processing. This is not to say that all exceptions should be given at this level. Any abnormality that can be handled at a lower level, that is, when the problem occurs, the logic understands more conditions, where to deal with exceptions. However, if the abnormality cannot be processed at a lower level, it will continue to advance and give an exception along the way. In this way, all of those unrecoverable errors will only be processed in one place (highest level of exception processor), not throughout the system. The entire issue given in Exception Listing 1 does not start when the programmer decides to give the class Exception from the Cleanuperything () method. When the method gives six different abnormal times, it becomes quite confusing: the method's declaration becomes unreadable, the calling method is forced to capture six different exceptions, see Listing 10: Listing 10 Public void cleanupeverything () THROWS ExceptionOne, ExceptionTWO, Exceptionthree, ExceptionFour, ExceptionFive, Exceptionsix { CleanupConnections (); Cleanupfiles (); REMOVELISTENERS (); } Public void done () { Try { Dostuff (); Cleanupeverything (); Domorestuff (); } Catch (ExceptionOne E1) { // log e1 } Catch (ExceptionTwo E2) { // log e2 } Catch (Exceptionthree E3) { // log e3 } Catch (ExceptionFour E4) { // log e4 } Catch (ExceptionFive E5) { // log e5 } Catch (ExceptionSIX E6) { // log e6 } }