Bill Venners, the chenkw translation from: www.javaresearch.org
Summary
This article is a column of design technology to discuss the problem of abnormal design. This article focuses on when to use exceptions and shows examples of proper use of exceptions. In addition, this article provides some basic principles of exception design.
Five months ago, I started writing articles about design objects. This article is a continuation of the design Literature series, and discusses the design principles of error reporting and exception. I assume that the reader already knows what the exception is and how the exception works. If you want to review the knowledge of the anomaly, please read the sister article Java exception in this article.
The benefits of exceptions
Exceptions bring many benefits. First, it separates the error-handling code from the normal code. You can encapsulate those code with execution probabilities of 99.9% in a try block, and then----the exception-handling code that is not often executed----placed in a catch clause. The benefit of this approach is that the normal code is therefore more concise.
If you don't know how to handle a particular error in a method, you can throw an exception in the method and give the processing to someone else. If you throw a check exception (checked exception), then the Java compiler will force the client programmer (cilent programmer) to handle the potential exception, or catch it, or declare it in the throws clause of the method. The Java compiler ensures that check exceptions are handled, which makes the Java program more robust.
When to throw an exception
When should the exception be thrown? The answer is attributed to one principle:
If the method encounters an unexpected condition (abnormal condition) that does not know how to handle it, then it should throw an exception.
Unfortunately, although this principle is easy to remember and cite, it is not very clear. In fact, it raises another question: what is an unexpected situation?
It's a $64,000 issue. It is a subjective decision whether to treat a special event as an "unexpected condition". The basis is usually not obvious. Because of this, it is valuable.
A more useful rule of thumb is:
Avoid using exceptions when there is sufficient reason to treat a situation as a typical function (typical functioning) part of the method.
Therefore, an unexpected situation is a situation outside the normal functioning of the method. Allow me to illustrate the problem with a few examples.
A few examples
The first example uses the FileInputStream class and the DataInputStream class of the java.io package. This is the code that uses the FileInputStream class to send the file contents to standard output:
In source packet in file Except/ex9/example9a.java
Import java.io.*;
Class EXAMPLE9A {
public static void Main (string[] args)
Throws IOException {
if (Args.length = = 0) {
SYSTEM.OUT.PRINTLN ("must give filename as first arg.");
Return
}
FileInputStream in;
try {
in = new FileInputStream (args[0]);
}
catch (FileNotFoundException e) {
System.out.println ("Can ' t Find file:" + args[0]);
Return
}
int ch;
while ((ch = in.read ())! =-1) {
System.out.print ((char) ch);
}
System.out.println ();
In.close ();
}
}
In this example, the Read method of the FileInputStream class reports the "End of file reached" condition, but instead of throwing an exception, it returns a special value:-1. In this method, reaching the end of the file is considered the "normal" part of the method, which is not an exception. The usual way to read a byte stream is to continue reading down until the end of the byte stream.
In contrast, the DataInputStream class takes another way to report the end of a file:
In source packet in file Except/ex9b/example9b.java
Import java.io.*;
Class Example9b {
public static void Main (string[] args)
Throws IOException {
if (Args.length = = 0) {
SYSTEM.OUT.PRINTLN ("must give filename as first arg.");
Return
}
FileInputStream fin;
try {
Fin = new FileInputStream (args[0]);
}
catch (FileNotFoundException e) {
System.out.println ("Can ' t Find file:" + args[0]);
Return
}
DataInputStream din = new DataInputStream (FIN);
try {
int i;
for (;;) {
i = Din.readint () ;
System.out.println (i);
}
}
catch (eofexception e) {
}
Fin.close ();
}
}
The Readint () method of the DataInputStream class reads four bytes at a time and interprets it as an int type data. When the end of the file is read, the ReadInt () method throws Eofexception.
This method throws an exception for two reasons. First, readInt () cannot return a special value to indicate that the end of the file has been reached because all possible return values are valid integer data. (for example, it cannot use the special value of-1 to indicate the end of the file, because-1 is probably the normal data in the stream.) Second, if Readint () reads only one, two, or three bytes at the end of the file, this can be considered an "unexpected situation". Originally this method is to read four bytes, but only one to three bytes readable. Since this exception is an integral part of using this class, it is designed to be an inspection-type exception (subclass of the exception class). The client programmer is forced to handle the exception.
The third way to indicate the "at the end" condition is demonstrated in the StringTokenizer class and the Stack class:
In source packet in file Except/ex9b/example9c.java
This program prints the White-space separated tokens of a
ASCII file in reverse order of their appearance in the file.
Import java.io.*;
Import java.util.*;
Class Example9c {
public static void Main (string[] args)
Throws IOException {
if (Args.length = = 0) {
SYSTEM.OUT.PRINTLN ("must give filename as first arg.");
Return
}
FileInputStream in = null;
try {
in = new FileInputStream (args[0]);
}
catch (FileNotFoundException e) {
System.out.println ("Can ' t Find file:" + args[0]);
Return
}
//Read file into a stringbuffer
StringBuffer buf = new StringBuffer ();
try {
int ch;
while (ch = in.read ())! =-1) {
Buf.append ((char) ch);
}
}
finally {
In.close ();
}
Separate stringbuffer into tokens and
Push each token into a Stack
StringTokenizer tok = new StringTokenizer (buf.tostring ());
Stack stack = new stack ();
while (Tok.hasmoretokens ()) {
Stack.push (Tok.nexttoken ());
}
Print out tokens in reverse order.
while (!stack.empty ()) {
System.out.println (String) Stack.pop ());
}
}
}
The above program reads the file byte by bit, converts the byte data into character data, and then puts the character data into the StringBuffer. It uses the StringTokenizer class to extract tokens that are delimited by whitespace characters (here is a string), extracting one at a time and pressing it into the stack. Finally, all tokens are ejected from the stack and printed, one per line. Because the stack class implements a last-in-first-out (LIFO) stack, the printed data order is exactly the opposite of the data in the file.
Both the StringTokenizer class and the stack class must be able to indicate the "reached end" condition. The construction method of the StringTokenizer accepts the source string. Each call to the Nexttoken () method returns a string that is the next token of the source string. All tokens of the source string are bound to be consumed, and the StringTokenizer class must somehow indicate that no more tokens have been returned. In this case, it would have been possible to use a special value of NULL to indicate that there were no more tokens. However, designers of this class have adopted another approach. He provides an additional method of Hasmoretokens (), which returns a Boolean value to indicate whether the end has been reached. Each time you call the Nexttoken () method, you must first call Hasmoretokens ().
This approach indicates that the designer does not believe that reaching the end of the token stream is an unexpected situation. Instead, it is a general case of using this class. However, if you do not check Hasmoretokens () before calling Nexttoken (), you will eventually get an exception nosuchelementexception. Although the exception is thrown when it reaches the end of the token stream, it is a non-check exception (subclass of RuntimeException). The exception was thrown not to indicate "reached the end" but instead to indicate a software defect----you did not use the class correctly.
Similarly, the Stack class has a similar method of empty (), which returns a Boolean indicating that the stack is already empty. You must first call the empty () method before each call to Pop (). If you forget to call the empty () method and call the Pop () method directly on an empty stack, you will get an exception emptystackexception. Although the exception is thrown when the stack is empty, it is also a non-check exception. It does not detect empty stacks, but rather indicates a software flaw in the customer code (improper use of the stack Class).
exception indicates failure to comply with contract
From the above example, you should have been initially aware of when exceptions should be thrown instead of using other methods for communication. If you look at anomalies from another perspective, as if you were "not complying with the contract," you might have a deeper understanding of how exceptions should be used.
A design approach that is often discussed in object-oriented programming is contract design, which states that the method is the contract between the client (the caller of the method) and the class that declares the method. This contract includes the preconditions (precondition) that the customer must satisfy and the post condition (postcondition) that the method itself must meet.
Front-facing conditions
The charat (int index) method of the string class is a method with a predecessor condition. This method specifies that the minimum value of the index parameter passed to the client is 0, and the maximum value is the result of calling the length () method on the string object minus 1. That is, if the string length is 5, then the value of the index parameter is limited to 0, 1, 2, 3, 4.
Post conditions
The post condition of the String class's Charat (int index) method requires that the return value must be the character data of the string object at the index position, and that the string object must remain unchanged.
If a customer calls Charat () and passes in a value that is as large or larger as 1, and length (), it is considered that the customer has not complied with the contract. In this case, the CharAt () method is not executed correctly, and it throws an exception stringindexoutofboundsexception. This exception indicates that there is a flaw in the client program or improper use of the string class.
If the Charat () method receives no problem with the input (the customer follows the contract), but for some reason it cannot return the character data on the specified index (which does not meet the post condition), it throws an exception to indicate the situation. This exception indicates that the implementation of the method contains defects or methods that have problems in obtaining a run-time resource.
Therefore, if an event represents an "exception condition" or "does not comply with a contract", the Java program has to do is throw an exception.
Throw what?
Once you decide to throw an exception, you will have to decide what exception to throw. You can throw an object of Throwable or its subclasses. You can throw Throwable objects defined in the Java API, or custom. So, how to decide?
In general, you only need to throw exceptions, not errors. Error is a subclass of Throwable, which is used to indicate catastrophic errors, such as OutOfMemoryError, which will be reported by the JVM. Sometimes an error can also be thrown by the Java API, such as Java.awt.AWTError. However, in your code, you should strictly restrict yourself to throwing exceptions (exception subclasses). Leave the wrong throw to those who are Daniel.
Check type and non-checked exceptions
Now, the main question is whether to throw a check-type exception or a non-check exception. The check-type exception is a subclass of exception (or the exception class itself), but does not include RuntimeException and its subclasses. A non-checked exception is runtimeexception and any of its subclasses. The error class and its subclasses are also checked, but you should focus only on exceptions, and you should do so by deciding whether to throw runtimeexception (non-check exceptions) or exception subclasses (check for exceptions).
If a check-type exception is thrown (without capturing it), then you need to declare the exception in the throws clause of the method. This method is used by the client programmer either to capture and handle the exception within his or her method, or to throw it in the throws clause. Check-type exceptions force client programmers to take action on exceptions that might be thrown.
If you throw a non-checked exception, the client programmer can decide whether to capture it or not. However, the compiler does not force the client programmer to take action on non-checked exceptions. In fact, they don't even know what these anomalies might be. Obviously, the client programmer will have less brains on the non-inspection anomaly.
there is a simple principle:
If you want the client programmer to take action consciously, throw a check-type exception.
In general, the exception that represents the misuse of a class should be a non-checked exception. The stringindexoutofboundsexception thrown by the Chartat () method of the string class is a non-checked exception. The designer of the string class does not intend to force the client programmer to check the validity of the index parameter every time the charat (int index) is called.
On the other hand, the read () method of the Java.io.FileInputStream class throws the IOException, which is a check exception. This exception indicates an error occurred while trying to read the file. This does not mean that the client programmer mistakenly uses the FileInputStream class, but that the method is unable to perform its duty of reading the next byte from the file. The FileInputStream designers consider this unexpected situation to be common and important, forcing the client programmer to deal with it.
This is where the trick lies. If an unexpected situation is that a method cannot perform its duties, and you think it is common or important, the client programmer must take action, then throw a check-type exception. Otherwise, a non-checked exception is thrown.
Custom Exception Classes
Finally, you decide to instantiate an exception class and then throw an instance of the exception class. There are no specific rules here. Do not throw a exception class that indicates an unexpected condition with a string of information, but instead customize an exception class or choose a suitable one from an existing exception class. Then, the client programmer can define the corresponding catch statements for different exceptions, or capture only a subset of them.
You might want to embed some information in the exception object to tell the catch clause more details about the exception. However, you don't just rely on embedded information to differentiate between different exceptions. For example, you do not want the client programmer to query the exception object to determine whether the problem occurs on I/O or illegal parameters.
Note that when String.charat (int index) receives an illegal input, it throws not runtimeexception, nor even illegalargumentexception, But stringindexoutofboundsexception. This type name indicates that the problem comes from a string index, and this illegal index can be found by querying the exception object.
Conclusion
The point of this article is that exceptions are unexpected situations and should not be used to report on the normal functions that can act as methods. Although you can use exceptions to separate regular and error-handling code to improve the readability of your code, inappropriate use of exceptions can reduce the readability of your code.
The following are the exception design principles presented in this article:
If the method encounters an unexpected condition that cannot be handled, an exception is thrown.
Avoid using exceptions to indicate situations that can be considered as common features of a method.
If the client is found to be violating the contract (for example, passing in an illegal input parameter), the non-checked exception is thrown.
If the method cannot fulfill the contract, then a check-type exception is thrown and a non-check exception can be thrown.
If you think the client programmer needs to take action consciously, throw a check-type exception.
about the author
Bill Venners has over 12 years of experience in software. He provides software consulting and training services in Silicon Valley on behalf of Artima software company. He is proficient in multiple languages on different platforms, including assembly programming for microprocessors, C programming on UNIX, C + + programming on Windows, and Java development on the web, and has developed software covering industries such as electronics, education, semiconductors, and life insurance. He is the author of "Deep Java Virtual machine".
Resources
Recommended Books on Java Design
Http://www.artima.com/designtechniques/booklist.html
Source packet that contains the example code used in this article
Http://www.artima.com/flexiblejava/exceptions.html
The discussion forum devoted to the material presented in this article
Http://www.artima.com/flexiblejava/fjf/exceptions/index.html
Object Orientation FAQ
http://www.cyberdyne-object-sys.com/oofaq/
7237 Links on Object Orientation
Http://www.rhein-neckar.de/~cetus/software.html
The object-oriented Page
Http://www.well.com/user/ritchie/oo.html
Collection of information on OO approach
Http://arkhp1.kek.jp:80/managers/computing/activities/OO_CollectInfor/OO_CollectInfo.html
Design Patterns Home Page
Http://hillside.net/patterns/patterns.html
A Comparison of OOA and OOD Methods
Http://www.iconcomp.com/papers/comp/comp_1.html
object-oriented Analysis and Design methods:a comparative Review
Http://wwwis.cs.utwente.nl:8080/dmrg/OODOC/oodoc/oo.html
Patterns discussion FAQ
Http://gee.cs.oswego.edu/dl/pd-FAQ/pd-FAQ.html
Implementing Basic Design Patterns in Java (Doug Lea)
Http://g.oswego.edu/dl/pats/ifc.html
Patterns in Java AWT
Http://mordor.cs.hut.fi/tik-76.278/group6/awtpat.html
Software technology ' s Design Patterns Page
http://www.sw-technologies.com/dpattern/
Previous Design Techniques Articles
"Object finalization and cleanup"--how to design classes for proper object cleanup. Http://www.javaworld.com/jw-06-1998/jw-06-techniques.html
"What's a method to does?"--how to maximize cohesion while avoiding explosion. Http://www.javaworld.com/jw-05-1998/jw-05-techniques.html
"Designing Fields and Methods" – how to keep fields focused and methods decoupled. Http://www.javaworld.com/jw-04-1998/jw-04-techniques.html
"Designing object Initialization"--ensure proper initialization of your objects at the all times. Http://www.javaworld.com/jw-03-1998/jw-03-techniques.html
Quot;introduction to ' design techniques '--A look at the role of the the overall software Developme NT Process.http://www.javaworld.com/jw-02-1998/jw-02-techniques.html
(turn) Exception design----The principle of how to use exceptions