• 沒有找到結果。

Performing Cleanup

在文檔中 Beginning Java 7Get coding with (頁 173-177)

In some situations, you might want to prevent an exception from being thrown out of a method before the method’s cleanup code is executed. For example, you might want to close a file that was opened, but could not be written, possibly because of insufficient disk space. Java provides the finally block for this situation.

The finally block consists of reserved word finally followed by a body, which provides the cleanup code. A finally block follows either a catch block or a try block. In the former case, the exception is handled (and possibly rethrown) before finally executes. In the latter case, finally executes before the exception is thrown and handled.

Listing 3-29 demonstrates the finally block in the context of a file-copying application.

Listing 3-29. Cleaning up after handling a thrown exception import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.FileNotFoundException;

import java.io.IOException;

class Copy {

public static void main(String[] args) {

if (args.length != 2) {

System.err.println("usage: java Copy srcfile dstfile");

return;

}

FileInputStream fis = null;

try {

fis = new FileInputStream(args[0]);

FileOutputStream fos = null;

try {

fos = new FileOutputStream(args[1]);

int b; // I chose b instead of byte because byte is a reserved word.

while ((b = fis.read()) != -1) fos.write(b);

}

catch (FileNotFoundException fnfe) {

String msg = args[1]+" could not be created, possibly because "+

"it might be a directory";

System.err.println(msg);

}

catch (IOException ioe)

CHAPTER 3  EXPLORING ADVANCED LANGUAGE FEATURES

{

String msg = args[0]+" could not be read, or "+args[1]+

" could not be written";

System.err.println(msg);

} finally {

if (fos != null) try

{

fos.close();

}

catch (IOException ioe) {

System.err.println("unable to close "+args[1]);

} } }

catch (FileNotFoundException fnfe) {

String msg = args[0]+" could not be found or might be a directory";

System.err.println(msg);

} finally {

if (fis != null) try

{

fis.close();

}

catch (IOException ioe) {

System.err.println("unable to close "+args[0]);

} } } }

■ Note Do not be concerned if you find this listing’s file-oriented code difficult to grasp; I will formally introduce

I/O and the listing’s file-oriented types in Chapter 8. I’m presenting this code here because file copying provides a perfect example of the finally block.

Listing 3-29 presents an application that copies bytes from a source file to a destination file via a nested pair of try blocks. The outer try block uses a java.io.FileInputStream object to open the source file for reading; the inner try block uses a java.io.FileOutputStream object to create the destination file for writing, and also contains the file-copying code.

CHAPTER 3  EXPLORING ADVANCED LANGUAGE FEATURES

If the fis = new FileInputStream(args[0]); expression throws FileNotFoundException, execution flows into the outer try statement’s catch (FileNotFoundException fnfe) block, which outputs a suitable message to the user. Execution then enters the outer try statement’s finally block.

The outer try statement’s finally block closes an open source file. However, when

FileNotFoundException is thrown, the source file is not open—no reference was assigned to fis. The finally block uses if (fis != null) to detect this situation, and does not attempt to close the file.

If fis = new FileInputStream(args[0]); succeeds, execution flows into the inner try block, which executes fos = new FileOutputStream(args[1]);. If this expression throws FileNotFoundException, execution moves into the inner try’s catch (FileNotFoundException fnfe) block, which outputs a suitable message to the user.

This time, execution continues with the inner try statement’s finally block. Because the destination file was not created, no attempt is made to close this file. In contrast, the open source file must be closed, and this is accomplished when execution moves from the inner finally block to the outer finally block.

FileInputStream’s and FileOutputStream’s close() methods throw IOException when a file is not open. Because IOException is checked, these exceptions must be handled; otherwise, it would be necessary to append a throws IOException clause to the main() method header.

You can specify a try statement with only a finally block. You would do so when you are not prepared to handle an exception in the enclosing method (or enclosing try statement, if present), but need to perform cleanup before the thrown exception causes execution to leave the method. Listing 3-30 provides a demonstration.

Listing 3-30. Cleaning up before handling a thrown exception import java.io.FileInputStream;

public static void main(String[] args) {

if (args.length != 2) {

System.err.println("usage: java Copy srcfile dstfile");

return;

catch (FileNotFoundException fnfe) {

CHAPTER 3  EXPLORING ADVANCED LANGUAGE FEATURES

static void copy(String srcFile, String dstFile) throws IOException {

FileInputStream fis = new FileInputStream(srcFile);

try {

FileOutputStream fos = new FileOutputStream(dstFile);

try

Listing 3-30 provides an alternative to Listing 3-29 that attempts to be more readable. It

accomplishes this task by introducing a copy() method that uses a nested pair of try-finally constructs to perform the file-copy operation, and also close each open file whether an exception is or is not thrown.

If the FileInputStream fis = new FileInputStream(srcFile); expression results in a thrown FileNotFoundException, execution leaves copy() without entering the outer try statement. This statement is only entered after the FileInputStream object has been created, indicating that the source file was opened.

If the FileOutputStream fos = new FileOutputStream(dstFile); expression results in a thrown FileNotFoundException, execution leaves copy() without entering the inner try statement. However,

CHAPTER 3  EXPLORING ADVANCED LANGUAGE FEATURES

execution leaves copy() only after entering the finally block that is mated with the outer try block. This finally block closes the open source file.

If the read() or write() method in the inner try statement’s body throws an IOException object, the finally block associated with the inner try block is executed. This finally block closes the open

destination file. Execution then flows into the outer finally block, which closes the open source file, and continues on out of copy().

■ Caution If the body of a try statement throws an exception, and if the finally block results in another exception

being thrown, this new exception replaces the previous exception, which is lost.

Despite Listing 3-30 being somewhat more readable than Listing 3-29, there is still a lot of boilerplate thanks to each finally block requiring a try statement to close a file. This boilerplate is necessary; its removal results in a new IOException possibly being thrown from the catch block, which would mask a previously thrown IOException.

在文檔中 Beginning Java 7Get coding with (頁 173-177)