您当前的位置:首页 > 电脑百科 > 程序开发 > 语言 > JAVA

聊聊Java中的异常及处理

时间:2020-07-03 11:52:39  来源:  作者:

前言

在编程中异常报错是不可避免的。特别是在学习某个语言初期,看到异常报错就抓耳挠腮,常常开玩笑说编程1分钟,改bug1小时。今天就让我们来看看什么是异常和怎么合理的处理异常吧!

异常与error介绍

下面还是先让我们来看一下基本概念吧!

​ 异常指程序运行过程中出现的非正常现象,例如用户输入错误、除数为零、需要处理的文件不存在、数组下标越界等。异常机制本质就是当程序出现错误,程序安全退出的机制。在JAVA的异常处理机制中,引进了很多用来描述和处理异常的类,称为异常类。异常类定义中包含了该类异常的信息和对异常进行处理的方法。

​ Java是采用面向对象的方式来处理异常的。处理过程:

  1. 抛出异常:在执行一个方法时,如果发生异常,则这个方法生成代表该异常的一个对象,停止当前执行路径,并把异常对象提交给JRE。
  2. 捕获异常:JRE得到该异常后,寻找相应的代码来处理该异常。JRE在方法的调用栈中查找,从生成异常的方法开始回溯,直到找到相应的异常处理代码为止。

让我们来看看前面所讲到的异常类究竟是个什么东西!

其实所有的异常对象都是派生于Throwable类的一个实例。如果内置的异常类不能够满足需要,还可以创建自己的异常类。所有异常的根类为java.lang.Throwable。看看它的家族长什么样。

聊聊Java中的异常及处理

 

Throwable类下面主要是两大门派。ErrorException

  • Error是程序无法处理的错误,表示运行应用程序中较严重问题,系统JVM已经处于不可恢复的崩溃状态中。例如,说内存溢出和线程死锁等系统问题。
  • Exception是程序本身能够处理的异常。Exception类是所有异常类的父类,其子类对应了各种各样可能出现的异常事件。 通常Java的异常可分为:RuntimeException 运行时异常CheckedException 已检查异常下面我们来研究研究这两个异常。

RuntimeException和 CheckedException异同

首先我们先来看看什么是运行时异常

这类异常通常是由编程错误导致的,所以在编写程序时,并不要求必须使用异常处理机制来处理这类异常,而是经常需要通过增加“逻辑处理来避免这些异常”。

比如以下常见的几种异常:

ArithmeticException异常

 int b=0;
 System.out.println(1/b);
 //解决:
   if(b!=0){
      System.out.println(1/b);
   }

NumberFormatException异常

String str = "1234abcf";
System.out.println(Integer.parseInt(str));
//解决: 
Pattern p = Pattern.compile("^\d+$");
Matcher m = p.matcher(str);
if (m.matches()) { // 如果str匹配代表数字的正则表达式,才会转换
    System.out.println(Integer.parseInt(str));
}

ClassCastException异常

Animal a=new Dog();
Cat c=(Cat)a;
//解决:    
if (a instanceof Cat) {
    Cat c = (Cat) a;
}

这里再补充两点,方便大家更好的理解java异常的机制和处理过程。

  1. 在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。
  2. 运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

上面我们讲述了什么是运行时异常以及一些处理方式,下面就再来看看什么是已检查异常吧!

所有不是RuntimeException的异常,统称为Checked Exception,又被称为“已检查异常”,如IOException、SQLException等以及用户自定义的Exception异常。 这类异常在编译时就必须做出处理, 否则无法通过编译。

通常异常的处理方式有两种:

  1. 使用“try/catch”捕获异常
  2. 使用“throws”声明异常。

下面就来详细的聊聊吧!

异常的处理

上面已经提了,异常处理通常有2种方式。先看看捕获异常吧。

捕获异常是通过3个关键词来实现的:try-catch-finally。用try来执行一段程序,如果出现异常,系统抛出一个异常,可以通过它的类型来捕捉(catch)并处理它,最后一步是通过finally语句为异常处理提供一个统一的出口,finally所指定的代码都要被执行。

这个捕获异常其实也是我们在面试的时候会经常碰到的问题。下面我们分别再来对各个部分做一个简单的提示吧!

  • try

一个try语句必须带有至少一个catch语句块或一个finally语句块 。当异常处理的代码执行结束以后,不会再回到try语句去执行尚未执行的代码。

  • catch

每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常对象。在此介绍一些常用的方法,这些方法均继承自Throwable类 。

  1. toString ()方法,显示异常的类名和产生异常的原因。
  2. getMessage()方法,只显示产生异常的原因,但不显示类名。
  3. printStackTrace()方法,用来跟踪异常事件发生时堆栈的内容。

这里有一个需要特别注意的地方,那就是catch捕获异常时的捕获顺序:

如果异常类之间有继承关系,在顺序安排上就需注意。越是顶层的类,越放在下面,再不然就直接把多余的catch省略掉。 也就是说先捕获子类异常再捕获父类异常。

  • finally

finally语句块中始终都要执行,除了遇到了System.exit(0)结束程序运行。针对这个特性,所以我们通常在finally中关闭程序块已打开的资源,比如:关闭文件流、释放数据库连接等。

即使try和catch块中存在return语句,finally语句也会执行。是在执行完finally语句后再通过return退出。

在这里就有一道非常经典的一个面试题。

public class Test {
    public static void main(String[]args) {
       System.out.println(new Test().test());;
    }
    static int test(){
       int x = 1;
       try{
          retun x;
       }finally{
          System.out.print("jdbk"+ ++x);
       }
    }
}
// 问输出结果?

先解释哈这里存在的玄妙吧!

看了上面的讲述,我们都知道了当try和catch中有return时,finally仍然会执行,所以正常逻辑来说此题的答案应该是“jdbk2 2”,但这里存在一个陷阱,那就是:

finally是在return后面的表达式运算后执行的(此时并没有返回运算后的值,而是先把要返回的值保 存起来,不管finally中的代码怎么样,返回的值都不会改变,任然是之前保存的值),所以函数返回值是 在finally执行前确定的。因此正确答案应该是:“jdbk2 1”。

还有一点需要注意的就是:finally中最好不要包含return,否则程序会提前退出,返回值不是try或catch中保存的返回值。

接下来再来讲讲声明异常吧,它相对来说就比较简单了。

在一些情况下,当前方法并不需要处理发生的异常,而是向上传递给调用它的方法处理。如果一个方法抛出多个已检查异常,就必须在方法的首部列出所有的异常,之间以逗号隔开。

public static void readFile(String fileName) throws FileNotFoundException,IOException {
}

需要注意的地方就是:

  1. 在方法重写中声明异常时:子类重写父类方法时,如果父类方法有声明异常,那么子类声明的异常范围不能超过父类声明的范围。
  2. 声明异常我们一般在server层中。在controller层或则数据访问层一般是捕获异常。

自定义异常

我们为什么要自定义异常?还不是因为在程序中,可能会遇到JDK提供的任何标准异常类都无法充分描述清楚我们想要表达的问题。此时我们就可以创建自己的异常类,即自定义异常类。

那我们怎么自定义异常类呢?相信你看了上面的异常类的家族图应该就猜到了。不错,自定义异常类只需从Exception类或者它的子类派生一个子类即可。如果你继承Exception类,则为受检查异常,必须对其进行处理;如果不想处理,可以让自定义异常类继承运行时异常RuntimeException类。通常我们自定义异常类应该包含2个构造器:一个是默认的构造器,另一个是带有详细信息的构造器。这里举一个例子。

/**IllegalAgeException:非法年龄异常,继承Exception类*/
class IllegalAgeException extends Exception {
    //默认构造器
    public IllegalAgeException() {

    }
    //带有详细信息的构造器,信息存储在message中
    public IllegalAgeException(String message) {
        super(message);
    }
}

   public void setAge(int age) throws IllegalAgeException {
        if (age < 0) {
            throw new IllegalAgeException("人的年龄不应该为负数");
        }
        this.age = age;
    }

最后给大家讲述一点使用异常机制的建议:

1.要避免使用异常处理代替错误处理,这样会降低程序的清晰性,并且效率低下。

2.处理异常不可以代替简单测试---只在异常情况下使用异常机制。

3.不要进行小粒度的异常处理---应该将整个任务包装在一个try语句块中。

4.异常往往在高层处理。



Tags:Java   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
面向对象的特征之一封装 面向对象的特征之二继承 方法重写(override/overWrite) 方法的重载(overload)和重写(override)的区别: 面向对象特征之三:多态 Instanceof关键字...【详细内容】
2021-12-28  Tags: Java  点击:(2)  评论:(0)  加入收藏
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  Tags: Java  点击:(3)  评论:(0)  加入收藏
文章目录 如何理解面向对象编程? JDK 和 JRE 有什么区别? 如何理解Java中封装,继承、多态特性? 如何理解Java中的字节码对象? 你是如何理解Java中的泛型的? 说说泛型应用...【详细内容】
2021-12-24  Tags: Java  点击:(5)  评论:(0)  加入收藏
1、通过条件判断给变量赋值布尔值的正确姿势// badif (a === &#39;a&#39;) { b = true} else { b = false}// goodb = a === &#39;a&#39;2、在if中判断数组长度不为零...【详细内容】
2021-12-24  Tags: Java  点击:(6)  评论:(0)  加入收藏
Java与Lua相互调用案例比较少,因此项目使用需要做详细的性能测试,本内容只做粗略测试。目前已完成初版Lua-Java调用框架开发,后期有时间准备把框架进行抽象,并开源出来,感兴趣的...【详细内容】
2021-12-23  Tags: Java  点击:(11)  评论:(0)  加入收藏
传统游戏项目一般使用TCP协议进行通信,得益于它的稳定和可靠,不过在网络不稳定的情况下,会出现丢包严重。不过近期有不少基于UDP的应用层协议,声称对UDP的不可靠进行了改造,这意...【详细内容】
2021-12-23  Tags: Java  点击:(12)  评论:(0)  加入收藏
文章目录1、Quartz1.1 引入依赖<dependency> <groupId>org.quartz-scheduler</groupId> <artifactId>quartz</artifactId> <version>2.3.2</version></dependency>...【详细内容】
2021-12-22  Tags: Java  点击:(11)  评论:(0)  加入收藏
Java从版本5开始,在 java.util.concurrent.locks包内给我们提供了除了synchronized关键字以外的几个新的锁功能的实现,ReentrantLock就是其中的一个。但是这并不意味着我们可...【详细内容】
2021-12-17  Tags: Java  点击:(11)  评论:(0)  加入收藏
一、概述final是Java关键字中最常见之一,表示“最终的,不可更改”之意,在Java中也正是这个意思。有final修饰的内容,就会变得与众不同,它们会变成终极存在,其内容成为固定的存在。...【详细内容】
2021-12-15  Tags: Java  点击:(17)  评论:(0)  加入收藏
1、问题描述关于java中的日志管理logback,去年写过关于logback介绍的文章,这次项目中又优化了下,记录下,希望能帮到需要的朋友。2、解决方案这次其实是碰到了一个问题,一般的情况...【详细内容】
2021-12-15  Tags: Java  点击:(19)  评论:(0)  加入收藏
▌简易百科推荐
面向对象的特征之一封装 面向对象的特征之二继承 方法重写(override/overWrite) 方法的重载(overload)和重写(override)的区别: 面向对象特征之三:多态 Instanceof关键字...【详细内容】
2021-12-28  顶顶架构师    Tags:面向对象   点击:(2)  评论:(0)  加入收藏
一、Redis使用过程中一些小的注意点1、不要把Redis当成数据库来使用二、Arrays.asList常见失误需求:把数组转成list集合去处理。方法:Arrays.asList 或者 Java8的stream流式处...【详细内容】
2021-12-27  CF07    Tags:Java   点击:(3)  评论:(0)  加入收藏
文章目录 如何理解面向对象编程? JDK 和 JRE 有什么区别? 如何理解Java中封装,继承、多态特性? 如何理解Java中的字节码对象? 你是如何理解Java中的泛型的? 说说泛型应用...【详细内容】
2021-12-24  Java架构师之路    Tags:JAVA   点击:(5)  评论:(0)  加入收藏
大家好!我是老码农,一个喜欢技术、爱分享的同学,从今天开始和大家持续分享JVM调优方面的经验。JVM调优是个大话题,涉及的知识点很庞大 Java内存模型 垃圾回收机制 各种工具使用 ...【详细内容】
2021-12-23  小码匠和老码农    Tags:JVM调优   点击:(11)  评论:(0)  加入收藏
前言JDBC访问Postgresql的jsonb类型字段当然可以使用Postgresql jdbc驱动中提供的PGobject,但是这样在需要兼容多种数据库的系统开发中显得不那么通用,需要特殊处理。本文介绍...【详细内容】
2021-12-23  dingle    Tags:JDBC   点击:(13)  评论:(0)  加入收藏
Java与Lua相互调用案例比较少,因此项目使用需要做详细的性能测试,本内容只做粗略测试。目前已完成初版Lua-Java调用框架开发,后期有时间准备把框架进行抽象,并开源出来,感兴趣的...【详细内容】
2021-12-23  JAVA小白    Tags:Java   点击:(11)  评论:(0)  加入收藏
Java从版本5开始,在 java.util.concurrent.locks包内给我们提供了除了synchronized关键字以外的几个新的锁功能的实现,ReentrantLock就是其中的一个。但是这并不意味着我们可...【详细内容】
2021-12-17  小西学JAVA    Tags:JAVA并发   点击:(11)  评论:(0)  加入收藏
一、概述final是Java关键字中最常见之一,表示“最终的,不可更改”之意,在Java中也正是这个意思。有final修饰的内容,就会变得与众不同,它们会变成终极存在,其内容成为固定的存在。...【详细内容】
2021-12-15  唯一浩哥    Tags:Java基础   点击:(17)  评论:(0)  加入收藏
1、问题描述关于java中的日志管理logback,去年写过关于logback介绍的文章,这次项目中又优化了下,记录下,希望能帮到需要的朋友。2、解决方案这次其实是碰到了一个问题,一般的情况...【详细内容】
2021-12-15  软件老王    Tags:logback   点击:(19)  评论:(0)  加入收藏
本篇文章我们以AtomicInteger为例子,主要讲解下CAS(Compare And Swap)功能是如何在AtomicInteger中使用的,以及提供CAS功能的Unsafe对象。我们先从一个例子开始吧。假设现在我们...【详细内容】
2021-12-14  小西学JAVA    Tags:JAVA   点击:(21)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条