文章目录
前言
首先举一个现实生活中的例子,带大家理解异常。
小李是一个骑行爱好者,有一天他骑着自行车去西藏旅行。
一种情况:骑着骑着车轱辘坏了,是个大的问题,该问题他没有能力解决不了。(error)
另一种情况:车胎漏气了,是个小问题,该问题他有能力解决,但可以不解决。(运行期exception)
另一种情况:出门前,他检查出手刹松了,该问题他必须解决,才能上路。(编译器exception)
一、异常概述及分类
1.异常概述
- 异常就是Java程序运行过程中出现的错误。
- Java中有一个类Throwable类用于描述,错误和异常。
2.异常的继承结构
- Error错误
严重性无法解决的问题。(车轱辘飞了) - Exception异常
一般性可解决的问题。
- 编译器异常:发生在编译器期间,必须要处理。(手刹松了)
- 运行期异常:发生在运行期间,选择处理或不处理。(车胎漏气)
3.异常的继承机构图
二、运行期异常-RuntimeException
1.JVM如何默认处理异常
首先我们演示一个运行期异常。
代码:
public class 运行期异常 {
public static void main(String[] args) {
int a=1;
int b=0;
System.out.println(a / b);
System.out.println("继续运行");
}
}
执行结果:
- 我们没有处理运行期异常,默认交由JVM处理,JVM处理异常的方法是:打印异常的信息,然后退出JVM,不执行接下来的操作。
2.try…catch的方式处理单个异常
我们对JVM默认处理异常的方式不满意,希望异常不影响接下来代码的运行,我们可以适用**try…catch**捕获异常,不交由JVM处理。
- 格式
try {
可能出现问题的代码 ;
}catch(异常类型 变量名){
针对问题的处理 ;
}
- 代码示例
public class 运行期异常 {
public static void main(String[] args) {
int a=1;
int b=0;
//试图捕获可能出现的异常
//try里面的代码:有可能出现异常的代码,不一定必须存在异常
//catch(异常类型 异常变量名) :一旦try里面发生了catch中所捕获的异常类型,catch里面的代码就会执行。所以catch里面就放我们处理具体处理异常的操作。
try {
System.out.println(a / b);
}catch (ArithmeticException e){
System.out.println("初始为0");
}
System.out.println("继续运行");
}
}
- 注意事项
- try中的代码越少越好
- catch中要做处理,哪怕是输出一条语句,不要将异常信息隐藏
3.try…catch的方式处理多个异常
当程序中出现多个异常时,我们可以使用多个catch进行异常的捕获。
- 多个异常时并列关系,放置顺序无所谓,如果异常类有父子关系,父类要放后面。
- 对于始料不及的异常,我们可以最后使用所有异常的父类Exception来捕获。(但是我们最好能够明确代码中可能出现的异常,不建议使用该方法)
代码示例:
public class 运行期异常2 {
public static void main(String[] args) {
int [] arr={1,2,3};
//存在角标越界异常,是运行期异常
try {
arr[10] = 20;
int a=10/0;
arr = null;
System.out.println(arr.length);
//注意:如果捕获的异常不是出现的异常的情况,仍然是交由JVM处理
}catch (ArrayIndexOutOfBoundsException a){
//调用该方法可以打印详细的异常信息
a.printStackTrace();
}catch (ArithmeticException e){
System.out.println("除数为0的异常");
}catch (Exception c){
System.out.println("其他异常");
}
System.out.println("继续执行");
}
}
//!!!注意代码的执行顺序:捕获到try中的第一个异常,并使对应的catch进行捕获处理后,下一步不再执行try内的代码,而是执行程序接下来的代码。
4.代码示例
需求:用户输入一个整数,如果不是整数需要重新输入
- 调用scanner方法:调用hasNextInt()方法进行判断
public class 运行期异常的举例 {
public static void main(String[] args) {
while(true){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个整数");
//调用hasNextInt()方法实现
if(scanner.hasNextInt()){
int i = scanner.nextInt();
System.out.println(i);
System.out.println("输入正确");
break;
}else {
System.out.println("请重新输入");
}
}
}
}
- 使用try…catch实现
public class 运行期异常的举例 {
public static void main(String[] args) {
while(true){
Scanner scanner = new Scanner(System.in);
System.out.println("请输入一个整数");
try {
int i = scanner.nextInt();
System.out.println(i);
break;
//通过该代码我们更好的了解catch如何使用,但是我们通常是输出异常信息
} catch (InputMismatchException e) {
System.out.println("输入有误,请重新输入");
}
}
}
}
三、编译期异常-非RuntimeException及其子类
1.编译器异常的处理方式一(抓)
- 使用try…catch自己捕获处理
代码示例:
public class 编译器异常处理方式1 {
public static void main(String[] args) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
//编译器异常必须处理,处理方式有两种
//1.使用try...catch自己捕获处理
try {
Date date=simpleDateFormat.parse("2020202-1");
}catch (ParseException p){
p.printStackTrace();
}
System.out.println("继续执行");
}
}
执行效果:
2.编译器异常的处理方式二(抛)
- 采用throws抛异常,甩锅给调用者,谁调用谁处理。
代码示例1:main方法抛异常,交由JVM处理异常
public class 编译器异常处理方式2 {
//2.采用throws抛异常,甩锅给调用者,谁调用谁处理。
//一般甩给main方法就不再甩了,如果继续甩到虚拟机接下来的代码就无法执行了。所以一般在main方法中捕获异常。
public static void main(String[] args) throws ParseException {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
Date date = simpleDateFormat.parse("202101-23");
System.out.println("继续执行");
}
}
代码示例2:main方法抓异常
public class 编译器异常处理方式2 {
public static void main(String[] args) {
//main方法调用了,即将存在编译器异常的代码甩给了main方法
//此时main方法可选择抛异常或者抓异常
//抛异常:交给jvm处理,不再执行接下来的代码
//抓异常:执行接下来的代码(一般需求需要抓)
try {
show();
} catch (ParseException e) {
e.printStackTrace();
}
System.out.println("继续执行");
}
public static void show() throws ParseException {
SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd");
Date d=s.parse("202020-2");
}
}
四、finally关键字和throw关键字
1.finally关键字
- 用在try…catch…语句中来释放资源,其特点是始终被执行。
- 代码示例:
public class 关键字finally {
public static void main(String[] args) {
try {
System.out.println(1/0);
}catch (Exception e){
System.out.println("catch代码执行了");
//catch里面是try里面发生了所捕获的异常,那么catch里面的代码才会执行
}finally {
//不管try里面有没有遇到异常,finally里面的代码都会执行
//常用于善后处理工作
System.out.println("finally内的代码执行了");
}
}
}
2.throw关键字
- 在功能方法内部出现某种情况,程序不能继续运行,需要进行跳转时,就用throw把异常对象抛出。
- 代码示例:
public class 关键字throw {
//throws 对编译器异常,进行抛出,抛给调用者去处理
//throw 用在方法内部,对异常进行抛出
public static void main(String[] args) {
double r = division(12, 0);
System.out.println("继续执行");
System.out.println(r);
}
private static double division(int a, int b) {
double r=0;
//一旦判断除数为0
if(b==0){
//使用throw抛出异常,后续代码不再执行
throw new ArithmeticException("除数为0");
}else{
r=a/b;
}
return r;
}
}
- 注意
使用throw抛出异常,代码将不再执行。
2.throws与throw的区别
- throws
- 用在方法声明后面,跟的**异常类名**
- 可以跟多个异常类名,用逗号隔开
- 表示抛出异常,由该方法的调用者来处理
- throws表示出现异常的一种可能性,并不一定会发生这些异常
- throw
- 用在方法体内,跟的是**异常对象名**
- 只能抛出一个异常对象名
- 这个异常对象可以是编译期异常对象,可以是运行期异常对象
- 表示抛出异常,由方法体内的语句处理
- throw则是抛出了异常,执行throw则一定抛出了某种异常
五、自定义异常
- 因为在以后的开发过程中,我们可能会遇到各种问题,而Jdk不可能针对每一种问题都给出具体的异常类与之对应, 为了满足需求,我们就需要自定义异常。
代码示例1:银行取款余额不足异常
public class 自定义异常 {
static int money = 100;
public static void main(String[] args) {
withdrawal();
}
//取款的方法
private static void withdrawal() {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入你的取款金额");
int num = scanner.nextInt();
//取款金额小于账号余额
if(num<=money){
money-=num;
System.out.println("取款成功");
}else{
//自定义异常抛出
throw new NoMoneyException("余额不足异常");
}
}
}
//自定义余额不足的异常,继承java中的异常类
class NoMoneyException extends RuntimeException{
public NoMoneyException() {
super();
}
public NoMoneyException(String message) {
super(message);
}
}
代码示例2:学生成绩输出异常
public class 自定义异常之成绩非法异常 {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入要录入的成绩");
int i = scanner.nextInt();
if(i>=0&&i<=100){
System.out.println("成绩录入成功");
}else{
throw new ScoreException("成绩有误");
}
}
}
//自定义异常
class ScoreException extends RuntimeException{
public ScoreException() {
super();
}
public ScoreException(String message) {
super(message);
}
}
所谓自定义异常也就是创建我们自定义的异常类继承Java中的异常类,并在合理的处抛出异常满足程序需求即可。
六、异常的注意事项(针对编译器异常)
- 子类在重写父类方法时,父类方法没有抛出异常,子类就不能抛出异常
- 如果父类抛出异常,子类在重写该方法时,可以抛出与父类方法一样的异常或者该异常的子类,也可以不抛异常,自己捕获。
- 子类抛出的异常,不能比父类大只能是父类异常或该异常的子类。
总结
通过本文我们详细的了解了Java中的异常机制,其实在我们实际开发中遇到运行期异常,只需要选中我们认为可能会出现问题的代码,按住快捷键**ctrl+alt+t** 捕获处理即可。对于编译器异常,按住快捷键**alt+enter**,根据需求选择抛异常或者抓异常即可。