JavaSE-异常

目录

[TOC]

第一章:认识异常

1.1-什么是异常?

​ 异常,在Java程序中指的是:因程序问题而中断程序执行的现象。

1.2-异常分类

​ 在Java中,为了维护程序正常执行,Java提供了处理异常的异常机制(异常类)。

​ 在Java提供的异常机制中,其中java.lang.Throwable是根类,而根类的派生类有java.lang.Errorjava.lang.Excepiton两个子类。

Error,错误(绝症),该类型异常在程序中无法处理,只能尽量避免。

Excepiton,编译期异常(写源代码时)(小毛病,比如:类似感冒),该类型异常在程序中是可处理的。Excepiton类型还有一个子类RunTimeException,表示运行期异常(程序运行的过程中),该类型异常在程序中也是可处理的。

​ 为了更好的区分以上描述的异常分类,我们看以下程序。

// 【Error异常】
// Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
// 内存溢出。超出了分配给JVM内存大小。
// 该程序只能修改源代码解决问题。
int[]nums = new int[1024*1024*1024];

// 【Exception异常】
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// Unhandled exception: java.text.ParseException
// 此处在编写源代码时就发生异常,该异常后续可以通过相关的处理机制处理和避免
Date date = format.parse("2083-10-10"); 

// 【RunTimeException异常】
int[] nums = {1,2,3};
// Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException
// 在程序运行时出现的异常,数组下标超出。该异常可以通过相关异常机制处理
System.out.println(nums[4]);

1.3-异常产生的过程

在编程中,为了更好的处理异常,我们首先要了解异常产生的过程。下面通过一段异常代码来分析。

  • 代码

      // 需求:定义一个方法,用来获取指定数组的指定位置的元素
      public static void main(String[] args) {
        int[]nums = {1,2,3};
        System.out.println(getElement(nums,4));
      }
      public static int getElement(int[]arr,int index){
        return arr[index];
      }
    
  • 分析

    1. 在执行getElement方法的过程中,JVM检测出数组索引越界异常,此时JVM会做两件事:
      • 创建一个ArrayIndexOutOfBoundsException异常对象,该对象中包含了异常的信息(内容、原因、位置)
      • 因为getElment方法中没有异常处理,JVM会把异常对象抛给getElment方法的调用处-main方法。
    2. main方法接收到了ArrayIndexOutOfBoundsException异常对象,也没有处理异常,则会把异常对象抛给JVM
    3. JVM接收到了main方法后,会做两件事:
      • 在控制台打印异常信息(内容、原因、位置)
      • 终止程序的执行。
  • 图解

第二章:处理异常

2.1 关键字

异常相关的关键字有:

  • try
  • catch
  • finally
  • throw
  • throws

2.2 抛出异常-throw

  • 作用:在指定的方法内部抛出异常。

  • 格式throw new xxxException(参数)

    • 参数是字符串格式,用来描述异常。
  • 注意事项:

    1. throw关键字必须写在方法内部。
    2. throw关键字后创建的对象必须是Exception或Exception的子类对象。
    3. throw关键字抛出的异常对象,我们必须处理异常对象。
      • throw关键字抛出的时RunTimeException或RunTimeException子类异常对象时,我们可以不处理,因为JVM会默认处理(打印异常信息、中断程序)。
      • throw关键字抛出的时编译期异常时(编写源代码时),我们必须处理(可以通过throws或try-catch的方式处理)
  • 代码

      public static void main(String[] args) {
        int[]nums = {1,2,3};
        // getElement(nums,4); // 异常:java.lang.ArrayIndexOutOfBoundsException: 索引越界:4
        int[]nums2=null;
        getElement(nums2,0); // 异常java.lang.NullPointerException: 传入的数组对象为null
      }
      public static int getElement(int[]arr,int index){
        if(arr==null){
          throw new NullPointerException("传入的数组对象为null");
        }
        if(index<0||index>=arr.length){
          throw new ArrayIndexOutOfBoundsException("索引越界:" + index);
        }
        return arr[index];
      }
    

2.3 Objects非空判断

​ Objects中提供了一个方法,可以检测一个对象为null并抛出异常,我们来应用一下,并查看其源代码更好的理解throw关键字的应用。

  • 方法名称:public static <T> T requireNonNull(T obj):查看指定引用对象不是null。

  • 源代码:

        public static <T> T requireNonNull(T obj) {
            if (obj == null)
                throw new NullPointerException();
            return obj;
        }
    
  • 应用:

      public static void main(String[] args) {
        int[]nums = {1,2,3};
        // getElement(nums,4); // 异常:java.lang.ArrayIndexOutOfBoundsException: 索引越界:4
        int[]nums2=null;
        getElement(nums2,0); // 异常java.lang.NullPointerException
      }
      public static int getElement(int[]arr,int index){
        Objects.requireNonNull(arr); // 应用
        if(index<0||index>=arr.length){
          throw new ArrayIndexOutOfBoundsException("索引越界:" + index);
        }
        return arr[index];
      }
    

2.4 声明异常-throws

当程序抛出一个异常对象给调用者时,调用者必须要处理异常,此时可以选择throws关键字来处理。

throws关键字的作用就是把异常对象继续抛给其他调用者,若其他调用者没有处理异常,则最终会抛给JVM来处理。

  • 格式

    /*
        修饰符 返回值类型 方法名() throws XXX1Exception,XXX2Exception... {
            throw new XXX1Exception("产生的原因");
            throw new XXX2Exception("产生的原因");
        }
    */
    
  • 注意事项

    1. throws关键字必须写在方法声明出。
    2. throws关键字后声明的异常必须是Exception或Exception子类异常。
    3. 方法内部抛出多个异常时,那么throws关键字后必须声明多个异常。
      • 如果抛出的多个异常之间存在继承关系,那么直接抛出父类即可。
    4. 调用了一个声明异常的方法,调用者必须处理该异常,处理方式有:
      • 调用者继续声明throws抛给其他调用者。最终抛给JVM。
      • 在调用者内部通过try-catch处理异常。
  • 代码

      // 需求:定义一个读取文件的方法,检测文件的路径和后缀名
      public static void main(String[] args) throws Exception {
        retFile("a.txt");
      }
      // 读取文件的方法
      //  private static void retFile(String path) throws FileNotFoundException,IOException {
      private static void retFile(String path) throws IOException {
        // 如果后缀名不是.txt则抛出
        if(!path.contains(".txt")){
          throw  new FileNotFoundException("文件后缀名不是.txt");
        }
        if(path.contains("c:")) {
          throw  new IOException("文件路径错误");
        }
      }
    

2.5 捕获处理异常-(try-catch)

throws声明异常的弊端是:异常后续代码无法执行(因为交给了JVM,JVM会终止程序)。

try-catch可以让调用者处理异常,并会继续执行后续程序。

  • 格式

    try {
        // 可能会发生异常的程序
        // 若发生异常,try会检测到异常对象
    }catch(异常类型 变量) {
        // catch会接收到异常对象,可以在此代码块中进行处理
    }catch(异常类型 变量) {
        // catch会接收到异常对象,可以在此代码块中进行处理
    }...
    
  • throwable中常用的异常方法

    • public String getMessage() :获取异常的描述信息,原因(提示给用户的时候,就提示错误原因。
    • public String toString() :获取异常的类型和异常描述信息(不用)。
    • public void printStackTrace() :打印异常的跟踪栈信息并输出到控制台。
      • 包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。
  • 代码

      // 需求:定义一个读取文件的方法,检测文件的路径和后缀名
      public static void main(String[] args)  {
        try{
          retFile("d.txt");
        }catch (Exception e) {
          e.printStackTrace();
        }
        System.out.println("后续代码");
        /*
         执行结果:
          java.io.IOException: 文件路径错误
                at it.leilei.cn.demo01.Main01.retFile(Main01.java:27)
                at it.leilei.cn.demo01.Main01.main(Main01.java:12)
            后续代码
        */
    
    
      }
      // 读取文件的方法
      private static void retFile(String path) throws IOException {
        // 如果后缀名不是.txt则抛出
        if(!path.contains(".txt")){
          throw  new FileNotFoundException("文件后缀名不是.txt");
        }
        if(!path.contains("c:")) {
          throw  new IOException("文件路径错误");
        }
      }
    
     
    

2.6 finally代码块

  • finally关键字的作用:有一些特定的代码无论异常是否发生,都需要执行。另外,因为异常会引发程序跳转,导致有些语句执行 不到。而finally就是解决这个问题的,在finally代码块中存放的代码都是一定会被执行的。

  • finally使用格式try...catch...finally自身需要处理异常,最终还得关闭资源。

  • finally关键字的应用场景:当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),我们都得在使用完之后,最终关闭打开的资源。

  • 代码:

      // 需求:定义一个读取文件的方法,检测文件的路径和后缀名
      public static void main(String[] args)  {
        try{
          retFile("d.txt");
        }catch (Exception e) {
          e.printStackTrace();
        }finally {
          System.out.println("释放资源");
        }
        System.out.println("后续代码");
    
    
      }
      // 读取文件的方法
      private static void retFile(String path) throws IOException {
        // 如果后缀名不是.txt则抛出
        if(!path.contains(".txt")){
          throw  new FileNotFoundException("文件后缀名不是.txt");
        }
        if(!path.contains("c:")) {
          throw  new IOException("文件路径错误");
        }
      }
    

2.7 异常处理注意事项

  • 多个异常使用捕获又该如何处理呢?

    1. 多个异常分别处理。
    2. 多个异常一次捕获,多次处理。
    3. 多个异常一次捕获一次处理
  • 一般我们是使用一次捕获多次处理方式,格式如下:

    • 格式:

      try{ 
          编写可能会出现异常的代码 
      }catch(异常类型A e){ 
          当try中出现A类型异常,就用该catch来捕获. 
          处理异常的代码 
          //记录日志/打印异常信息/继续抛出异常 
      }catch(异常类型B e){ 
          当try中出现B类型异常,就用该catch来捕获. 
          处理异常的代码 
          //记录日志/打印异常信息/继续抛出异常 
      }
      
    • 注意:这种异常处理方式,要求多个catch中的异常不能相同,并且若catch中的多个异常之间有子父类异

      常的关系,那么子类异常要求在上面的catch处理,父类异常在下面的catch处理。

  • 运行时异常被抛出可以不处理。即不捕获也不声明抛出(JVM默认会进行处理)。

  • 如果finally有return语句,永远返回finally中的结果,避免该情况.

      public static void main(String[] args)  {
        int num = getNum();
        System.out.println(num); // 100
      }
      // 读取文件的方法
      public static int getNum() {
        int a = 10;
        try {
          return  a;
        }catch (Exception e){}
        finally {
          a = 100;
          return  a;
        }
      }
    
  • 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异 常。

    class Fu{
      // 如果父类抛出了多个异常,子类重写父类方法时,抛出和父类相同的异常或者是父类异常的子类或者不抛出异 常。
      public void show1() throws IndexOutOfBoundsException{};
    }
    class Zi extends Fu{
      //  子类重写父类方法时,抛出和父类相同的异常
      // public void show1() throws IndexOutOfBoundsException{};
      // 或者是父类异常的子类ArrayIndexOutOfBoundsException 继承 IndexOutOfBoundsException
      // public void show1() throws ArrayIndexOutOfBoundsException{};
      // 不抛出异常
      public void show1()  {};
    }
    

第三章:自定义异常类

3.1 为什么要自定义异常类?

​ 我们说了Java中不同的异常类,分别表示着某一种具体的异常情况,那么在开发中总是有些异常情况是SUN没有定义好的,此时我们根据自己业务的异常情况来定义异常类。例如年龄负数问题,考试成绩负数问题等等。

3.2 什么是自定义异常类?

​ 在开发中根据自己业务的异常情况来定义异常类.

​ 自定义一个业务逻辑异常: RegisterException。一个注册异常类。

3.3 如何定义异常类

  1. 自定义一个编译期异常: 自定义类 并继承于 java.lang.Exception 。
  2. 自定义一个运行时期的异常类:自定义类 并继承于 java.lang.RuntimeException 。

3.4 代码

要求:我们模拟注册操作,如果用户名已存在,则抛出异常并提示:亲,该用户名已经被注册。

  public static void main(String[] args)  {
    // 接收用户注册的用户名
    String name = new Scanner(System.in).next();
    try{
      check(name);
    }catch (RegisterException e){
      e.printStackTrace();
      return;
    }
    System.out.println("注册成功");
  }

  public static void check(String n) throws RegisterException{
    String[]users={"张三","李四","王五"};
    for (int i = 0; i < users.length; i++) {
      if (n.equals( users[i])) {
        throw new RegisterException("亲,该用户名已经注册");
      }
    }
  }
 

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

}

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 一、基本数据类型 注释 单行注释:// 区域注释:/* */ 文档注释:/** */ 数值 对于byte类型而言...
    龙猫小爷阅读 4,253评论 0 16
  • JAVA面试题 1、作用域public,private,protected,以及不写时的区别答:区别如下:作用域 ...
    JA尐白阅读 1,143评论 1 0
  • 一、基础知识:1、JVM、JRE和JDK的区别:JVM(Java Virtual Machine):java虚拟机...
    杀小贼阅读 2,362评论 0 4
  • # Java NIO # Java NIO属于非阻塞IO,这是与传统IO最本质的区别。传统IO包括socket和文...
    Teddy_b阅读 581评论 0 0
  • Java异常类型 所有异常类型都是Throwable的子类,Throwable把异常分成两个不同分支的子类Erro...
    予别她阅读 915评论 0 2