Blakes 21 Days Chapter 7 Document
Day 7, Exceptions and Threads
- Top
- 191 Your first week in the Java language ends with two of its most useful elements, threads and exceptions.
- Threads are objects that implement the Runable interface or extend the Thread class, indicating that they can run simultaneously with other parts of a Java program.
- Exceptions are objects that represent errors that may occur as a Java program runs.
- Top
- 191 Threads enable programs to make efficient use of resources by isolating the computing-intensive parts of a program so that they don't slow down everything else.
- 191 Exceptions enable programs to recognize errors and respond to them.
- Exceptions even make it possible for programs to correct the conditions and continue running, when possible.
- 191 Exceptions are covered first because they're one of the things you use when working with threads.
Exceptions
- Top
- 192 Programmers in any language endeavor to write programs that are bug-free, never crash, can handle any circumstance with grace, and always recover from unusual situations.
- So much for that idea.
- Errors occur because programmers didn't anticipate problems or didn't test enough.
- Or programs encounter situations out of their control, such as bad data from users, corrupt files that don't have the correct data in them,
- network connections that don't connect, hardware devices that don't respond, etc, etc
- 192 In Java, the strange events that might cause a program to fail are called exceptions.
- Java defines a number of language features that deal with exceptions.
- How to handle exceptions in your code and recover gracefully from potential problems
- How to tell code that uses your classes that you're expecting a potential exception
- How to create an exception if you detect one
- How your code is limited yet made more robust by exceptions
- 192 With most programming languages, handling error conditions requires much more work than handling a program that is running properly.
- It can require a confusing structure of conditional statements to deal with errors that might occur.
- 192 As an example, consider the following code that could be used to load a file from disk.
- File input and output can be problematic because of disk errors, file-not-found errors, and the like.
- If the program must have the data from the file to operate properly, it must deal with all these circumstances before continuing.
- Top
- 192 Here's the structure of one possible solution:
int status = loadTextFile();
if (status != 1) {
// something unusual happened, report it
switch (status) {
case 2:
System.out.println("File not found");
break;
case 3:
System.out.println("Disk error");
break;
case 4:
System.out.println("File corrupted");
break;
default:
System.out.println("Error");
}
} else {
// file unusual loaded OK, continue with program
}
- Top
- 193 This code tries to load a file by calling the method loadTextFile(), which presumably has been defined elsewhere in the class.
- The method returns an integer that indicates whether the file loaded properly (a value of 1) or an error occurred (2, 3, 4, or higher).
- 193 The program uses a switch statement keyed on that error code to address the problem.
- The end result is a block of code in which the most common circumstance - a successful file load - can be lost amid the error-handling code.
- This is the result of handling only one possible error.
- If other errors take place later in this program, you might end up with more nested if-else and switch-case blocks.
- 193 As you can see, error management would become unmanageable in larger programs, making a Java class difficult to read and maintain.
- 193 Dealing with errors in this manner makes it impossible for the compiler to check for consistency
- the way it can check to make sure that you called a method with the right arguments or set a variable to the right class of object.
- 193 Although the previous example uses Java syntax, you never have to deal with errors that way with the Java language.
- You can use a group of classes called exceptions that work much better.
- 193 Exceptions include errors that could be fatal to your program and other circumstances that indicate a problem.
- By managing exceptions, you can manage errors and possibly work around them.
- 193 Errors and other conditions in Java programs can be more easily managed through a combination of language features,
- consistency checking at compile time, and a set of extensible exception classes.
- 193 With these features, you can add a whole new dimension to the behavior and design of your classes, your class hierarchy, and your overall system.
- Your classes and interface describe how your program is suppose to behave under the best circumstances.
- With exceptions, you can consistently describe how the program will behave when circumstances are not ideal
- and allow programmers who use your classes to know what to expect in those cases.
Exception Classes
- Top
- 194 At this point, it's likely that you've run into at least one Java exception.
- Maybe you tried to run a Java application without providing the command-line arguments that were needed and saw an ArrayIndexOutOfBoundsException message.
- 194 Chances are, when an exception occurred, the application quit and spewed a bunch of mysterious erros to the screen.
- Those errors are exceptions.
- When your program stops without successfully finishing its work, an exception is thrown.
- Exceptions can be thrown by the Java Virtual Machine (JVM), by classes you use, or intentionally in your own programs.
- 194 Just as exceptions are thrown, they also can be caught.
- Catching an exception involves dealing with the exceptional circumstance so that your program dosen't crash, as you learn later today.
- 194 Exceptions in Java are instances of classes that inherit from the Throwable class.
- An instance of a Throwable class is created when an exception is thrown.
- 194 Throwable has two subclasses: Error and Exception.
- Instances of Error are internal errors involving the JVM.
- These errors are rare and usually fatal to the program, there's not much you can do about them, other than catch them or throw them yourself.
- 194 The class Exception is more relevant to your own programming.
- Subclasses of Exception fall into two general groups:
- Unchecked exceptions (subclasses of the class RuntimeExecption) such as ArrayIndexOutOfBoundsException, SecurityException, and NullPointerException.
- Checked exceptions such as EOFException and MalformedURLException
- Unchecked exceptions, also called runtime exceptions, usually occur because of code that isn't very robust.
- An ArrayIndexOutOfBounds exception, for example, should never be thrown if you're properly checking to make sure that your code stays within the bounds of an array.
- NullPointerException exceptions happen when you try to use a variable that doesn't refer to an object yet.
- Top
- 194 CAUTION: If your program is causing unchecked exceptions, you should fix those problems by improving your code.
- Don't rely on exception management to handle programming mistakes that can be corrected while you're creating a Java Program.
- Top
- 195 Checked exceptions indicate that something strange and out of the control is happening.
- An EOFException, for example, happens when you're reading a file and the file ends before it was expected to.
- A MalformedURLException happens when a web address (also called a URL) isn't in the right format.
- This group includes exceptions that you create to signal unusual cases that might occur in your own programs.
- 195 Exceptions are arranged in a hierarchy just as other classes are, where superclasses are more general kinds of problems and the subclasses are more specific.
- This organization becomes more important to you as you deal with exceptions in your own code.
- 195 The primary exception classes are part of the java.lang package: Throwable, Exception, and RuntimeException.
- Many of the other packages in the Java Class Library define other exceptions, which are used throughout the library.
- 195 The java.io package defines a general exception class called IOException.
- It is subclassed not only in the java.io package for input and output exceptions (EOFException and FileNotFoundException) but also in the java.net classes for networking exceptions such as MalformedURLException and in the java.util package with ZipException.
Managing Exceptions
- Top
- 195 Now that you know what an exception is, how do you deal with one in your own code ?
- In many cases, the Java compiler enforces exception management when you try to use methods that throw exceptions,
- you need to deal with those exceptions in your own code, or it won't compile and NetBeans will flag the error.
- In this section, you'll learn about consistency checking and how to use three new keywords - try, catch, and finally - to deal with exceptions that might occur.
Exception Consistency Checking
- Top
- 195 The more you work with the Java Class Library, the more likely you are to run into an exception such as this one:
- Output pane
- Exception java.lang.InterruptedException
must be caught or it must be declared in the throws clause
of this method.
- 195 In Java, a method can indicate the kinds of errors it might potentially throw.
- For example, methods that read from files can throw IOException errors, so those methods are declared with a special modifier that indicates potential errors.
- When you use those methods in your own Java programs, you have to protect your code against the exceptions.
- 196 This rule is enforced by the compiler itself, in the same way that it checks to make sure that you're using methods with the correct number of arguments and that all your variable types match what you're assigning to them.
- 196 Why is this check in place ?
- It makes programs less likely to crash with fatal errors, because you know up front the kind of exceptions that can be thrown by the methods a program uses.
- 196 If you define your methods so that they indicate the exceptions they can throw, Java can tell your objects' users to handle those errors.
Protecting Code and Catching Exceptions
- Top
- 196 Assume that you've been happily coding and an exception occurs as a class is compiled.
- According to the error message, you have to either catch the error or declare that your method throws it.
- 196 First, you deal with catching potential exceptions, which requires two things:
- Protect the code that contains the method that might throw an exception inside a try block.
- Handle an exception inside a catch block.
- 196 A try block tries a block of code to see if it can execute all of it without causing an exception.
- If it fails and an exception occurs, a catch block deals with it.
- You've seen try and catch before.
- On Day 6, "Packages, Interfaces, and Other Class Features," you used the following code to create an integer from a Sting value:
public SquareTool(String input) {
try {
float in = Float.parseFloat(input);
// rest of method
} catch (NumberFormatException nfe) {
System.out.println(input + " is not a valid number.");
}
}
- In this code, the Float.parseFloat() class method might throw an exception of the class NumberFormatException,
- which signifies that the string is not in a valid format as a number.
- (One situation that triggers this exception is if input equals 15x, which id not a number).
- Top
- 197 To handle the exception, the call to parseFloat() is placed inside a try block, and an associated catch block has been set up.
- The catch block receives any NumberFormatException objects thrown within the try block.
- The part of the catch clause inside the parentheses is similar to a method definition's argument list.
- It contains the class of exception to be caught and a variable name.
- You can use the variable to refer to that exception object inside the catch block.
- An exception object has a getMessage() method that displays a detailed error message describing what happened.
- The following example is a revised version of the try-catch block used on Day 6:
try {
float in = Float.parseFloat(input);
} catch (NumberFormatException nfe) {
System.out.println("Oops: " + nfe.getMessage());
}
- The examples you have seen thus far catch a specific type of exception.
- Because exception classes are organized into a hierarchy and you can use a subclass anywhere that a superclass is expected, you can catch groups of exceptions within the same catch statement.
- When you write programs that handle input and output from files, Internet servers, and similar places,
- you deal with several types of IOException exceptions (the IO stands for input/output).
- These exceptions include two of its subclasses, EOFException and FileNotFoundException.
- By catching IOException, you also catch instances of any IOException subclasses.
- To catch several different exceptions that aren't related by inheritance, you can use multiple catch blocks for a single try, like this:
try {
// code that might generate exceptions
} catch (IOException ioe) {
System.out.println("Input/output error");
System.out.println(ioe.getMessage());
} catch (ClassNotFoundException cnfe) {
System.out.println("Class not found");
System.out.println(cnfe.getMessage());
} catch (InterruptedException ie) {
System.out.println("Program Interrupted");
System.out.println(ie.getMessage());
}
- In a multiple catch block, the first catch block that matches is executed, and the rest is ignored.
- Top
- 198 CAUTION: You can run into unexpected problems by using an Exception superclass in a catch block followed by one or more of its subclasses in their own catch blocks.
- For example, the input/output exception IOException is the superclass of the end-of-file exception EOFException.
- If you put an IOException block above an EOFException block, the subclass never catches any exceptions.
- 198 You also can catch more than one class of exceptions in the same catch statement.
- The classes must be separated by a pipe character |. Here's an example:
try {
// code that reads a filefrom disk
} catch (EOFException | FileNotFoundException exc) {
System.out.println("File error: " + exc.getMessage());
}
- This code catches two exceptions, EOFException and FileNotFoundException, in the same catch block.
- The exception is assigned to the exc argument, and its getMessage() method is called.
- The first class in the list that matches the thrown exception will be assigned to the argument.
- The exceptions declared as alternatives in the catch statement cannot be superclasses or subclasses of each other unless they are in the proper order.
- The following would not work:
try {
// code that reads a filefrom disk
} catch (IOException | EOFException | FileNotFoundException exc) {
System.out.println("File error: " + exc.getMessage());
}
- This code fails to compile because IOException is the superclass of the other two exceptions and it precedes them in the list.
- Because a superclass can catch exceptions of its subclasses, the second and third exceptions in that statement never would be caught.
- Here's a fixed version that would work:
try {
// code that reads a filefrom disk
} catch (EOFException | FileNotFoundException exc) {
System.out.println("File error: " + exc.getMessage());
} catch (IOException ice) {
System.out.println("IO error: " + ice.getMessage());
}
- Top
- 199 Another way to make it work would be to put the superclass last in the catch statement:
try {
// code that reads a filefrom disk
} catch (EOFException | FileNotFoundException | IOException exc) {
System.out.println("File error: " + exc.getMessage());
}
- Top
- 199 CAUTION: Exceptions have a printStackTrace() method that displays the sequence of method calls that led to the statement that generated the exception.
- If you use this in a program, NetBeans flags it for a warning in the source code editor.
- The reason is that printStackTrace() contains debugging information that for security reasons should not be shared with users after a program has been finished.
- Top
- 199 A catch statement must be needed by the corresponding try block.
- The exception class in catch has to be one that could be thrown in that block (or a superclass of one that could be thrown).
- The compiler will fail with an error otherwise.
- 199 For example, if you need catch for FileNotFoundException in a program that did not read any files, the program would not complete.
The finally Clause
- Top
- 199 Suppose that there is some action in your code that you absolutely must do, no matter what happens, regardless of whether an exception is thrown.
- This is usually to free some external resource after aquiring it, to close a file after opening it, or something similar.
- 199 One example is when you are working with databases, as you do during Day 18, "Accessing Databases with JDBC 4.2 and Derby."
- The database connection and objects you create to access the database are closed in a finally block to free those resources because they're no longer needed.
- 199 Although you could put that action both inside a catch block and outside it, duplicating the same code in two different places should be avoided as much as possible in your programming.
- 199 Instead, put that code inside a special optional block of the try-catch statement that uses the keyword finally:
try {
readTextFile();
} catch (IOException ioe) {
// deal with IO errors
} finally {
closeTextFile();
}
- 200 Today's first project shows how a finally statement can be used inside a method.
First Program
- Top
- 200 The HexReader application in Listing 7.1 reads sequences of two-digit hexadecimal numbers and displays their decimal values.
- There are three sequences to read:
- 000A110D1D260219
- 78700F1318141E0C
- 6A197D45B0FFFFFF
- As you learned on Day 2, "The ABCs of Programming." hexadecimal is a base 16 numbering system in which the single-digit numbers range from 00 (decimal 0) to 0F (decimal 15).
- Double-digit numbers range from 10 (decimal 16) to FF (decimal 255).
- 200 Create this class in NetBeans as an empty Java file in the com.java21days package and enter the source code of Listing 7.1
Listing 7.1
- Top
- page 200 - 201
package com.java21days;
class HexReader {
String[] input = { "000A110D1D260219 ",
"78700F1318141E0C ",
"6A197D45B0FFFFFF " };
public static void main(String() arguments) {
HexReader hex = new HexReader();
for (int i = 0); i < hex.input.length; i++)
hex.readLine(hex.input[i]);
}
void readLine(String code) {
try {
for (int j = 0; j + 1 < code.length(); j += 2) {
String sub = code.substring(j, j + 2);
int num = Integer.parseInt(sub, 16);
if (num == 255) {
return;
}
System.out.print(num + " ");
}
} finally {
System.out.println("**");
}
return;
}
}
- Top
- 201 The output of this program is shown in Figure 7.1
Figure 7.1 goes here
- Output pane
0 10 17 13 29 38 2 25 **
120 112 15 19 24 20 30 12 **
106 25 125 69 176 **
BUILD SUCCESSFUL intial time: 0 seconds)
The Explanation
- Top
- 201 Line 17 of the program reads two characters from code, the string that was sent to the readLine() method, by calling the string's substring(int, int) method.
- 201 NOTE: In the substring() method of the String class, you select a substring in a somewhat counterintuitive way.
- The first argument specifies the index of the first character to include in the substring, but the second argument does not specify the last character.
- Instead, the second argument indicates the index of the last character plus 1.
- A call to substring(2, 5) for a string would return the characters from index position 2 to index position 4.
- Top
- 201 The two-character substring contains a hexadecimal number stored as a String.
- The Integer class method parseInt can be used with a second argument to convert this number into an integer.
- Use 16 as the argument for a hexadecimal (base 16) conversion, 8 for an octal (base 8) conversion, and so on.
- 201 In the HexReader application, the hexadecimal FF is used to fill out the end of a sequence and should not be displayed as a decimal value.
- This is accomplished by using a try-finally block in lines 15 - 26 of Listing 7.1
- 201 The try-finally block causes an unusual thing to happen when the return statement is encountered at line 27.
- You would expect return to cause the readLine() method to be exited immediately.
- 201 Because it is within a try-finally block, the statement within the finally block is executed no matter how the try block is exited.
- The text "**" is displayed at the end of a line of decimal values.
try-with-resources
- Top
- 202 There's a way to ensure that resources are freed properly even when an operation inside a try block fails with an exception.
- The try-with-resources feature enables statements that claim resources to be declared inside parentheses in a try statement.
- 202 The following code contains two statements that read data from an Internet server using a newtworking socket (a type of connection):
Socket digit = new Socket(host, 79);
BufferedReader in = new BufferedReader(
new InputStreamReader(digit.getInputStream()));
- To ensure that resources are properly released, they can be declared inside the try statement:
try (Socket digit = new Socket(host, 79);
BufferedReader in = new BufferedReader(
new InputStreamReader(digit.getInputStream()));
) {
// code goes here
} catch (IOExecption e) {
System.out.println("IO Error:" + e.getMessage());
}
- No matter how the code in the try block exits, whether through success or an exception, the digit and in resources will be disposed of properly.
- NetBeans issues a warning in the source code editor on any statement that ought to be in a try-with resources statement but isn't.
- Take this advice whenever you can, because this technique eliminates the common error of forgetting to close a resource when no longer in use.
Declaring Methods That Might Throw Exceptions
- Top
- 202 You have learned how to deal with methods that might throw exceptions by protecting code and catching any exceptions that occur.
- The Java compiler checks to make sure that you've delt with a method's exceptions.
- But how does it know which execptions to tell you about ?
- 202 The answer is that the original method indicated the exceptions that it might possibly throw as part of its definition.
- You can use this mechanism in your own methods.
- In fact, it's good style to do so to make sure that users of your classes are alerted to the errors your methods might experience.
- 203 To indicate that a method will possibly throw an exception, you use a special clause in the method definition called throws.
The throws Clause
- Top
- 203 If some code in your method's body might throw an exception,
- add the throws keyword after the method's closing parenthesis,
- followed by the name or names of the exception that your method throws.
- Here's an example:
public void getPoint (int x, int y) throws NumberFormatException {
// body of method
}
- If your method might throw multiple kinds of exceptions, you can declare them all in the throws clause separated by commas:
public void storePoint (int x, int y)
throws NumberFormatException, EOFExecption {
// body of method
}
- As with catch, you can use a superclass of a group of exceptions to indicate that your method might throw any subclass of that exception. For instance:
- loadPoint() throws IOException {
// body of method
}
- Top
- 203 Keep in mind that adding a throws clause to your method definition simply means that the method might throw an exception if something goes wrong, not that it actually will.
- The throws clause provides extra information in your method definition about potential exceptions
- and allows Java to make sure that your method is being used correctly by other classes.
- Think of a method's overall description as a contract between the designer of that method and the caller of the method.
- (You can be on either side of that contract, of course.)
- Usually the description indicates the types of a method's arguments, what it returns, and the particulars of what it normally does.
- By using throws, you are adding information about the abnormal things the method can do.
- This new part of the contract helps make explicit all the places where exceptional conditions should be handled in your program.
Which Exceptions Should You Throw?
- Top
- 204 After you decide to declare that your method might throw an exception,
- you must decide which exceptions it might throw and actually throw them or call a method that will throw them.
- (You learn about throwing your own exceptions in the next section.)
- 204 In many instances, this is apparent from the operation of the method itself.
- Perhaps you're already creating and throwing your own exceptions, in which case you'll know exactly which exceptions to throw.
- You don't have to list all possible exceptions that your method could throw.
- Unchecked exceptions are handled by the JVM and are so common that you don't have to deal with them.
- In particular, exceptions of the Error or RuntimeException classes or any of their subclasses do not have to be listed in your throws clause.
- They get special treatment because they can occur anywhere within a Java program and are usually conditions that you, as a programmer, did not directly cause.
- One good example is OutOfMemoryError when the JVM has run out of memory, which can happen anywhere, at any time, for any number of reasons.
- Unchecked exceptions are subclasses of the RuntimeException and Error classes and are usually thrown by the JVM.
- You don't have to declare that your method throws them and usually do not need to deal with them in any other way.
- Top
- 204 NOTE: You can choose to list these errors and runtime exceptions in your throws clause if you want, but classes that call the method will not be forced to handle them.
- Only non-runtime exceptions must be handled.
- 204 All other exceptions are called checked exceptions and are potential candidates for a throws clause in your method.
Passing on Exceptions
- Top
- 204 There are times when it doesn't make sense for your method to deal with an exception.
- It might be better for the method that calls your method to deal with that exception.
- 204 For example, consider the hypothetical example of WebRetriever, a class that loads a web page using its web address and stores it in a file.
- As you learn in Day 17, "Communicating Across the Internet," you can't work with web addresses without dealing with MalformedURLException,
- the exception thrown when an address is in the wrong format.
- 205 To use WebRetriever, another class calls its constructor with the address as an argument.
- If the address specified by the other class is in the wrong format, a MalformedURLException is thrown.
- Instead of dealing with this, the constructor of the WebRetriever class could have the following declaration:
public WebRetriever() throws MalformedURLException {
// body of constructor
}
- This would force any class that works with WebRetriever objects to deal with MalformedURLException errors or pass the buck with its own throws clase.
- One thing is always true: It's better to pass on exceptions to calling methods than to catch them and do nothing in response.
- In addition to declaring methods that throw exceptions, there's one other instance in which your method definition may include a throws clause:
- Within that method, you want to call a method that throws an exception, but you don't want to catch or deal with that exception.
- Rather than using the try and catch clauses in your method's body,
- you can declare your method with a throws clause so that it, too, might possibly throw the appropriate exception.
- It's then the responsibility of the method that calls your method to deal with that exception.
- This is the other case that tells the Java compiler that you have done something with a given exception.
- Using this technique, you could create a method that deals with a NumberFormatException without a try-catch block:
public void readFloat(String input) throws NumberFormatException {
float in = Float.parseFloat(input);
}
- After you declare your method to throw an exception, you can use other methods that also throw those exceptions inside the body of this method, without needing to catch the exception.
- Top
- 205 NOTE: You can, of course, deal with other exceptions using try and catch in the body of your meyhod in addition to passing on the exceptions you listed in the throws clause.
- You also can both deal with the exception in some way and then rethrow it so that your method's calling method has to deal with it anyhow.
throws and Inheritance
- Top
- 206 If your method definition overrides a method in a superclass that includes a throws clause, there are special rules for how your overridden method deals with throws.
- Unlike other parts of the method signature that must mimic those of the method it is overriding, your new method does not require the same set of exceptions listed in the throws clause.
- 206 Because there's a possibility that your new method might deal with an exception instead of throwing it, your method can potentially throw fewer types of exceptions.
- It could even throw no exceptions.
- This means that you can have the following two class definitions and things will work fine:
public class RadioPlayer {
public void startPlaying() throws SoundException {
// body of method
}
}
public class StereoPlayer extends RadioPlayer {
public void startPlaying() {
// body of method
}
}
- 206 The converse of this rule is not true: A subclass method cannot throw more checked exceptions (either exceptions of different types or more general exception classes) than its superclass method.
- Any exception thrown by the subclass must be the same as the superclass or a subclass of that exception. Consider this example:
void readFields() throws IOException {
// body of method
}
- 206 If this method is in a superclass and you override the method, this would not be allowed in the subclass:
void readFields() throws SQLException {
// body of method
}
- SQLException is not a subclass of IOException, so this code will not compile.
- But the method could throw FileNotFoundEception, because that's a subclass of IOException.
Creating and Throwing Exceptions
- Top
- 207 There are two sides to every exception: the side that throws the exception and the side that catches it.
- An exception can be tossed around a number of times to a number of methods before it's caught, but eventually it will be caught and delt with.
- 207 Many exceptions are thrown by the Java runtime or by methods inside the Java classes themselves.
- You also can throw any of the standard exceptions that the Java Class Library defines, or you can create and throw your own exceptions.
Throwing Exceptions
- Top
- 207 Declaring that your method throws an exception is useful to classes that use your method and to the Java compiler, which checks to make sure that all your exceptions are being handled.
- The declaration itself doesn't do anything to throw that exception should it occur, you must do that as needed in the body of the method.
- You need to create a new object of an exception class to throw an exception.
- After you have that object, use the throw statement to throw it.
- Here's an example using a hypothetical NotInServiceException class that is a subclass of the Exception class:
NotInServiceException nise = new NotInServiceException();
throw nise;
- You only can throw objects that are subclasses of the Throwable class.
- Depending on the exception class, the exception also may have arguments to its constructor that you can use.
- The most common of these is a string argument, which enables you to describe the problem in greater detail ( which can be useful for debugging purposes).
- Here's an example:
NotInServiceException nise = new
NotInServiceException("Database Not in Service");
throw nise;
- After an exception is thrown, the method exits without executing any other code, other than the code inside a finally block if one exists.
- The method won't return a value either.
- If the calling method does not have a try or catch surrounding the call to your method, the program might exit based on the exception you threw.
Creating Your Own Exceptions
- Top
- 207 Creating new exceptions is easy.
- Your new exception should inherit from another exception in the Java class hierarchy.
- All user-created exceptions should be part of the Exception hierarchy rather than the Error hierarchy, which is reserved for errors involving the JVM.
- Look for an exception that's close to the one you're creating, for example, an exception for a bad file format would logically be an IOException.
- If you can't find a closely related exception for your new exception, consider inheriting from Exception, which sits atop the exception hierarchy for checked exceptions.
- Unchecked exceptions should inherit from RunTimeException.
- Exception classes typically have two constructors:
- The first takes no arguments, and the second takes a single string argument.
- Exception classes are like other classes. Here's an extremely simple one:
public class SunSpotException extends Exception {
public SunSpotException() {}
public SunSpotException(String message) {
super(message);
}
}
Combining throws, try, and throw
- Top
- 208 What if you want to combine the approaches described thus far: You want to handle incoming exceptions in your method, but you also want the option to pass on the exception to your method's caller.
- Simply using try and catch doesn't pass on the exception, and adding a throws clause doesn't give you a chance to deal with the exception.
- 208 If you want to both manage the exception and pass it on to the caller, use all three mechanisms: the throws clause, the try statement, and a throw statement to explicitly rethrow the exception.
- Here's a method that uses this technique:
public void readMessage() throws IOException {
MessageReader mr = new MessageReader();
try {
mr.loadHeader();
} catch (IOException e) {
// do something to handle the
// IO Exception and then rethrow
// the exception ...
throw e;
}
}
- 209 This works because exception handlers can be nested.
- You handle the exception by doing something responsible with it but decide that it is important enough to give the method's caller a chance to handle it as well.
- 209 Exceptions can float all the way up the chain of method callers this way (not being handled by most of them),
- until finally the JVM handles any uncaught exceptions by aborting your program and printing an error message.
- If it's possible for you to catch an exception and do something necessary with it, you should.
- When you use throw in a catch block for an exception superclass, it throws that superclass.
- This represents a potential loss of information, because the exception could be a subclass with more information about the error.
- Here's a situation where that occurs:
- A try-catch statement in a file reader looks for an IOException.
- An EOFException occurs because the end of the file is reached.
- The exception is caught in the catch block, because IOException is the superclass of EOFException.
- 209 If throw is used with this exception, it will throw an IOException, not an EOFException.
- Java 8 introduces a technique that enables the more precise exception to be thrown:
- Use the final keyword in the catch statement for the object.
- This code rewites the previous example to do this:
try {
mr.loadHeader();
catch (final IOException e) {
throw e;
}
- Top
- 209 CAUTION: New features in Java 8 require that NetBeans is set up to recognize them or the IDE will flag them as an error.
- If you enter this code in NetBeans and it displays an error message, make sure your project has been set to the current version of the language.
- Choose File, Project Properties to open the Project Properties dialog, choose the categaory Libraries,
- and make sure the Java Platform drop-down is set to JDK 1.8
When Not to Use Exceptions
- Top
- 210 There are several situations where you should not use exceptions.
- First, don't use them in circumstances you could avoid easily in your code.
- For example, although you can rely on an ArrayIndexOutofBounds exception to indicate when you've gone past the end of an array,
- it's simple to use the array's length variable to keep from going beyond the bounds.
- In addition, if your users will enter data that must be an integer,
- testing to make sure that the data is an integer is a much better idea than throwing an exception and dealing with it somewhere else.
- Exceptions take up a lot of processing time.
- A simple conditional will run much faster than than exception handling and make your program more efficient.
- Exceptions should be used only for truly exceptional cases that are out of your control.
- It's also easy to get carried away with exceptions and to try to make sure all your methods have been declared to throw all the possible exceptions that they can throw.
- You create more work for everyone involved when you get carried away with exceptions.
- Declaring a method to throw either few or many exceptions is a trade-off,
- the more exceptions your method can throw, the more complex that method is to use.
- Declare only the exceptions that have a reasonably fair chance of happening and that make sense for the overall design of your classes.
Bad Style Using Exceptions
- Top
- 210 When you first start using exceptions, it might be appealing to work around the compiler errors that result when you use a method that declares a throws statement.
- Although it is permissible to add an empty catch clause or to add a throws statement to your own method (and there are appropriate reasons for doing so),
- intentionally dropping exceptions without dealing with them subverts the checks that the Java compiler does for you.
- 210 Compiler errors regarding exceptions are there to remind you to reflect on these issues.
- Take the time to deal with the exceptions that might affect your code.
- This extra care richly rewards you as you reuse your classes in later projects and in larger and larger programs.
- The Java Class Library has been written with exactly this degree of care, and that's one of the reasons it's robust enough to be used in your Java projects.
Threads
- Top
- 211 One thing to consider in Java programming is how system resources are being used.
- Graphics, complex math computations, and other intensive tasks can take up a lot of processor time.
- This is especially true of programs that have a graphical user interface, which is a style of software that you explore next week.
- If you write a graphical Java program that does something that consumes a lot of the computer's time,
- you might find that the program's user interface responds slowly,
- Drop-down lists take a second or more to appear, button clicks are recognized slowly, and so on.
- To solve this problem, you can segregate the processor-hogging functions in a Java class so that they run separately from the rest of the program.
- This is possible through the use of threads.
- Threads are parts of a program that run on their own while the rest of the program does something else.
- This also is called multitasking because the program handles more than one task simultaneously.
- Threads are ideal for anything that takes up a lot of processing time and runs continuously.
- By putting the program's hardest workload into a thread, you free up the rest of the program to handle other things.
- You also make the program easier for the JVM because the most intensive work is isolated.
Writing a Threaded Program
- Top
- 211 Threads are implemented in Java with the Thread class in the java.lang package.
- The simplest use of threads is to make a program pause in execution and stay idle during that time.
- To do this, call the Thread class method sleep(long) with the number of milliseconds to pause as the only argument.
- This method throws an exception, InterruptedException, when the paused thread has been interrupted for some reason.
- (One possible reason is when a user closes the program while it is sleeping.)
- 212 The following statements stop a program in its tracks for 3 seconds:
try {
Thread.sleep(3000);
} catch (InterruptedException ie) {
// do nothing
}
- The catch block does nothing, which is typical when you're using sleep().
- One way to use threads is to put all the time-consuming behavior in its own class.
- A thread can be created in two ways:
- by subclassing the Thread class
- or implementing the Runnable interface in another class.
- Both belong to the java.lang package.
- Because the Thread class implements Runnable, both techniques result in objects that start and stop threads in the same manner.
- To implement the Runnable interface, add the keyword implements to the class declaration followed by the name of the interface, as in this example:
public class StockTicker implements Runnable {
public void run() {
// ...
}
}
- The Runnable interface contains only one method to implement, run().
- The first step in creating a thread is to create a reference to an object of the Thread class:
- This statement creates a reference to a thread, but no Thread object has been assigned to it yet.
- Threads are created by calling the constructor Thread(Object) with the threaded object as an argument.
- You should create a threaded StockerTicker object with the following statement:
StockerTicker tix = new StockerTicker();
Thread tickerThread = new Thread(tix);
- Two good places to create threads are the constructor for an application and the constructor for a component (such as a panel).
- A thread is begun by calling its start() method, as in the following statement:
- Top
- 213 The following statements can be used in a thread class to start the thread:
Thread runner = null;
if (runner == null) {
runner = new Thread(this);
runner.start();
}
- The this keyword used in the Thread() constructor refers to the object in which these statements are contained.
- The runner variable has a value of null before any object is assigned to it, so the if statement is used to make sure that the thread is not started more than once.
- To run a thread, its start() method is called.
- Calling a thread's start() method causes another method to be called, the run() method that must be present in all threaded objects.
- The run() method is the engine of a threaded class, containing the processor-intensive behavior and calling methods to perform it.
A Threaded Application
- Top
- 213 Threaded programming should become more clear when you see it in action.
- 213 Listing 7.2 contains PrimeFinder, a class that finds a specific prime number in a sequence, such as 100th, 1,000th, or 30,000th prime.
- This can take some time, especially for numbers beyond 100,000, so the search for the right prime takes place in its own thread.
- 213 Enter the code shown in Listing 7.2 in NetBeans and save it as the class name PrimeFinder in the package com.java21days.
Listing 7.2
- Top
- page 213 - 214
package com.java21days;
public class PrimeFinder implements Runnable {
public long target;
public long prime;
public boolean finished = false;
private Thread runner;
PrimeFinder(long inTarget) {
target = inTarget;
if (runner == null) {
runner = new Thread(this);
runner.start();
}
}
public void run() {
long numPrimes = 0;
long candidate = 2;
while (numPrimes < target) {
if (isPrime(candidate)) {
numPrimes++;
prime = candidate;
}
candidate++;
}
finished = true;
}
boolean isPrime(long checkNumber) {
double root = Math.sqrt(checkNumber);
for (int i = 2; i <= root; i++) {
if (checkNumber % i == 0)
return false;
}
return true;
}
}
- Top
- 214 Save the PrimeFinder class when you're finished.
The Explanantion
- Top
- 214 This class doesn't have a main() method, so you can't run it as an application.
- Next you'll create a program that uses this class.
- 214 There are three public variables:
- target is a long that indicates when the specified prime in the sequence has been found. If you're looking for the 5,000th prime, target equals 5000
- prime is a long that holds the last prime number found by this class.
- finished is a Boolean that indicates when the target has been reached.
- 214 There's also a private instance variable called runner that holds the Thread object this class runs in.
- This object equalls null before the thread is started.
- 214 The PrimeFinder constructor method in lines 9 - 15 sets the target instance variable and starts this thread if it hasn't been started.
- When the thread's start() method is called, it in turn calls the run() method of the threaded class.
- 214 The run() method is in lines 17 - 28.
- This method does most of the work of the thread.
- 215 This method uses two new variables:
- numPrimes, the number of primes that have been found,
- and candidate, the number that might possibly be prime.
- The candidate variable begins at the first possible prime number which is 2.
- 215 The while loop in lines 20 - 26 continues until the right number of primes has been found.
- First, it checks whether the current candidate is prime by calling the prime isPrime(long) method, which returns true if the number is prime and false otherwise.
- If the candidate is prime, numPrimes increases by 1, and the prime instance variable is set to this prime number.
- The candidate variable is then incremented by 1, and the loop continues.
- After the right number of primes has been found, the while loop ends, and the finished instance variable is set to true.
- This indicates that the PrimeFinder object has found the right prime number and is finished searching.
- The end of the run() method is reached in line 28, and the thread no longer does any work.
- 215 The isPrime() method is contained in lines 30 - 37.
- This method determines whether a number is prime by using the % operator, which returns the remainder of a division operation
- If a number is easily divisible by 2 or any higher number (leaving a remainder of 0), it is not a prime number.
- Top
- 215 Listing 7.3 is an application that uses the PrimeFinder class.
- Enter the code shown in Listing 7.3 in NetBeans as a new Java class named PrimeThreads in the com.java21days package.
Listing 7.3 (The Third Program)
- Top
- page 215 - 216
package com.java21days;
public class PrimeThreads {
public static void main(String[] arguments) [
PrimeThreads pt = new PrimeThreads(arguments);
}
public PrimeThreads(String[] arguments) [
PrimeFinder[] finder = new PrimeFinder[arguments.length];
for (int i = 0; i < arguments.length; i++) {
try [
long count = Long.parseLong(arguments[i]);
finder[i] = new PrimeFinder(count);
System.out.println("Looking for prime " + count);
} catch (NumberFormatException nfe) {
System.out.println("Error: " + nfe.getMessage());
}
}
boolean complete = false;
while (!complete) {
complete = true;
for (int j = 0; j < finder.length; j++) {
if (finder[j] == null) continue;
if (!finder[j].finished) {
complete = false;
} else {
displayResult(finder[j]);
finder[j] = null;
}
}
try {
Thread.sleep(1000);
} catch (InterrupedException ie) {
// do nothing
}
}
}
private void displayResult(PrimeFinder finder) {
System.out.println("Prime: " + finder.target
+ " is " + finder.prime);
}
}
Run Program
- Top
- 216 Specify the prime numbers that you're looking for as command-line arguments (using Run, Set Project Configuration, Customize), and include as many as you want.
- 216 If this program is run with the command-line arguments 5000 12000 50000 120000, it is likely to pruduce the out in Figure 7.2
- Because there's no guarantee of the order threads will finish, the report of each found prime may be ordered differently.
Figure 7.2 goes here
Explanantion
- Top
- 216 The for loop in lines 10 - 18 of the PrimeThreads application creates one PrimeFinder object for each command-line argument specified when the program is run.
- 217 Because arguments are Strings and the PrimeFinder constructor requires long values,
- the Long.parseLong(String) class method is used to handle the conversion.
- All the number-parsing methods throw NumberFormatException exceptions,
- so they are enclosed in try-catch blocks to deal with arguments that are not numeric.
- When a PrimeFinder object is created, the object starts running in its own thread (as specified in the PrimeFinder construcctor).
- 217 The while loop in lines 20 - 36 checks to see whether any PrimeFinder thread has completed, which is indicated by its finished instance variable equaling true.
- When a thread has completed, the displayResult() method is called in line 27 to display the prime number that was found.
- The thread then is set to null, freeing the object's resources (and preventing the result from being displayed more than once).
- 217 The call to Thread.sleep(1000) in line 32 causes the while loop to pause for one second during each pass through the loop.
- A slowdown in loops helps keep the JVM from executing statements at such a furious pace that it becomes bogged down.
Stopping a Thread
- Top
- 217 Stopping a thread is a little more complicated than starting one.
- 217 The best way to stop a thread is to place a loop in the thread's run() method that ends when a variable changes in value, as in the following example:
public void run() {
while (okToRun == true) {
// ...
}
}
- The okToRun variable could be an instance variable of the thread's class.
- If it is changed to false, the loop inside the run() method ends.
- Another option you can use to stop a thread is to loop in the run() method only while the currently running thread has a variable that references it.
- A class method, Thread.currentThread(), returns a reference to the current thread (in other words, the thread in which the object is running).
- The following run() method loops as long as runner and currentThread() refer to the same object:
public void run() {
Thread thisThread = Thread.cuurentThread();
while (runner == thisThread) {
// body of loop
}
}
- If you use a loop like this, you can stop the thread anywhere in the class with the following statement:
- runner = null;
Summary
- Top
- 218 Exceptions and threads strengthen the robustness of your programs.
- 218 Exceptions enable you to manage potential errors.
- By using try, catch, and finally, you can protect code that might result in exceptions by handling those exceptions as they occur.
- 218 Handling exceptions is only half the battle, the other half is generating and throwing exceptions.
- A throws clause tells a method's users that the method might throw an exception.
- It also can be used to pass on an exception from a method call in the body of your method.
- 218 You learned how to create and throw your own methods by defining new exception classes and by throwing instances of any exception classes using throw.
- 218 Threads enable you to run the most processor-intensive parts of a Java class separately from the rest of the class.
- This is especially useful when the class is doing something computing-intensive such as animation, complex math, or looping through large amount of data quickly.
- 218 You also can use threads to do several things at once and to start and stop threads externally.
- 218 Threads implement the Runnable interface, which contains one method: run().
- When you start a thread by calling its start() method, the thread's run() method is called automatically.
Q & A
- Top
- Q I'm not sure I understand the difference between exceptions, errors, and runtime exceptions. Is there another way of looking at them ?
- A Errors are caused by dynamic linking or JVM problems.
- Thus, they are too low-level for most programs to care about - or to be able to handle even if they did care.
- Runtime exceptions are generated by the normal execution of Java code.
- Although they occasionally reflect a condition you will want to handle explicitly,
- more often they reflect a coding mistake made by the programmer, and thus simply print an error to help flag that mistake.
- Non-runtime exceptions (IOExecptions, for example) are conditions that, because of their nature, should be explicitly handled by any robust and well-thought-out code.
- The Java Class Library has been written using only a few of these, but those few are important to using the system safely and correctly.
- The compiler helps you handle these exceptions properly via its throws clause checks and restrictions.
- Q Does Java support unit testing to make programs more reliable ?
- A Unit testing, a technique for ensuring the reliability of software by adding tests, is supported by the open source Java Class Library JUint.
- This is the most popular unit-testing framework for Java programmers.
- Visit www.junit.org to download it.
- With JUnit, you write a set of tests, called a suite, that create the Java objects you've developed and call your methods.
- The values produced by these tests are checked to see whether they're what you expected.
- All tests must pass for your software to pass.
- Although unit testing is only as good as the tests you create, the existence of a test suite is extremely helpful when you make changes to your software.
- By running the tests again after the changes, you can better assure yourself that it continues to work correctly.
- Some Java programmers believe so strongly in the benefits of unit testing that they write tests before any code.
Quiz - Questions
- What keyword is used to jump out of a try block and into a finally block ?
- catch
- return
- while
- What class should be the superclass of any exceptions you create in Java ?
- Throwable
- Error
- Exception
- If a class implements the Runnable interface, what methods must the class contain ?
- start(), stop(), and run().
- actionPerformed()
- run()
Answers
- Top | Note A is 1, B is 2, and C is 3
- B. The return statement exits the block.
- C. The kinds of errors you'll want to note in your programs generally belong in the Exception hierarchy.
- C. The Runnable interface requires only the run() method.
Certification Practice
- Top
- 092 The following question is the kind of thing you could expect to be asked on a Java programming certification test. Answer it without looking at today's material or using the Java compiler to test the code. The answer is available on the book's website at www.java21days.com
- The AverageValue application is suppose to take up to 10 floating-point numbers as command-line arguments and display their average
- Given:
public class AverageValue {
public static void main(String[] arguments) {
float[] temps = new float[10];
float sum = 0;
int count = 0;
int i;
for (i = 0; i < arguments.length & i < 10; i++) {
try {
temps[i] = Float.parseFloat(arguments[i]);
count++;
} catch (NumberFormatException nfe) {
System.out.println("Invalid input: " + arguments[i]);
}
sum += temps[i];
}
System.out.println("Average: " + (sum / i));
}
}
- Which statement contains an error ?
- for (i = 0; i < arguments.length & i< 10; i++) {
- sum += temps[i];
- System.out.println("Average: " + (sum / i));
- None of them, the program is correct.
Exercise
- Top
- To extend your knowledge of the subjects covered today, try the following exercises:
- Modify the PrimeFinder class so that it throws a new exception, NegativeNumberException, if a negative number is sent to the constructor.
- Modify the PrimeThreads application so that it can handle the new NegativeNumberException error.
Reserve