异常机制学习

异常机制

类型有两种:

  1. 非检测性异常,编译ok,运行时异常。如算术异常等,下面的代码不会执行
  2. 检测性异常。是编译错误,不处理就无法到达运行阶段
    在这里插入图片描述

异常捕获

//打开c盘a.txt文件
        FileInputStream fis = null;
        try {
    
    
            fis = new FileInputStream("d:/b.txt");
            System.out.println("1");
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        }
        //关闭文件
        try {
    
    
            fis.close();
            System.out.println("2");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }catch (NullPointerException e){
    
    
            e.printStackTrace();
        }
        System.out.println("1111");

我们是没有b文件的
这里的第二个catch就是手动处理了异常,如果没有第二个catch的内容,异常没有被处理,则1111不会被输出。
只有一个catch的结果:
在这里插入图片描述

加入了catch (NullPointerException e)这行代码
catch了NullPointerException(即手动处理了异常)结果是
在这里插入图片描述
使用了catch语句以后成功执行了输出,也输出了异常

这里改进一下

用多态的离线,IOException和NullPointerException的父类的就是Exception,所以这里改成

public static void main(String[] args) {
    
    
        //打开c盘a.txt文件
        FileInputStream fis = null;
        try {
    
    
            fis = new FileInputStream("d:/b.txt");
            System.out.println("1");
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        }
        //关闭文件
        try {
    
    
            fis.close();
            System.out.println("2");
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("1111");
    }

结果也是一样的
在这里插入图片描述
但是在实际开发中还是推荐上面那种写法,提高代码可读性
另外可以把Exception放后面,但不可以在前面,如:

public static void main(String[] args) {
    
    
        //打开c盘a.txt文件
        FileInputStream fis = null;
        try {
    
    
            fis = new FileInputStream("d:/b.txt");
            System.out.println("1");
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        }
        //关闭文件
        try {
    
    
            fis.close();
            System.out.println("2");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }catch (NullPointerException e){
    
    
            e.printStackTrace();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }
        System.out.println("1111");
    }

是可行的

finally

try{
    
    
...
}catch{
    
    
}
...
finally{
    
    
编写无论是否发生异常都要执行的代码
}

特别注意一下这里

public static void main(String[] args) {
    
    
        //打开c盘a.txt文件
        FileInputStream fis = null;
        try {
    
    
            fis = new FileInputStream("d:/b.txt");
            System.out.println("1");
        } catch (FileNotFoundException e) {
    
    
            e.printStackTrace();
        }
        //关闭文件
        try {
    
    
            fis.close();
            System.out.println("2");
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }catch (NullPointerException e) {
    
    
            e.printStackTrace();
            String str = null;
            str.length();//会发生空指针异常
        } finally {
    
    
            System.out.println("2222");
        }
        System.out.println("1111");
    }

结果finally中语句2222被输出,而因为空指针异常没有被处理,则1111不会输出
在这里插入图片描述
所以finally执行一些关闭资源的操作

finally的笔试考点

public class ExceptationCatch {
    
    
    public static int test(){
    
    
        try{
    
    
            int[] a = new int[5];
            System.out.println(a[5]);
            return 0;
        } catch (ArrayIndexOutOfBoundsException e){
    
    
            e.printStackTrace();
            return 1;
        } finally {
    
    
            return 2;
        }
    }
    public static void main(String[] args) {
    
    
        int s1 = test();
        System.out.println("返回值是:"+s1);
    }
}

结果是
在这里插入图片描述
finally中的return会提前结束方法并返回数据
在e.printStackTrace();后就到了finally中了,不会由catch中的return结束

异常抛出

在某些情况下有些异常不能处理或不便于处理,就可以将该异常转移给方法的调用者。上面的捕获是把异常就地处理了,而这里throw只是转移,并没有处理异常。
当方法执行出现异常时,底层生成一个异常类抛出,异常代码的后续代码不再执行
且不建议在main方法中抛出异常,会直接把异常抛给java虚拟机,加重负担

public class ExceptionThrowTest {
    
    
    public static void show() throws IOException {
    
    
        FileInputStream fis = new FileInputStream("d:/b.txt");
        System.out.println("查看是否继续向下执行");
        fis.close();
    }

    public static void main(String[] args) {
    
    
        try {
    
    
            show();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

结果是
在这里插入图片描述

补充

  • 子类重写的方法可以抛出和父类一样的异常
  • 子类重写的方法可以抛出和更小的异常
  • 子类重写也可以不抛出异常
  • 子类的重写不可以抛出和父类平级不一样的异常
  • 子类的重写不可以抛出更大的异常

捕获与抛出的选择

  1. 若父类中被重写的方法没有抛出异常,则子类中重写的方法只能进行捕获处理
  2. 若一个方法内以递进方式调用了好几个其他方法(A调B,B调C,C调D),则建议这些方法内部可以使用抛出的方法,一直抛出到最外面那层再使用捕获处理

第2点看以下代码所示

public class ExceptionThrowTest {
    
    
    public static void show() throws IOException {
    
    
        FileInputStream fis = new FileInputStream("d:/b.txt");
        System.out.println("查看是否继续向下执行");
        fis.close();
    }
    public static void test1() throws IOException {
    
    
        show();
    }

    public static void test2() throws IOException {
    
    
        test1();
    }
    public static void test3() throws IOException {
    
    
        test2();
    }

    public static void main(String[] args) {
    
    
        try {
    
    
            test3();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }
    }
}

自定义异常

Java可以自定义异常,而自定义异常都应该继承Exception基类
定义异常类时通常需要提供两个构造器:一个是无参数的构造器;另一个是带个字符串参数的构造器,这个字符串将作为该异常对象的描述信息(也就是异常对象的getMessage()方法的返回值

这里自定义一个年龄异常

public class AgeException extends Exception{
    
    
static final long serialVersionUID = -3387516993124229948L;//序列化版本号 与序列化操作有关系
    // 1、提供一个无参构造器
    public AgeException() {
    
    
    }

    // 2、带一个字符串参数的构造器
    public AgeException(String msg) {
    
    
        super(msg);
    }
}

那么在父类Exception中也能使用AgeException中的内容了
上面代码中的做法是把msg内容传到父类中去,这样就新建了Exception的一个子类

自定义异常类的使用

对于上面自己定义的AgeException类

public class AgeException extends Exception {
    
    
    static final long serialVersionUID = -3387516993124229948L;//序列化版本号 与序列化操作有关系
    // 1、提供一个无参构造器
    public AgeException() {
    
    
    }

    // 2、带一个字符串参数的构造器
    public AgeException(String msg) {
    
    
        super(msg);

    }
}

我们构造一个person类

public class Person {
    
    
    private String name;
    private int age;

    public Person() {
    
    
    }

    public Person(String name, int age) throws AgeException {
    
    
        setName(name);
        setAge(age);
    }

    public String getName() {
    
    
        return name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void setAge(int age) throws AgeException {
    
    
        if(age>0 && age <150) {
    
    
            this.age = age;
        }else{
    
    
            //System.out.println("输入的年龄不合理!");
            throw new AgeException("输入的年龄不合理!");
        }
    }

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Person类的测试

public class PersonTest {
    
    
    public static void main(String[] args) {
    
    
        Person s1 = null;
        try {
    
    
            s1 = new Person("XIAOMING",-30);
        } catch (AgeException e) {
    
    
            e.printStackTrace();
        }
        System.out.println("s1="+s1);
    }

}

这时候就看到我们定义的AgeException的运用:
在这里插入图片描述

或者上面的方式也可以这样做

AgeException是不变的
Person类改为:

public class Person {
    
    
    private String name;
    private int age;

    public Person() {
    
    
    }

    public Person(String name, int age) /*throws AgeException*/ {
    
    
        setName(name);
        setAge(age);
    }

    public String getName() {
    
    
        return name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public void setAge(int age) /*throws AgeException*/{
    
    
        if(age>0 && age <150) {
    
    
            this.age = age;
        }else{
    
    
            //System.out.println("输入的年龄不合理!");
            try {
    
    
                throw new AgeException("输入的年龄不合理!");
            } catch (AgeException e) {
    
    
                e.printStackTrace();
            }
        }
    }

    @Override
    public String toString() {
    
    
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

PersonTest类改为

public class PersonTest {
    
    
    public static void main(String[] args) {
    
    
        /*Person s1 = null;
        try {
            s1 = new Person("XIAOMING",-30);
        } catch (AgeException e) {
            e.printStackTrace();
        }
        System.out.println("s1="+s1);*/
        Person s1 = new Person("XIAOMING",-30);
        System.out.println("s1="+s1);
    }

}

不同之处:这样的结果对象是可以new出来的(之前是null),但age的值是0
在这里插入图片描述
建议是用前一种方法,因为这样new出来的对象并没有什么意义

おすすめ

転載: blog.csdn.net/Maybe_do_it/article/details/118872796