异常1(Java)

1. 认识异常

1.1 初始异常

在我们刚开始接触Java SE的时候,也接触到了一些异常。如限免
(1)除以0

System.out.printLn(5/0);

执行结果如下:
在这里插入图片描述
(2)数组下标越界

int [] arr={1,2,3};
        System.out.println(arr[10]);

在这里插入图片描述
(3)访问null对象

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

在这里插入图片描述
那么我们在学习Java SE 的时候接触了这么多的异常,那么什么呢?异常就是程序在运行时 出现错误时通知调用者的一种机制。
关键字“运行时”
我们在写代码时,有时候会出现这样的错误,有些关键字写错。比如System.out.println写成system.out.println,此时编译过程中就会出错,这是“编译期”出错。
而运行时指的是程序已经编译通过得到了class文件了,再由JVM执行过程中出现的错误。

1.2 防御式编程

错误在代码中时客观存在的,因此我们要让程序出现问题的时候及时通知程序员。我们主要有两种主要的方式:
(1)LBYL :Look Before You Leap,在操作之前做好充分的检查;
(2) EAFP: It is Easier to Ask Forgiveness than Permission."事后获取原谅此事前获取许可更容易“,也就是先操作,遇到问题在处理。

1.3 异常的好处

我在这里用伪代码演示一局王者荣耀的过程:
LBYL 风格的代码(不使用异常)

boolean ret=false;
ret=登录游戏();
if(!ret){
	处理登录游戏错误;
	return;
	}
ret=开始匹配();
if(!ret){
	处理匹配错误;
	return;
	}
ret=游戏确认();
if(!ret){
	处理游戏确认错误;
	return;
	}
ret=选择英雄():
if(!ret){
	处理选择英雄错误;
	return;
	}
ret=载入游戏画面();
if(!ret){
	处理载入游戏错误;
	return;
	}

EAFP 风格的代码

try{
	登录游戏();
	开始匹配();
	游戏确认();
	选择英雄();
	载入画面游戏();
}catch(登录游戏异常){
	处理登录游戏异常;
}catch(开始匹配异常){
	处理匹配异常;
}catch(游戏确认异常){
	处理游戏确认异常;
}catch(选择英雄异常){
	处理选择英雄异常;
}catch(载入画面游戏异常){
	处理载入画面游戏异常;
}

对于两种不同风格的代码,我们可以发现,使用第一种方式,正确流程和错误流程是混在一起的,代码整体显得比较混乱,而第二种方式正确流程和错误是分开的,更容易理解代码。

2.异常的基本语法

2.1捕获异常

基本语法:

try{
	有可能出现异常的语句;
}[catch(异常类型 异常对象){
}...]
[finally{
	异常的出口
}]
  • try 代码块中的放的是可能出现代码的异常
  • catch代码块中放的是出现异常后的处理行为
  • finally代码块中的代码用于处理善后工作,会在最后执行
  • 其中catch和finally都可以根据情况选择加或者不加。

2.2不处理异常

public class Text {
    public static void main(String[] args) {
        int [] arr={1,2,3};
        System.out.println("before");
        System.out.println(arr[10]);
        System.out.println("after");
    }
}

在这里插入图片描述
我们发现一旦出现异常,程序就终止了,after没有正确输出。

2.3使用try catch后的执行过程

public class Text {
    public static void main(String[] args) {
        int [] arr={1,2,3};
        try{
            System.out.println("before");
            System.out.println(arr[10]);
            System.out.println("after");
        }catch(ArrayIndexOutOfBoundsException e){
            //打印出现异常的调用栈
            e.printStackTrace();
        }
        System.out.println("after try catch");
    }
}

执行结果:
在这里插入图片描述
我们发现,一旦try中出现异常,那么try代码块中的程序就不会继续执行,而是交给catch中的代码来执行,catch执行完毕后,会继续往下执行。
关于调用栈
方法之间是存在相互调用关系的,这种调用关系我们可以用"调用栈"来描述,在JVM中有一块内存空间称为"虚拟机栈"专门存储方法之间的调用关系。当代码发现异常的时候,我们就可以使用e.printStackTrace();的方式查看出现异常代码的调用栈。

2.4 catch只能处理对应种类的异常

public class Text {
    public static void main(String[] args) {
        int [] arr=null;
        try{
            System.out.println("before");
            System.out.println(arr[10]);
            System.out.println("after");
        }catch(ArrayIndexOutOfBoundsException e){
            //打印出现异常的调用栈
            e.printStackTrace();
        }
        System.out.println("after try catch");
    }
}

执行结果:
在这里插入图片描述
此时,catch语句不能捕获到刚才的空指针异常,因为异常类型不匹配。

2.5 catch可以是多个

public class Text {
    public static void main(String[] args) {
        int [] arr=null;
        try{
            System.out.println("before");
            System.out.println(arr[10]);
            System.out.println("after");
        }catch(ArrayIndexOutOfBoundsException e){
            //打印出现异常的调用栈
            System.out.println("这是个数组下标越界异常");
            e.printStackTrace();
        }catch(NullPointerException e){
            System.out.println("这是个空指针异常");
            e.printStackTrace();
        }
        System.out.println("after try catch");
    }
}

执行结果:
在这里插入图片描述
一段代码可能会抛出多种不同的异常,不同的异常有不同的处理方式,因此可以搭配多个catch代码块,如果多个异常的处理方式完全相同,也可以写成这样:

catch(ArrayIndexOutOfBoundsException|NullPointerException e){
....
}

2.6 可以用一个catch捕获所有的异常

public class Text {
    public static void main(String[] args) {
        int [] arr=null;
        try{
            System.out.println("before");
            System.out.println(arr[10]);
            System.out.println("after");
        }catch(Exception e) {
            //打印出现异常的调用栈
            e.printStackTrace();
        }
        System.out.println("after try catch");
    }
}

执行结果:
在这里插入图片描述

2.7 finally完成最后的工作

public class Text {
    public static void main(String[] args) {
        int[] arr = null;
        try {
            System.out.println("before");
            System.out.println(arr[10]);
            System.out.println("after");
        } catch (Exception e) {
            //打印出现异常的调用栈
            e.printStackTrace();
        } finally {
            System.out.println("finally code");
        }
    }
}

执行结果:
在这里插入图片描述
无论是否存在着异常,finally中的代码一定都会执行到,也可以用finally回收资源。

2.8使用try负责回收资源

上面的代码的代码的一种等价写法,将Scanner对象在try()中创建,就能保证在try执行完毕后自动调用的Scanner的close方法:

//此时当try catch代码块执行完毕之后,Scanner就直接关闭。
import java.util.Scanner;

public class Solution {
    public static void main(String[] args) {
      try(Scanner scanner=new Scanner(System.in)){
      //使用scanner进行若干操作
          int num=scanner.nextInt();
          System.out.println("num = " + num);
      }catch(Exception e){
          e.printStackTrace();
      }
    }
}

2.9 如果本方法中没有合适的处理异常的方式,就会沿着调用栈向上传递

public static void main(String [] args){
func1();
}
public static void func1(){
try{
	func2();
}catch(ArrayIndexOutOfBoundsException e){
	System.out.println("异常");
	}
public static void func2(){
//try{
	int [] a={1,2,3};
	System.out.println(a[100]);
	//}catch(ArrayIndexOutOfBoundsException e){
		//System.out.println("异常");
		//}
	}
}
//该异常可以往fun1()向上传递

如果一直向上传递,一直到最上面也没有方法来try catch,此时就回到了最开始的JVM执行的异常。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值