Java基础语法(二十一)

版权声明:作者已开启版权声明,如转载请注明转载地址。 https://blog.csdn.net/qq_34829447/article/details/82151588

一.异常

1.异常的概述

  • 什么是异常?Java代码在运行时期发生的问题就是异常
  • 异常类
    • 在Java中,把异常信息封装成了一个类
    • 当出现了问题时,就会创建异常类对象并抛出异常相关的信息(如异常出现的位置、原因等)
  • 我们见过的异常
    • 数组角标越界异常ArrayIndexOutOfBoundsException
    • 空指针异常NullPointerException

2.异常的继承体系和错误区别

  • 异常的继承体系

    Throwable: 它是所有错误与异常的超类(祖宗类)
    |- Error 错误
    |- Exception 编译期异常,进行编译JAVA程序时出现的问题
           |- RuntimeException 运行期异常, JAVA程序运行过程中出现的问题
  • 异常与错误的区别

    • 异常

      • 指程序在编译、运行期间发生了某种异常(XxxException),我们可以对异常进行具体的处理

      • 若不处理异常,程序将会结束运行

      • 实例代码

      public static void main(String[] args) {
        int[] arr = new int[3];
        System.out.println(arr[0]);
        System.out.println(arr[3]);
        // 该句运行时发生了数组索引越界异常
        // ArrayIndexOutOfBoundsException,
        // 由于没有处理异常,导致程序无法继续执行,程序结束。
        System.out.println("over"); // 由于上面代码发生了异常,此句代码不会执行
      }
    • 错误

      • 指程序在运行期间发生了某种错误(XxxError),Error错误通常没有具体的处理方式,程序将会结束运行

      • Error错误的发生往往都是系统级别的问题,都是jvm所在系统发生的,并反馈给jvm的

      • 我们无法处理

      • 实例代码

      public static void main(String[] args) {
        int[] arr = new int[1024*1024*100];
        //该句运行时发生了内存溢出错误OutOfMemoryError,开辟了过大的数组空间,
        //导致JVM在分配数组空间时超出了JVM内存空间,直接发生错误。
      }

3.异常对象的产生原因和处理方式

  • 异常对象的产生原因

    • 实例代码
    //工具类
    class ArrayTools{
        //对给定的数组通过给定的角标获取元素。
        public static int getElement(int[] arr,int index)   {
            int element = arr[index];
            return element;
        }
    }
    //测试类
    class ExceptionDemo2 {
        public static void main(String[] args)  {
            int[] arr = {34,12,67};
            int num = ArrayTools.getElement(arr,4)
            System.out.println("num="+num);
            System.out.println("over");
        }
    }
    • 原因分析

      • 由于没找到4索引,导致运行时发生了异常。这个异常JVM认识:ArrayIndexOutOfBoundsException。这个异常Java本身有描述:异常的名称、异常的内容、异常的产生位置。java将这些信息直接封装到异常对象中。new ArrayIndexOutOfBoundsException(4)。
      • throw new ArrayIndexOutOfBoundsException(4);产生异常对象。JVM将产生的异常抛给调用者main()方法。
      • main()方法接收到了数组索引越界异常对象。由于main()方法并没有进行处理异常,main()方法就会继续把异常抛给调用者JVM。当JVM收到异常后,将异常对象中的名称、异常内容、位置都显示在就控制台上。同时让程序立刻终止。

这里写图片描述

  • 异常的处理方式

    • JVM的默认处理方式
    • 把异常的名称,原因,位置等信息输出在控制台,同时会结束程序。
      • 一旦有异常发生,其后来的代码不能继续执行。
    • 解决程序中异常的手动方式
      • 编写处理代码try...catch...finally
      • 抛出 throws

4.方法内部抛出对象throw关键字

​ 在java中,提供了一个throw关键字,它用来抛出一个指定的异常对象。

  • 什么时候使用throw关键字?当调用方法使用接受到的参数时,首先需要先对参数数据进行合法的判断,数据若不合法,就应该告诉调用者,传递合法的数据进来。这时需要使用抛出异常的方式来告诉调用者。

  • 使用throw关键字具体操作

    • 创建一个异常对象。封装一些提示信息(信息可以自己编写)。
    • 通过关键字throw将这个异常对象告知给调用者。throw 异常对象;
    • throw 用在方法内,用来抛出一个异常对象,将这个异常对象传递到调用者处,并结束当前方法的执行。
  • throw关键字使用格式

    • 使用格式:throw new 异常类名(参数);

    • 异常抛出举例

    throw new NullPointerException("要访问的arr数组不存在");
    throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");
  • 实例代码

    //编写工具类,提供获取数组指定索引处的元素值
    class ArrayTools{
      //通过给定的数组,返回给定的索引对应的元素值。
      public static int getElement(int[] arr,int index) {
          /*
        若程序出了异常,JVM它会打包异常对象并抛出。但是它所提供的信息不够给力。想要更清晰,需要自己抛出异常信息。
    下面判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。
        */
          if(arr==null){
              throw new NullPointerException("arr指向的数组不存在");
          }
          if(index<0 || index>=arr.length){
              throw new ArrayIndexOutOfBoundsException("错误的角标,"+index+"索引在数组中不存在");
          }
          int element = arr[index];
          return element;
      }
    }
    //测试类
    class ExceptionDemo3 {
      public static void main(String[] args) {
          int[] arr = {34,12,67}; //创建数组
          int num = ArrayTools.getElement(null,2);// 调用方法,获取数组中指定索引处元素
          //int num = ArrayTools.getElement(arr,5);// 调用方法,获取数组中指定索引处元素
          System.out.println("num="+num);//打印获取到的元素值
      }
    }

5.方法声明异常关键字throws

  • 声明:将问题标识出来,报告给调用者。如果方法内通过throw抛出了编译时异常,而没有捕获处理(稍后讲解该方式),那么必须通过throws进行声明,让调用者去处理。

  • 声明异常格式:修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2… { }

  • 注意事项: throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。

  • 实例代码:多个异常的处理

    class Demo{
      /*
    如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明。
    */
      public void show(int x)throws Exception   {
          if(x>0){
              throw new Exception();
          } else {
              System.out.println("show run");
          }
      }
    }
    //throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。
    //多个异常的情况,例如:
    public static int getElement(int[] arr,int index) throws NullPointerException, ArrayIndexOutOfBoundsException {
      if(arr==null){
          throw new NullPointerException("arr指向的数组不存在");
      }
      if(index<0 || index>=arr.length){
          throw new ArrayIndexOutOfBoundsException("错误的角标,"+index+"索引在数组中不存在");
      }
      int element = arr[index];
      return element;
    }

6.try…catch异常处理

  • 捕获:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理

  • 捕获异常格式

    try {
      //需要被检测的语句。
    }
    catch(异常类 变量) { //参数。
      //异常的处理语句。
    }
    finally {
      //一定会被执行的语句。
    }
  • 格式说明

    • try:该代码块中编写可能产生异常的代码。
    • catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。
    • finally:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。try…catch…处理掉异常后,程序可以继续执行。
  • 实例代码

    //捕获异常格式
    class ExceptionDemo{
      public static void main(String[] args){ //throws ArrayIndexOutOfBoundsException
          try   {
              int[] arr = new int[3];
              System.out.println( arr[5] );// 会抛出ArrayIndexOutOfBoundsException
              //当产生异常时,必须有处理方式。要么捕获,要么声明。
          }
          catch (ArrayIndexOutOfBoundsException e) { //括号中需要定义什么呢?try中抛出的是什么异常,在括号中就定义什么异常类型。 
              System.out.println("异常发生了");
          } finally {
              arr = null; //把数组指向null,通过垃圾回收器,进行内存垃圾的清除
          }
          System.out.println("程序运行结果");
      }
    }

    这里写图片描述

7.多catch处理

  • 一个try 多个catch组合:对代码进行异常检测,并对检测的异常传递给catch处理。对每种异常信息进行不同的捕获处理。

  • 多catch处理的格式

    void show(){ //不用throws 
      try{
          throw new Exception();//产生异常,直接捕获处理
      }catch(XxxException e){
          //处理方式    
      }catch(YyyException e){
          //处理方式    
      }catch(ZzzException e){
          //处理方式    
      }
    }
    //注意事项:在捕获异常处理中,变量也是有作用域的,如可以定义多个catch中异常变量名为e。

    这里写图片描述

8.多catch处理细节

  • 细节:多个catch小括号中,写的是异常类的类名,有没有顺序的概念?有顺序关系。
  • 平级异常:抛出的异常类之间,没有继承关系,没有顺序
    • NullPointerException extends RuntimeException
    • NoSuchElementException extends RuntimeException
    • ArrayIndexOutOfBoundsException extends IndexOutOfBoundsException extends RuntimeException
  • 上下级关系的异常:越高级的父类,越写在下面

9.finally代码块

  • finally的特点:被finally控制的语句体一定会执行
  • finally的作用:无论程序是否有异常出现,程序必须执行释放资源

10.调用抛出异常方法try和throws处理方式

  • 在实际开发中使用哪种异常处理方式呢?能自己处理的尽量自己处理。(建议用try…catch)

11.运行时期异常的特点

  • 运行时期异常的概述:RuntimeException和他的所有子类异常,都属于运行时期异常。NullPointerException,ArrayIndexOutOfBoundsException等都属于运行时期异常。
  • 运行时期异常的特点
    • 方法中抛出运行时期异常,方法定义中无需throws声明,调用者也无需处理此异常。
    • 运行时期异常一旦发生,需要程序人员修改源代码。
    • 设计原因:运行异常一旦发生,一定是源代码的错误,会影响到后面的代码,故需要修改源代码

12.运行异常的案例

  • 计算圆的面积

    • 定义方法计算圆形的面积
    • 但是如果违反了真实情况,参数小于等于0时需要停止程序不继续计算
  • 实例代码

    public class RuntimeExceptionDemo {
      public static void main(String[] args) {
          double d = getArea(1);
          System.out.println(d);
      }
      /*
         *  定义方法,计算圆形的面积
         *  传递参数0,或者负数,计算的时候没有问题
         *  但是,违反了真实情况
         *  参数小于=0, 停止程序,不要在计算了
         */
      public static double getArea(double r){
          if(r <= 0)
              throw new RuntimeException("圆形不存在");
          return r*r*Math.PI;
      }
    }

13.方法重写时异常的处理

  • 方法重写时候异常的处理

    • 子类覆盖父类方法时,如果父类的方法声明异常,子类只能声明父类异常或者该异常的子类或不声明。
    class Fu {
        public void method () throws RuntimeException {}
    }
    class Zi extends Fu {
        public void method() throws RuntimeException { }  //抛出父类一样的异常
        //public void method() throws NullPointerException{ } //抛出父类子异常
    }
    • 当父类方法声明多个异常时,子类覆盖时只能声明多个异常的全部或子集
    class Fu {
        public void method () throws NullPointerException, ClassCastException{}
    }
    class Zi extends Fu {
        public void method()throws NullPointerException, ClassCastException { }         
        public void method() throws NullPointerException{ } //抛出父类异常中的一部分
        public void method() throws ClassCastException { } //抛出父类异常中的一部分
    }
    • 当被覆盖的方法没有异常声明时,子类覆盖时无法声明异常的。
    class Fu {
        public void method (){
        }
    }
    class Zi extends Fu {
        public void method() throws Exception { }//错误的方式
    }
  • 父类中会存在下列这种情况,接口也有这种情况。接口中没有声明异常,而实现的子类覆盖方法时发生了异常,怎么办?

    无法进行throws声明,只能catch的捕获。万一问题处理不了呢?catch中继续throw抛出,但是只能将异常转换成RuntimeException子类抛出。

    interface Inter {
      public abstract void method();
    }
    class Zi implements Inter {//无法声明 throws Exception
      public void method(){ 
          int[] arr = null;
          if (arr == null) {
              //只能捕获处理
              try{
                  throw new Exception(“哥们,你定义的数组arr是空的!”);
              } catch(Exception e){
                  System.out.println(“父方法中没有异常抛出,子类中不能抛出Exception异常”);
                  //我们把异常对象e,采用RuntimeException异常方式抛出
                  throw new RuntimeException(e);
              }
          }
      }
    }

14.Throwable方法

  • 常见方法

    • getMessage()方法,返回该异常的详细信息字符串,即异常提示信息
    • toString()方法,返回该异常的名称与详细信息字符串
    • printStackTrace()方法,在控制台输出该异常的名称与详细信息字符串、异常出现的代码位置
  • 实例代码

    try {
      Object p= null;
      if (p==null) {
          throw new NullPointerException("出现空指针异常了,请检查对象是否为null");
      }
    } catch (NullPointerException e) {
      String message = e.getMessage();
      System.out.println(message );//出现空指针异常了,请检查对象是否为null
      String result = e.toString();
      System.out.println(result);   //java.lang.NullPointerException: 出现空指针异常了,请检查对象是否为null
      e.printStackTrace(); //java.lang.NullPointerException: 出现空指针异常了,请检查对象是否为null,at TestException.main(TestException.java:8)
    }

15.自定义异常

  • 自定义异常的定义

    • 每个异常中都调用了父类的构造方法,把异常描述信息传递给了父类,让父类帮我们进行异常信息的封装。

    • 定义格式

    Class 异常名 extends Exception{ //或继承RuntimeException
        public 异常名(){
        }
        public 异常名(String s){ 
            super(s); // 如果自定义异常需要异常信息,可以通过调用父类的带有字符串参数的构造函数即可。
        }
    }   
  • 自定义异常的练习

    • 练习内容:在Person类的有参数构造方法中,进行年龄范围的判断,若年龄为负数或大于200岁,则抛出NoAgeException异常,异常提示信息“年龄数值非法”。 要求:在测试类中,调用有参数构造方法,完成Person对象创建,并进行异常的处理。

    • 实例代码

    //异常类
    class NoAgeException extends Exception{
        NoAgeException() {
            super();
        }
        NoAgeException(String message)  {
            super(message);
        }
    }
    //Person类
    class Person{
        private String name;
        private int age;
        Person(String name,int age) throws NoAgeException   {
            //加入逻辑判断。
            if(age<0 || age>200)        {
                throw new NoAgeException(age+",年龄数值非法");
            }
            this.name = name;
            this.age = age;
        }
        //定义Person对象对应的字符串表现形式。覆盖Object中的toString方法。
        public String toString()    {
            return "Person[name="+name+",age="+age+"]";
        }
    }
    //测试类
    class ExceptionDemo{
        public static void main(String[] args) {
            try {
                Person p = new Person("xiaoming",20);
                System.out.println(p);
            }
            catch (NoAgeException ex){
                System.out.println("年龄异常啦");
            }
            System.out.println("over");
        }
    }
  • 关于构造函数方法抛出异常类总结:构造函数到底抛出这个NoAgeException是继承Exception呢?还是继承RuntimeException呢?

    • 继承Exception,必须要throws声明,一声明就告知调用者进行捕获,一旦问题处理了调用者的程序会继续执行。
    • 继承RuntimeExcpetion,不需要throws声明的,这时调用是不需要编写捕获代码的,因为调用根本就不知道有问题。一旦发生NoAgeException,调用者程序会停掉,并有jvm将信息显示到屏幕,让调用者看到问题,修正代码。

猜你喜欢

转载自blog.csdn.net/qq_34829447/article/details/82151588
今日推荐