java中的异常--Exceptions in Java-- 第三部分

简介: Must enter integer as first argument. Although the above example had only one catch clause, you can have many catch clauses associated with a single try block.
Must enter integer as first argument.

Although the above example had only one catch clause, you can have many catch clauses associated with a single try block. Here's an example:

  // In Source Packet in file except/ex1/VirtualCafe.java
class VirtualCafe {
    public static void serveCustomer(VirtualPerson cust,
        CoffeeCup cup) {
        try {
            cust.drinkCoffee(cup);
            System.out.println("Coffee is just right.");
        }
        catch (TooColdException e) {
            System.out.println("Coffee is too cold.");
            // Deal with an irate customer...
        }
        catch (TooHotException e) {
            System.out.println("Coffee is too hot.");
            // Deal with an irate customer...
        }
    }
}

                    


If any code inside a try block throws an exception, its catch clauses are examined in their order of appearance in the source file. For example, if the try block in the above example throws an exception, the catch clause for TooColdException will be examined first, then the catch clause for TooHotException. During this examination process, the first catch clause encountered that handles the thrown object's class gets to "catch" the exception. The ordering of catch-clause examination matters because it is possible that multiple catch clauses of a try block could handle the same exception.

catch clauses indicate the type of abnormal condition they handle by the type of exception reference they declare. In the example above, the catch clauses declare exception type TooColdException and TooHotException. Had a single catch clause declared a TemperatureException, a thrown TooColdException or TooHotException still would have been caught, because TemperatureException is the superclass of both these classes. In the object-oriented way of thinking, a TooColdException is a TemperatureException, therefore, a catch clause for TemperatureException also will catch a thrown TooColdException. An example of this is shown below:

                        // In Source Packet in file except/ex2/VirtualCafe.java
class VirtualCafe {
    public static void serveCustomer(VirtualPerson cust,
        CoffeeCup cup) {
        try {
            cust.drinkCoffee(cup);
            System.out.println("Coffee is just right.");
        }
        catch (TemperatureException e) {
            // This catches TooColdException, TooHotException,
            // as well as TemperatureException.
            System.out.println("Coffee is too cold or too hot.");
            // Deal with an irate customer...
        }
    }
}

                    


Multiple catch clauses could handle the same exception because you may, for example, declare two catch clauses, one for TooColdException and another for TemperatureException. In this case, however, you must place the catch clause for TooColdException above the one for TemperatureException, or the source file won't compile. If a catch clause for TemperatureException could be declared before a catch clause for TooColdException, the first catch clause would catch all TooColdExceptions, leaving nothing for the second catch clause to do. The second catch clause would never be reached. The general rule is: subclass catch clauses must precede superclass catch clauses. Here's an example of both orders, only one of which compiles:

                        // In Source Packet in file except/ex3/VirtualCafe.java
class VirtualCafe {
    public static void serveCustomer(VirtualPerson cust,
        CoffeeCup cup) {
        try {
            cust.drinkCoffee(cup);
            System.out.println("Coffee is just right.");
        }
        catch (TemperatureException e) {
            // This catches TooColdException, TooHotException,
            // as well as TemperatureException.
            System.out.println("Coffee is too cold or too hot.");
            // Deal with an irate customer...
        }
        // THIS WON'T COMPILE, BECAUSE THIS catch clause
        // WILL NEVER BE REACHED.
        catch (TooColdException e) {
            System.out.println("Coffee is too cold.");
        }
    }
}
// In Source Packet in file except/ex4/VirtualCafe.java
// This class compiles fine.
class VirtualCafe {
    public static void serveCustomer(VirtualPerson cust,
        CoffeeCup cup) {
        try {
            cust.drinkCoffee(cup);
            System.out.println("Coffee is just right.");
        }
        catch (TooColdException e) {
            System.out.println("Coffee is too cold.");
            // Deal with an irate customer...
        }
        catch (TemperatureException e) {
            // This catches TooHotException as well
            // as TemperatureException.
            System.out.println(
                "There's temperature trouble in this coffee.");
            // Deal with an irate customer...
        }
    }
}

                    


Embedding information in an exception object
When you throw an exception, you are performing a kind of structured go-to from the place in your program where an abnormal condition was detected to a place where it can be handled. The Java virtual machine uses the class of the exception object you throw to decide which catch clause, if any, should be allowed to handle the exception. But an exception doesn't just transfer control from one part of your program to another, it also transmits information. Because the exception is a full-fledged object that you can define yourself, you can embed information about the abnormal condition in the object before you throw it. The catch clause can then get the information by querying the exception object directly.

The Exception class allows you to specify a String detail message that can be retrieved by invoking getMessage() on the exception object. When you define an exception class of your own, you can give client programmers the option of specifying a detail message like this:

                        // In Source Packet in file except/ex5/UnusualTasteException.java
class UnusualTasteException extends Exception {
    UnusualTasteException() {
    }
    UnusualTasteException(String msg) {
        super(msg);
    }
}

                    


Given the above declaration of UnusualTasteException, client programmers could create an instance in one of two ways:

new UnusualTasteException()
new UnusualTasteException("This coffee tastes like tea.")

 

A catch clause can then query the object for a detail string, like this:

                        // In Source Packet in file except/ex5/VirtualCafe.java
class VirtualCafe {
    public static void serveCustomer(VirtualPerson cust,
        CoffeeCup cup) {
        try {
            cust.drinkCoffee(cup);
            System.out.println("Coffee tastes just right.");
        }
        catch (UnusualTasteException e) {
            System.out.println(
                "Customer is complaining of an unusual taste.");
            String s = e.getMessage();
            if (s != null) {
                System.out.println(s);
            }
            // Deal with an unhappy customer...
        }
    }
}

                    


When you need to embed more information into an exception object than you can represent with a String, you can add data and access methods to your exception class. For example, you could define the temperature exception classes like this:

                        // In Source Packet in file except/ex6/TemperatureException.java
abstract class TemperatureException extends Exception {
    private int temperature; // in Celsius
    public TemperatureException(int temperature) {
        this.temperature = temperature;
    }
    public int getTemperature() {
        return temperature;
    }
}
// In Source Packet in file except/ex6/TooColdException.java
class TooColdException extends TemperatureException {
    public TooColdException(int temperature) {
        super(temperature);
    }
}
// In Source Packet in file except/ex6/TooHotException.java
class TooHotException extends TemperatureException {
    public TooHotException(int temperature) {
        super(temperature);
    }
}

                    


Given a TemperatureException family as defined above, catch clauses can query the exception object to find out the precise temperature that caused the problem. The temperature field of the exception object must be set when the object is created, as in:

                        // In Source Packet in file except/ex6/VirtualPerson.java
class VirtualPerson {
    private static final int tooCold = 65;
    private static final int tooHot = 85;
    public void drinkCoffee(CoffeeCup cup) throws
        TooColdException, TooHotException {
        int temperature = cup.getTemperature();
        if (temperature <= tooCold) {
            throw new TooColdException(temperature);
        }
        else if (temperature >= tooHot) {
            throw new TooHotException(temperature);
        }
        //...
    }
    //...
}

                    


Wherever the exception is caught, the catch clause can easily determine the actual temperature of the coffee and act accordingly, as in:

                        // In Source Packet in file except/ex6/VirtualCafe.java
class VirtualCafe {
    public static void serveCustomer(VirtualPerson cust,
        CoffeeCup cup) {
        try {
            cust.drinkCoffee(cup);
            System.out.println("Coffee is just right.");
        }
        catch (TooColdException e) {
            int temperature = e.getTemperature();
            System.out.println("Coffee temperature is "
                + temperature + " degrees Celsius.");
            if (temperature > 55 && temperature <= 65) {
                System.out.println("Coffee is cooling off.");
                // Add more hot coffee...
            }
            else if (temperature > 0 && temperature <= 55) {
                System.out.println("Coffee is too cold.");
                // Give customer a new cup of coffee with the
                // proper temperature...
            }
            else if (temperature <= 0) {
                System.out.println("Coffee is frozen.");
                // Deal with an irate customer...
            }
        }
        catch (TooHotException e) {
            int temperature = e.getTemperature();
            System.out.println("Coffee temperature is "
                + temperature + " degrees Celsius.");
            if (temperature >= 85 && temperature < 100) {
                System.out.println("Coffee is too hot.");
                // Ask customer to let it cool a few minutes...
            }
            else if (temperature >= 100 && temperature < 2000) {
                System.out.println(
                    "Both coffee and customer are steamed.");
                // Deal with an irate customer...
            }
            else if (temperature >= 2000) {
                System.out.println(
                    "The coffee is plasma.");
                // Deal with a very irate customer...
            }
        }
    }
}

                    


The program could deal with the temperature problem differently depending upon the coffee's actual temperature. If the coffee is just a little cold, the program could add more hot coffee to the cup. If the coffee is so cold that the customer's lips were instantly frozen to the cup, alternative measures could be taken.

Exceptions and the method invocation stack
Code inside a try block is in a sense surrounded by the catch clauses associated with the try block. When an exception is thrown, the surrounding catch clauses are examined in inside-out order. You can nest try blocks inside try blocks, in effect building up more and more layers of catch clauses that surround the code. When a method is invoked from within a try block, the catch clauses associated with that try block surround the code in the invoked method as well. If that method has try blocks and catch clauses, they are added as inner surrounding layers. What this means is that an exception may be thrown far up the method invocation stack before landing in a catch clause that can handle it.

As an example, consider the following exception classes, which are simpler versions of exceptions introduced in examples above:

                        // In Source Packet in file except/ex7/TemperatureException.java
class TemperatureException extends Exception {
}
// In Source Packet in file except/ex7/TooColdException.java
class TooColdException extends TemperatureException {
}
// In Source Packet in file except/ex7/TooHotException.java
class TooHotException extends TemperatureException {
}
// In Source Packet in file except/ex7/UnusualTasteException.java
class UnusualTasteException extends Exception {
}

                    


When the drinkCoffee() method of class VirtualPerson is invoked, it throws one of these four exceptions, chosen at random:

                        // In Source Packet in file except/ex7/VirtualPerson.java
class VirtualPerson {
    public void drinkCoffee(CoffeeCup cup) throws TooColdException,
        TemperatureException, UnusualTasteException {
        try {
            int i = (int) (Math.random() * 4.0);
            switch (i) {
            case 0:
                throw new TooHotException();
            case 1:
                throw new TooColdException();
            case 2:
                throw new UnusualTasteException();
            default:
                throw new TemperatureException();
            }
        }
        catch (TooHotException e) {
            System.out.println("This coffee is too hot.");
            // Customer will wait until it cools to an
            // acceptable temperature.
        }
    }
    //...
}

                    


If variable i in the drinkCoffee() method above happens to be set to a value of 0 the switch statement will instantiate and throw a TooHotException. Because the switch statement itself is enclosed within a try block that has a catch clause for TooHotException, execution continues at that catch clause. The program prints out:

                        This coffee is too hot.

                    


If variable i in the drinkCoffee() method above happens to be set to the value of 1, the switch statement will instantiate and throw a TooColdException. When this exception is thrown, the Java virtual machine will first check the catch clauses of the try block that surrounds the switch statement. In this case, however, no catch clause matches the thrown exception.

Because the TooColdException is not caught by the drinkCoffee() method, the Java virtual machine throws the exception up the method invocation stack to the method that invoked drinkCoffee(). As used here, a method invocation stack (or call stack) is a list of the methods that have been invoked by a thread, starting with the first method the thread invoked and ending with the current method. A method invocation stack shows the path of method invocations a thread took to arrive at the current method.

A graphical representation of the method invocation stack for drinkCoffee() is shown in Figure 3. In this figure, the method invocation stack is shown on the right and the corresponding Java stack is shown on the left. The Java stack is where methods keep their state inside the Java virtual machine. Each method gets a stack frame (or frame), which is pushed onto the stack when the method is invoked and popped from the stack when the method completes. The frame is an area in memory that contains the method's local variables, parameters, return value, and other information needed by the Java virtual machine to execute the method. In Figure 3, the stack is shown growing downwards. The top of the stack is at the bottom of the picture.

 

Figure 3. The method invocation stack for drinkCoffee()

When a method completes by executing a return statement, or by successfully executing the last statement in a method declared as void, it is said to complete normally. The Java virtual machine pops the returning method's stack frame, and continues executing just after the method invocation in the calling method. The calling method becomes the current method and its stack frame becomes the current frame.

When a method throws an exception that it doesn't catch itself, it is said to complete abruptly. Methods do not return a value when they complete abruptly, though they do pass along an exception object.

For example, when the drinkCoffee() method throws a TooColdException, it completes abruptly. Because the exception isn't caught by drinkCoffee(), the Java virtual machine pops drinkCoffee()'s stack frame. It then examines the next method up the invocation stack, in this case the serveCustomer() method of VirtualCafe, to see if it has a catch clause prepared to handle the exception.

Here's the code for VirtualCafe:

                        // In Source Packet in file except/ex7/VirtualCafe.java
class VirtualCafe {
    public static void serveCustomer(VirtualPerson cust,
        CoffeeCup cup)throws TemperatureException,
        UnusualTasteException {
        try {
            cust.drinkCoffee(cup);
        }
        catch (TooColdException e) {
            System.out.println("This coffee is too cold.");
            // Add more hot coffee...
        }
    }
}

                    


The serveCustomer() method above does indeed surround its invocation of drinkCoffee() with a try block that has an attached catch clause for TooColdException. So the exception stops here. The Java virtual machine makes the serveCustomer() method's stack frame current and continues execution at the first statement inside the catch clause. The program prints out:

                        This coffee is too cold.

                    


If variable i in the drinkCoffee() method above happens to be set to the value of 2, the switch statement will instantiate and throw an UnusualTasteException. When this exception is thrown, the Java virtual machine will first check the catch clauses of the try block that surrounds the switch statement. In this case, no catch clause matches the thrown exception. The virtual machine will then pop drinkCoffee()'s stack frame and examine the serveCustomer() method. But in serveCustomer(), no catch clause attached to the try block matches the thrown exception either. The virtual machine will therefore pop serveCustomer()'s stack frame and examine the next method up the invocation stack: the main() method of class Example7.

Here's the code for Example7:

                        // In Source Packet in file except/ex7/Example7.java
class Example7 {
    public static void main(String[] args)
        throws TemperatureException {
        // Create a new coffee cup.
        CoffeeCup cup = new CoffeeCup();
        // Create and serve a virtual customer.
        try {
            VirtualPerson cust = new VirtualPerson();
            VirtualCafe.serveCustomer(cust, cup);
        }
        catch (UnusualTasteException e) {
            System.out.println("This coffee has an unusual taste.");
        }
    }
}

                    


This main() method was farsighted enough to surround its invocation of serveCustomer() with a try block that includes a catch clause for UnusualTasteException. Thus, the Java virtual machine will make the main() method's stack frame current and will continue execution at the first statement in the catch clause. The program will print:   Continued  

目录
相关文章
|
12天前
|
Java
在 Java 中捕获和处理自定义异常的代码示例
本文提供了一个 Java 代码示例,展示了如何捕获和处理自定义异常。通过创建自定义异常类并使用 try-catch 语句,可以更灵活地处理程序中的错误情况。
|
12天前
|
Java
在 Java 中,如何自定义`NumberFormatException`异常
在Java中,自定义`NumberFormatException`异常可以通过继承`IllegalArgumentException`类并重写其构造方法来实现。自定义异常类可以添加额外的错误信息或行为,以便更精确地处理特定的数字格式转换错误。
|
13天前
|
IDE 前端开发 Java
怎样避免 Java 中的 NoSuchFieldError 异常
在Java中避免NoSuchFieldError异常的关键在于确保类路径下没有不同版本的类文件冲突,避免反射时使用不存在的字段,以及确保所有依赖库版本兼容。编译和运行时使用的类版本应保持一致。
|
14天前
|
Java 编译器
如何避免在 Java 中出现 NoSuchElementException 异常
在Java中,`NoSuchElementException`通常发生在使用迭代器、枚举或流等遍历集合时,尝试访问不存在的元素。为了避免该异常,可以在访问前检查是否有下一个元素(如使用`hasNext()`方法),或者使用`Optional`类处理可能为空的情况。正确管理集合边界和条件判断是关键。
|
17天前
|
Java
Java异常捕捉处理和错误处理
Java异常捕捉处理和错误处理
14 1
|
19天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
40 2
|
26天前
|
Java
如何在 Java 中处理“Broken Pipe”异常
在Java中处理“Broken Pipe”异常,通常发生在网络通信中,如Socket编程时。该异常表示写入操作的另一端已关闭连接。解决方法包括:检查网络连接、设置超时、使用try-catch捕获异常并进行重试或关闭资源。
|
28天前
|
存储 安全 Java
如何避免 Java 中的“ArrayStoreException”异常
在Java中,ArrayStoreException异常通常发生在尝试将不兼容的对象存储到泛型数组中时。为了避免这种异常,确保在操作数组时遵循以下几点:1. 使用泛型确保类型安全;2. 避免生类型(raw types)的使用;3. 在添加元素前进行类型检查。通过这些方法,可以有效防止 ArrayStoreException 的发生。
|
29天前
|
人工智能 Oracle Java
解决 Java 打印日志吞异常堆栈的问题
前几天有同学找我查一个空指针问题,Java 打印日志时,异常堆栈信息被吞了,导致定位不到出问题的地方。
34 2
|
1月前
|
Java 索引
如何避免在 Java 中引发`StringIndexOutOfBoundsException`异常
在Java中,处理字符串时若访问了不存在的索引,会抛出`StringIndexOutOfBoundsException`异常。为避免此异常,应确保索引值在有效范围内,例如使用`length()`方法检查字符串长度,并确保索引值不小于0且不大于字符串长度减1。
下一篇
无影云桌面