【Java基础 四】---异常处理

来源:互联网 发布:外国域名注册机构 编辑:程序博客网 时间:2024/06/17 06:52

异常的基本知识

异常的概念

要知道,异常指的都是运行时异常!编译错误一般指语法什么的错误,在运行之前IDE就会有提示。

这里写图片描述

这里写图片描述
上图中的代码就是数组下标越界

异常的分类

这里写图片描述
error是系统异常,例如当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError。 程序处理不了。exception是可以处理的,runtime exception是一种经常出的错误,可以catch也可以不catch。而其它exception,=是必须得逮的exception,方法自己就自带的exception
这里写图片描述
Java中的异常分为两大类:
  1.Checked Exception(非Runtime Exception)
  2.Unchecked Exception(Runtime Exception)
运行时异常
RuntimeException类是Exception类的子类,它叫做运行时异常,Java中的所有运行时异常都会直接或者间接地继承自RuntimeException类。
非运行时异常
Java中凡是继承自Exception,而不继承自RuntimeException类的异常都是非运行时异常。对于非运行时异常(checked exception),必须要对其进行处理,否则无法通过编译。
  处理方式有两种:
  1.使用try..catch..finally进行捕获;
  一个try后面可以跟多个catch,但不管多少个,最多只会有一个catch块被执行。
  2.在产生异常的方法声明后面写上throws 某一个Exception类型,如throws Exception,将异常抛出到外面一层去。
对于运行时异常(runtime exception),可以对其进行处理,也可以不处理。推荐不对运行时异常进行处理。

处理异常

知道了上边异常的组成部分,我们针对其中需要处理的(非运行时异常)的两种处理方式。

Try…catch…finally

基本流程

这里写图片描述
这里写图片描述

在发现语句1出错,后边的语句不会执行,也就是说语句2不会被执行了,而是执行finally之后的 try catch之外的其它语句
这里写图片描述
这里写图片描述
这里写图片描述

public class Fileexception {    public static void main(String[] args) {        FileInputStream in = null;  //一定要定义在try外边,记住作用域是在大括号之间        try {            in = new FileInputStream("myfile.txt");            int b;            b = in.read();            while (b != -1) {                System.out.print((char) b);                b = in.read();            }        } catch (FileNotFoundException e) {    //一定要先写FileNotFoundException,因为FileNotFoundException是IOException的子类,因为写catch是先小后大            e.printStackTrace();        } catch (IOException e) {            System.out.println(e.getMessage());        } finally {            try {                in.close();            } catch (IOException e) {                e.printStackTrace();            }        }    }}

使用多重catch

很多情况下,由单个的代码段可能引起多个异常。处理这种情况,我们需要定义两个或者更多的catch子句,每个子句捕获一种类型的异常,当异常被引发时,每个catch子句被依次检查,第一个匹配异常类型的子句执行,当一个catch子句执行以后,其他的子句将被旁路。

顺序问题:先小后大,即先子类后父类

这里写图片描述

Java通过异常类描述异常类型。对于有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。例如:RuntimeException异常类包括运行时各种常见的异常,ArithmeticException类和ArrayIndexOutOfBoundsException类都是它的子类。因此,RuntimeException异常类的catch子句应该放在最后面,否则可能会屏蔽其后的特定异常处理或引起编译错误。

try ,catch,finally组合情况

亲测后有以下几条规则
1,try+catch后有没有finally无所谓
try+catch+finally可以使用
try+catch可以使用
2,try必不可少
try+finally,try+catch都可以
3,三个变量不能单独使用任何一个,即使是try如果不加catch也必须有finally(声明了异常就一定要处理,不管是在catch还是finally中)

总结就是必须有try和(catch,finally,catch+finally)三个组合中的一种

return的执行时机

通用场景

1、不管有没有出现异常,finally块中代码都会执行;

finally里没有return的场景,但try或catch里有

2、当try和catch中有return时,finally里没有return,finally仍然会执行,因此在return返回时不是直接返回变量的值,而是复制一份,然后返回,因此,对于基本类型的,finally的改变没有影响,对于引用类型的就有影响了

package test;/ * @author 田茂林 * @data 201796日 下午9:46:42 */public class TestFinally{    public static int testFinally1(){ //基本类型的        int result =1;        try {            result = 2;            return result;        } catch (Exception e) {            return 0;        }finally{            result =3;            System.out.println("execult Finally1");        }    }    public static StringBuffer testFinally2(){          //引用类型的        StringBuffer s = new StringBuffer("Hello");        try {            return s;        } catch (Exception e) {            return null;        }finally{            s.append("world");            System.out.println("execult Finally2");        }    }    public static void main(String[] args) {              int result1 = testFinally1();              System.out.println(result1);              StringBuffer result2 = testFinally2();              System.out.println(result2);    }}

输出结果

execult Finally12execult Finally2Helloworld

finally里有return的情况

3、finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。返回值是finally里的return返回的值

注意,如果是return i++返回i的值,return ++i返回的是++i的值,也就是永远返回当前值

package test;/** * @author 田茂林 * @data 2017年9月6日 下午9:46:42 */public class TestFinally {    @SuppressWarnings("finally")    public static int testFinally() { // 基本类型的        int i = 1;        try {            ++i;            return i;        } catch (Exception e) {        } finally {            return i++; // 2            // return ++i; //3        }    }    public static void main(String[] args) {        int result1 = testFinally();        System.out.println(result1);    }}

总结:
对于finally块中没有return语句的情况,方法在返回之前会先将返回值保存在局部变量表中的某个slot中,然后执行finally块中的语句,之后再将保存在局部变量表中某个slot中的数据放入操作数栈的栈顶并进行返回,因此对于基本数据类型而言,若在finally块中改变其值,并不会影响最后return的值。

而对于finally块中包含了return语句的情况,则在try块中的return执行之前,会先goto到finally块中,而在goto之前并不会对try块中要返回的值进行保护,而是直接去执行finally块中的语句,并最终执行finally块中的return语句,而忽略try块中的return语句,因此最终返回的值是在finally块中改变之后的值。

catch块和finally块里可以抛出异常么

可以但是需要加try,catch环绕,或者直接从方法上声明抛出

这里写图片描述

这里写图片描述

throws和throw

这里写图片描述
这里写图片描述

public class ManangeException {    public static void main(String[] args) {   //直到抛到main方法里,不要再main方法里写throws,main方法会打印堆栈信息,但最好在main方法里处理异常         ManangeException m=new ManangeException();         try {            m.f2();        } catch (IOException e) {            System.out.println("没有找到该文件");        }    }    public void f() throws FileNotFoundException,IOException{   //不处理只抛出        FileInputStream in = null;         in = new FileInputStream("myfile.txt");        int b;        b = in.read();        while (b != -1) {            System.out.print((char) b);            b = in.read();        }    }    public void f2() throws IOException{  //一级一级往外抛        f();    }    ============================================================    public void m(int i) throws ArithmeticException{        if(i==0) throw new ArithmeticException("被除数为0");   //手动抛出异常        }    }

main调用了f2()——f2()调用了f()——f()抛出了异常
这里写图片描述

自定义异常

这里写图片描述

class MyException extends Exception {    private int id;    public MyException(String message, int id) {        super(message);        this.id = id;    }    public int getId() {        return id;    }}public class TestMyException {     public void regist(int num) throws MyException{         if(num<0) throw new MyException("不合法的人数", 1);    //这要是异常抛出,则不会打印下边的那句话         System.out.println(num);     }     public void m(int i){         try {            regist(i);               //方法体里边不能再写方法,但是可以直接调用就好        } catch (MyException e) {            System.out.println("出现故障");        }     }     public static void main(String[] args) {        TestMyException t=new TestMyException();        t.m(1);    }}

这里写图片描述

常见异常类

图片来自http://www.cnblogs.com/Qian123/p/5715402.html#_label2

这里写图片描述