exception异常

这篇博客详细介绍了Java中的异常处理机制,包括异常的简介、UML图、编译时异常和运行时异常的区别、异常处理的两种方式(throws和try...catch)、异常对象的常用方法、try...finally语句的使用,以及自定义异常和异常在方法覆盖中的规则。通过实例代码和面试题,帮助读者深入理解Java中的异常处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Exception

1.简介

  1. 什么是异常,java提供异常机制有什么用?
    当程序执行过程中发生了不正常的情况,而这种不正常的情况叫做:异常。

    java语言是很完善的,提供了异常处理机制,当程序执行过程中出现了不正常的情况,java把异常信息打印输出到控制台,供程序员参考,程序员看到异常信息后,可以对程序进行修改,让程序看起来更加的健壮。

  2. 如:

    public class ExceptionTest01 {
        public static void main(String[] args) {
            int a=10;
            int b=0;
            //实际上JVM在执行到此处的时候,会new一个异常对象:new ArithmeticException("/by zero")
            //并且JVM将new的异常对象抛出,打印输出到控制台。
            int c=a/b;
            System.out.println(c);
        }
    }
    

    以上程序出现了:

    Exception in thread “main” java.lang.ArithmeticException: / by zero
    at com.ma.javase.exception.ExceptionTest01.main(ExceptionTest01.java:9)

    这个信息我们称为:异常信息。这个信息是JVM打印出来的。

  3. java语言中异常是以什么形式存在的?

    异常在java中是以类的形式存在,每一个异常类都可以创建异常对象。

    如:

    public class ExceptionTest02 {
        public static void main(String[] args) {
            //通过异常类实例化异常对象
            NumberFormatException nfe=new NumberFormatException("数字格式化异常");
            System.out.println(nfe);//java.lang.NumberFormatException: 数字格式化异常
    
            //再通过异常类创建对象
            NullPointerException npe=new NullPointerException("空指针异常");
            System.out.println(npe);//java.lang.NullPointerException: 空指针异常
        }
    }
    
    

2. UML

  1. java 的异常处理机制:
  • 异常在java中以类和对象的形式存在。那么异常的继承结构是怎么样的?

  • 我们可以使用UML图来描述一下继承结构。

  • 画UML图有很多工具,例如:Rational Rose(收费的),startURL等…

  1. 什么是UML?有什么用?

    • UML是一种统一建模语言。

    • UML不是只有java中使用。只要是面向对象的编程语言,都有UML。

    • 一般画UML图的都是软件架构师或者是系统分析师。这些级别的人员使用的。

    • 在UML图中可以描述类和类之间的关系,程序执行的流程,对象状态等。

3.编译时异常和运行时异常

  1. 编译时异常:

    所有Exception的直接子类,都叫做编译时异常。编译时异常是表示必须在编写程序的时候预先对这种异常进行处理,如果不处理编译器会报错。编译时异常又称为受检异常。

  2. 运行时异常:

    运行时异常在编写程序阶段,你可以选择处理,也可以不处理。

4.异常处理的两种方式

  1. java语言中对异常的处理包括两种方式:

    第一种方式:在方法声明的位置上,使用thows关键字 抛给上一级。

    第二种方式:使用try…catch语句进行异常的捕捉。

  2. 两种方式如何选择:
    如果希望调用者来处理,选择throws上报。其他情况选择捕捉的方式。

    如:

    public class ExceptionTest04 {
        //第一种处理方式:在方法声明位置上继续使用throws上抛
       // public static void main(String[] args) throws ClassNotFoundException {
            //main方法中调用doSome();
            //因为doSome()方法声明位置上有:throws ClassNotFoundException
            //我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
            //如果不处理,编译器就报错
            //编译器报错:
         //   doSome();
        //}
        
        
        //第二种处理方式:try...catch捕获异常
          public static void main(String[] args) {
            //main方法中调用doSome();
            //因为doSome()方法声明位置上有:throws ClassNotFoundException
            //我们在调用doSome()方法的时候必须对这种异常进行预先的处理。
            //如果不处理,编译器就报错
            //编译器报错:
            try {
                doSome();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    
    
        /**
         * doSome方法在方法声明的位置上:throws ClassNotFoundException
         * 这个代码表示doSome()方法在执行过程中,有可能会出现ClassNotFoundException异常。
         * 叫类没找到异常。这个异常直接父类是:Exception 所有ClassNotFoundException属于编译时异常
         * @throws ClassCastException
         */
        public static  void doSome() throws ClassNotFoundException{
            System.out.println("doSome");
        }
    }
    
  3. 异常捕捉和上报的联合使用:

    /*
    * 处理异常的第一种方式:在方法声明的位置上使用throws关键字抛出,谁调用我这个方法,我就抛给谁。
    * 抛给调用者来处理
    * throws后面可以写多个异常:用逗号隔开
    * */
    public class ExceptionTest05 {
        //一般不建议在main方法上使用throws 因为这个异常一旦发生,JVM只有终止
        //异常处理机制的作用就是增强程序的健壮性。怎么能做到异常发生了,也不影响程序的执行。
        //所以一般main方法上的异常建议使用try..catch进行捕捉
        public static void main(String[] args) {
            System.out.println("main begin");
            try {
                //try尝试
                m1();
            } catch (FileNotFoundException e) {
                //这个分支中可以使用e引用,e引用保存的内存地址是那个new出来异常对象的内存地址
                //catch是捕捉异常后走的分支
                System.out.println("文件不存在,可能路径错误");
            }
            //try...catch把异常抓住后,这里的代码会继续执行
            System.out.println("main over");
        }
    
        private static void m1() throws FileNotFoundException {
            System.out.println("m1 begin");
            m2();
            //若出现异常,不执行
            System.out.println("m1 over");
        }
    
        private static void m2() throws FileNotFoundException {
            System.out.println("m2 begin");
            //编译器报错的原因:m3()方法声明位置上有:throws FileNotFoundException
            //我们在这里调用m3()没有对异常进行预处理,所有编译报错
            m3();
            //若出现异常不执行
            System.out.println("m2 over");
        }
    
    
    
  4. try…catch特性:

    /*
    * 深入try..catch
    *  1.catch后面的小括号中的类型可以是具体的异常类型,也可以是异常类型的父类型
    *  2.catch可以写多个,建议catch的时候,精确的一个一个处理。这样有利于程序的调试
    *  3.catch写多个的时候,从上到小,必须遵守从小到大
    *
    * */
    public class ExceptionTest06 {
        public static void main(String[] args) {
            try {
                FileInputStream fs=   new FileInputStream("C:\\Users\\user\\Desktop\\MarkDown学习\\Day01.md");
                System.out.println("以上代码出现异常,这里无法执行!");
                fs.read();
            } catch (FileNotFoundException e) {
                System.out.println("文件不存在");
            } catch (IOException e) {
                System.out.println("读文件报错");
            }
            //都会执行
            System.out.println("hello world");
        }
    }
    
  5. jdk8新特性:

    public class ExceptionTest06 {
        public static void main(String[] args) {
     //jdk8新特性:
            try {
                FileInputStream fs=   new FileInputStream("C:\\Users\\user\\Desktop\\MarkDown学习\\Day01.md");
            //进行数学运算:
                System.out.println(100/0); //运行时异常
    
            } catch (FileNotFoundException | ArithmeticException | NullPointerException e) {
                System.out.println("有异常");
            }
    
        }
    }
    

5.异常对象的常用方法

异常对象有两个非常重要的方法

  1. 获取异常简单的描述信息:

    String msg=exception.getMessage();
    
  2. 打印异常追踪的堆栈信息

     exception.printStackTrace();
    
  3. 如:

    /*
    * 异常对象有两个非常重要的方法
    * 获取异常简单的描述信息:
    * String msg=exception.getMessage();
    *
    *
    * 打印异常追踪的堆栈信息
    * exception.printStackTrace();
    * */
    public class ExceptionTest07 {
        public static void main(String[] args) {
            NullPointerException e=new NullPointerException("空指针异常");
            //获取异常简单描述的信息:这个信息实际上就是构造方法上面String参数
            String msg= e.getMessage();
            System.out.println(msg);//空指针异常
    
            //打印异常信息
            e.printStackTrace();//java.lang.NullPointerException: 空指针异常
            //at com.ma.javase.exception.ExceptionTest07.main(ExceptionTest07.java:13)
            System.out.println("hello world");
        }
    }
    
    

6.try,catch和finally

  1. 关于try,catch中的finally子句:

    1. 在finally子句中的代码是最后执行的,并且是一定会执行的,即使try语句块中的代码出现异常。
      finally子句必须和try一起出现,不能单独编写。

    2. finally语句通常使用在哪些情况下呢?

      通常在finally语句块中完成资源的释放或关闭。

      因为finally中的代码比较有保障。

      即使try语句块中的代码出现异常,finally中的代码也会执行。

  2. 代码如下:

    public class ExceptionTest09 {
        public static void main(String[] args) {
            FileInputStream fils=null;//声明位置放在try外面,这样在finally中才能使用
            try {
                //创建输入流对象
                fils=new FileInputStream("C:\\Users\\user\\Desktop\\MarkDown学习\\Day01.md");
                //这里一定会出现空指针异常
                String s="";
                s.toString();
    
                //流使用完需要关闭,因为流是占用资源的
                //即使以上程序出现异常,流也必须关闭
                //放在这里有可能关闭不了
                fils.close();
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }catch (IOException e){
                e.printStackTrace();
            }catch (NullPointerException e){
                e.printStackTrace();
            }
    
    
            finally {
                //流的关闭放在这里比较保险
                //finally中的代码是一定会执行的
                //即使try中出现异常
                if(fils!=null){     //避免空指针异常
                    try {
                        //close()方法有异常,采用捕捉的方式
                        fils.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
    
    
  3. try和finally联合使用:

    public class ExceptionTest10 {
        public static void main(String[] args) {
            //可以try和finally,没有catch
            //执行顺序:先执行try..  再执行finally..   最后执行return...(语句只要执行,方法必然结束)
    
            try{
                System.out.println("try...");
                return;
            }finally {
                System.out.println("finally...");
            }
    
             //这里不能写语句,因为这个代码是执行不到的
            //System.out.println("...");
        }
    
    
    }
    
    

面试题:

  1. public class ExceptionTest11 {
        public static void main(String[] args) {
            System.out.println(m()); //100
        }
    
        /*
        * java语法规则:
        *     1.方法体中的代码必须遵循自上而下的顺序,依次逐行执行。
        *     2.return语句一旦执行,整个方法必须结束。
        *
        *
        * */
    
        public static int m(){
            int i=100;
            try{
                //这行代码出现在int i=100下面,所以必须返回100
                return i;
            }finally {
                ++i;
            }
    
        }
    }
    
    
    
  2. final,finally,finalize的区别:

    public class ExceptionTest12 {
        public static void main(String[] args) {
            //final是一个关键字,表示最终的,不变的
            final int i=100;
            //i=122;
            
            //finally也是一个关键字,和try联合使用,使用在异常处理机制中
            //在finally语句块中的代码是一定会执行的
            try{
                
            }finally {
                System.out.println("...");
            }
            
            //finalize()是Object类中的一个方法,作为方法名出现
            //所有finalize是标识符。
            //finalize()方法是JVM的gc垃圾回收器负责调用
            
            
        }
    }
    
    
  3. throws和throw的区别:

    1. throws:在方法声明位置上使用,表示上报异常信息给调用者。
    2. throw: 手动抛出异常。

7.自定义异常

  1. SUN公司提供的JDK内置的异常肯定是不够用的。在实际开发中,有很多业务,这些业务出现异常之后 JDK中都是没有的。那么异常类我们程序员可以自己去定义。

  2. Java中怎么自定义异常呢?
    两步:
    第一步:编写一个类继承Exception(编译时异常)或者RuntimeException(运行时异常)。
    第二步:提供两个构造方法,一个无参数的构造方法,一个带有String参数的构造方法。

  3. 代码演示:

    1. 创建异常:

      /*
      * 1. SUN公司提供的JDK内置的异常肯定是不够用的。在实际开发中,有很多业务
      * 这些业务出现异常之后 JDK中都是没有的。那么异常类我们程序员可以自己去
      * 定义。
      * 2. Java中怎么自定义异常呢?
      *    两步:
      *         第一步:编写一个类继承Exception(编译时异常)或者RuntimeException(运行时异常).
      *         第二步:提供两个构造方法,一个无参数的,一个带有String参数的。
      *
      * */
      public class MyStackOperationException extends Exception{//编译时异常
          //无参构造方法
          public MyStackOperationException(){};
          //带有String参数的有参构造方法
          public MyStackOperationException(String s){
              //父类方法
              super(s);
          };
      }
      
      /*public class MyStackOperationException extends RuntimeException{//运行时异常
      }*/
      
      
    2. 使用异常:throw new MyStackOperationException(String )

      弹栈,压栈案例:

      public class MyStack {
          private Object[]ele;
          private int index;
      
      
      
          public MyStack() {
              this.ele = new Object[10];
              this.index = -1;
          }
      
          public Object[] getEle() {
              return ele;
          }
      
          public void setEle(Object[] ele) {
              this.ele = ele;
          }
      
          public int getIndex() {
              return index;
          }
      
          public void setIndex(int index) {
              this.index = index;
          }
      
          public void push(Object obj) throws MyStackOperationException {
              if (index>=ele.length-1){
                  //创建异常对象
                  // MyStackOperationException msop=new MyStackOperationException("压栈失败,栈已满");
                  //手动将异常抛出去
                  // throw msop; //这里用try..catch没有意义。栈已满这个信息需要传递出去,所以用throws
      
                  //合并
                  throw new MyStackOperationException("压栈失败,栈已满");
      
              }
              index++;
              ele[index]=obj;
              System.out.println("压栈"+obj+"压栈成功,栈帧指向"+index);
          }
      
          public void POP() throws MyStackOperationException {
              if(index<0)
              {
                  throw new MyStackOperationException("弹栈失败");
              }
      
              System.out.print("弹栈"+ele[index]+"元素成功,");
              //栈帧向下移动一位
              index--;
              System.out.println("栈帧指向"+index);
      
          }
      }
      
      
      
    3. 测试:

      public class Test {
          public static void main(String[] args) {
              MyStack ms=new MyStack();
              try {
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
                  ms.push(new Object());
      
              } catch (MyStackOperationException e) {
                  System.out.println(e.getMessage());
              }
      
              try {
                  ms.POP();
                  ms.POP();
                  ms.POP();
                  ms.POP();
                  ms.POP();
                  ms.POP();
                  ms.POP();
                  ms.POP();
                  ms.POP();
                  ms.POP();
                  ms.POP();
              } catch (MyStackOperationException e) {
                  System.out.println(e.getMessage());
              }
      
      
      
          }
      }
      
      

8.异常与方法的覆盖

  1. 重写之后的方法不能比重写之前的方法抛出更多(更宽泛)的异常,可以更少。

如:

class Animal{
    public void doSome(){

    }
    public void doOther() throws Exception{

    }
}

class Cat extends Animal{

    //编译报错
   /* public void doSome() throws Exception{

    }*/

   //编译正常
  /* public void doOther() {

   }*/

   //编译正常
/*   public void doOther() throws Exception{

   }*/

//编译正常
public void doOther() throws NullPointerException{

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值