Error Handling with Exceptions【2】

The keyword throw causes a number of relatively magical things to happen. Typically, you’ll first use new to create an object that represents the error condition. You give the resulting reference to throw. The object is, in effect, “returned” from the method, even though that object type isn’t normally what the method is designed to return. A simplistic way to think about exception handling is as a different kind of return mechanism, although you get into trouble if you take that analogy too far. You can also exit from ordinary scopes by throwing an exception. But a value is returned, and the method or scope exits.

Throw关键词引发了一系列相关的事情,典型的,你首先会用new方法创建一个对象来描述异常的条件,将这个对象给了throw方法。这个方法会返回这个对象,尽管它的类型不是这个方法设计所返回的类型。一种简单的方式来看“异常处理程序”就是一种不同返回类型的机制,但是这种机制不要走的太远否则就麻烦了,你也可以在一段顺序执行的区域采用throw 一个异常来退出程序。但是返回结果,退出了方法或者作用域。

Any similarity to an ordinary return from a method ends here, because where you return is someplace completely different from where you return for a normal method call. (You end up in an appropriate exception handler that might be far—many levels away on the call stack—from where the exception was thrown.)

异常处理和正常程序的返回相同点就是这些了。因为你抛出异常的地方和你返回正常发发调用的地方有着本质的区别。你解决这个异常信息的地方可能距离异常的抛出地方很远。

In addition, you can throw any type of Throwable (the exception root class) object that you want. Typically, you’ll throw a different class of exception for each different type of error. The information about the error is represented both inside the exception object and implicitly in the name of the exception class, so someone in the bigger context can figure out what to do with your exception. (Often, the only information is the type of exception, and nothing meaningful is stored within the exception object.)

另外,你可以抛出任何类型的以Throwable为基类的对象。典型的,你会根据不同的错误抛出不同类型的异常信息。关于错误的信息的描述在对象里并且暗含在类的名字当中,所以在更高的环境中可以识别出根据异常信息如何处理。(通常,有用的信息就是这个对象的类型,而在对象中存储的信息没有什么意义)

Catching an exception

If a method throws an exception, it must assume that exception will be “caught” and dealt with. One of the advantages of exception handling is that it allows you to concentrate on the problem you’re trying to solve in one place, and then deal with the errors from that code in another place.

如果一个方法抛出了一个异常,它必须确保这个异常信息可以被捕获和处理掉。“异常处理程序”的优势之一就是它允许你在一个地方集中精力解决这个问题,而到另外一个地方来解决这个异常。

To see how an exception is caught, you must first understand the concept of a guarded region. This is a section of code that might produce exceptions and is followed by the code to handle those exceptions.

要想知道如何捕获异常,你必须首先的知道被保护区的概念。这是一段可能会产生异常信息的代码区,紧接着下面的代码就是处理这个异常信息的。

The try block

If you’re inside a method and you throw an exception (or another method you call within this method throws an exception), that method will exit in the process of throwing. If you don’t want a throw to exit the method, you can set up a special block within that method to capture the exception. This is called the try block because you “try” your various method calls there. The try block is an ordinary scope preceded by the keyword try:

如果你在一个方法内部抛出了一个异常信息(或者你调用的一个方法抛出一个异常),这个方法抛出异常的时候就会退出执行。如果你不希望不抛出一个退出的方法,你可以建立一个特殊的块来捕获这个异常信息。这被叫做“Try”区,因为你在这里Try各种方法的调用。这个区域只是紧紧跟随关键词Try的一段普通代码区。

try {
  
  
  // Code that might generate exceptions
  
  
}
  
  

If you were checking for errors carefully in a programming language that didn’t support exception handling, you’d have to surround every method call with setup and error testing code, even if you call the same method several times. With exception handling, you put everything in a try block and capture all the exceptions in one place. This means your code is much easier to write and read because the goal of the code is not confused with the error checking.

你必须为了不支持异常处理程序的语言仔细的检查错误信息,你必须在每个方法周围写很多测试错误的代码,甚至你调用一个方法很多次。而有了异常处理程序之后,你把所有的程序放置到Try Block,再把捕获异常信息的代码放到一个位置。这将使得你的代码书写更加简单,这样的话你的逻辑代码和错误检查代码就不会很复杂的交织在一起。

Exception handlers

Of course, the thrown exception must end up someplace. This “place” is the exception handler, and there’s one for every exception type you want to catch. Exception handlers immediately follow the try block and are denoted by the keyword catch:

当然,抛出异常也必须在某些位置结束。而这个位置就是“异常处理程序”,这里你可以书写你希望捕获的所有类型的异常信息。异常处理程序紧紧跟随Try Block区并且以关键词Catch来标识。

try {
  
  
  // Code that might generate exceptions
  
  
} catch(Type1 id1) {
  
  
  // Handle exceptions of Type1
  
  
} catch(Type2 id2) {
  
  
  // Handle exceptions of Type2
  
  
} catch(Type3 id3) {
  
  
  // Handle exceptions of Type3
  
  
}
  
  

  
  
   
    
  
  
// etc...
  
  

Each catch clause (exception handler) is like a little method that takes one and only one argument of a particular type. The identifier (id1, id2, and so on) can be used inside the handler, just like a method argument. Sometimes you never use the identifier because the type of the exception gives you enough information to deal with the exception, but the identifier must still be there.

每个catch的句子都更像是一个小的方法,并且都拥有一个特定的类型参数。这些标识符可以在异常处理程序中使用,就像参数一样使用。有时候你可能根本用不到这些标识符因为他异常的类型已经提供了足够的信息来处理这个异常,但是你仍然要保留这个参数。

The handlers must appear directly after the try block. If an exception is thrown, the exception handling mechanism goes hunting for the first handler with an argument that matches the type of the exception. Then it enters that catch clause, and the exception is considered handled. The search for handlers stops once the catch clause is finished. Only the matching catch clause executes; it’s not like a switch statement in which you need a break after each case to prevent the remaining ones from executing.

异常处理模块必须直接出现在Try Block区之后。如果抛出了一个异常信息,那么异常处理机制会搜索第一个参数与异常类型匹配的处理程序。然后进入异常处理语句,这个异常就被认为得到了处理。当异常处理结束的时候搜索匹配的程序也就停止了。只有匹配的异常处理部分得到执行。它不像是switch语句还需要在每个case中使用break来阻止剩余部分继续执行。

Note that within the try block, a number of different method calls might generate the same exception, but you need only one handler.

需要说明的是在Try block,不同的方法可能会产生一个同样的异常信息,但是我们只需要处理一个就足够了。
Termination vs. resumption

There are two basic models in exception handling theory. In termination (which is what Java and C++ support), you assume that the error is so critical that there’s no way to get back to where the exception occurred. Whoever threw the exception decided that there was no way to salvage the situation, and they don’t want to come back.

在异常处理理论中有两个基本的模型。在terminationjavaC++支持),你认为这个异常信息太严重了没有办法回退到异常抛出的地方了。抛出这个异常则意味着没有办法可以解决,并且希望这个异常不要再回来了。

The alternative is called resumption. It means that the exception handler is expected to do something to rectify the situation, and then the faulting method is retried, presuming success the second time. If you want resumption, it means you still hope to continue execution after the exception is handled. In this case, your exception is more like a method call—which is how you should set up situations in Java in which you want resumption-like behavior. (That is, don’t throw an exception; call a method that fixes the problem.) Alternatively, place your try block inside a while loop that keeps reentering the try block until the result is satisfactory.

另外一个被称为继续。它的意思是异常处理程序希望作某些工作来调整当前的情形,然后再执行出错的地方,希望第二次可以成功。如果你希望继续执行,那就是说你希望异常被处理之后还继续执行。在这种情况下,你的异常更像是方法调用-如果你想在Java中实现这种效果可以用这种办法来设置环境(也就是说不抛出吟唱而是调用一个方法来修补这个问题),

还有一种办法将你的try block区在得到满意的结果之前一直进入try block区。

Historically, programmers using operating systems that supported resumptive exception handling eventually ended up using termination-like code and skipping resumption. So although resumption sounds attractive at first, it isn’t quite so useful in practice. The dominant reason is probably the coupling that results; your handler must often be aware of where the exception is thrown, and contain nongeneric code specific to the throwing location. This makes the code difficult to write and maintain, especially for large systems where the exception can be generated from many points.

长久以来,程序员们都是一直使用支持继续模式的异常处理的操作系统,但是自己的代码确实一直使用终止模式来跳过继续执行。所以虽然继续模式听起来很吸引人,但是在实际当中还是很少使用的。最主要的原因还是担心它所导致的耦合。你的异常处理程序意识到哪里会抛出异常信息,并且需要泛型的代码来标识出异常抛出的地点。这使得代码非常难写和维护,尤其是对于那些很大的系统会有很多的地方抛出异常。

Creating your own exceptions

You’re not stuck using the existing Java exceptions. The JDK exception hierarchy can’t foresee all the errors you might want to report, so you can create your own to denote a special problem that your library might encounter.

你应该不介意使用Java已经存在的异常信息。JDK的异常关系统并不能预知所有的错误你都想抛出,所以你可以建立异常类,来描述在自己的类库中可能遇到的问题。

To create your own exception class, you must inherit from an existing exception class, preferably one that is close in meaning to your new exception (although this is often not possible). The most trivial way to create a new type of exception is just to let the compiler create the default constructor for you, so it requires almost no code at all:

创建一个自己的异常类,你必须继承一个已经存在的异常类,最好还在意思上和你要建立的异常类相近(虽然一般来讲是不太可能的)。创建新异常类的最简单的办法就是让编译器为你创建一个默认的构造方法,这样你几乎就不需要写任何代码了。

import com.bruceeckel.simpletest.*;
  
  

  
  
   
    
  
  
class SimpleException extends Exception {}
  
  

  
  
   
    
  
  
public class SimpleExceptionDemo {
  
  
  private static Test monitor = new Test();
  
  
  public void f() throws SimpleException {
  
  
    System.out.println("Throw SimpleException from f()");
  
  
    throw new SimpleException();
  
  
  }
  
  
  public static void main(String[] args) {
  
  
    SimpleExceptionDemo sed = new SimpleExceptionDemo();
  
  
    try {
  
  
      sed.f();
  
  
    } catch(SimpleException e) {
  
  
      System.err.println("Caught it!");
  
  
    }
  
  
    monitor.expect(new String[] {
  
  
      "Throw SimpleException from f()",
  
  
      "Caught it!"
  
  
    });
  
  
  }
  
  
}
  
  

 

The compiler creates a default constructor, which automatically (and invisibly) calls the base-class default constructor. Of course, in this case you don’t get a SimpleException(String) constructor, but in practice that isn’t used much. As you’ll see, the most important thing about an exception is the class name, so most of the time an exception like the one shown here is satisfactory.

编译器自动的创建了一个构造方法,这个方法是不可见的,它会调用基类的构造方法。当然,这样你就没有SimpleException(String)的构造方法,但是在实际中这并不常见。就像你看到的最重要的是异常的类名,所以大部分时间这个异常也就足够了。

Here, the result is printed to the console standard error stream by writing to System.err. This is usually a better place to send error information than System.out, which may be redirected. If you send output to System.err, it will not be redirected along with System.out so the user is more likely to notice it.

在这里System.err会把错误信息打印到控制台的standard error stream。这比将错误信息发送到System.out要好的多,因为后者可能会重新定向。如果你发送错误消息到System.err,它就不会被重新定向,这样就能更好的引起用户的注意。

You can also create an exception class that has a constructor with a String argument:

你也可以创建一个带有String参数的异常类

import com.bruceeckel.simpletest.*;
  
  

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

  
  
   
    
  
  
public class FullConstructors {
  
  
  private static Test monitor = new Test();
  
  
  public static void f() throws MyException {
  
  
    System.out.println("Throwing MyException from f()");
  
  
    throw new MyException();
  
  
  }
  
  
  public static void g() throws MyException {
  
  
    System.out.println("Throwing MyException from g()");
  
  
    throw new MyException("Originated in g()");
  
  
  }
  
  
  public static void main(String[] args) {
  
  
    try {
  
  
      f();
  
  
    } catch(MyException e) {
  
  
      e.printStackTrace();
  
  
    }
  
  
    try {
  
  
      g();
  
  
    } catch(MyException e) {
  
  
      e.printStackTrace();
  
  
    }
  
  
    monitor.expect(new String[] {
  
  
      "Throwing MyException from f()",
  
  
      "MyException",
  
  
      "%% /tat FullConstructors.f//(.*//)",
  
  
      "%% /tat FullConstructors.main//(.*//)",
  
  
      "Throwing MyException from g()",
  
  
      "MyException: Originated in g()",
  
  
      "%% /tat FullConstructors.g//(.*//)",
  
  
      "%% /tat FullConstructors.main//(.*//)"
  
  
    });
  
  
  }
  
  
}
  
  

The added code is small: two constructors that define the way MyException is created. In the second constructor, the base-class constructor with a String argument is explicitly invoked by using the super keyword.

添加了很少的代码,MyException类里面拥有了两个构造方法。在第二个构造方法中,基类中有String参数的方法也使用关键词super得到了调用。

In the handlers, one of the Throwable (from which Exception is inherited) methods is called: printStackTrace( ). This produces information about the sequence of methods that were called to get to the point where the exception happened. By default, the information goes to the standard error stream, but overloaded versions allow you to send the results to any other stream as well.

在异常处理程序中,调用了ThrowableException是从那里继承而来的)类中的printStackTrace()方法。它会产生一个关于“被调用的方法经历了一个怎样的顺序到达了错误的发生地点”,默认的这个错误信息会被送到标准的错误信息流,但是重载厚的版本可能允许你把它送到其它的信息流。

The process of creating your own exceptions can be taken further. You can add extra constructors and members:

创建自己的异常类的过程可以走的更远,你可以增加一些额外的构造方法和成员。

import com.bruceeckel.simpletest.*;
  
  

  
  
   
    
  
  
class MyException2 extends Exception {
  
  
  private int x;
  
  
  public MyException2() {}
  
  
  public MyException2(String msg) { super(msg); }
  
  
  public MyException2(String msg, int x) {
  
  
    super(msg);
  
  
    this.x = x;
  
  
  }
  
  
  public int val() { return x; }
  
  
  public String getMessage() {
  
  
    return "Detail Message: "+ x + " "+ super.getMessage();
  
  
  }
  
  
}
  
  

  
  
   
    
  
  
public class ExtraFeatures {
  
  
  private static Test monitor = new Test();
  
  
  public static void f() throws MyException2 {
  
  
    System.out.println("Throwing MyException2 from f()");
  
  
    throw new MyException2();
  
  
  }
  
  
  public static void g() throws MyException2 {
  
  
    System.out.println("Throwing MyException2 from g()");
  
  
    throw new MyException2("Originated in g()");
  
  
  }
  
  
  public static void h() throws MyException2 {
  
  
    System.out.println("Throwing MyException2 from h()");
  
  
    throw new MyException2("Originated in h()", 47);
  
  
  }
  
  
  public static void main(String[] args) {
  
  
    try {
  
  
      f();
  
  
    } catch(MyException2 e) {
  
  
      e.printStackTrace();
  
  
    }
  
  
    try {
  
  
      g();
  
  
    } catch(MyException2 e) {
  
  
      e.printStackTrace();
  
  
    }
  
  
    try {
  
  
      h();
  
  
    } catch(MyException2 e) {
  
  
      e.printStackTrace();
  
  
      System.err.println("e.val() = " + e.val());
  
  
    }
  
  
    monitor.expect(new String[] {
  
  
      "Throwing MyException2 from f()",
  
  
      "MyException2: Detail Message: 0 null",
  
  
      "%% /tat ExtraFeatures.f//(.*//)",
  
  
      "%% /tat ExtraFeatures.main//(.*//)",
  
  
      "Throwing MyException2 from g()",
  
  
      "MyException2: Detail Message: 0 Originated in g()",
  
  
      "%% /tat ExtraFeatures.g//(.*//)",
  
  
      "%% /tat ExtraFeatures.main//(.*//)",
  
  
      "Throwing MyException2 from h()",
  
  
      "MyException2: Detail Message: 47 Originated in h()",
  
  
      "%% /tat ExtraFeatures.h//(.*//)",
  
  
      "%% /tat ExtraFeatures.main//(.*//)",
  
  
      "e.val() = 47"
  
  
    });
  
  
  }
  
  
} 
  
  

A field i has been added, along with a method that reads that value and an additional constructor that sets it. In addition, Throwable.getMessage( ) has been overridden to produce a more interesting detail message. getMessage( ) is something like toString( ) for exception classes.

增加了i数据成员,并且有一个方法读取这个值,另外的一个构造方法还有一个方法在构造方法中对其进行初始化。还有,Throwable.getMessage()被覆写为产生很有意思的详细信息了。这个方法在exception类中更像是toString()方法。

Since an exception is just another kind of object, you can continue this process of embellishing the power of your exception classes. Keep in mind, however, that all this dressing-up might be lost on the client programmers using your packages, since they might simply look for the exception to be thrown and nothing more. (That’s the way most of the Java library exceptions are used.)

因为异常和其它的对象没有什么区别,所以你可以继续修饰这个Exception类。但是记住,你修饰的这些在客户端程序员面前很可能丢失掉,因为他们仅仅是用它来抛出一个异常信息而已其它的什么都不需要。JavaException类库大部分都是这么使用的。

The exception specification

In Java, you’re encouraged to inform the client programmer, who calls your method, of the exceptions that might be thrown from your method. This is civilized, because the caller can know exactly what code to write to catch all potential exceptions. Of course, if source code is available, the client programmer could hunt through and look for throw statements, but often a library doesn’t come with sources. To prevent this from being a problem, Java provides syntax (and forces you to use that syntax) to allow you to politely tell the client programmer what exceptions this method throws, so the client programmer can handle them. This is the exception specification and it’s part of the method declaration, appearing after the argument list.

Java中,鼓励你告诉那些调用你的方法的客户端程序员们,在你的方法中可能会抛出异常。这是很明智的做法,因为只有这样才知道如何书写代码来捕获那些潜在的异常信息。当然如果有源代码的话,客户端程序员可以查找源代码来寻找throw抛出异常的语句,但是一个类库通常是不提供源码的。要防止这个称为一个问题,Java提供了一种语法并且强制要求你使用的语法,可以让你很客气的告诉程序员这个方法会抛出什么类型的异常信息,这样客户端程序员就可以捕获它们。这就是异常的语法,它经常出现在方法的声明部分并且紧跟在参数的后面。

The exception specification uses an additional keyword, throws, followed by a list of all the potential exception types. So your method definition might look like this:

异常语法使用了一个其它的关键词throws,后面紧接着是一些可能会抛出的异常的类型,所以你的方法定义可能像下面这样了:

void f() throws TooBig, TooSmall, DivZero { //... 
  
  

If you say

void f() { // ...
  
  

it means that no exceptions are thrown from the method (except for the exceptions inherited from RuntimeException, which can be thrown anywhere without exception specifications—this will be described later).

这的意思是这个方法不会抛出任何异常(除了从RuntimeException继承下来的异常信息,因为这个异常不需要声明就可以抛出来,这个我们稍后会讲到)。

You can’t lie about an exception specification. If the code within your method causes exceptions, but your method doesn’t handle them, the compiler will detect this and tell you that you must either handle the exception or indicate with an exception specification that it may be thrown from your method. By enforcing exception specifications from top to bottom, Java guarantees that a certain level of exception correctness can be ensured at compile time.

不要对异常说明说谎。如果你的代码发生了一个异常,而你没有去捕获它们,编译器会发现它并且告诉你要么捕获这个异常要么在异常说明部分提示这个方法会抛出异常。通过强化这种从顶层到底层的异常说明,Java在一定程序上在编译期就可以纠正你的异常。

There is one place you can lie: You can claim to throw an exception that you really don’t. The compiler takes your word for it, and forces the users of your method to treat it as if it really does throw that exception. This has the beneficial effect of being a placeholder for that exception, so you can actually start throwing the exception later without requiring changes to existing code. It’s also important for creating abstract base classes and interfaces whose derived classes or implementations may need to throw exceptions.

那是不是任何地方你都不能说谎了呢?你可以在根本不会抛出异常的地方要求抛出异常,编译器会当真的,它会强制要求使用这个方法的用户像处理这个真的会抛出异常一样处理。这么作的好处就是它先为异常占据了一个位置,当你真的想要抛出异常的时候不需要修改任何代码。这对于创建抽象基类和接口也很重要,因为它们的派生类和实现很可能需要抛出异常信息。

Exceptions that are checked and enforced at compile time are called checked exceptions.

会在编译期间检查并且得到处理的异常叫做checked exceptions

Catching any exception

It is possible to create a handler that catches any type of exception. You do this by catching the base-class exception type Exception (there are other types of base exceptions, but Exception is the base that’s pertinent to virtually all programming activities):

catch(Exception e) {
  
  
  System.err.println("Caught an exception");
  
  
}
  
  

创建一个能够处理任何类型的异常的异常处理程序是完全可行的。你可以通过捕获Exception的基类来完成,还有其它关于Exception的基类,但是这个和编程关系最相关。

This will catch any exception, so if you use it you’ll want to put it at the end of your list of handlers to avoid preempting any exception handlers that might otherwise follow it.

这个能够捕获任何异常信息,所以如果你使用的话你需要把它放到所有的异常类型的最后,避免它在其它更详尽的异常前面把异常抛出。

Since the Exception class is the base of all the exception classes that are important to the programmer, you don’t get much specific information about the exception, but you can call the methods that come from its base type Throwable:

因为Exception是所有异常信息的基类,所以这个对于程序员来讲是很重要的,你没能得到更多的关于exception的信息,但是你可以调用它的基类Throwable的方法。

String getMessage( )
String getLocalizedMessage( )
Gets the detail message, or a message adjusted for this particular locale.

String toString( )
Returns a short description of the Throwable, including the detail message if there is one.

void printStackTrace( )
void printStackTrace(PrintStream)
void printStackTrace(java.io.PrintWriter)
Prints the Throwable and the Throwable’s call stack trace. The call stack shows the sequence of method calls that brought you to the point at which the exception was thrown. The first version prints to standard error, the second and third prints to a stream of your choice (in Chapter 12, you’ll understand why there are two types of streams).

Throwable fillInStackTrace( )
Records information within this Throwable object about the current state of the stack frames. Useful when an application is rethrowing an error or exception (more about this shortly).

In addition, you get some other methods from Throwable’s base type Object (everybody’s base type). The one that might come in handy for exceptions is getClass( ), which returns an object representing the class of this object. You can in turn query this Class object for its name with getName( ). You can also do more sophisticated things with Class objects that aren’t necessary in exception handling.

还有,你可以调用Throwable的基类Object的一些方法。有一个方法是很有用的,getClass(),它返回了这个对象所属的类的描述,你可以使用getName()查询这个类型的对象的名字,当然你还可以在Object类得到更多有用的东西,但是对异常处理就不是很关键了。

Here’s an example that shows the use of the basic Exception methods:

下面给出一个例子来展示基本的Exception的方法:

import com.bruceeckel.simpletest.*;
  
  

  
  
   
    
  
  
public class ExceptionMethods {
  
  
  private static Test monitor = new Test();
  
  
  public static void main(String[] args) {
  
  
    try {
  
  
      throw new Exception("My Exception");
  
  
    } catch(Exception e) {
  
  
      System.err.println("Caught Exception");
  
  
      System.err.println("getMessage():" + e.getMessage());
  
  
      System.err.println("getLocalizedMessage():" +
  
  
        e.getLocalizedMessage());
  
  
      System.err.println("toString():" + e);
  
  
      System.err.println("printStackTrace():");
  
  
      e.printStackTrace();
  
  
    }
  
  
    monitor.expect(new String[] {
  
  
      "Caught Exception",
  
  
      "getMessage():My Exception",
  
  
      "getLocalizedMessage():My Exception",
  
  
      "toString():java.lang.Exception: My Exception",
  
  
      "printStackTrace():",
  
  
      "java.lang.Exception: My Exception",
  
  
      "%% /tat ExceptionMethods.main//(.*//)"
  
  
    });
  
  
  }
  
  
} 
  
  

You can see that the methods provide successively more information—each is effectively a superset of the previous one.

你能看到这些方法一个比一个提供了更多的信息,其实它们每一个都是前面一个的超集。

Rethrowing an exception

Sometimes you’ll want to rethrow the exception that you just caught, particularly when you use Exception to catch any exception. Since you already have the reference to the current exception, you can simply rethrow that reference:

典型来说,你使用Exception捕获了所有的异常信息,有时候你可能会想把刚才捕获的异常信息再抛出去,因为你已经获得了这个异常的对象,所以可以很简单的把这个对象再抛出去。

catch(Exception e) {
  
  
  System.err.println("An exception was thrown");
  
  
  throw e;
  
  
}
  
  

Rethrowing an exception causes it to go to the exception handlers in the next-higher context. Any further catch clauses for the same try block are still ignored. In addition, everything about the exception object is preserved, so the handler at the higher context that catches the specific exception type can extract all the information from that object.

重抛异常信息将这个异常跑出到一个更外层的环境中,所有针对于try语句中的catch语句将都被忽略。当然异常对象中的所有信息都得到了报出,所以当外层捕获这个异常信息的时候就能分解出这个对象的所有信息了。

If you simply rethrow the current exception, the information that you print about that exception in printStackTrace( ) will pertain to the exception’s origin, not the place where you rethrow it. If you want to install new stack trace information, you can do so by calling fillInStackTrace( ), which returns a Throwable object that it creates by stuffing the current stack information into the old exception object. Here’s what it looks like:

如果你只是简单的抛出异常信息,关于异常对象的信息你可以使用printStackTrace()打印出来,它指向的是抛出异常的原始位置而不是再次抛出异常的位置。如果你还希望转载一些栈轨迹信息,你可以使用fillInStackTrace()方法,这个方法会将当前的栈信息写进就的异常对象中并返回一个Throwable的对象,下面就是它的工作方式:

import com.bruceeckel.simpletest.*;
  
  

  
  
   
    
  
  
public class Rethrowing {
  
  
  private static Test monitor = new Test();
  
  
  public static void f() throws Exception {
  
  
    System.out.println("originating the exception in f()");
  
  
    throw new Exception("thrown from f()");
  
  
  }
  
  
  public static void g() throws Throwable {
  
  
    try {
  
  
      f();
  
  
    } catch(Exception e) {
  
  
      System.err.println("Inside g(),e.printStackTrace()");
  
  
      e.printStackTrace();
  
  
      throw e; //17
   
   
      //throw e.fillInStackTrace(); 18
  
  
    }
  
  
  }
  
  
  public static void  main(String[] args) throws Throwable {
  
  
    try {
  
  
      g();
  
  
    } catch(Exception e) {
  
  
      System.err.println(
  
  
        "Caught in main, e.printStackTrace()");
  
  
      e.printStackTrace();
  
  
    }
  
  
    monitor.expect(new String[] {
  
  
      "originating the exception in f()",
  
  
      "Inside g(),e.printStackTrace()",
  
  
      "java.lang.Exception: thrown from f()",
  
  
      "%% /tat Rethrowing.f(.*?)",
  
  
      "%% /tat Rethrowing.g(.*?)",
  
  
      "%% /tat Rethrowing.main(.*?)",
  
  
      "Caught in main, e.printStackTrace()",
  
  
      "java.lang.Exception: thrown from f()",
  
  
      "%% /tat Rethrowing.f(.*?)",
  
  
      "%% /tat Rethrowing.g(.*?)",
  
  
      "%% /tat Rethrowing.main(.*?)"
  
  
    });
  
  
  }
  
  
} 
  
  

The important line numbers are marked as comments. With line 17 uncommented (as shown), the output is as shown, so the exception stack trace always remembers its true point of origin no matter how many times it gets rethrown.

最重要的被标识为注释了,而第17行代码我们看到了输出结果,所以这个异常信息始终记得它被抛出的原始位置,不管被抛出了多少次

With line 17 commented and line 18 uncommented, fillInStackTrace( ) is used instead, and the result is:

如果将第17行注释掉而把18行执行,那么fillInStackTrace()会被执行,结果就是:

originating the exception in f()
  
  
Inside g(),e.printStackTrace()
  
  
java.lang.Exception: thrown from f()
  
  
        at Rethrowing.f(Rethrowing.java:9)
  
  
        at Rethrowing.g(Rethrowing.java:12)
  
  
        at Rethrowing.main(Rethrowing.java:23)
  
  
Caught in main, e.printStackTrace()
  
  
java.lang.Exception: thrown from f()
  
  
        at Rethrowing.g(Rethrowing.java:18)
  
  
        at Rethrowing.main(Rethrowing.java:23)
  
  

(Plus additional complaints from the Test.expect( ) method.) Because of fillInStackTrace( ), line 18 becomes the new point of origin of the exception.

加上一些Test.expect()方法的错误信息加上finllInStackTrace(),第18行就称为了新的异常的抛出地了。

The class Throwable must appear in the exception specification for g( ) and main( ) because fillInStackTrace( ) produces a reference to a Throwable object. Since Throwable is a base class of Exception, it’s possible to get an object that’s a Throwable but not an Exception, so the handler for Exception in main( ) might miss it. To make sure everything is in order, the compiler forces an exception specification for Throwable. For example, the exception in the following program is not caught in main( ):

Throwable类必须要出现在g()main()方法中,因为fillInStackTrace()方法返回了一个Throwable的对象。因为ThrowableException的基类,所以在main方法中捕获Exception异常则可能会捕获不到,为了确保程序的有序执行,编译器强制要求在异常说明中使用Throwable。举例来讲,下面的程序中main方法就没有捕获到exception信息。

public class ThrowOut {
  
  
  public static void
  
  
  main(String[] args) throws Throwable {
  
  
    try {
  
  
      throw new Throwable();
  
  
    } catch(Exception e) {
  
  
      System.err.println("Caught in main()");
  
  
    }
  
  
  }
  
  
}
  
  

It’s also possible to rethrow a different exception from the one you caught. If you do this, you get a similar effect as when you use fillInStackTrace( )—the information about the original site of the exception is lost, and what you’re left with is the information pertaining to the new throw:

在你捕获异常的地方你可以抛出一个新的异常信息也是可以的。如果你这么做的话,你得到的结果和刚才调用fillInStackTrace()的效果是一样的,原始位置发生异常信息都丢失了,所留下的只是异常再次被抛出时异常的信息了。

import com.bruceeckel.simpletest.*;
  
  

  
  
   
    
  
  
class OneException extends Exception {
  
  
  public OneException(String s) { super(s); }
  
  
}
  
  

  
  
   
    
  
  
class TwoException extends Exception {
  
  
  public TwoException(String s) { super(s); }
  
  
}
  
  

  
  
   
    
  
  
public class RethrowNew {
  
  
  private static Test monitor = new Test();
  
  
  public static void f() throws OneException {
  
  
    System.out.println("originating the exception in f()");
  
  
    throw new OneException("thrown from f()");
  
  
  }
  
  
  public static void  main(String[] args) throws TwoException {
  
  
    try {
  
  
      f();
  
  
    } catch(OneException e) {
  
  
      System.err.println(
  
  
        "Caught in main, e.printStackTrace()");
  
  
      e.printStackTrace();
  
  
      throw new TwoException("from main()");
  
  
    }
  
  
    monitor.expect(new String[] {
  
  
      "originating the exception in f()",
  
  
      "Caught in main, e.printStackTrace()",
  
  
      "OneException: thrown from f()",
  
  
      "/tat RethrowNew.f(RethrowNew.java:18)",
  
  
      "/tat RethrowNew.main(RethrowNew.java:22)",
  
  
      "Exception in thread /"main/" " +
  
  
      "TwoException: from main()",
  
  
      "/tat RethrowNew.main(RethrowNew.java:28)"
  
  
    });
  
  
  }
  
  
} 
  
  

The final exception knows only that it came from main( ) and not from f( ).

最后的异常对象只是知道这个异常来自main(),而不是f();

You never have to worry about cleaning up the previous exception, or any exceptions for that matter. They’re all heap-based objects created with new, so the garbage collector automatically cleans them all up.

你不必担心清理这些建立的异常对象,或者其它的异常信息。它们都是通过new方法在堆中创建的,所以垃圾回收器会自动的将它们清理掉。

Exception chaining

Often you want to catch one exception and throw another, but still keep the information about the originating exception—this is called exception chaining. Prior to JDK 1.4, programmers had to write their own code to preserve the original exception information, but now all Throwable subclasses may take a cause object in their constructor. The cause is intended to be the originating exception, and by passing it in you maintain the stack trace back to its origin, even though you’re creating and throwing a new exception at this point.

往往我们希望捕获了一个异常但是抛出了另外一个异常信息,而且希望记录原始的异常的信息,这叫做“异常链”。早在JDK1.4的时候程序员们就不得不去写代码来保存原始的异常信息,但是现在所有的Throwable的子类的构造方法都能接受一个cause对象,这个cause就是来保存上一个异常的,这样通过你维护的栈轨迹就可用回退到异常原始发生的,即便是你在这个位置创建并且抛出了新的异常也可以。

It’s interesting to note that the only Throwable subclasses that provide the cause argument in the constructor are the three fundamental exception classes Error (used by the JVM to report system errors), Exception, and RuntimeException. If you want to chain any other exception types, you do it through the initCause( ) method rather than the constructor.

比较有意思的是Throwable子类中能够支持cause参数构造方法的一共有三个基础异常类,分别是:Error(JVM报告系统错误使用)ExceptionRuntimeException;如果你要链接其它的异常类,那么你只能使用initCause()方法而不能使用构造方法了。

Here’s an example that allows you to dynamically add fields to a DynamicFields object at run time:

下面给出一个例子,允许你运行时动态的在DynamicFields对象中增加数据项;

 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值