Java编程思想之十二 通过异常处理错误

Java的基本概念是结构不佳的代码不能运行余下的问题必须在运行期间解决,这就需要错误源能通过某种方式,把适当的信息传递给某个接收者——该接收者将知道如何正确处理这里问题。

12.1 概念

使用异常所带来的另一个相当明显的好处,它往往能够降低错误处理代码的复杂度。

12.2 基本异常

异常情形是指阻止当前方法或作用域继续执行的问题。把异常情形与普通问题相区分很重要,普通问题是指,在当前环境下能得到足够的信息,总能处理这个错误。而对于异常情形,就不能继续下去了,因为在当前环境下无法获得必要的信息来解决问题。你所能做的就是从当前环境跳出,并且把问题提交给上一级环境。这就是抛出异常所发生的事情。
当抛出异常后,有几件事就会发生。首先,同Java中其他对象的创建一样,将使用new在堆上创建异常对象,然后,当前的执行路径被终止,并且从当前环境中弹出堆异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方来继承执行程序。这个恰当的地方就是异常处理程序,它的任务就是将程序从错误状态中恢复,以使程序能要么换一种方式运行,要么继承运行下去。

12,2,1 异常参数

我们总用new在堆上创建异常对象,这也伴随着存储控件的分配和构造器的调用。所有标准异常类都有两个构造器:

  • 默认构造器
  • 接受字符串作为参数,把相关信息放入异常对象的构造器。

使用new创建了异常对象之后,此对象的引用将传给throw。
可以简单把异常处理看成一种不同的返回机制。抛出异常的方法从当前的作用域退出。将返回一个异常对象,然后退出方法作用域。

12.3 捕获异常

异常如何被捕获,必须首先理解监控区域

12.3.1 try块

异常处理机制,可以把所有动作都放在try块中,然后再一个地方处理捕获的异常就可以了。

12.3.2 异常处理程序

抛出的异常必须在某处得到处理。这个地点就是异常处理程序。异常处理程序紧跟在try块后,以关键字catch表示。
当异常抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。

终止与恢复

异常处理理论有两种基本模型:

  • Java支持终止模型:假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行,一旦异常被抛出,就表明错误已无法挽回,也不能会来继续执行。
  • 恢复模式:异常处理程序的工作就是修正错误,然后重新尝试调用出问题的放大,并认为第二次能够成功。

恢复模型会导致耦合:恢复性的处理程序需要了解异常抛出的地点,着势必要包含依赖于抛出位置的非通用性代码。这增加了代码编写和维护的困难。

12.4 创建自定义异常

要自己定义异常,必须从已有的异常类继承,最好选择意思相近的异常类继承。

public class InheritingExceptions
{
    public void f() throws SimpleException
    {
        System.out.println("Throw SimpleException from f()");
        throw new SimpleException();
    }
    public static void main(String[] args)
    {
        InheritingExceptions sed = new InheritingExceptions();
        try
        {
            sed.f();
        }
        catch (SimpleException e)
        {
            System.out.println("Caught it!");
        }
    }
}
class SimpleException extends Exception
{
}

编译器创建了默认构造器,它将自动调用基类的默认构造器。

public class FullConstructors
{
    public static void f() throws MyException{
        System.out.println("Throwing MyExcetion from f()");
        throw new MyException();
    }
    public static void g() throws MyException{
        System.out.println("Throwing MyExcetion from g()");
        throw new MyException("Originated in g()");
    }
    public static void main(String[] args){
        try{
            f();
        }
        catch (MyException e){
            e.printStackTrace(System.out);
        }
        try{
            g();
        }
        catch (MyException e){
            e.printStackTrace(System.out);
        }
    }
}
class MyException extends Exception{
    public MyException(){}
    public MyException(String msg){super(msg);}
}

12.4.1 异常与记录日志

使用基本日志记录功能

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;
public class LoggingExceptions
{
    public static void main(String[] args){
        try{
            throw new LoggingException();
        }
        catch (LoggingException e){
            System.out.println("Caught "+e);
        }
        try{
            throw new LoggingException();
        }
        catch (LoggingException e){
            System.out.println("Caught "+e);
        }
    }
}
class LoggingException extends Exception{
    private static Logger logger= Logger.getLogger("LoggingException ");
    public LoggingException(){
        StringWriter trace=new StringWriter();
        printStackTrace(new PrintWriter(trace));
        logger.severe(trace.toString());
    }
}

需要捕获或记录他人编写的异常,必须在异常处理程序中生成日志消息

import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Logger;

public class LoggingExceptions2
{
    private static Logger logger=Logger.getLogger("LoggingExceptions2");
    static void logException(Exception e){
        StringWriter trace=new StringWriter();
        e.printStackTrace(new PrintWriter(trace));
        logger.severe(trace.toString());
    }
    public static void main(String[] args){
        try{
            throw new NullPointerException();
        }
        catch(NullPointerException e){
            logException(e);
        }
    }
}

更进一步自定义异常,加入额外的构造器和成员

public class ExtraFeatures
{
    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 g()",47);
    }
    public static void main(String[] args){
        try{
            f();
        }
        catch (MyException2 e){
            e.printStackTrace(System.out);
        }
        try{
            g();
        }
        catch (MyException2 e){
            e.printStackTrace(System.out);
        }
        try{
            h();
        }
        catch (MyException2 e){
            e.printStackTrace(System.out);
            System.out.println("e.val()="+e.val());
        }
    }
}
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();
    }
}

异常也是一种对象,所有可以继续修改异常类,获得更强大的功能。

12.5 异常说明

Java提供语法,使可以告知客户端程序员某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。这就是异常说明,它属于方法说明的一部分,紧跟在形式参数之后。
异常说明使用了附加的关键字throws,后面接一个潜在的异常类型的列表,所以方法定义可能看起来像这样:

void f() throws TooBig,TooSmall
{
}

如果方法里的代码产生了异常却没有处理,编译器会发现这个问题并且提醒你,要么处理这个异常,要么就在异常说明中表明此方法将产生异常。
这种编译时被强制检查的异常称为被检查的异常

12.6 捕获所有异常

gillInStackTrace()用于在Throwable对象的内部记录栈帧的当前状态。这在程序重新抛出错误或异常时很有用。

12.6.1 栈轨迹

printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中,每个元素表示栈中的一贞。

public class WhoCalled
{
    static void f(){
        try{
            throw new Exception();
        }
        catch (Exception e){
            for(StackTraceElement ste:e.getStackTrace())
                System.out.println(ste.getMethodName());
        }
    }
    static void g(){f();}
    static void h(){g();}
    public static void main(String[] args){
        f();
        System.out.println("--------------");
        g();
        System.out.println("--------------");
        h();
    }
}

12.6.2 重新抛出异常

重新抛出异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch子句将被忽略。异常对象的所有信息都得以保持,所以高一级环境中捕获此异常的处理程序可以从这个异常对象中得到所有信息。

public class Rethrowing
{
    public static void f() throws Exception{
        System.out.println("originating the ");
        throw new Exception("thrown from f()");
    }
    public static void g() throws Exception{
        try{
            f();
        }
        catch (Exception e){
            System.out.println("Inside g(),e.printStackTrace()");
            e.printStackTrace(System.out);
            throw e;
        }
    }
    public static void h() throws Exception{
        try{
            f();
        }catch (Exception e){
            System.out.println("Inside h(),e.printStackTrace()");
            e.printStackTrace(System.out);
            throw (Exception) e.fillInStackTrace();
        }
    }
    public static void main(String[] args){
        try{
            g();
        }catch (Exception e){
            System.out.println("main: printStackTrace()");
            e.printStackTrace(System.out);
        }
        try{
            h();
        }catch (Exception e){
            System.out.println("main: printStackTrace()");
            e.printStackTrace(System.out);
        }
    }
}

有可能在捕获异常之后抛出另一种异常。这么做的话,得到的效果类似于使用fillInStackTrace(),有关原来异常发生点的信息会丢失,剩下的是于新的跑出点有关的信息:

public class RethrowNew
{
    public static void f() throws OneException{
        System.out.println("originating the exception");
        throw new OneException("thrown from f()");
    }
    public static void main(String[] args){
        try{
            try{
                f();
            }catch (OneException e){
                System.out.println("Caught in inner try,e.printStackTrace");
                e.printStackTrace(System.out);
                throw new TwoException("from inner try");
            }
        }catch (TwoException e){
            System.out.println("Caught in inner try,e.printStackTrace");
            e.printStackTrace(System.out);
        }
    }
}
class OneException extends Exception{
    public OneException(String s){super(s);}
}
class TwoException extends Exception{
    public TwoException(String s){super(s);}
}

异常都是用new在堆上创建的对象,所有垃圾回收器会自动把它们清理掉。

12.6.3 异常链

想在捕获一个异常后抛出另一个异常,并且希望把原始异常信息保持下来,这被称为异常链。现在所有Throwable的子类在构造器中都可以接受一个causr对象作为参数。这个cause就用来表示原始异常,这样通过把原始异常传递给新的异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链最终到最初发生的位置。

class DynamicFieldsException extends Exception {}
public class DynamicFields {
    private Object[][] fields;
    public DynamicFields(int initialSize) {
        fields = new Object[initialSize][2];
        for(int i = 0; i < initialSize; i++)
            fields[i] = new Object[] { null, null };
    }
    public String toString() {
        StringBuilder result = new StringBuilder();
        for(Object[] obj : fields) {
            result.append(obj[0]);
            result.append(": ");
            result.append(obj[1]);
            result.append("\n");
        }
        return result.toString();
    }
    private int hasField(String id) {
        for(int i = 0; i < fields.length; i++)
            if(id.equals(fields[i][0]))
                return i;
        return -1;
    }
    private int
    getFieldNumber(String id) throws NoSuchFieldException {
        int fieldNum = hasField(id);
        if(fieldNum == -1)
            throw new NoSuchFieldException();
        return fieldNum;
    }
    private int makeField(String id) {
        for(int i = 0; i < fields.length; i++)
            if(fields[i][0] == null) {
                fields[i][0] = id;
                return i;
            }
        // No empty fields. Add one:
        Object[][] tmp = new Object[fields.length + 1][2];
        for(int i = 0; i < fields.length; i++)
            tmp[i] = fields[i];
        for(int i = fields.length; i < tmp.length; i++)
            tmp[i] = new Object[] { null, null };
        fields = tmp;
        // Recursive call with expanded fields:
        return makeField(id);
    }
    public Object
    getField(String id) throws NoSuchFieldException {
        return fields[getFieldNumber(id)][1];
    }
    public Object setField(String id, Object value)
            throws DynamicFieldsException {
        if(value == null) {
            // Most exceptions don't have a "cause" constructor.
            // In these cases you must use initCause(),
            // available in all Throwable subclasses.
            DynamicFieldsException dfe =
                    new DynamicFieldsException();
            dfe.initCause(new NullPointerException());
            throw dfe;
        }
        int fieldNumber = hasField(id);
        if(fieldNumber == -1)
            fieldNumber = makeField(id);
        Object result = null;
        try {
            result = getField(id); // Get old value
        } catch(NoSuchFieldException e) {
            // Use constructor that takes "cause":
            throw new RuntimeException(e);
        }
        fields[fieldNumber][1] = value;
        return result;
    }
    public static void main(String[] args) {
        DynamicFields df = new DynamicFields(3);
        System.out.print(df);
        try {
            df.setField("d", "A value for d");
            df.setField("number", 47);
            df.setField("number2", 48);
            System.out.print(df);
            df.setField("d", "A new value for d");
            df.setField("number3", 11);
            System.out.print("df: " + df);
            System.out.print("df.getField(\"d\") : " + df.getField("d"));
            Object field = df.setField("d", null); // Exception
        } catch(NoSuchFieldException e) {
            e.printStackTrace(System.out);
        } catch(DynamicFieldsException e) {
            e.printStackTrace(System.out);
        }
    }
} 

Java标准异常

Throwable这个Java类被用来表示任何可以作为异常抛出的类。Throwable对象可分为两中类型:Error用来表示编译时和系统错误。Exception是可以被抛出的基本类型。
RuntimeException类型的异常也许会穿越所有执行路径直达main()方法,而不会被捕获。

public class NeverCaught
{
    static void f(){
        throw new RuntimeException("From f()");
    }
    static void g(){
        f();
    }
    public static void main(String[] args){
        g();
    }
}

RuntimeException代表的是编程错误:

  • 无法预料的错误
  • 作为程序员,应该在代码中检查的错误
    应该把异常机制用来处理一些烦人的运行时错误,这些错误往往是由代码控制能力之外的因素导致的。

    12.8 使用finally进行清理

    对于一些代码,可能会希望无论try中的异常是否抛出,都能得到执行。可以使用finally子句。

public class FinallyWorks
{
    static int count=0;
    public static void main(String[] args){
        while(true){
            try{
                if(count++==0)
                    throw new ThreeException();
                System.out.println("No exception");
            }
            catch (ThreeException e){
                System.out.println("ThreeException");
            }
            finally
            {
                System.out.println("finally cluse "+count);
                if(count==2)break;
            }
        }
    }
}
class ThreeException extends Exception{}

12.8.1 finally用来做什么

当要把除内存之外的资源恢复到初始状态时,就要用到finally子句

public class WithFinally
{
    static Switch sw = new Switch();
    public static void main(String[] args) {
        try {
            sw.on();
            // Code that can throw exceptions...
            OnOffSwitch.f();
        } catch(OnOffException1 e) {
            System.out.println("OnOffException1");
        } catch(OnOffException2 e) {
            System.out.println("OnOffException2");
        } finally {
            sw.off();
        }
    }
}
class Switch {
    private boolean state = false;
    public boolean read() { return state; }
    public void on() { state = true; System.out.print(this); }
    public void off() { state = false; System.out.print(this); }
    public String toString() { return state ? "on" : "off"; }
}
class OnOffSwitch {
    private static Switch sw = new Switch();
    public static void f()
            throws OnOffException1,OnOffException2 {}
    public static void main(String[] args) {
        try {
            sw.on();
            // Code that can throw exceptions...
            f();
            sw.off();
        } catch(OnOffException1 e) {
            System.out.println("OnOffException1");
            sw.off();
        } catch(OnOffException2 e) {
            System.out.println("OnOffException2");
            sw.off();
        }
    }
}
class OnOffException1 extends Exception {}
class OnOffException2 extends Exception {}

当设计break和continue语句时,finally也会得到执行

12.8.2 在return中使用finally

因为finally子句总会执行,所以在一个方法中,可以从多个点返回,并且可以保证重要的清理工作仍旧会执行:

public class MultipleReturns
{
    public static void f(int i){
        System.out.println("Initialization that requires");
        try{
            System.out.println("Point 1");
            if (i==1)return;
            System.out.println("Point 2");
            if (i==2)return;
            System.out.println("Point 3");
            if (i==3)return;
        }
        finally
        {
            System.out.println("end");
        }
    }
    public static void main(String[] args){
        for(int i=1;i<=3;i++)
            f(i);
    }
}

12.8.3 遗憾:异常丢失

public class LoseMessage
{
    void f() throws VeryImportantException {
        throw new VeryImportantException();
    }
    void dispose() throws HoHumException {
        throw new HoHumException();
    }
    public static void main(String[] args) {
        try {
            LoseMessage lm = new LoseMessage();
            try {
                lm.f();
            } finally {
                lm.dispose();
            }
        } catch(Exception e) {
            System.out.println(e);
        }
    }
}
class VeryImportantException extends Exception {
    public String toString() {
        return "A very important exception!";
    }
}
class HoHumException extends Exception {
    public String toString() {
        return "A trivial exception";
    }
}

前一个异常还没有处理就抛出了下一个异常。

12.9 异常的限制

当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。

class BaseballException extends Exception {}
class Foul extends BaseballException {}
class Strike extends BaseballException {}

abstract class Inning {
    public Inning() throws BaseballException {}
    public void event() throws BaseballException {
        // Doesn't actually have to throw anything
    }
    public abstract void atBat() throws Strike, Foul;
    public void walk() {} // Throws no checked exceptions
}

class StormException extends Exception {}
class RainedOut extends StormException {}
class PopFoul extends Foul {}

interface Storm {
    public void event() throws RainedOut;
    public void rainHard() throws RainedOut;
}

public class StormyInning extends Inning implements Storm {
    // OK to add new exceptions for constructors, but you
    // must deal with the base constructor exceptions:
    public StormyInning()
            throws RainedOut, BaseballException {}
    public StormyInning(String s)
            throws Foul, BaseballException {}
    // Regular methods must conform to base class:
//! void walk() throws PopFoul {} //Compile error
    // Interface CANNOT add exceptions to existing
    // methods from the base class:
//! public void event() throws RainedOut {}
    // If the method doesn't already exist in the
    // base class, the exception is OK:
    public void rainHard() throws RainedOut {}
    // You can choose to not throw any exceptions,
    // even if the base version does:
    public void event() {}
    // Overridden methods can throw inherited exceptions:
    public void atBat() throws PopFoul {}
    public static void main(String[] args) {
        try {
            StormyInning si = new StormyInning();
            si.atBat();
        } catch(PopFoul e) {
            System.out.println("Pop foul");
        } catch(RainedOut e) {
            System.out.println("Rained out");
        } catch(BaseballException e) {
            System.out.println("Generic baseball exception");
        }
        // Strike not thrown in derived version.
        try {
            // What happens if you upcast?
            Inning i = new StormyInning();
            i.atBat();
            // You must catch the exceptions from the
            // base-class version of the method:
        } catch(Strike e) {
            System.out.println("Strike");
        } catch(Foul e) {
            System.out.println("Foul");
        } catch(RainedOut e) {
            System.out.println("Rained out");
        } catch(BaseballException e) {
            System.out.println("Generic baseball exception");
        }
    }
}

派生类构造器不能捕获基类构造器抛出的异常。
派生类可以不抛出任何异常,即使它是基类定义的异常。
如果处理的刚好是派生类对象的话,编译器只会强制要求你捕获这个类所抛出的异常,如果将它向上转型成基类,那么编译器就会要求你捕获基类的异常。
在继承中,基类的方法必须出现在派生类里,在继承和覆盖的过程中,某个特定的方法异常说明接口不是变大而是变小——这恰好和类接口在继承时的情形相反。

12.10 构造器

一般在构造器中,会把对象设置成安全的初始状态。如果在构造器内抛出异常,这些清理行为也许就不能正常工作了。就算使用finally每次都执行了清理代码,如果构造器在器执行的中途异常,那么某些对象就没有被创建成功,而这些部分在finally子句中却要被清理。

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
public class InputFile
{
    private BufferedReader in;

    public InputFile(String fname) throws Exception
    {
        try
        {
            in = new BufferedReader(new FileReader(fname));
            // Other code that might throw exceptions
        }
        catch (FileNotFoundException e)
        {
            System.out.println("Could not open " + fname);
            // Wasn't open, so don't close it
            throw e;
        }
        catch (Exception e)
        {
            // All other exceptions must close it
            try
            {
                in.close();
            }
            catch (IOException e2)
            {
               System.out.println("in.close() unsuccessful");
            }
            throw e; // Rethrow
        }
        finally
        {
            // Don't close it here!!!
        }
    }
    public String getLine()
    {
        String s;
        try
        {
            s = in.readLine();
        }
        catch (IOException e)
        {
            throw new RuntimeException("readLine() failed");
        }
        return s;
    }
    public void dispose()
    {
        try
        {
            in.close();
            System.out.println("dispose() successful");
        }
        catch (IOException e2)
        {
            throw new RuntimeException("in.close() failed");
        }
    }
}

对于构造阶段可能会抛出的异常,并且要求清理的类,最安全的时使用嵌套try子句:

public class Cleanup
{
    public static void main(String[] args)
    {
        try
        {
            InputFile in = new InputFile("Cleanup.java");
            try
            {
                String s;
                int i = 1;
                while ((s = in.getLine()) != null)
                    ; // Perform line-by-line processing here...
            }
            catch (Exception e)
            {
                System.out.println("Caught Exception in main");
                e.printStackTrace(System.out);
            }
            finally
            {
                in.dispose();
            }
        }
        catch (Exception e)
        {
            System.out.println("InputFile construction failed");
        }
    }
}

对InputFile对象的构造在其自己的try语句块中有效,如果构造失败,将进入外部的catch子句,而dispose()方法不会被调用。如果构造成功,我们肯定要确保对象能够被清理,因此在构造器之后立即创建一个新的try语句。执行清理的finally与内部热try语句块相关联。这种方式中,finally子句在构造失败时不会执行的,而是构造成功时才会被执行。
这种通用的清理惯用法在构造器不抛出任何异常时也应该运用。基本规则是:在创建需要清理的对象之后,立即进入一个try-finally语句中:

class NeedsCleanup
{ // Construction can't fail
    private static long counter = 1;
    private final long id = counter++;

    public void dispose()
    {
        System.out.println("NeedsCleanup " + id + " disposed");
    }
}

class ConstructionException extends Exception
{
}

class NeedsCleanup2 extends NeedsCleanup
{
    // Construction can fail:
    public NeedsCleanup2() throws ConstructionException
    {
    }
}

public class CleanupIdiom
{
    public static void main(String[] args)
    {
        // Section 1:
        NeedsCleanup nc1 = new NeedsCleanup();

        try
        {
            // ...
        }

        finally
        {
            nc1.dispose();
        }

        // Section 2:
        // If construction cannot fail you can group objects:
        NeedsCleanup nc2 = new NeedsCleanup();
        NeedsCleanup nc3 = new NeedsCleanup();
        try
        {
            // ...
        }
        finally
        {
            nc3.dispose(); // Reverse order of construction
            nc2.dispose();
        }

        // Section 3:
        // If construction can fail you must guard each one:
        try
        {
            NeedsCleanup2 nc4 = new NeedsCleanup2();
            try
            {
                NeedsCleanup2 nc5 = new NeedsCleanup2();
                try
                {
                    // ...
                }
                finally
                {
                    nc5.dispose();

                }
            }
            catch (ConstructionException e)
            { // nc5 constructor
                System.out.println(e);
            }
            finally
            {
                nc4.dispose();
            }
        }
        catch (ConstructionException e)
        { // nc4 constructor
            System.out.println(e);
        }
    }
}

Section1相当简单:遵循了在可去除对象之后紧跟try—finally原则。如果对象构造不能失败,就不需要任何catch。在Section2中,为了构造和清理,可以看到具有不能失败的构造器对象可以群组在一起。
Section3展示了如何处理那些具有可以失败的构造器,且需要清理的对象。

异常匹配

抛出异常的时候,异常处理系统会按照代码的书写顺序找出最近的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不在继续查找。

如果把捕获基类的catch子句放在最前面,以此想把派生类的异常全屏蔽,就像这样:

try
{
throw new Sneeze();
}
catch(Annoance a){}
catch(Sneeze s){}

这样编译器就会发现Sneeze的catch子句永远也得不到执行,因此它会向你报错。

12.12 其他可选方式

异常处理系统就像一个活门,使你能放弃程序的正常执行序列。开发异常处理的初衷是为了方便处理错误。
异常处理的一个重要原则:只有在你知道如何处理的情况下才捕获异常。就是把错误的代码同错误发生地点相分离。
被检查的异常:因为它们强制你在可能还没准备好处理错误的时候被迫加上catch子句,这就导致了吞食则有害的问题。

12.12.1 把异常传递给控制台

12.13 异常使用指南

应在下列情况下使用异常:

  • 在恰当的级别处理问题。(知道如何处理的情况下捕获异常)
  • 解决问题并且重新调用产生异常的方法
  • 进行少许修补,然后绕过异常发生的地方继续执行。
  • 用别的数据进行计算,以代替方法预计会返回的值。
  • 把当前运行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层。
  • 把当前运行环境下能做的事情尽量做完,然后把不同的异常重抛到更高层。
  • 终止程序
  • 进行简化。
  • 让类库和程序更安全。

原文地址:https://www.cnblogs.com/Tan-sir/p/11235136.html

时间: 2024-11-10 13:23:19

Java编程思想之十二 通过异常处理错误的相关文章

Java编程思想---第十二章 通过异常处理错误(中)

第十二章  通过异常处理错误(中) 12.4 创建自定义异常 我们不必拘泥于Java中已有的异常类型,Java提供的异常体系不可能预见所有的错误,所以可以自己定义异常类来表示程序中可能会遇到的特定问题:要自己定义异常类,必须从已有的异常类继承,最好是选择意思相近的异常类继承,建立新的异常类最简单的方法就是让编译器为你产生默认构造器,所以这几乎不用写多少代码: class SimpleException extends Exception { } public class InheritingEx

Java编程思想学习(十二) 数组和容器

一.数组 1).数组的多种初始化方式 下面总结了初始化数组的多种方式,以及如何对指向数组的引用赋值,使其指向另一个数组对象.值得注意的是:对象数组和普通数组的各种操作基本上都是一样的:要说有什么不同的话就是对象数组默认值为null,而基本数组视本身情况而定. 1 package lkl; 2 3 import java.util.Arrays; 4 5 ///下面演示了数组的初始化 6 //包括对象数组和普通数组 7 class Base{ 8 private static long count

java编程思想总结(二)

java编程思想总结(二) java编程思想总结是一个持续更新的系列,是本人对自己多年工作中使用到java的一个经验性总结,也是温故而知新吧,因为很多基础的东西过了这么多年,平时工作中用不到也会遗忘掉,所以看看书,上上网,查查资料,也算是记录下自己的笔记吧,过一段时间之后再来看看也是蛮不错的,也希望能帮助到正在学习的人们,本系列将要总结一下几点: 面向对象的编程思想 java的基本语法 一些有趣的框架解析 实战项目的整体思路 代码的优化以及性能调优的几种方案 整体项目的规划和视角 其它遗漏的东西

JAVA编程思想(2) - 操作符(二)

5. 直接常量 -一般来说,如果程序里使用了"直接常量",编译器可以准确的知道要生成什么样的类型,但有时候却是模棱两可的.这时候需要我们对编译器进行适当的"指导" -直接常量后面的后缀字符标示了它的类型. -指数记数法:e代表"10的幂次" -注意如果编译器能够正确的识别类型,就不必在数值后附加字符,例如语句: float f4 = 200; 不存在含糊不清的地方,所以200后面不需要加L,但是对于:float f4 = 1e-43f; 编译器通

java 编程思想笔记(二)——初始化

一.问题 随着计算机革命的发展,"不安全"的编程方式已逐渐成为编程代价高昂的主因之一. 初始化和清理正是涉及安全的两个问题. 二.java 解决方案 构造器:一种特殊的方法.java 中"初始化"和"创建"捆绑在一起,两者不能分离. 垃圾回收器:释放无用的对象. 三.方法重载 为什么要在这里讲方法重载?我觉得首先:构造器是一种特殊的方法,再一个,构造器除了默认构造器还有有参构造器.为了让方法名称相同而形式参数不同的构造器同时存在,就必须用到方法重

Java编程思想学习(十六) 并发编程

线程是进程中一个任务控制流序列,由于进程的创建和销毁需要销毁大量的资源,而多个线程之间可以共享进程数据,因此多线程是并发编程的基础. 多核心CPU可以真正实现多个任务并行执行,单核心CPU程序其实不是真正的并行运行,而是通过时间片切换来执行,由于时间片切换频繁,使用者感觉程序是在并行运行.单核心CPU中通过时间片切换执行多线程任务时,虽然需要保存线程上下文,但是由于不会被阻塞的线程所阻塞,因此相比单任务还是大大提高了程序运行效率. 1.线程的状态和切换: 线程的7种状态及其切换图如下: 2.多线

Java编程思想学习(十四) 枚举

关键字enum可以将一组具名的值有限集合创建一种为新的类型,而这些具名的值可以作为常规的程序组件使用. 基本enum特性 调用enum的values()方法可以遍历enum实例,values()方法返回enum实例数组,且数组中元素保持在enum声明时的顺序. 1 public class TestEnum { 2 public static void main(String[] args) { 3 Fruit[] values = Fruit.values(); 4 for (Fruit fr

Java编程思想之十 内部类

可以将一个类定义放在另一个类的定义内部,这就是内部类. 10.1 创建内部类 创建内部类就是把类的定义置于外部类里面. public class Parcell { class contents{ int i=0; public void GetI(){ System.out.println("contents"+i); i++; } } class Destintion{ private String label; public void GetI(String s){ System

【Java编程思想】二、一切都是对象

尽管Java是基于C++的,但是相比之下,Java是一种更“纯粹”的面向对象设计语言. 2.1 用引用操纵对象 一切都被视为对象, 因此可采用单一固定的语法.尽管一切都被看作对象,但操纵的标识符实际上是对象的一个“引用”. Java里的“引用”与C++中的“引用”是有区别的,但是这个概念不需要完全明白,学会处理引用就可以了. 2.2 必须由你创建所有对象 new关键字的意思就是“给我一个新对象”! 2.2.1对象存储到什么地方 1)寄存器 2)堆栈(RAM) 3)堆(RAM) 4)常量存储(RO