How to capture and generate category exceptions

Source: Internet
Author: User

Summary

Java provides a rich Exception Handling framework, but many programmers find that it is much easier to skip this rich Exception Handling framework and use only generic exceptions. This article discusses the risks of generating, capturing, and ignoring generic exceptions, and provides the best solution to handle general complex exceptions in complex software projects.

In a recent project, there is a piece of code to clear the source code. Because it has many different calls, it is likely to produce six different exceptions. After trying to simplify the code (or save the Input Program), the programmer declared that the program not only produces 6 different exceptions. Therefore, the Code call must be encapsulated in a try/Catch Block that can catch exceptions. The programmer determines: Because the code is used to clear the source code, failure is not important, so the Catch Block is empty, just as the system is closed.

Obviously, this is not the best programming solution, but it seems that there are no major errors, except for a small logical 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 < connections.length; 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 cleanupEverything() throws Exception {   cleanupConnections();   cleanupFiles();   removeListeners();}public void done() {   try {      doStuff();      cleanupEverything();      doMoreStuff();   }   catch (Exception e) {}}
     

In another part of the code, the connections arrangement is not initialized until the first connection is created. However, if the connection is not clear, the connection arrangement is null. Therefore, in some cases, the call to connections [I]. Release () will produce nullpointerexception. This is also a relatively easy solution. Just add a connections! = NULL check.

However, no exceptions are reported. Cleanupconnections () generates an exception. cleanupeverything () generates another exception and is caught by done. The done () method does not process or even record a single application. Because cleanupeverything () can only be called through done (), no exception is found. Therefore, the Code is not resolved.

Therefore, in this failure case, the cleanupfiles () and removelisteners () methods are not called (so their resources are not released), nor domorestuff () is called, so done () the final processing is not completed. Worse, the system does not call done () when it is shut down. On the contrary, the program only calls done () to complete each transaction. Therefore, the resources in each transaction are missing.

This is undoubtedly a major problem: no error is reported, and resources are missing. But the Code itself does not seem to be a problem, and from the code writing method, this problem is difficult to trace back. However, by using a few simple guidelines, you can discover and solve the problem:

· Do not ignore exceptions

· Do not capture category exception

· No category exception

Ignore exceptions

The most obvious problem in the listing 1 code is that errors in the program are completely ignored. An unexpected exception is generated (an exception is unexpected in nature), and the exception is prepared with a rag. The Code assumes that the expected exception does not occur.

In most cases, exceptions should be recorded at least. Several record packages (see "logging exceptions" in the supplemental bar) can record system errors and exceptions without affecting system performance. Most record systems allow printing stack paths to provide valuable information about the location and cause of exceptions. Finally, because records are usually written into files, we can review and analyze abnormal records. To view the example of the record stack path, see listing 11 in the supplemental column.

Record exceptions are not important in some special circumstances. Such as the finally clause.

Exception in finally Clause

In Listing 2, some data is read from the file. The close () method can be encapsulated in the finally clause, regardless of whether the exception reads data files. However, if the file is closed due to an exception, we cannot use it:

     
      Listing 2 public void loadFile(String fileName) throws IOException {   InputStream in = null;   try {      in = new FileInputStream(fileName);      readSomeData(in);   }   finally {      if (in != null) {         try {            in.close();         }         catch(IOException ioe) {            // Ignored         }      }   }}
     

Note that LoadFile () still reports ioexception to the calling method, as long as the actual data loading fails because of I/O (input/output) problems. Note: Even if you ignore the close () exception, the code will still use annotations to clearly mark it so that everyone who is engaged in the code can understand it. You can use the same program to clear all I/O information flows, close plug-ins and JDBC connections, and so on.

One important reason for ignoring exceptions is to ensure that only one method is enclosed in the ignored try/Catch Block (so that other methods in the encapsulated block can still be called) only a special exception is caught. This special case is essentially different from the exception capture of a category. In other cases, one field should be recorded (at least), preferably using a stack path record.

Do not capture category exception

Generally, in a complex software, a series of exceptions are generated when a given source code is executed. Dynamic Loading class and initialization object will produce several different exceptions, including classnotfoundexception, instantiationexception, illegalaccessexception, and classcastexception.

Busy programmers usually do not add four catch blocks to try blocks, but wrap method calls in try/catch blocks that can catch exceptions of the class (as shown in listing 3 below ). Although it seems that there is nothing wrong with it, it still produces unexpected negative effects. For example, if classname () is null, class. forname () will give nullpointerexception, which can be caught.

In this case, the block captures exceptions that it does not want to capture, because nullpointerexception is the subset of runtimeexception, and runtimeexception is the subset of exception. In this way, catch (exception e) captures all the subsets of runtimeexception, including nullpointerexception, indexoutofboundsexception, and arraystoreexception. In fact, programmers are not willing to capture those exceptions.

In listing 3, null classname will cause nullpointerexception, which indicates that the class name of the method to be called 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 a catch clause is that the record is restricted because catch does not know the special exceptions to be captured. Some programmers always add a check (for example, listing 4) to check the exception type in the face of this problem, which is contrary to the original intention of using catch blocks:

     
      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, which may be of interest to some programmers. It does not need the instanceof operator because it can catch special exceptions. Every checked exception (classnotfoundexception, instantiationexception, illegalaccessexception) is captured and processed. In special cases where classcastexception is generated (the class is fully loaded, but the someinterface interface is not executed), you can check this exception for verification.

     
      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.toString());   }   catch (InstantiationException e) {      log.error("Cannot create class: " + className + ", " + e.toString());   }   catch (IllegalAccessException e) {      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 better to give a known exception (or create a new exception) than to handle the exception in the method. This allows the call method to handle exceptions by placing exceptions in a known context.

The following listing 6 provides alternative versions of the buildinterface () method. If a problem occurs during the loading and initialization of the class, it will provide classnotfoundexception. In this example, the call method does receive an appropriate initialization object or exception. Therefore, you do not need to check whether the returned object is empty when calling a method.

Note: This example uses the Java 1.4 method: Create a new exception, which is wrapped in another exception to save the original stack path information. Otherwise, the stack Path Display Method buildinstance () creates an exception. In fact, newinstance () produces a 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.toString());      throw e;   }   catch (InstantiationException e) {      throw new ClassNotFoundException("Cannot create class: " + className, e);   }   catch (IllegalAccessException e) {      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 whether the conditions can be restored. Use this idea to analyze the class examples in Listing 6.

In listing 7, the Code returns a default object for invalid classname, but provides an exception for illegal operations, such as invalid data type conversion or security violation.

Note: illegalclassexception is a domain exception class. It is mentioned here only for demonstration.

     
      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 (InstantiationException e) {      log.warn("Invalid class name: " + className + ", using default");   }   catch (IllegalAccessException e) {      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 DefaultImplemantation();   }   return impl;}
     

When should exceptions be caught

You can capture a category exception when it is convenient and necessary. These situations are special, but they are important for large-scale Fault-Tolerant Systems. In listing 8, requests are always read and processed from the request queue in sequence. However, if any exception occurs during request processing (either badrequestexception or any subclass of runtimeexception, including nullpointerexception), the exception can be caught out of processing during the loop. Therefore, any errors can stop the processing loop, and cannot execute any save command. This indicates that the method for handling errors during 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 request processing: make two perfect changes to the logic, such as listing 9. First, move try/catch to the request processing cycle. In this way, any errors in the processing cycle can be captured and processed, and they will not cause loop cracking. Therefore, it does not matter if the processing of a single request fails. Next, modify the try/Catch Block so that it can capture the exception of the class, so that any exception can be caught 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);      }   }}
     

Capturing a category exception sounds like a violation of the principles mentioned at the beginning-indeed. But the specific situation is analyzed. In this example, the catch category exception is used to prevent a single exception from causing the entire system to stop. When requests, transactions, or events are processed in a loop, the loop must continue, even if an exception occurs during the process.

In listing 9, The try/Catch Block in the processing loop can be considered as the highest level exception processor, which needs to capture and record any exceptions on the code at this level. In this way, a request will not be ignored or lost, but the exception does not interrupt the rest of the request to be processed.

Every large and complex system has a high-level exception processor (maybe each subsystem has one, depending on how the system executes the processing ). The highest level exception processor has no intention of solving the root cause of the exception, but it should be able to capture and record the problem without stopping the processing. This does not mean that all exceptions should be given at this level. Any exception that can be processed at a lower level, that is, when the logic of the problem occurs, you can learn more about the conditions and handle the exception. However, if the exception cannot be handled at a lower level, proceed and give an exception along the way. In this way, all the unrecoverable errors will only be handled in one place (the highest level exception processor), rather than throughout the entire system.

No category exception is given

The entire problem given in Listing 1 starts when the programmer decides to give a class exception from the cleanupeverything () method. When the methods are given with six different exceptions, the Code becomes quite messy: the Declaration of the method becomes unreadable, and the calling method is forced to catch 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   }}
     

But even if the code is a bit messy, it should at least be clear. The use of special exceptions can avoid a few very practical problems: giving a category exception hides the details of the fundamental problem and thus has no chance to deal with the real problem. In addition, any call that forces a class exception to this method is given, either capturing the class exception (which is problematic when capturing the class exception, as discussed earlier) or re-providing the class Exception Propagation problem.

In general, when a method announces that it is giving a class exception, it is nothing more than one of the following two reasons: one case is that the method calls several other methods that may give many different exceptions (such as the mediator or fa c ade design pattern) and hides the details of the exception conditions. This method does not create or give an exception at the domain level (used to enclose low-level exceptions), but simply declares that it wants to give exceptions regardless of the results. Another case is that the method illustrates and provides the exception (throw new exception (), because the programmer does not have to think about the exception to be used to really express his situation.

In both cases, you only need to think a little bit about the design to solve the problem: how should the detailed domain-level exceptions really be given? This design includes a simple declaration method to provide possible exceptions. Another way is to create a domain-level exception to wrap the exception and declare it. In most cases, the exception (or exception group) given by the method should be as detailed as possible. For detailed exceptions, You can provide more information about error conditions to allow error handling or at least to obtain detailed records.

The exception class is checked exception, which means that any call to the method that declares that it gives an exception must either declare that it gives the exception itself, either the package method is called within the try/Catch Block that can capture exceptions of the class. I have already explained the problems related to this method.

Use category exception with caution

This article discusses several aspects of Exception Handling: they should not be given and cannot be ignored. They should be captured very little (only in special cases. They do not provide detailed information that allows you to effectively process them, nor allow you to stop capturing detailed information about exceptions you do not want to capture.

Exceptions are a powerful component of Java. If they are used correctly, they can improve programmer efficiency and shorten your development cycle, especially in the testing and debugging stages. If they are incorrectly used, they will be against you: hiding problems in your system. Note the location and method in which you use the exception class.

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.