From JavaWorld, see the original article:When catching exceptions, dont cast your net too wide
By Dave Schweisguth
Understand how the Java compiler checks catch clauses during compilation.
Summary
Compared with other languages, Java's simplicity and consistency enable the compiler to detect many errors. Java developers realize how to rely on the compiler to capture incorrect types, nonexistent methods (the topic of this Article), and incorrect exception handling. However, when you really need to know what you are doing, the situations that you don't want to see will suddenly appear. If you can understand exactly how Java makes you throw and catch exceptions, You will know when you need to be especially careful and what kind of habits will keep you away from worries.
The Java compilation check perfectly supports the framework that maintains exception security. If a method declaration throws an exception, you cannot call this method in your method without capturing this exception or declaring your method. (For more extensive discussions, see "Designing with Exception") the compiler sometimes prevents you from capturing an Exception that is not thrown in the try block, but it is not always the case, most of the time. This Java Tip once again discusses the compile-time check.
Check the compilation period of the throw clause
First, let's differentiate how Java checks exceptions caught by catch clauses and how Java checks exceptions declared in a method that are thrown. (In this article, when I start with a lower-case letter e, it refers to java. lang. Throwable and its subclass. When I want to specify a specific class, such as java. lang. Exception, I will include a package sentence or a class name that starts with at least an uppercase letter .) At the beginning, this method seems very similar: Both of them indicate the expected exception through the Association of code blocks. However, when Java requires a method to declare the exception it throws, it does not rely on to require that method to throw every declared exception, java allows you to design APIs that keep the program stable when you add functions.
Check the original version of the self-created connection pool in the following figure:
- Public ClassConnectionPool {
- PublicConnectionPool ()ThrowsConnectionException {
- }
- PublicConnection getConnection ()ThrowsConnectionException {
- // Allocate a connection (possibly throwing a ConnectionException or
- // Subclass) if necessary, then return it
- }
- }
The constructor does nothing, and the code in the getConnection () method may throw a ConnectionException exception. Therefore, in this implementation, the method does not need to declare any exceptions. However, in the next version, we overwrite this class to speed up getConnection:
- Public ClassConnectionPool {
- PublicConnectionPool ()ThrowsConnectionException {
- // Allocate all the connections we think well ever need
- }
- PublicConnection getConnection ()ThrowsConnectionException {
- // Allocate a connection if necessary (not likely), then return it
- }
- }
The constructor we wrote in the first version declares ConnectionException, so the code using it does not need to be rewritten to use the second version. Java checks the throw clause to ensure consistent stability in all other classes that call this constructor. This is a good way.
Check the compilation period of the catch clause
The catch clause and throw clause have different contents. This does not apply to the idea of API stability: When a method declaration is part of a public interface of a class, a try/catch Block is an implementation that hides details from the caller's perspective. Not only does it not understand how to catch a try block without throwing an exception because of a catch clause, but it also ensures that it does not do so in order to capture serious code errors. For this reason, Java requires that your try block indeed throw an exception captured by their catch clause. For example, if you do not forget your previous operating system and write the following code:
- Public ClassRm {
- Public Static VoidMain (String[] Args ){
- For(IntI = 0; I <args.Length; I ++ ){
- Try{
- NewFile (args [I]). delete ();
- }Catch(IOException e ){// Wont compile !!!
- System. Err. println ("rm: Couldnt delete" + args [I]);
- }
- }
- }
- }
Sun Microsystems's Java compiler will tell you IOException "is never thrown in body of corresponding try statement. "That's because File. delete () does not throw any exception at all, but returns false if the object cannot be deleted. If it is not an exception check for the catch clause during compilation, you may accidentally write a program that fails but has no obvious errors.
"But, wait", you are a thoughtful and experienced programmer. "People have been capturing java in huge code. lang. exception, and does not always throw java in those try blocks. lang. exception, but it can still be compiled!" You are right. Here we will soon reach our goal: what are the rules Java uses when checking exceptions that can be caught in a catch clause?
The catch clause also captures child classes.
The answer has two parts: first, a catch clause captures its parameter type exceptions and its subclass. This is also a valuable language feature: a method for declaring and throwing exceptions, such as javax. naming. NamingException, can indeed throw many sub-classes of NamingException. The caller of the method needs to know that a catch clause can be written for a special subclass, so that NamingException cannot be captured only. Furthermore, if the implementation of a method that will throw the NamingException subclass changes in later versions, the original implementation does not need to be changed, and its callers do not need to change. This flexibility also gives the API greater stability.
As a matter of fact, capturing sub-classes in a catch clause also has some minor issues. For example, many readers may have written practical methods similar to the following:
- Public ClassConnectionUtil {
- /** Close the connection silently. Keep going even if theres a problem .*/