你写的代码编译时是否经常报错?来看看这篇文章帮你解决大部分问题。(Java中的异常)

简介: 异常简单理解就是不正常,Java中的异常就是程序在执行过程中出现不正常的行为称之为异常。

异常的概念与体系结构


概念

异常简单理解就是不正常,Java中的异常就是程序在执行过程中出现不正常的行为称之为异常


常见的异常

1. 算数异常(常见的是除数为0)

System.out.println(10/0);
//执行结果:
Exception in thread "main" java.lang.ArithmeticException: / by zero


2.数组越界异常

int[] arr = {1,2,3};
        System.out.println(arr[10]);
//执行结果:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 10

   

3. 空指针异常

int[] arr = null;
 System.out.println(arr.length);
//执行结果:
Exception in thread "main" java.lang.NullPointerException


从上述例子可以看出:不同的异常对应的异常描述不同,也就是每个异常都有对应的类来进行描述。


异常的体系结构

异常的种类非常的多,为了易于区分和管理,Java内部维护了一个异常的体系结构。见下图:

微信图片_20221028194813.jpg



对上面图进行说明:


1.Throwable:是异常的顶层类,其派生出两个子类:Error和Exception


2. Error:指的是Java虚拟机无法解决的问题,此类问题比较严重,例如:JVM内部错误,资源耗尽等,典型代表:StackOverflowError和OutOfMemoryError,该类异常一但发生,就无法解决,就像人得了癌症。


3.Exception:该类异常产生后可由程序员进行分析及处理,使得程序继续运行,就比如我们人感冒咳嗽一样。


异常的分类

因为异常发生的时机是不一样的,我们根据此将异常分为:编译时异常,运行时异常。


1. 编译时的异常


它指的是在程序编译期间发生的异常,也可称之为受检查异常(Checked Exception)


2. 运行时的异常


它指的是在程序执行期间发生的异常,也称之为非受检查异常(Unchecked Exception)


RuntimeException以及其子类对应的异常都称为运行时异常,例如:NullPointerException等


注意:编译时出现的语法错误不能称为异常,比如将某个单词拼写错误,此时编译过程中就会出错,这是“编译期”出错,而运行时异常指的是,编译已经完成得到class文件,再由JVM执行过程中出现的错误。


异常的处理

防御式编程  

1.LBYL:Look Before You Leap :指的是在对代码进行操作之前做充分的检查,即:事前防御  


例:

boolean ret = false;
      ret = 登录游戏();
      if(!ret){
          处理登录游戏错误;
          return;
      }
      ret = 开始匹配();
      if(!ret){
          处理匹配错误;
          return;
      }
      ret = 游戏确认();
      if(!ret){
          处理游戏确认错误;
          return;
      }
        ......
这种处理方式的缺陷:正确的流程和处理错误的流程混在一起了,整体比较混乱,不易阅读
2.EAFP:It's Easier to Ask Forgiveness than Permission.指的是先进行操作,遇到了问题在处理,即:事后认错型
  try{
            登录游戏();
            开始匹配();
            游戏确认();
            ......
        }catch(登录游戏异常){
            处理登录游戏异常;
        }catch(开始匹配异常){
            处理开始匹配异常;
        }catch(游戏确认异常){
            处理游戏确认异常;
        }


优势:正常和错误流程分开,代码清晰,易于阅读理解


异常处理的5个主要关键字:throw  try  catch  final  throws


异常的抛出

在写程序时,如果程序出现错误,就要将错误的信息告诉给调用者,在Java中,借助throw关键字,抛出一个指定的异常对象,将错误信息告知给调用者,具体语法如下:

throw new XXXException("异常产生的原因");

举一个数组传参的例子进行说明:

public static int getElement(int[] array,int index){
        if(null==array){
            throw new NullPointerException("传递数组为null");
        }
        if(index<0||index>=array.length){
            throw new ArrayIndexOutOfBoundsException("传递的数组下标越界");
        }
        return array[index];
    }
    public static void main(String[] args) {
        int[] array = {1,2,3};
        getElement(array,3);
    }


   运行结果:

 Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 传递的数组下标越界


注意事项:


1.throw必须写在方法体内部


2. 抛出的对象必须是Exception或者Exception的子类对象


3. 如果抛出的是RuntimeException或者它的子类,可以不用处理,JVM会帮我们处理


4. 如果抛出的是编译时异常,用户必须处理,否则无法通过编译


5. 异常一旦抛出,其后的代码就不会执行


异常的捕获

异常的捕获也就是异常的具体处理方式,主要有两种:用throws进行异常声明以及try-catch捕获处理。


异常声明(使用关键字throws)

当程序抛出编译时异常时而且用户不想处理该异常,就可以借助throws将异常抛给方法的调用者来处理。


语法格式:

修饰符 返回值类型 方法名(参数列表) throws 异常类型1,异常类型2...{
}


注意事项:


1. throws必须跟在方法的参数列表之后


2. 声明的异常必须是Exception或者其子类


3. 方法内部如果抛出多个异常,throws之后跟多个异常类型,之间用逗号隔开,如果抛出多个异常类型具有父子关系,则直接声明父类即可


4.调用抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用throws抛出


try-catch捕获处理

throws对异常并没有处理,而是交给调用者处理,如果要对异常真正进行处理,就需要用try-catch


语法格式:

try{
    //这里为可能出现异常的代码
 }catch(要捕获的异常类型 e){
    //当抛出异常与catch捕获异常类型相同时,异常就会被捕获到
     //对异常进行处理完后,会跳出try-catch,继续执行后序代码
 }[catch(异常类型 e){
    //对异常处理
}finally{
    //此处代码一定会被执行
 }]
 //后序代码,异常被捕获处理了,后序代码就一定会执行
 //异常没有捕获到,后序代码就不会执行


注意:[  ]中表示可选项,可以添加,也可以不添加


关于异常的处理方式:


1.异常的种类非常多,我们要根据不同的场景环境来决定


2. 对于更严重的问题(例如栈相关场景),应该让程序直接崩溃,防止造成更严重的后果


3.对于不太严重的问题,可以记录错误日志,并通过监控报警及时通知程序员


4. 对于可能会恢复的问题(网络相关场景),可尝试进行重试


注意事项:


1. try块内抛出异常位置之后的代码将不会被执行


2. 如果抛出异常类型与catch内的异常类型不匹配,即异常不会被成功捕获,也不会被处理,继续往外抛,直到JVM收到后中断程序---异常是按照类型来捕获的


3. try中可能会抛出多种类型异常,则必须用多个catch来捕获多种类型


4. 如果异常之间具有父子关系,一定是子类异常的catch在前,父类异常的catch在后,否则语法错误


由于Exception是所有异常类的父类,所以可以用这个类型表示捕捉所有异常


关键字finally  

在写代码时,有些特定的代码无论程序是否发生异常都需要执行,比如程序中的打开资源,网络连接,数据库连接,IO等,在程序正常或者异常退出时,必须要对资源进行回收,另外,因为异常会引发程序的跳转,可能导致有些语句执行不到,finally就是用来解决这一问题的。


语法格式:

try{
       //可能发生异常的代码 
    }catch(异常类型 e){
        //对捕获的异常进行处理
    }finally{
      //这里的代码无论异常是否被捕获到,都会执行  
    }
    //如果没有异常或者异常被捕获处理了,这里后序的代码也执行

举数组的例子进行说明:

public static void main(String[] args) {
        try{
            int[] array = {1,2,3};
            array[10] = 10;
            array[0] = 10;
        }catch(ArrayIndexOutOfBoundsException e){
            System.out.println(e);
        }finally{
            System.out.println("这里代码一定执行");
        }
        System.out.println("如果没有异常,或者异常被捕获处理,这里代码也将执行");
    }

执行结果:

java.lang.ArrayIndexOutOfBoundsException: 10

这里代码一定执行

如果没有异常,或者异常被捕获处理,这里代码也将执行


这里存在了一个问题:既然finally和try-catch-finally后的代码都会执行,为什么还要有finally的?看完下面的代码,这个问题那就会迎刃而解。

public static int getData(){
        Scanner sc = null;
        try{
            sc = new Scanner(System.in);
            int data = sc.nextInt();
            return data;
        }catch(InputMismatchException e){
           e.printStackTrace();
        }finally{
            System.out.println("finally中的代码");
        }
        System.out.println("try-catch-finally之后的代码");
        if(null!=sc){
            sc.close();
        }
        return 0;
    }
    public static void main(String[] args) {
        int data = getData();
        System.out.println(data);
    }
    输入:10
运行结果:
finally中的代码
10

上述程序,正常输入,return data后,方法就结束了,try-catch-finally后的代码根本没有执行,即输入流没有释放,造成资源泄露。


注意:finally中的代码一定会执行的,一般在finally中进行一些资源清理的扫尾工作。


如果finally中也存在return语句,则执行finally中的return语句,就不会执行try中的return语句


异常的处理流程

1. 程序先执行try中的代码


2. 如果try中的代码出现异常,就会结束try中的代码,与catch中的异常类型是否匹配


3. 如果找到匹配的异常类型,就会执行catch中的代码


4.如果没有找到匹配的类型,就会将异常向上传递到上层调用者


5. 无论是否找到匹配的异常类型,finally中的代码都会被执行到(在该方法结束之前执行)


6.如果上层调用者也没有处理异常,就会继续向上传递,一直到main方法也没有合适的代码处理异常,就会交给JVM来进行处理,此时程序就会异常终止


相关文章
|
2月前
|
Java 数据安全/隐私保护
快手小红书抖音留痕工具,自动留痕插件工具,java代码开源
这个框架包含三个核心模块:主操作类处理点赞评论、配置管理类和代理管理类。使用时需要配合
|
1月前
|
算法 IDE Java
Java 项目实战之实际代码实现与测试调试全过程详解
本文详细讲解了Java项目的实战开发流程,涵盖项目创建、代码实现(如计算器与汉诺塔问题)、单元测试(使用JUnit)及调试技巧(如断点调试与异常排查),帮助开发者掌握从编码到测试调试的完整技能,提升Java开发实战能力。
223 0
|
2月前
|
Java 机器人 API
tiktok群控脚本,养号关注私信点赞脚本插件,java代码分享
这个代码模拟了一个社交机器人的基本行为模式,包括登录、关注、点赞、私信等操作。请注意
|
2月前
|
Java 编译器 数据库连接
Java异常处理:写出更健壮的代码
Java异常处理:写出更健壮的代码
149 0
|
2月前
|
安全 Java 测试技术
Java 项目实战中现代技术栈下代码实现与测试调试的完整流程
本文介绍基于Java 17和Spring技术栈的现代化项目开发实践。项目采用Gradle构建工具,实现模块化DDD分层架构,结合Spring WebFlux开发响应式API,并应用Record、Sealed Class等新特性。测试策略涵盖JUnit单元测试和Testcontainers集成测试,通过JFR和OpenTelemetry实现性能监控。部署阶段采用Docker容器化和Kubernetes编排,同时展示异步处理和反应式编程的性能优化。整套方案体现了现代Java开发的最佳实践,包括代码实现、测试调试
118 0
|
2月前
|
SQL Java 数据库连接
Java 期末考试救急必备涵盖绝大多数核心考点及五大类经典代码助你过关
本文为Java期末考试复习指南,涵盖基础语法、面向对象编程、异常处理、文件操作、数据库连接五大核心考点,提供详细解析与实用代码示例,助力快速掌握重点,高效备考,轻松应对考试。
69 0
|
Java
使用Java代码打印log日志
使用Java代码打印log日志
398 1
|
Java BI API
在Java代码中打日志需要注意什么?
日志是什么?日志是你在代码运行时打印出来的一些数据和记录,是快速排查问题的好帮手,是撕逼和甩锅的利器!
781 0
|
缓存 Java 网络架构
别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
别在 Java 代码里乱打日志了,这才是正确的打日志姿势!
211 0

热门文章

最新文章