目录

1.初识异常

2.异常的基本用法

异常处理流程

3.为什么要使用异常?

异常应只用于异常的情况

4. 异常的种类

4.1 受查异常

解决方案:

4.2非受查异常

5.如何使用异常

避免不必要的使用受查异常

6.自定义异常


1.初识异常

我们在写代码的时候都或多或少碰到了大大小小的异常,例如:

public class Test {public static void main(String[] args) {int[] arr = {1,2,3};System.out.println(arr[5]);}
}

当我们数组越界时,编译器会给我们报数组越界,并提示哪行出了错。

再比如:

class Test{    int num = 10;public static void main(String[] args) {Test test = null;System.out.println(test.num);}
}

当我们尝试用使用空对象时,编译器也会报空指针异常:

那么究竟什么是异常?

所谓异常指的就是程序在 运行时 出现错误时通知调用者的一种机制.

关键字 "运行时" ,有些错误是这样的, 例如将 System.out.println 拼写错了, 写成了

system.out.println. 此时编译过程中就会出 错, 这是 "编译期" 出错.

而运行时指的是程序已经编译通过得到 class 文件了, 再由 JVM 执行过程中出现的错误.

2.异常的基本用法

Java异常处理依赖于5个关键字:try、catch、finally、throws、throw。下面来逐一介绍下。

①try:try块中主要放置可能会产生异常的代码块。如果执行try块里的业务逻辑代码时出现异

常,系统会自动生成一个异常对象,该异常对象被提交给运行环境,这个过程被称为抛出

(throw)异常。Java环境收到异常对象时,会寻找合适的catch块(在本方法或是调用方

法)。

②catch: catch 代码块中放的是出现异常后的处理行为,也可以写此异常出错的原因或者打

印栈上的错误信息。但catch语句不能为空,因为一旦将catch语句写为空,就代表忽略了此

异常。如:

空的catch块会使异常达不到应有的目的,即强迫你处理异常的情况。忽略异常就如同忽略

火警信号一样——若把火警信号关掉了,当真正的火灾发生时,就没有人能看到火警信号

了。或许你会侥幸逃过一劫,或许结果将是灾难性的。每当见到空的catch块时,我们都应该

警钟长鸣。

当然也有一种情况可以忽略异常,即关闭fileinputstream(读写本地文件)的时候。因为你还

没有改变文件的状态,因此不必执行任何恢复动作,并且已经从文件中读取到所需要的信

息,因此不必终止正在进行的操作。

③finally:finally 代码块中的代码用于处理善后工作, 会在最后执行,也一定会被执行。当遇

到try或catch中return或throw之类可以终止当前方法的代码时,jvm会先去执行finally中的语

句,当finally中的语句执行完毕后才会返回来执行try/catch中的return,throw语句。如果

finally中有return或throw,那么将执行这些语句,不会在执行try/catch中的return或throw语

句。finally块中一般写的是关闭资源之类的代码。但是我们一般不在finally语句中加入return

语句,因为他会覆盖掉try中执行的return语句。例如:

finally将最后try执行的return 10覆盖了,最后结果返回了20.

④throws:在方法的签名中,用于抛出此方法中的异常给调用者,调用者可以选择捕获或者

抛出,如果所有方法(包括main)都选择抛出(或者没有合适的处理异常的方式,即异常类

型不匹配)那么最终将会抛给JVM,就会像我们之前没使用try、catch语句一样。JVM打印出

栈轨迹(异常链)。

⑤throw:用于抛出一个具体的异常对象。常用于自定义异常类中。

ps:

关于 "调用栈",方法之间是存在相互调用关系的, 这种调用关系我们可以用 "调用栈" 来描述.

在 JVM 中有一块内存空间称为 "虚拟机栈" 专门存储方法之间的调用关系. 当代码中出现异常

的时候, 我们就可以使用 e.printStackTrace() 的方式查看出现异常代码的调用栈,一般写在catch语句中。

异常处理流程

 程序先执行 try 中的代码

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

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

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

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

 如果上层调用者也没有处理的了异常, 就继续向上传递.

 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止.

3.为什么要使用异常?

存在即合理,举个例子

             //不使用异常int[] arr = {1, 2, 3};System.out.println("before");System.out.println(arr[100]);System.out.println("after");

当我们不使用异常时,发现出现异常程序直接崩溃,后面的after也没有打印。

               //使用异常int[] arr = {1, 2, 3};try {System.out.println("before");System.out.println(arr[100]);System.out.println("after");} catch (ArrayIndexOutOfBoundsException e) {//  打印出现异常的调用栈e.printStackTrace();}System.out.println("after try catch");

当我们使用了异常,虽然after也没有执行,但程序并没有直接崩溃,后面的sout语句还是执行了

这不就是异常的作用所在吗?

再举个例子,当玩王者荣耀时,突然断网,他不会让你直接程序崩溃吧,而是给你断线重连的机会吧:

我们再用伪代码演示一把王者荣耀的对局过程:

不使用异常处理
boolean ret = false;ret = 登陆游戏();if (!ret) {处理登陆游戏错误;return;}ret = 开始匹配();if (!ret) {处理匹配错误;return;}
ret = 游戏确认();if (!ret) {处理游戏确认错误;return;}
ret = 选择英雄();if (!ret) {处理选择英雄错误;return;}ret = 载入游戏画面();if (!ret) {处理载入游戏错误;return;}......
使用异常处理
try {登陆游戏();开始匹配();游戏确认();选择英雄();载入游戏画面();...} catch (登陆游戏异常) {处理登陆游戏异常;} catch (开始匹配异常) {处理开始匹配异常;} catch (游戏确认异常) {处理游戏确认异常;} catch (选择英雄异常) {处理选择英雄异常;} catch (载入游戏画面异常) {处理载入游戏画面异常;}
......

我们能明显的看到不使用异常时,正确流程和错误处理代码混在一起,不易于分辨,而用了

异常后,能更易于理解代码。

当然使用异常的好处还远不止于此,我们可以在try、catch语句中加入信息提醒功能,比如你

开发了一个软件,当那个软件出现异常时,发个信息提醒你及时去修复。博主就做了一个小

小的qq邮箱信息提醒功能,源码在码云,有兴趣的可以去看看呀!需要配置qq邮箱pop3服

务,友友们可以去查查怎么开启呀,我们主旨不是这个所以不教怎么开启了。演示一下:

别群发消息哦,不然可能会被封号???

异常应只用于异常的情况

try{int i = 0;while(true)System.out.println(a[i++]);
}catch(ArrayIndexOutOfBoundsException e){}

这段代码有什么用?看起来根本不明显,这正是它没有真正被使用的原因。事实证明,作为

一个要对数组元素进行遍历的实现方式,它的构想是非常拙劣的。当这个循环企图访问数组

边界之外的第一个数组元素时,用抛出(throw)、捕获(catch)、

忽略(ArrayIndexOutOfBoundsException)的手段来达到终止无限循环的目的。假定它与数

组循环是等价的,对于任何一个Java程序员来讲,下面的标准模式一看就会明白:

for(int m : a)System.out.println(m);

为什么优先异常的模式,而不是用行之有效标准模式呢?

可能是被误导了,企图利用异常机制提高性能,因为jvm每次访问数组都需要判断下标是否越

界,他们认为循环终止被隐藏了,但是在foreach循环中仍然可见,这无疑是多余的,应该避

免。

上面想法有三个错误:

1.异常机制设计的初衷是用来处理不正常的情况,所以JVM很少对它们进行优化。

2.代码放在try…catch中反而阻止jvm本身要执行的某些特定优化。

3.对数组进行遍历的标准模式并不会导致冗余的检查。

这个例子的教训很简单:顾名思义,异常应只用于异常的情况下,它们永远不应该用于正常

的控制流。

总结:异常是为了在异常情况下使用而设计的,不要用于一般的控制语句。

4. 异常的种类

在Java中提供了三种可抛出结构:受查异常(checked exception)、运行时异常(run-time exception)和错误(error)。

(补充)

4.1 受查异常

什么是受查异常?只要不是派生于error或runtime的异常类都是受查异常。举个例子:

我们自定义两个异常类和一个接口,以及一个测试类

interface IUser {void changePwd() throws SafeException,RejectException;
}class SafeException extends Exception {//因为继承的是execption,所以是受查异常类public SafeException() {}public SafeException(String message) {super(message);}}class RejectException extends Exception {//因为继承的是execption,所以是受查异常类public RejectException() {}public RejectException(String message) {super(message);}
}public class Test {public static void main(String[] args) {IUser user = null;user.changePwd();}
}

我们发现test测试类中user使用方法报错了,因为java认为checked异常都是可以再编译阶

段被处理的异常,所以它强制程序处理所有的checked异常,java程序必须显式处checked

异常,如果程序没有处理,则在编译时会发生错误,无法通过编译。

解决方案:

①try、catch包裹

 IUser user = null;try {user.changePwd();}catch (SafeException e){e.printStackTrace();}catch (RejectException e){e.printStackTrace();}

②抛出异常,将处理动作交给上级调用者,调用者在调用这个方法时还是要写一遍try、catch

包裹语句的,所以这个其实是相当于声明,让调用者知道这个函数需要抛出异常

public static void main(String[] args) throws SafeException, RejectException {IUser user = null;user.changePwd();}

4.2非受查异常

派生于error或runtime类的所有异常类就是非受查异常。

可以这么说,我们现在写程序遇到的异常大部分都是非受查异常,程序直接崩溃,后面的也

不执行。

像空指针异常、数组越界异常、算术异常等,都是非受查异常。由编译器运行时给你检查出

来的,所以也叫作运行时异常。

5.如何使用异常

避免不必要的使用受查异常

如果不能阻止异常条件的产生,并且一旦产生异常,程序员可以立即采取有用的动作,这种

受查异常才是可取的。否则,更适合用非受查异常。这种例子就是

CloneNotSuppportedException(受查异常)。它是被Object.clone抛出来的,Object.clone

只有在实现了Cloneable的对象上才可以被调用。

被一个方法单独抛出的受查异常,会给程序员带来非常高的额外负担,如果这个方法还有其

他的受查异常,那么它被调用是一定已经出现在一个try块中,所以这个异常只需要另外一个

catch块。但当只抛出一个受查异常时,仅仅一个异常就会导致该方法不得不处于try块中,也

就导致了使用这个方法的类都不得不使用try、catch语句,使代码可读性也变低了。

受查异常使接口声明脆弱,比如一开始一个接口只有一个声明异常

interfaceUser{  //修改用户名,抛出安全异常  publicvoid changePassword() throws MySecurityExcepiton;
} 

但随着系统开发,实现接口的类越来越多,突然发现changePassword还需要抛出另一个异

常,那么实现这个接口的所有类也都要追加对这个新异常的处理,这个工程量就很大了。

总结:如果不是非用不可,尽量使用非受查异常,或将受查异常转为非受查异常。

6.自定义异常

我们用自定义异常来实现一个登录报错的小应用

class NameException extends RuntimeException{//用户名错误异常public NameException(String message){super(message);}
}
class PasswordException extends RuntimeException{//密码错误异常public PasswordException(String message){super(message);}
}

test类来测试运行

public class Test {private static final String name = "bit";private static final String password ="123";public static void Login(String name,String password) throws NameException,PasswordException{try{if(!Test.name.equals(name)){throw new NameException("用户名错误!");}}catch (NameException e){e.printStackTrace();}try {if(!Test.password.equals(password)){throw new PasswordException("密码错误");}}catch (PasswordException e){e.printStackTrace();}}public static void main(String[] args) {Scanner scanner = new Scanner(System.in);String name = scanner.nextLine();String password = scanner.nextLine();Login(name,password);}
}

关于异常就到此为止了,怎么感觉还有点意犹未尽呢?

Java——你真的了解Java异常处理机制吗?相关推荐

  1. java异常详细讲解_Java异常处理机制的详细讲解和使用技巧

    一起学习 1. 异常机制 1.1 异常机制是指当程序出现错误后,程序如何处理.具体来说,异常机制提供了程序退出的安全通道.当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器. 1.2 ...

  2. java finally 抛出异常_java的异常处理机制(try catch finally)

    1 引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过, 我亲自体验的"教训"告诉我,这个东西可不是想象 ...

  3. 深入理解java异常处理机制

    1. 引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象 ...

  4. java 异常机制_深入理解Java异常处理机制

    一.引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象中 ...

  5. Java基础——Java异常处理机制

    一.引言 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象中 ...

  6. Java异常处理机制很有意思

    版权声明:欢迎转载,请注明沉默王二原创. https://blog.csdn.net/qing_gee/article/details/43015379 前言:在网络上看到一篇<深入理解Java ...

  7. Java 异常处理机制

    异常处理是程序设计中一个非常重要的方面,也是程序设计的一大难点,从C开始,你也许已经知道如何用if...else...来控制异常了,也许是自发的,然而这种控制异常痛苦,同一个异常或者错误如果多个地方出 ...

  8. java异常处理代码详解_Java异常处理机制总结

    概念介绍 异常是发生在程序执行过程中阻碍程序正常执行的错误事件,当一个程序出现错误时,可能的情况有如下3种: 语法错误 代码的格式错了,某个字母输错了 运行时错误 空指针异常,数组越界,除数为零等 逻 ...

  9. java 只有try 不catch_Java异常处理只有Try-Catch吗?

    今天,我们将讨论一个非常重要的主题-Java 中的异常处理.尽管有时可能会对此主题进行过多的讨论,但并非每篇文章都包含有用且相关的信息. Java 中最常见的异常处理机制通常与 try-catch 块 ...

最新文章

  1. BZOJ2055 80人环游世界
  2. java 判断数的位数_Java判断数字位数的两种方法
  3. pyqt 槽任意参数_PyQt5快速入门(二)PyQt5信号槽机制
  4. Qt中rcc工具简介
  5. redis linux无法启动服务,CentOS7 下redis不能开机启动,求解?
  6. hibernate 懒加载_Hibernate懒/急加载示例
  7. 将z-blog改成英文blog所遇到的问题
  8. 详解手把手Maven搭建SpringMVC+Spring+MyBatis框架(超级详细版)
  9. C++STL笔记(四):vector详解
  10. [转]导出数据到Excel的几种方法
  11. 天语手机android 4.4.4,天语 V8 4.4.4 ROM刷机包 MIUI 6 合作版
  12. 一位资深程序员大牛给予Java的学习路线建议
  13. centos6 安装glibc-2.14.1
  14. 《软件方法》第8章 分析 之 分析类图——知识篇Part1(20211029更新)
  15. BIN文件和HEX文件差异
  16. “神舟八号”飞船将瞄准11月1日5时58分发射
  17. 分享盘点9个可免费使用的网站CDN加速服务
  18. 通过proj4js实现不同椭球之间的坐标点的七参数转换
  19. mini2440硬件篇之Nand Flash
  20. swift学习笔记一

热门文章

  1. Siamese目标跟踪:STMTrack: Template-free Visual Tracking with Space-time Memory Networks(CVPR2021)
  2. pom env.java home_maven 内置pom属性(示例代码)
  3. 2023年普通高等学校江苏专转本考试考前提醒
  4. Linux学习-Linux系统及编程基础笔记
  5. 微信小程序之组件(一)
  6. mongodump逻辑备份策略
  7. 基本功 | Litho的使用及原理剖析(转)
  8. 关于移动硬盘分区的经验
  9. AMBA AHB的burst termination
  10. Flutter 图片、圆形头像、圆角图片....各种形状