JavaSE高阶篇-异常

一、异常的概述

二、异常的体系结构以及常见的异常

java.lang.Throwable:异常体系的根父类
    • java.lang.Throwable:异常体系的根父类。
    • Throwable 中的常用方法:
    ◦ public void printStackTrace():
        打印异常的详细信息。
        包含了异常的类型、异常的原因、异常出现的位置、在开发和调试阶段都得使用 printStackTrace()。
    ◦ public String getMessage():
    Throwable 可分为两类:
    Error 和 Exception。
    分别对应着 java.lang.Error 与 java.lang.Exception 两个类。

    |---java.lang.Error:错误。Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况。
                         一般不编写针对性的代码进行处理。
               |---- StackOverflowError、OutOfMemoryError

    |---java.lang.Exception:异常。我们可以编写针对性的代码进行处理。
               |----编译时异常:(受检异常)在执行javac.exe命令时,出现的异常。
                    |----- ClassNotFoundException序列号化对象异常抛出
                    |----- FileNotFoundException  文件找不到异常
                    |----- IOException  输入输出异常
               |----运行时异常:(非受检异常)在执行java.exe命令时,出现的异常。
                    |---- ArrayIndexOutOfBoundsException 数组越界异常
                    |---- NullPointerException  空指针异常
                    |---- ClassCastException  类型转换异常
                    |---- NumberFormatException 数据格式异常
                    |---- InputMismatchException  输入类型不匹配异常
                    |---- ArithmeticException 算数异常

 三、编译时异常和运行时异常

package com.atguigu01.throwable;

import org.junit.Test;

import java.util.Date;
import java.util.Scanner;

/**
 */
public class ExceptionTest {

    /*
    * ArrayIndexOutOfBoundsException 数组越界异常
    * */
    @Test
    public void test1(){
        int[] arr = new int[10];
        System.out.println(arr[10]);
    }

    /*
    * NullPointerException  空指针异常
    * */
    @Test
    public void test2(){
//        String str = "hello";
//        str = null;
//        System.out.println(str.toString());

//        int[] arr = new int[10];
//        arr = null;
//        System.out.println(arr[0]);

        int[][] arr1 = new int[10][];//空指针
        System.out.println(arr1[0][0]);
    }

    /*
    * ClassCastException  类型转换异常
    * */
    @Test
    public void test3(){
        //父类的引用对象类型是子类的时候才可以进行强制类型转换
        //date和String算同类型,都继承于object 所以强制转换会报错
        Object obj = new String();
//        String str = (String) obj;

        Date date = (Date) obj;
    }
    /*
    * NumberFormatException  数据格式异常
    * */
    @Test
    public void test4(){
        String str = "123";
        str = "abc";
        int i = Integer.parseInt(str);
        System.out.println(i);
    }

    /*
    * InputMismatchException  输入类型不匹配异常
    * */
    @Test
    public void test5(){
        Scanner scanner = new Scanner(System.in);
        int num = scanner.nextInt();
        System.out.println(num);
    }

    /*
    * ArithmeticException 算数异常
    * */
    @Test
    public void test6(){
        int num = 10;
        System.out.println(num / 0);
    }

    //******************以上是运行时异常,以下是编译时异常*****************************

    /*
    * ClassNotFoundException
    * */
    @Test
    public void test7(){
//        Class clz = Class.forName("java.lang.String");
    }

    @Test
    public void test8() {
//        File file = new File("D:\\hello.txt");
//
//        FileInputStream fis = new FileInputStream(file); //可能报FileNotFoundException
//
//        int data = fis.read(); //可能报IOException
//        while(data != -1){
//            System.out.print((char)data);
//            data = fis.read(); //可能报IOException
//        }
//
//        fis.close(); //可能报IOException

    }
}

四、异常处理的方式

在编写程序时,经常要在可能出现错误的地方加上检测的代码,如进行 x/y 运算时,要*检测分母为*0*,*数据为空*,*输入的不是数据而是字符*等。过多的 if-else 分支会导致程序的*代码加长*、*臃肿*,*可读性差*,程序员需要花很大的精力“**堵漏洞**”。因此采用异常处理机制。

Java 异常处理:

Java 采用的异常处理机制,是*将异常处理的程序代码集中在一起*,与正常的程序代码分开,使得程序简洁、优雅,并易于维护。

Java 异常处理的方式:

方式一:try-catch-finally

方式二:throws + 异

 【常类型】

1. 方式一(抓抛模型):try-catch-finally
过程1:“抛”
    程序在执行的过程当中,一旦出现异常,就会在出现异常的代码处,生成对应异常类的对象,并将此对象抛出。
    一旦抛出,此程序就不执行其后的代码了。

过程2:“抓”
    针对于过程1中抛出的异常对象,进行捕获处理。此捕获处理的过程,就称为抓。
    一旦将异常进行了处理,代码就可以继续执行。

2. 基本结构:
try{
    ...... //可能产生异常的代码
}
catch( 异常类型1 e ){
    ...... //当产生异常类型1型异常时的处置措施
}
catch( 异常类型2 e ){
    ......     //当产生异常类型2型异常时的处置措施
}
finally{
    ...... //无论是否发生异常,都无条件执行的语句
}

3. 使用细节:
> 将可能出现异常的代码声明在try语句中。一旦代码出现异常,就会自动生成一个对应异常类的对象。并将此对象抛出。
> 针对于try中抛出的异常类的对象,使用之后的catch语句进行匹配。一旦匹配上,就进入catch语句块进行处理。
  一旦处理接触,代码就可继续向下执行。
> 如果声明了多个catch结构,不同的异常类型在不存在子父类关系的情况下,谁声明在上面,谁声明在下面都可以。
  如果多个异常类型满足子父类的关系,则必须将子类声明在父类结构的上面。否则,报错。
> catch中异常处理的方式:
   ① 自己编写输出的语句。
   ② printStackTrace():打印异常的详细信息。 (推荐)
   ③ getMessage():获取发生异常的原因。
> try中声明的变量,出了try结构之后,就不可以进行调用了。
> try-catch结构是可以嵌套使用的。

4. 开发体会:
   > 对于运行时异常:
        开发中,通常就不进行显示的处理了。
        一旦在程序执行中,出现了运行时异常,那么就根据异常的提示信息修改代码即可。

   > 对于编译时异常:
        一定要进行处理。否则编译不通过。

5. finally的使用说明:
5.1 finally的理解
> 我们将一定要被执行的代码声明在finally结构中。
> 更深刻的理解:无论try中或catch中是否存在仍未被处理的异常,无论try中或catch中是否存在return语句等,finally中声明的语句都一定要被执行。

> finally语句和catch语句是可选的,但finally不能单独使用。

5.2 什么样的代码我们一定要声明在finally中呢?
> 我们在开发中,有一些资源(比如:输入流、输出流,数据库连接、Socket连接等资源),在使用完以后,必须显式的进行
关闭操作,否则,GC不会自动的回收这些资源。进而导致内存的泄漏。
  为了保证这些资源在使用完以后,不管是否出现了未被处理的异常的情况下,这些资源能被关闭。我们必须将这些操作声明
在finally中!

 

 异常处理的方式2-thows

1. 格式:在方法的声明除,使用"throws 异常类型1,异常类型2,..."

2. 举例:

public void test() throws 异常类型1,异常类型2,.. {
    //可能存在编译时异常
}


3. 是否真正处理了异常?
> 从编译是否能通过的角度看,看成是给出了异常万一要是出现时候的解决方案。此方案就是,继续向上抛出(throws)。
> 但是,此throws的方式,仅是将可能出现的异常抛给了此方法的调用者。此调用者仍然需要考虑如何处理相关异常。
  从这个角度来看,throws的方式不算是真正意义上处理了异常。


4. 方法的重写的要求:(针对于编译时异常来说的)
子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类。


5. 开发中,如何选择异常处理的两种方式?(重要、经验之谈)
- 如果程序代码中,涉及到资源的调用(流、数据库连接、网络连接等),则必须考虑使用try-catch-finally来处理,
  保证不出现内存泄漏。
- 如果父类被重写的方法没有throws异常类型,则子类重写的方法中如果出现异常,只能考虑使用try-catch-finally
  进行处理,不能throws。
- 开发中,方法a中依次调用了方法b,c,d等方法,方法b,c,d之间是递进关系。此时,如果方法b,c,d中有异常,
  我们通常选择使用throws,而方法a中通常选择使用try-catch-finally。

 

[复习]方法声明的格式:权限修饰符 返回值类型 方法名(形参列表) [throws 异常类型] { //方法体 }

具体规则:
① 父类被重写的方法与子类重写的方法的方法名和形参列表必须相同。
② 子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符
   > 子类不能重写父类中声明为private权限修饰的方法。
③ 关于返回值类型:
    > 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型必须是void
    > 父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须与被重写的方法的返回值类型相同。
    > 父类被重写的方法的返回值类型是引用数据类型(比如类),则子类重写的方法的返回值类型可以与被重写的方法的返回值
      类型相同 或 是被重写的方法的返回值类型的子类
④ (超纲)子类重写的方法抛出的异常类型可以与父类被重写的方法抛出的异常类型相同,或是父类被重写的方法抛出的异常类型的子类。

补充说明:方法体:没有要求。但是子类重写的方法的方法体必然与父类被重写的方法的不同。

 五、手动抛出异常

Java 中异常对象的生成有两种方式:

• 由虚拟机自动生成:

程序运行过程中,虚拟机检测到程序发生了问题,那么针对当前代码,就会在后台自动创建一个对应异常类的实例对象并抛出。

• 由开发人员手动创建:

new 异常类型(实参列表);

如果创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样,但是一旦 throw 抛出,就会对程序运行产生影响了。。

 

1. 为什么需要手动抛出异常对象?

在实际开发中,如果出现不满足具体场景的代码问题,我们就有必要手动抛出一个指定类型的异常对象。

2. 如何理解"自动 vs 手动"抛出异常对象?

过程1:“抛”
    "自动抛" : 程序在执行的过程当中,一旦出现异常,就会在出现异常的代码处,自动生成对应异常类的对象,并将此对象抛出。

    "手动抛" :程序在执行的过程当中,不满足指定条件的情况下,我们主动的使用"throw + 异常类的对象"方式抛出异常对象。

过程2:“抓”
    狭义上讲:try-catch的方式捕获异常,并处理。
    广义上讲:把“抓”理解为“处理”。则此时对应着异常处理的两种方式:① try-catch-finally ② throws 后面跟异常类型

3. 如何实现手动抛出异常?
在方法内部,满足指定条件的情况下,使用"throw 异常类的对象"的方式抛出。


4. 注意点:throw后的代码不能被执行,编译不通过。

 六、自定义异常的方法

1. 如何自定义异常类?
① 继承于现有的异常体系。通常继承于RuntimeException \ Exception
② 通常提供几个重载的构造器
③ 提供一个全局常量,声明为:static final long serialVersionUID;大小根据程序所定

2. 如何使用自定义异常类?
> 在具体的代码中,满足指定条件的情况下,需要手动的使用"throw + 自定义异常类的对象"方式,将异常对象抛出。
> 如果自定义异常类是非运行时异常,则必须考虑如何处理此异常类的对象。(具体的:① try-catch-finally ② throws)


3. 为什么需要自定义异常类?
我们其实更关心的是,通过异常的名称就能直接判断此异常出现的原因。既然如此,我们就有必要在实际开发场景中,
不满足我们指定的条件时,指明我们自己特有的异常类。通过此异常类的名称,就能判断出具体出现的问题。

 

七、抛异常时的注意事项

 1)父类中的方法抛出异常了,那么子类重写此方法之后要不要抛异常

可抛可不抛

2)父类中的方法没有抛出异常,那么子类重写此方法后要不要抛异常?

不要抛

八、try-catch 和throws的使用时机

1. 如果处理异常之后,还想让后续的代码正常执行,我们使用 try……catch
2. 如果方法之间是递进关系(调用),我们可以先 throws ,但是到了最后需要用 try……catch 做一个统一的异常处理

九、打印日常信息的三个方法

  • 17
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值