Thinking in Java:第十二章-通过异常处理错误

1:当抛出异常后,有几件事随之发生,当前执行路径被终止,并从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行程序。这个恰当的地方就是异常处理程序,它的任务是将程序从错误中恢复,以使得程序继续运行。
2: 恢复模型:操作系统有支持的恢复模型,但是最终在程序中还是选择了“终止模型”的代码,因为恢复模型会导致耦合,恢复性的程序需要了解异常抛出的地点,这势必包含依赖于异常抛出位置的非通用性代码。这增加了编写和维护的难度。
3:异常是一种“见名知意”,“望文知意”的定义,所以对异常类来说,最重要的部分就是类名。
4:使用程序包的客户端程序员可能仅仅查看一下抛出的异常类型,其他的就不管了,所以对异常添加的其他功能也许根本用不上。
5:可以声明方法将抛出异常,实际上却不抛出,编译器会强制使用此方法的用户像真的抛出异常那样使用这个方法,这样的好处是,为异常先占个位置,以后就可以抛出这种异常而不用修改已有的代码,在定义抽象类和接口时很有用。
6:异常链:在Throwable的子类中,只有三种基本的异常类提供了带cause参数的构造器,它们是Error, (用于java虚拟机报告系统错误),Exception ,以及RuntimeException,如果要把其他类型的异常链接起来,应该使用initCause()方法。
DynamicFields
public class DynamicFieldException extends Exception{
}

/**
 * Created by ivalue on 2017/12/25.
 */
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};
        }
    }

    @Override
    public String toString() {
        StringBuffer sb = new StringBuffer();
        for (Object[] obj : fields) {
            sb.append(obj[0]).append(":").append(obj[1]).append("\n");
        }
        return sb.toString();
    }

    public 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 fieldNumber = hasField(id);
        if (fieldNumber == -1) {
            throw new NoSuchFieldException();
        }
        return fieldNumber;
    }

    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 field, 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 DynamicFieldException {
        if (value == null) {
            // most exception do not have a "cause" constructor
            // in these cases, you must use initCause().
            DynamicFieldException dfe = new DynamicFieldException();
            dfe.initCause(new NullPointerException());
            throw dfe;
        }

        int fieldNumber = hasField(id);
        if (fieldNumber == -1) {
            fieldNumber = makeField(id);
        }

        fields[fieldNumber][1] = value;

        Object result = null;
        try {
            result = getField(id);
        } catch (NoSuchFieldException e) {
            throw new RuntimeException(e);
        }

        return result;
    }


    public static void main(String[] args) {
        DynamicFields df = new DynamicFields(2);
        System.out.println(df);

        try {
            df.setField("d", "a value of d");
            df.setField("number" , "47");
            System.out.println(df);

            df.setField("d", "a new value of d");
            df.setField("number3","11");
            System.out.println(df);

            Object field = df.setField("c", "c value");
            System.out.println(field);

        } catch (NullPointerException e) {
            e.printStackTrace();
        } catch (DynamicFieldException e) {
            e.printStackTrace();
        }
    }

}


7:Throwable可以分为两种类型,Error表示编译时和系统错误,Exception是可以被抛出的基本类型。
8:属于运行时异常的类型有很多,它们会自动被java虚拟机抛出,所以不必在异常说明中把它们列出来。这些异常都是从RuntimeException类继承来的。如NullPointException。RuntimeException代表的是编程错误:
1):无法预料的错误,如从控制范围之外传递进来的null引用。
2):作为程序员,应该在代码中检查的错误。
9:如果把try快放在循环里,就建立了一个“程序继续执行之前必须要达到”的条件,还可以加入一个static类型的计数器或者别的装置,使循环在放弃以前尝试一定的次数,这可以增加程序的健壮性。当要把除内存之外的资源恢复到他们的出事状态,就要用到finall子句,这种需要清理的资源包括:已经打开的文件或者网络连接,在屏幕上画的图形。
FinallyWorks
public class FinallyWorks {

    static int count = 0;

    public static void main(String[] args) {
        while (true) {
            try {
                if (count ++ == 0) {
                    throw new MyException();
                }
                System.out.println("no exception");
            } catch (MyException e) {
                System.out.println("MyException");
            } finally {
                System.out.println("in final clause");
                if (count == 2) {
                    break;
                }
            }
        }
    }
}

10:异常的限制:当覆盖方法的时候,只能抛出在基类方法中的异常说明里列出的那些异常。这个限制很有用,因为这意味着,当基类使用代码应用到其派生类对象的时候,一样能工作。
11:尽管在继承的过程中,编译器会对异常说明做强制要求,但是异常说明本身不属于方法类型的一部分,方法类型是由方法的名字和参数的类型组成的,因此,不能基于异常说明来重载方法。
12:有一点很重要,时刻询问自己(不是提醒自己),“如果异常发生了,所有的东西能被正确的处理吗?”对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式使使用嵌套的try子句。
InputFile, Cleanup
public class InputFile {

    private BufferedReader in;

    public InputFile(String filename) throws Exception {

       try {
           in = new BufferedReader(new FileReader(filename));
       } catch (FileNotFoundException e) {
           System.out.println("could not open " + filename);
           throw e;
       } catch (Exception e) {
           try {

               // 出现FileNotFoundException时,在这里释放文件资源
               in.close();
           } catch (IOException e2) {
               System.out.println("in.close unsuccessful");
           }
           throw e;
       }
       finally {
           // don't close it here!!!,
           // 因为希望文件在IuputFile 的整个生命周期都是打开状态
       }
    }

    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();
        } catch (IOException e) {
            throw new RuntimeException("in.close failed");
        }
    }
}

public class Cleanup {

    public static void main(String[] args) {

        try {
            InputFile inputFile = new InputFile("Cleanup.java");

            // 在创建需要清理的对象之后,立即进入一个try-finally语句块
            try {
                String s;
                int i = 1;
                while ((s = inputFile.getLine()) != null) {
                    System.out.println(s);
                }
            } catch (Exception e) {
                System.out.println("caught Exception in main");
                e.printStackTrace();
            } finally {

                // 当InputFile 对象使用完了之后,在这里释放文件资源
                inputFile.dispose();
            }
        } catch (Exception e) {
            System.out.println("InputFile construction failed");
        }
    }
}



12:异常处理的一个重要目标就是把错误处理的代码同错误发生的地点相分离,这使你能在一段代码中专注于要完成的事。


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值