你写的代码编译时是否经常报错?来看看这篇文章帮你解决大部分问题。(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来进行处理,此时程序就会异常终止


相关文章
|
1月前
|
JavaScript NoSQL Java
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
181 96
接替此文【下篇-服务端+后台管理】优雅草蜻蜓z系统JAVA版暗影版为例-【蜻蜓z系列通用】-2025年全新项目整合搭建方式-这是独立吃透代码以后首次改变-独立PC版本vue版搭建教程-优雅草卓伊凡
|
7天前
|
传感器 监控 Java
Java代码结构解析:类、方法、主函数(1分钟解剖室)
### Java代码结构简介 掌握Java代码结构如同拥有程序世界的建筑蓝图,类、方法和主函数构成“黄金三角”。类是独立的容器,承载成员变量和方法;方法实现特定功能,参数控制输入环境;主函数是程序入口。常见错误包括类名与文件名不匹配、忘记static修饰符和花括号未闭合。通过实战案例学习电商系统、游戏角色控制和物联网设备监控,理解类的作用、方法类型和主函数任务,避免典型错误,逐步提升编程能力。 **脑图速记法**:类如太空站,方法即舱段;main是发射台,static不能换;文件名对仗,括号要成双;参数是坐标,void不返航。
28 5
|
25天前
|
Java 程序员 开发者
Java社招面试题:一个线程运行时发生异常会怎样?
大家好,我是小米。今天分享一个经典的 Java 面试题:线程运行时发生异常,程序会怎样处理?此问题考察 Java 线程和异常处理机制的理解。线程发生异常,默认会导致线程终止,但可以通过 try-catch 捕获并处理,避免影响其他线程。未捕获的异常可通过 Thread.UncaughtExceptionHandler 处理。线程池中的异常会被自动处理,不影响任务执行。希望这篇文章能帮助你深入理解 Java 线程异常处理机制,为面试做好准备。如果你觉得有帮助,欢迎收藏、转发!
98 14
|
1月前
|
缓存 Java 应用服务中间件
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
java语言后台管理若依框架-登录提示404-接口异常-系统接口404异常如何处理-登录验证码不显示prod-api/captchaImage 404 (Not Found) 如何处理-解决方案优雅草卓伊凡
122 5
|
3月前
|
Java Maven
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
在Java项目中,启动jar包时遇到“no main manifest attribute”错误,且打包大小明显偏小。常见原因包括:1) Maven配置中跳过主程序打包;2) 缺少Manifest文件或Main-Class属性。解决方案如下:
972 8
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
|
2月前
|
SQL Java 数据库连接
如何在 Java 代码中使用 JSqlParser 解析复杂的 SQL 语句?
大家好,我是 V 哥。JSqlParser 是一个用于解析 SQL 语句的 Java 库,可将 SQL 解析为 Java 对象树,支持多种 SQL 类型(如 `SELECT`、`INSERT` 等)。它适用于 SQL 分析、修改、生成和验证等场景。通过 Maven 或 Gradle 安装后,可以方便地在 Java 代码中使用。
422 11
|
2月前
|
Java Windows
【Azure Function】部署Java Function失败:报错deploy [ERROR] Status code 401和警告 'China North 3' may not be a valid region
1:deploy [ERROR] Status code 401, (empty body). 2: China North 3 may not be a valid region,please refer to https://aka.ms/maven_function_configuration#supported-regions for values. 3:  <azure.functions.maven.plugin.version>1.36.0</azure.functions.maven.plugin.version>
49 11
|
2月前
|
JSON Java 数据挖掘
利用 Java 代码获取淘宝关键字 API 接口
在数字化商业时代,精准把握市场动态与消费者需求是企业成功的关键。淘宝作为中国最大的电商平台之一,其海量数据中蕴含丰富的商业洞察。本文介绍如何通过Java代码高效、合规地获取淘宝关键字API接口数据,帮助商家优化产品布局、制定营销策略。主要内容包括: 1. **淘宝关键字API的价值**:洞察用户需求、优化产品标题与详情、制定营销策略。 2. **获取API接口的步骤**:注册账号、申请权限、搭建Java开发环境、编写调用代码、解析响应数据。 3. **注意事项**:遵守法律法规与平台规则,处理API调用限制。 通过这些步骤,商家可以在激烈的市场竞争中脱颖而出。
|
3月前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
90 3
|
Java 索引 分布式计算

热门文章

最新文章