(java)异常

1.异常

(1)异常的介绍

异常,是对程序在运行过程中,遇到的种种的不正常的情况的描述。异常,在Java中,使用 Exception类来描述。如果程序遇到了未经处理的异常,将会导致程序无法编译或无法继续运行。

(2)异常的继承

在Java中,使用类 Throwable 来描述所有的不正常的情况。 Throwable有两个子类,Error 和 Exception 。其中 Error 用来描述发生在JVM级别的错误信息,这些错误,无法被处理。 Exception 用来描述程序在编译或者运行的过程中遇到的异常信息,这些异常一旦处理了,对程序的编译和运行是没有影响的。

(3)异常的分类

1.编译时异常

普通的异常发生,将会导致程序无法进行正常的编译。这种异常,称为None-Runtime Exception,非运行时异常,或者称为编译时异常,也有人称之为检查性异常,原因在于该种异常,都是javac编译器能直接检查到的异常

2.运行时异常

而在Exception的子类RuntimeException中,对异常进行了自动的处理,使其可以正常的编译 运行。但是一旦出现未经处理的异常,依然会导致程序无法继续向下执行。这样的异常称为-- Runtime Exception运行时异常。 这类异常,通常编译器是不做任何检查的,所以也叫非检查性异常。

2.异常的处理

程序员可以处理异常。如果不处理,即JVM帮忙处理,简单粗暴,就是终止程序的运行,这样用户体验度非常不好。

(1)异常处理的语法

        try{

                 //可能出现异常的代码块

                // 一旦try中的代码出现了异常,则从出现异常的位置开始,到try的大括号的扩回,这一段代码就不执行了。

                //jvm在运行程序时,如果出现了异常,会自动创建一个具体的异常对象

              }catch(需要捕获的异常的类型  异常类型变量){  //用于捕获异常操作

                //jvm创建的异常对象如果与catch捕获的异常对象类型匹配(或者向上造型没问题),就会就将地址值赋值给异常类型变量。(类型匹配)

                 //发生了异常并捕获到异常时,要执行的模块(如果没有捕获到,则不执行)

                // 一旦一个异常被捕获了,那么这个异常将不再影响程序的编译和执行

                //catch里要写处理语句

             }

案例:

public class Exception01 {
    public static void main(String[] args) {
        /*
        * 1. 在执行try里的代码片段时,如果出现了异常信息,JVM会主动帮助创建异常对象
        * 2. catch就会将对象的地址捕获到,赋值给小括号里的变量(类型匹配)
        * 3. 当catch捕获到异常对象的地址后,就会执行后面的{}里的逻辑代码。 没有捕获到就不执行{}。
        * 4. 当执行完catch的{}后,会继续向后执行后续的代码。
        * */
        try{
            int[]  nums = {1,2,3,4,5};
            for (int i = 0; i <= nums.length; i++) {
                int num = nums[i];
                System.out.println("num = " + num);
            }
        }catch (Exception e){
            //System.out.println("发生了异常");
            e.printStackTrace();
        }
        System.out.println("main方法结束");
    }
}

(2)多种异常的处理

当代码片段中可能出现多种不同类型的异常时,我们每一个异常都想要处理的情况下,使用多catch。

语法:

try {
   // 可能出现异常的代码
}catch (异常类型1 变量) {
   // 针对异常类型1的处理
}catch (异常类型2 变量) {
   // 针对异常类型2的处理
}catch (异常类型3 变量) {
   // 针对异常类型3的处理
}

1.多个异常类型没有继承关系,书写顺序无所谓

    比如:ArrayIndexOutOfBoundsException和NullPointerException

2.一旦有继承关系,必须先写子类型。

                 NullPointerException和Exception ,必须先写NullPointerException

public class Exception02 {
    public static void main(String[] args) {
        /**
         * 多catch的情况:
         *    1.多个异常类型没有继承关系,书写顺序无所谓
         *    ArrayIndexOutOfBoundsException和NullPointerException
         *    2.一旦有继承关系,必须先写子类型。
         *    NullPointerException和Exception
         *
         */

        try {
            int[] nums = new int[5];
            nums[5] = 100;
            String str = null;
            int length = str.length();
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("数组下标越界");
        }catch (NullPointerException e){
            System.out.println("空指针异常");
        }catch (Exception e){
            System.out.println("异常");
        }
       
    }
}

简化版1:没有继承关系的异常类型,可以写在一个catch中,使用|分开   前提条件是:处理逻辑一样(处理方式相同)(处理的代码逻辑都一样)

try {
            int[] nums = new int[5];
            nums[5] = 100;
            String str = null;
            int length = str.length();
        }catch (ArrayIndexOutOfBoundsException|NullPointerException e){
            e.printStackTrace();
        }

简化版2:使用这些异常的共同父类型即可 ,前提条件:处理逻辑一样(处理方式相同)

try {
            int[] nums = new int[5];
            nums[5] = 100;
            String str = null;
            int length = str.length();
        }catch (Exception e){
            e.printStackTrace();
        }

(3)finally模块

位置:位于try或者catch模块后。

特点:finally中的语句始终会执行。(无论try中的代码是否出现了异常,这里的代码都会执行)

 

public static void main(String[] args)  {
        String[] names = null;
        try{
            names = new String[3];
            String name = names[1];
            int length = name.length();//空指针异常
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            names[1] = "zhangsan"; //代码执行了
        }
        System.out.println(Arrays.toString(names));//try-catch不影响后续代码的执行
        System.out.println("--main方法结束--");
    }

使用场景:会在finally中做资源释放、流的关闭等操作。

 public static void main(String[] args)  {
       

        // finally的应用场景: 一般用于流的关闭操作。
        InputStream is = null; 
        try{
            is = Exception03.class.getClassLoader().getResourceAsStream("");
            BufferedImage image = ImageIO.read(is);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            try {
                is.close();
            } catch (IOException e) {
                throw new RuntimeException(e);
            }
        }
    }

(4)研究一下finally和return的特点

1.当try和finally里都没有return语句,return语句再外面时的执行情况,并且没有异常时

public static int test1(){
        int i = 1;
        try {
            i++;
            System.out.println("try block, i = "+i);//2
        } catch (Exception e) {
            i--;
            System.out.println("catch block i = "+i);//没有异常,该模块不执行
        } finally {
            i = 10;
            System.out.println("finally block i = "+i);//10
        }
        return i; //返回finally模块里的值
    }

2. 当try和finally里都没有return语句,return语句再外面时的执行情况,并且抛出异常时

public static int test2(){
        int i = 1;
        try {
            i++;
            System.out.println("try block, i = " + i);//2
            throw new Exception();//抛出一个异常

        } catch (Exception e) {
            i--;
            System.out.println("catch block i = " + i);//1
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);//10
        }
        return i;//10
    }

 3.当try和catch里return语句,finally里没有时,并且没有异常。

先执行finally模块代码,然后再执行try里的return关键字

public static int test3(){
        //try 语句块中有 return 语句时的整体执行顺序
        int i = 1;
        try{
            i++;
            System.out.println("try block, i = " + i);
            return i;
        } catch (Exception e) {
            i ++;
            System.out.println("catch block i = " + i);
            return i;
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);
        }
    }

 返回的是try里面的值。

4. 当try和catch里return语句,finally里没有时,并且没有异常。

public static int test3(){
        //try 语句块中有 return 语句时的整体执行顺序
        int i = 1;
        try{
            i++;
            String str = null;
            System.out.println(str.length());//空指针异常
            System.out.println("try block, i = " + i);//不执行
            return i;  //不执行
        } catch (Exception e) {
            i ++;
            System.out.println("catch block i = " + i);//3
            e.printStackTrace();
            return i;//3
        } finally {
            i = 10;
            System.out.println("finally block i = " + i);//10
        }
    }

返回的是catch里的return值。

5.当try和catch里、finally里都有return语句时,并且没有异常。

public static int test4(){
        //finally 语句块中有 return 语句
        int i = 1;
        try {
            i++;
            System.out.println("try block, i = " + i);//2
            return i;
        } catch (Exception e) { //没有异常不执行
            i++;
            System.out.println("catch block i = " + i);
            return i;
        } finally {
            i++;
            System.out.println("finally block i = " + i);//3
            return i;//3
        }
    }

 返回的是finally模块里return值。

6.当try和catch里、finally里都有return语句时,并且出现异常。

public static int test4(){
        //finally 语句块中有 return 语句
        int i = 1;
        try {
            i++;
            String str = null;
            System.out.println(str.length());//空指针异常
            System.out.println("try block, i = " + i);//不执行
            return i;//不执行
        } catch (Exception e) { 
            i++;
            System.out.println("catch block i = " + i);//3
            return i;
        } finally {
            i++;
            System.out.println("finally block i = " + i);//4
            return i;//4
        }
    }

 返回的是finally里的值

 try和finally里都有return, 一定执行的finally里的return.

3.自定义异常

1)为什么要自定义异常

 如果系统给我们提供的异常类型,已经不能满足我们的需求了,或者不知道用哪个了。此时就需要进行异常的自定义。

2)如何自定义异常

自定义我们自己的异常类型。

  • 继承自Exception类或者RuntimeException类或者异常子类。

  • 继承自Exception类的异常是 编译时异常。

  • 继承自RuntimeException类的异常是运行时异常。

格式如下:

Class 异常名 extends Exception{ //或继承RuntimeException
   public 异常名(){
   }
   public 异常名(String s){ 
      super(s); 
   }
}

案例:自定义一个异常,限制年龄的输入。

public class Exception05 {
    public static void main(String[] args) {
        try {
            Person p =  new Person("小明",130);
        }catch (AgeIllegalException e){
            e.printStackTrace();
        }
    }
}
class Person{
    private String name;
    private int age;
    public Person(String name, int age) throws AgeIllegalException{
        this.name = name;
        if (age<1 || age>120){
            throw new AgeIllegalException("年龄不合理,不应该<1或者大于120");
        }
        this.age = age;
    }
}

class AgeIllegalException extends Exception {
    public AgeIllegalException() {
        super();
    }
    public AgeIllegalException(String message) {
        super(message);
    }
}

throw和throws的特点:

1. throw是用在方法里,用于将一个异常对象抛出,自己不处理,抛给调用者,谁调用这个方法,谁就是调用者。(一个异常对象实例化完成后,没有任何意义,只有当把这个异常抛出之后,这个异常才会生效,具有阻止程序的编译或者运行的意义。)

 public Person(String name, int age) throws AgeIllegalException{
        this.name = name;
        if (age<1 || age>120){
            throw new AgeIllegalException("年龄不合理,不应该<1或者大于120");
        }
        this.age = age;
    }

2. throws是用在方法的定义上。表示告诉调用者需要处理的异常类型。

3. throw的如果是编译时异常,必须throws(必须告诉调用者)

         throw的如果是runtimeException, 就没有必要throws了。 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值