Java异常机制

Java异常机制

引入案例

public FileInputStream(String name) throw FileNotFoundException

上代码中,FileInputStream的构造函数将根据参数name构造一个FileInputStream,但是也有可能会抛出一个FileNotFoundException,当发生FileNotFoundExcepton时,运行时系统就会开始搜索异常处理器,以便知道如何处理FileNotFoundException。

受查异常

派生于Error类或RuntimeException类的异常被划分为非受查异常,其他异常为受查异常,对于非受查异常用户不需要抛出,对于受查异常,用于应当选择抛出。

Error类对应的异常如:{虚拟机内部错误,资源消耗殆尽},程序遇到这些错误时会直接终止运行,用户不应当抛出这类异常。

RuntimeException对应异常如:{NullPointerException,数组下标越界错误,除0错误},用户应当尽力避免这些错误,而不是抛出这些错误,抛出这些错误会造成程序冗长繁杂。

对于受查异常,如IOException,当一个方法抛出一个IOException时,一个重要的功能是提醒调用者应当注意这类异常。

## 抛出异常

在使用FileInputStream类的构造方法时,注意该方法的原型

public FileInputStream(File file)
                throws FileNotFoundException

throws FileNotFoundExcepiton表示该方法可能会抛出一个FileNotFoundException,此时必须显式抛出或捕捉此异常,否则编译不通过,当选择抛出时,注意下代码不仅ReadFile方法要抛出FileNotFoundException,ReadFile方法的调用者main也要抛出FileNotFoundException异常。一个方法抛出异常意味着此方法内部并不处理这个异常,方法的设计者希望方法的调用者去处理这个异常。


class Main
{
    public void readFile(String path) throws FileNotFoundException,IOException{
        FileInputStream fis=new FileInputStream(path);
        
        byte []buf=new byte[1024];
        int len=-1;
        while((len=fis.read(buf,0,buf.length))!=-1){
            System.out.println(new String(buf));    
        }
        fis.close();
    }

    public static void main(String [] args) throws FileNotFoundException,IOException{
        new Main().readFile("a.txt");
    }
}

关键字throw和throws

throws放在一个方法头中,表明该方法可能能够会抛出某某异常,throw常用于主动抛出一个异常,这个异常一般是自定义的异常,对于已经定义好的异常,如FileNotFoundException,这种异常有自己的“触发器”,当“触发器”触发时,该异常被自动抛出;但自定义的异常缺少“触发器”,需要手动的抛出,注意看自定义异常的小节。

捕获并处理异常

上代码中,异常一直被往上抛,交由JVM,这是不合适的,我们应当在适当的地方去捕捉并处理异常,如:

    public void readFile(String path){
        FileInputStream fis=null;
        try{
            fis=new FileInputStream(path);
        }catch(FileNotFoundException e){
            System.out.println("文件找不到!");
            e.printStackTrace();
        }
        byte []buf=new byte[1024];
        int len=-1;
        try
        {
            while((len=fis.read(buf,0,buf.length))!=-1){
                System.out.println(new String(buf,0,len));  
            }
        }catch (IOException e)
        {   
            e.printStackTrace();
        }finally{
            try{
                fis.close();
            }catch(IOException e){
                e.printStackTrace();
            }   
        }
    }

这样的代码显得很有些冗长繁杂……

try-catch-finally执行顺序

程序先进入try语句块,依次执行,在未发生异常的情况下执行完毕后进入finally语句块,依次执行(不执行catch中的语句)。

try语句块中,当发生异常时立即进入catch语句,依次执行catch语句中代码,执行完毕后进入finally语句,依次执行。

带资源的try语句

下代码中,并没有关闭输入流fis,带资源的try语句不论在那种情况下都会确保文件流被关闭。

public void readFile(String path) {
        byte[] buf = new byte[1024];
        int len = -1;
        try (FileInputStream fis = new FileInputStream(path)) {
            while ((len = fis.read(buf, 0, buf.length)) != -1) {
                System.out.println(new String(buf, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

自定义异常

public class Main {
    public class MyFormatException extends Throwable{
        public MyFormatException() {}
        public MyFormatException(String message) {
            super(message);
        }
        
    }
    public boolean juede(char []bf) throws MyFormatException {
        if(bf[0]!=1) {
            throw new Main.MyFormatException("字符数组格式错误");
        }
        return true;
    }
    public static void main(String[] args) {
        char []bf= {'0','1'};
        boolean a=false;
        try {
            a=new Main().juede(bf);
        }catch(MyFormatException e) {
            e.printStackTrace();
        }
        
        System.out.println(a);
    }

}

抛出异常的强制规则

1. 重写方法抛出异常抛出异常规则

重写方法只能抛出的异常必须被父类方法抛出的异常包含,当父类方法没有抛出异常时,重写方法不允许抛出异常,此时只能捕捉异常。

异常使用规范

1. 对于一些异常,使用检查而不捕捉

对比两种方式:

捕捉方式下:

    public static void main(String[] args) {
        Stack s=new Stack();
        try {
            s.pop();
        }catch(EmptyStackException e) {
            e.printStackTrace();
        }
    }

检查方式下:

    public static void main(String[] args) {
        Stack s=new Stack();
        if(!s.empty()) {
            s.pop();
        }
    }

检查上下的代码更简洁并且也更好!

2.

案例

下面的代码运行时是健壮的,设计良好的程序不容易因为一些偶然的原因而崩溃掉!

下面一块代码,没有意外的情况下,函数返回两数相除的结果,在发生意外时函数返回一个null值。


    public String divided(int a,int b) {
        String s=null;
        try {
            s=Integer.toString(a/b);
        }catch(ArithmeticException e){
            return null;
        }
        
        return s;
    }

个人感受

  1. 昨天晚上和今天上午主要看完了Java 异常框架,大部分应当是理解了,有些更细节的东西需要在实际编码中去领会。
  2. 下午看Java GUI,嗯,这条线没有被拓开,就感觉看的脑壳疼,毕业设计题目是与Java GUI相关,因此这块是应当去认真学习的,现在的话感觉需要做两件事,一者是梳理常见api,然后多码练习手感,二者是去感受里面的事件处理机制,这是比较拗口的一件事情,一旦这二者基本完成,我就可以正式的开始毕设作品的编写了。
  3. 晚上写斐波那契数列时,比较了递归和循环的效率,从第一项计算到第40项,递归需要耗时844ms,而循环仅需2ms,这其中的差别是巨大的。
  4. 传统的跳转语句有四种return\continue\break\goto,goto我从未用过,这个语句被业内所业务,这是很自然的,但是又换句话说,这又是有些不恰当的,goto语句无条件的任意跳转难以控制,但是想来有的时候或许也会是一种不错的方案。去掉goto语句的换,return\continue\break,已经足以胜任所有的程序跳转功能,唯一不好的一点时在多层循环的时候难以使用,仅能通过标志变量去精确控制其跳转行为。java提供一个标记循环的新特性,用于解决多层循环跳转的麻烦问题,这很好,但是又让我有些郁闷,这些新特性是很好(在此处我并不只针对该特性),但是却会加大学习的成本和大脑在应用时的负担,C语言短小精炼,嗯,学习的到一定程度都会对C的基本所有特性都达到掌握,但是学习Java却总感觉被山压着,一打开API文档#手动郁闷,靠#这又是啥玩意,丢下这种包袱的好的做法是从思维上去学习这门语言,以及其他的语言。

建议

  1. 不应当控制屏幕

  2. 不应当强制收手机

标签

评论

this is is footer