JAVA学习第十天之异常处理

学习目的

  1. 了解异常与异常机制的概念
  2. 了解异常的分类
  3. 掌握异常的处理方法(try-catch、throws)
  4. 了解自定义异常
  5. 了解方法覆盖(方法重写)与异常的关系

一、异常

  1. 概念
    异常是java 提供一个体系结构,当程序代码中出现问题时,它会把错误以及错误的详细信息告诉开发者,使得开发的程序能够更健壮。而开发者又可以把错误信息进行处理后告诉用户。
  2. 实质
    java异常都是一个类或者一个对象它从出错地点被抛出,继承于Object类。在异常类发生时,jvm会创建出异常对象,并携带一些提示错误的信息给开发者,开发者可以通过异常类把信息从中取出来。
  3. 异常出现
  • 语法结构/格式异常:编写的代码不按照指定规则(大小写、标识符名称、数值类型、变量类型等),不满足语法要求时出现异常;
  • 调用本地类方法异常:在调用本地类的一些方法时,被调用的本地方法本身就自带异常抛出,并不是只有编译错误才会出现异常。而是开发java语言的开发人员希望你可以解决预想到的异常。
public class ExceptionTest {
    public static void main(String[] args) throws Exception {
            input();
    }
    public static void input() throws Exception{
        new FileInputStream("文件路径");
    }
}
//FileInputStream源码的构造方法,自带抛出FileNotFoundException异常
public FileInputStream(String name) throws FileNotFoundException {
        this(name != null ? new File(name) : null);
    }

二、异常分类

  1. 分类
    异常主要分为错误error(不可处理)、一般性异常(受控异常)、运行期异常(非受控异常)等。
  2. 重点
  • 所有的错误Error 和 异常Exception都实现 Throwable接口;
  • 所有异常都是可抛出的
  1. 异常的继承结构体系


    异常的层次结构.png

2.1 错误Error

  1. 概念
    Error 是 Throwable的子类,用于指示合理的应用程序不应该试图捕获的严重问题。如果应用程序出现了错误 Error,将导致程序无法恢复,只能重新启动应用程序。
  2. Error异常
  • 最典型的Error异常:OutOfMemoryError<内存溢出>;

2.2 受控异常<编译期异常,Exception类>

  1. 概念
    受控异常指的是可以控制的异常,使用Exception表示,又称为一般性异常。Exception 类及其子类是 Throwable的一种形式,它指出了合理的应用程序想要捕获的条件。
  2. 出现时期
    受控异常 通常出现在编译期,需要开发者必须显示的处理,不显示处理该程序将无法编译通过。

2.3 非受控异常<运行期异常,RuntimeException类>

  1. 概念
    非受控异常指的是 无法控制的异常,通常出现在运行期,不受开发者控制。RuntimeException 是那些可能在 Java虚拟机正常运行期间(JVM内部)抛出的异常的超类。
  2. 出现时期
    非受控异常出现在编译通过后,执行代码运行期间。非受控异常发生在在运行期间,不能由程序员处理。

三.异常声明

3.1 受控异常声明

  1. 概念
    受控异常指的是可以受程序员控制的异常,一般在方法及方法体中进行声明的异常,称为受控异常。
  2. 要求
    必须对声明的每一个异常进行调用处理,尽可能使用 try...catch每一个声明的异常,避免编译出错。

3.2 非受控异常声明

  1. 概念
    不能由程序员自主控制,需要交由jvm虚拟机进行处理的异常称为非受控异常。若声明的异常为非受控异常,直接在方法声明处进行抛出给JVM虚拟机进行捕获处理。不需要使用 try...catch,而是在方法声明后面throws抛出。

3.3 异常的捕获顺序

  1. 定义
    异常的捕获顺序能够影响程序的编译,也可以决定程序的执行效率,对异常的捕获若是详细到每一个子异常且有序,有利于后期测试。
  2. 遵循规律
    异常的捕获顺序:从小到大<catch语句从上到下,先截获子异常,再截获父异常>。

四.异常处理(try、catch 、 finally)

  1. 概念
    异常处理指的是需要对可能发生的异常进行问题处理,保证程序的可行性和代码的健壮性。
  2. 处理方式
  • throws XXXException:异常抛出。在异常发生时,通常将该异常抛给调用者或上级处理,把"黑锅丢给别人"。该种方式可以一直不断往上抛(最终上级为jvm),直到抛给main方法也不进行处理时,jvm终止程序。
  • try...catch:异常捕获。即异常发生时,发生异常的本体立即进行异常捕获处理,以免异常往上抛。主动担当,快速处理。

4.1 try-catch异常处理

  1. 处理机制
    try-catch异常处理机制是通过在try语句块中执行可能出现异常的程序;
    在catch语句中捕获出现的具体异常对象,并使用该异常对象去处理异常;catch捕获的异常可以是出现本次的具体异常对象,也可以是具体异常的父类对象。
  2. 语法结构
try {
        //可能出现的异常java语句
}catch(OneException e1) { 
       //捕获异常1,通过异常类对象e1获取详细的异常信息
       //e1是栈区的引用,指向在堆内存中的OneException异常对象
}catch(TwoException e2) {
       //捕获异常2,通过异常类对象e2获取详细的异常信息
       //e2是栈区的引用,指向堆内存中的TwoException
}finally {
        // 最终都会执行的java语句
}
  1. 执行流程
  • try语句:try 语句中书写 可能产生异常的代码
  • catch语句:try 后面跟着catch语句,catch语句可以有一个或多个,类似else if语句;
  • catch 语句中书写 需要处理的捕获异常对象
    当 try 语句中的代码出现异常时,编写在异常语句后的java语句不会再执行,而是马上会跳转到 相应异常的catch语句块中进行异常捕获和处理。
    catch捕获的异常从小到大,并尽可能对应捕获到try中出现的每一种异常类型。
  • 如果try中无异常发生,则不会跳转到 catch中 。
  1. 注意点
  • catch捕获的异常对象e1:异常对象不是在进行异常处理的本类中new出来,而是在异常发生时,异常对象本类的底层源码中调用其构造方法 throw new出。e1只是一个"形参"从异常抛出类传到异常捕获类中。
  • 捕获父类异常:不建议使用Exception父类捕获全部异常,会不利于查找出try语句中哪个语句发生什么类型的异常,不利于调试。

4.2 try-catch-finally异常处理

  1. 处理机制
    try-catch-finaly异常处理机制是通过在try语句块中执行可能出现异常的程序;
    在catch语句中捕获出现的具体异常对象,并使用该异常对象去处理异常;
    在finally语句中,不管catch语句是否捕获并处理异常,finally语句都会执行。
  2. 语法结构
try {    
          //finally必须结合try语句才能存在
}catch(OneException e1) { 
         //catch语句有无都行
}finally {
         //finally在任何情况下都会执行,通常在 finally里关闭资源   
}
  1. 执行流程

  2. 注意点

  • finally先于jvm:finally语句只有在jvm退出前执行, java 虚拟机已经退出,则不会执行finally语句(在finally前面的语句中执行System.exit(-1)方法)。
  • return与finally执行关系:return若在fianlly语句之前执行,则结束整个方法,finally不再执行;若return在finally执行后再执行,return返回的数据可能是finally中最终返回的数据。
public static void main(String[] args) {
        int i1 = 100;
        int i2 = 10;
        try {
            int i3 = i1/i2; 
            System.out.println(i3);
            //return;//结束整个方法
            System.exit(-1); //exit(-1)退出虚拟机,finally语句不再执行
        }catch(ArithmeticException ae) {
                ae.printStackTrace();//打印异常在栈内存结构中的信息
        }finally {
              //只有 java 虚拟机退出不会执行 finally
              System.out.println("----------finally---------");
        }
}

4.3 throws异常抛出

  1. 处理机制
    除了使用try-catch在方法体中手动的捕获异常、处理异常外,java还提供一种使用throws在方法声明上抛出异常进行处理异常,将异常不断往上、往调用方抛出,最终抛到虚拟机,让虚拟机进行处理。
  2. 语法结构
//异常产生的地点
class A{
    public void add(){
        throw new xxxException();
    }
}
  1. 执行流程
  • 异常由实际产生的具体地点抛出;
  • 不断抛给调用方,调用方因调用 异常产生地的实例方法或属性,将异常抛给上级;
  • 不断循环,循序渐进,最终抛到main方法中,main方法将异常抛给最后的jvm虚拟机,又虚拟机进行处理;
  • 若虚拟机都无法进行处理,程序终止。
  1. 注意点
    在JDK8中,catch()语句捕获的异常类型除了可以使用具体异常、父类异常等,还可以使用 "异或"运算符 表示选择性捕获异常。
        try {
            //创建输入流
            FileInputStream fis = new FileInputStream("D:\\curse\\JavaSE\\document\\JavaSE-面向对象.pdf");
        } catch(FileNotFoundException | ArithmeticException | NullPointerException e) {  // 异或 | 运算符选择性捕获异常
            System.out.println("文件不存在?数学异常?空指针异常?都有可能!");
        }

4.4 异常处理方法

  1. getMessage()方法
    getMessage()方法用在catch语句中,主要用于获取所捕获异常对象的描述信息,所捕获异常对象的描述信息通常由该异常类中(底层的出错点)throw抛出。
  public static void main(String[] args) {
        try {
            m1();
        } catch (FileNotFoundException e) {
            String msg = e.getMessage(); // 获取异常的简单描述信息
            System.out.println(msg); //C:\\jetns-agent.jar
       }
   }
  private static void m1() throws FileNotFoundException {
       new FileInputStream("C:\\jetns-agent.jar");//参数就是异常信息
   }
  1. printStackTrace()方法
    printStackTrace()方法是打印所捕获异常的堆栈信息,适用于程序调试阶段。通过打印栈结构,从而追踪错误信息。
    jvm多线程采用异步方式调用printStackTrace()方法,与调用main方法的不是同一个线程。
public static void main(String[] args) {
        try {
            m1();
        } catch (FileNotFoundException e) {
            //打印异常堆栈追踪信息,实际开发中建议使用
            e.printStackTrace();//打印出异常问题具体错在哪(一行)
        }
        //往控制台执行以下打印输出程序 不耽误执行,服务器不会因为遇到上方异常而宕机
        System.out.println("Hello World!");
    }

    private static void m1() throws FileNotFoundException {
        m2();
    }
    private static void m2() throws FileNotFoundException {
        m3();
    }
    private static void m3() throws FileNotFoundException {
        new FileInputStream("C:\\jetns-agent.jar");
    }

4.5 异常查看方式

  1. 查看堆栈信息
    通过异常对象调用printStackTrace()方法,在控制台打印出栈结构,就会追踪到所有错误信息(具体出现在哪一行)。
  2. 查看错误信息重点
    查看错误的重点应在于自己编写的项目路径下的错误位置,不需要关注java本地路径出现的错误(java源码不会出错)。
    一般打印的栈结构信息,前半部都是java本地路径信息,后半部才是自己编写项目出现错误的位置及原因,且自己编写的异常信息是自上而下逐步影响的,因此应从自己编写出现的第一个异常开始查看。
//如以下在控制台打印的栈结构错误信息
//  java本地路径出现的异常
java.io.FileNotFoundException: C:\jetns-agent.jar (系统找不到指定的文件)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:213)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:155)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:110)
// 自己编写的代码出现异常的位置,重点在自己编写出错的位置
                at ExceptionTest09.m3(ExceptionTest09.java:31)
                at ExceptionTest09.m2(ExceptionTest09.java:27)
                at ExceptionTest09.m1(ExceptionTest09.java:23)
                at ExceptionTest09.main(ExceptionTest09.java:14)
                因为31行出问题导致了27行
                27行出问题导致23行
                23行出问题导致14行。
                应该先查看31行的代码。31行是代码错误的根源。

五、自定义异常

  1. 概念
    虽然jdk源码中提供了许多种类的异常给予程序员使用,但是现实中依然存在许多未被收录的异常,因此需要捕获一些除java本身提供的以外的异常来满足开发需求和提高效率。
  2. 遵循规律
    自定义异常通常继承 Exception 或 RuntimeException类,而到底继承哪个,看具体情况来定。
  • 自定义受控异常:继承Exception类,且方法体内 throw抛出时必须进行声明,被catch捕获后必须处理。
  • 自定义非受控异常:继承RuntimeException类,可以不用在方法体内 throw抛出,但必须方法声明后面throws 抛出给JVM处理。
  1. 自定义异常要求
  • 继承父类:异常是一个类或对象,必须继承Exception 或RuntimeException类,提供构造方法,并且显示调用父类的构造方法(super);
  • 显式调用Throwable接口的无参构造方法;
  • 重写Throwable接口的带参构造方法,参数为异常错误提示信息。
  1. 代码示例
//自定义受控异常(编译期异常)
public class MyException extends Exception{
    //java源码中每一个异常类的定义都实现(继承)了Throwable接口的构造方法
    public MyException() {
        //显示调用父类Throwable接口的构造方法
        super();
    }
    public MyException(String message) {
        //显示调用父类Throwable接口的带参构造方法
        super(message);
    }
}

//测试受控异常
public class MyExceptionTest {
    public static void main(String[] args) {
        //创建自定义受控异常对象
        MyException me = new MyException();
        //自定义异常为编译时异常,在main方法中不能抛给jvm处理
        try{
            //method1(10,0);
            method1(13,5);
        }catch (MyException e){
            //编译期异常,必须手动捕获解决
            System.out.println(e.getMessage());
        }
    }
//自定义非受控异常(运行时异常)
public class MyRunTimeException extends RuntimeException{
    //编译期异常、运行期异常都实现Throwable接口的构造方法
    public MyRunTimeException() {
        //显示调用父类接口的构造方法
        super();
    }
    public MyRunTimeException(String message) {
        super(message);
    }
}

//测试自定义非受控异常
public class MyRunTimeExceptionTest {
    public static void main(String[] args) throws MyRunTimeException{
        //创建自定义非受控异常对象
        MyRunTimeException mrt = new MyRunTimeException();
        /*未进行throws处理时
        method2(0,23);//这里出现异常,不能继续往下执行
        method2(13,26);
         */
        method2(13,26);
    }

    private static void method2(int i,int j) throws MyRunTimeException{
        if (i == 0){
            throw new MyRunTimeException("因数为0,不能进行相乘!");
        }
        int k = i*j;
        System.out.println(k);
    }
}

5.1 自定义异常的实际运用

  1. 用处
    当编写的代码程序可能出现的异常,已经超出java源码提供的异常本身,就可以使用自定义异常来完成异常处理。
  2. 应用
  • 定义异常:无参构造中,手动调用Throwable接口的构造方法;带参构造中手动调用Throwable接口的带参构造方法;
  • throw异常:使用throw new 异常对象(),将异常信息抛出。

5.2 方法覆盖与异常

  1. 子类重写的方法不能抛出比父类方法更多的异常,也不可以抛出比父类更大的异常,但可以抛出父类方法异常的子异常;
  2. 异常是类,即支持继承<接口继承 和 类继承>,因此产生父类异常和子类异常。

常见面试题

  1. final、finalize 和 finally的关系?
    答:
  • final是关键字,表示最终的。由final修饰的变量不可修改也不可重新赋值;由fianl修饰的类为最终的,不可继承。
  • finalize是JVM的一个方法,调用该方法可以释放资源。
  • finally也是关键字,是一个语句,常与try语句结合使用,且finally语句无论何时都会执行<jvm若提前退出则不执行finally语句,System.exit(0);>
  • finally语句的无论如何都执行 是相对于同程序块的try语句已经执行。若try语句未执行前就已经出错或者已经return,程序就结束了,也就谈不上finally语句一定会执行了。
  1. throws 和 throw 的区别?
    答:
  • thorw 是声明异常,后面一般是throw new Exception()创建出异常对象。而该对象就是catch语句中捕获得到的异常对象,只不过是在产生异常对象的位置,将其内存地址传递过来,到处理异常的类中。即该异常对象是跨越了两个类之间的一个引用形参。
  • throws是抛出异常,一般是在方法后面声明,声明该方法可能会产生的异常。声明后的异常,会抛给调给方法的对象。
  • throw相当于 return 语句,throw执行,下面的代码不再执行。
    区别于return语句,throw 虽然类似 return 语句,但 finally 依然会执行。而return执行,整个方法结束,finally也不会再执行
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 202,802评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,109评论 2 379
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,683评论 0 335
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,458评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,452评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,505评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,901评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,550评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,763评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,556评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,629评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,330评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,898评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,897评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,140评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,807评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,339评论 2 342

推荐阅读更多精彩内容