Chinaunix首页 | 论坛 | 博客
  • 博客访问: 117453
  • 博文数量: 18
  • 博客积分: 2015
  • 博客等级: 大尉
  • 技术积分: 245
  • 用 户 组: 普通用户
  • 注册时间: 2008-05-07 12:38
文章分类

全部博文(18)

文章存档

2010年(3)

2009年(1)

2008年(14)

我的朋友

分类: Java

2008-05-07 20:37:15

This second article in my series on exceptions in Java will introduce you to the finally block. I will also go on to explain the structure of the standard Java API so that you understand the classes you will more than likely be extending in your own applications.

Writing your Own Exceptions

By: SAJChurchey


Introduction

Welcome to my second article in my series on exceptions in Java. In this article, I hope to introduce you to the finally block. I really should have introduced you to this handy tool in the first article, but I'm going with the excuse that it is a little more difficult to use effectively in an exception handling scenario. It really has to be a part of your exception handling design as a whole. I will also go on to explain the structure of the standard Java API so that you understand the classes you will more than likely be extending in your own applications.

The finally Block

In addition to try-catch blocks, you can make your program even stronger against crashes. Have you ever forgot to call the close() method on a PrintWriter or other file output mechanism? You go to look in the file to see the results, but it's empty. This is because no changes are actually made to the file until the buffer is actually closed. Now imagine what could happen to a critical application that is manipulating files. The program crashes because of some exception; now, you're left with inconsistent data or worse yet, no data at all. The finally block of code was created to prevent such things from happening. Inside of a finally block, you can write code to be executed whenever the code of a try block is exited: normally or abnormally. It is executed before any of your exception code is executed. This means you can clean up after your try code no matter what happens, even an Exception that requires you to kill the application.

The finally block can be used in conjunction with try-catch blocks to indicate code that should be executed however the try block should exit. Typically, it is placed after any try-catch blocks:

try{
    //Code you are executing
}
catch(Exception e){
   //Code to be executed upon catching an Exception in executing code
}
finally{
   //Code to be executed whenever the executing code block is exited
}


A good rule of thumb is to put all cleanup code, code that does things like close buffers, dereference objects, double-checking data integrity, should be placed in finally blocks, and you can pretty much generalize this and say all code that should always be executed should go here as well.

The Full Scoop on Throwables

So far, you've learned that you can catch exceptions thrown by the Java Virtual Machine or Sun's Java API; It's a useful way to communicate errors and exceptions to different parts of a process. Now, you may be wondering how you can throw some exceptions of your own to communicate your own errors to different parts of your own applications. It is really based on a very simple principle: you can't throw an object that is not Throwable.

The Throwable class is the superclass of all classes that are used to communicate exceptions and errors between different functions and objects within a process. It contains the basic functions that manipulate the messages and stack traces associated with exceptions and errors.

You may be asking: "What's the difference between exceptions and errors?" Well, in Java an Error is a lot more serious than an Exception. While an Exception indicates an abnormal condition during the progress of a program, an Error indicates something seriously wrong reported by the virtual machine or lower-level APIs. For example, an OutOfMemoryError is serious and results in the termination of the program. Typically, you should not be concerned with writing your own errors because you won't be working at such low-levels in your code.

To throw an Exception, you use the throw keyword followed by a reference to a Throwable object:


public static void example(Object input){
  if(input != whatsExpected)
    throw new Exception();
}


In turn, the method that called example() can either catch and handle the exception or let it continue upwards to the method that called it.

Writing Your Own Exceptions

So, the next grey area we should clear up before we start coding our own exceptions is what constitutes an "abnormal condition?" This decision should be rooted in a basic methodology of object-oriented design: designing by contract. What this means is that contractual relationships exist between the calling function and the called function. The calling function expects the called function to perform a specific task, and the called function expects the calling function to provide it with data to be used while carrying out its operations. When either of these functions fails in its responsibilities an abnormal condition occurs, and an exception should be thrown usually in the form of a checked or unchecked exception.

Checked vs. Unchecked Exceptions

In code, the difference between checked and unchecked exceptions is rather simple: checked exceptions are subclasses of the Exception class, and an unchecked exception is a subclass of RuntimeException, which is a subclass of Exception. The role and design considerations are a little more involved here. Checked exceptions are enforced by the Java compiler and virtual machine. Client programmers will not be able to compile their code unless they have caught checked exceptions somewhere in the execution stack. Virtual machines will not run unless the appropriate exception classes are available. A checked exception indicates that a called function has failed to fulfill its end of the contract, and since the client programmer is dependent on it for their program's operation, it throws a checked exception so that client programmers are given the opportunity to deal with such an anomaly. For instance, IOException is a checked exception because if something goes wrong while opening or writing to a file, the client programmer has to know and be prepared to handle such a catastrophe. By contrast, StringIndexOutOfBounds is an unchecked exception. An unchecked exception is a sublcass of RuntimeException because it is discovered at runtime, not at compile time. An unchecked exception is thrown because the calling function did not fulfill its end of the contract by passing bad or invalid data. This is left for the client programmer to discover and correct because it is a problem with their implementation . . . not yours. To implement checked exceptions, you must list all checked exceptions that a called function throws by indicating it in the function header using the throws keyword.:

public static void example(Object input) throws MyCheckedException{
    /*
       throws indicates the names of checked exception classes
       that example() throws. It must be an Exception
       or a subclass that you or someone else has written thereof
    */

    if(input != valid){
      throw new MyRuntimeException("You didn't give me the info I wanted!");
      //throw unchecked exception
    }
    /*
      code performing example function
    */
    if(!performed)
      //performed is a boolean verifying successful operations
      throw new MyCheckedException("Oops, something serious happened on my end!");

    //Otherwise operation completed successfully
}


This is to signal to the compiler and the virtual machine that a checked exception may be thrown so that it can enforce the policies.

Customizing and Writing your Own

If you've derived your own classes from other libraries, making your own exceptions is fairly straightforward. We'll begin by creating the exception classes used in the previous example:

public class MyCheckedException extends Exception{
    //It can also extend RuntimeException if you need it to be unchecked
}


Consequently, you also have access to features of the Exception class. Such as an error message. You can write your own constructor and pass whatever error message you want to it, creating your own personal error messages for effective error reporting and debugging. You can even pass the constructor another Throwable such as a RuntimeException or other Exception that caused this error in the first place. The implications of this will be covered in a future article, but this data can be abstracted by other functions to give a more accurate picture of what caused the problem.

public class MyCheckedException extends Exception{
    public MyCheckedException(String msg){
      super(msg);
    }

    public MyCheckedException(String msg, Throwable t){
      super(msg,t);
    }
}


Don't forget that whenever you extend a class, you are free to add whatever you like in your own subclass. This provides a great deal of flexibility in what information you can keep track of and where the errors are stored. For instance, functions for logging to a file could be implemented at this level of the implementation.


As you can see, the sky's the limit. Whatever structures and messages you care to convey through throwing exceptions, you can. With proper planning, you can deliver very descriptive errors and relevant information to end-users telling them what's wrong, technical support can instantly locate and correct problems, and developers can more easily find and fix their own bugs. Just imagine if this practice could be perfected and fully harnessed: you would have some of the most usable and easily maintained code around. In my next article, I hope to explain the advanced concept of exception chaining which works on the fact that you can encapsulate the cause of an Exception so that higher layers can make more complex decisions on handling and maybe even the dynamic correction of problems. The possible applications are endless.
阅读(628) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~