目录
一、什么是异常
二、异常的好处
boolean ret = false;
ret = 登陆游戏();
if (!ret) {
处理登陆游戏错误;
return;
}
ret = 开始匹配();
if (!ret) {
处理匹配错误;
return;
}
ret = 游戏确认();
if (!ret) {
处理游戏确认错误;
return;
}
ret = 选择英雄();
if (!ret) {
处理选择英雄错误;
return;
}
ret = 载入游戏画面();
if (!ret) {
处理载入游戏错误;
return;
}
......
2.EAFP 风格的代码(使用异常)
try {
登陆游戏();
开始匹配();
游戏确认();
选择英雄();
载入游戏画面();
...
} catch (登陆游戏异常) {
处理登陆游戏异常;
} catch (开始匹配异常) {
处理开始匹配异常;
} catch (游戏确认异常) {
处理游戏确认异常;
} catch (选择英雄异常) {
处理选择英雄异常;
} catch (载入游戏画面异常) {
处理载入游戏画面异常; }
......
三、捕获异常
1.基本语法
try{
有可能出现异常的语句 ;
}catch (异常类型 异常对象) {
......
}
[finally {
异常的出口
}]
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,2,3};
try{
System.out.println(array[5]);
System.out.println("haha");//此处不能被打印
}catch(ArrayIndexOutOfBoundsException e){
System.out.println("捕捉到一个数组越界异常");
}
System.out.println("hehe");
}
}
编译并运行该代码,输出如下:
捕捉到一个数组越界异常
hehe
其实ArrayIndexOutOfBoundsException是一个类
2、一些注意事项
(1)"调用栈"
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,2,3};
try{
System.out.println(array[5]);
System.out.println("haha");
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
System.out.println("捕捉到一个数组越界异常");
}
System.out.println("hehe");
}
}
编译并运行该代码,输出如下:
我们再举个例子;
import java.util.Scanner;
public class TestDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
System.out.println(n);
}
}
编译并运行该代码,输出如下:
(2)catch 只能处理对应种类的异常
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,2,3};
try{
array = null;
System.out.println(array[2]);
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
System.out.println("捕捉到一个数组越界异常");
}
System.out.println("hehe");
}
}
编译并运行该代码,输出如下:
此时, catch 语句不能捕获到刚才的空指针异常. 因为异常类型不匹配
(3)catch 可以有多个
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,2,3};
try{
array = null;
System.out.println(array[2]);
}catch(ArrayIndexOutOfBoundsException e){
e.printStackTrace();
System.out.println("捕捉到一个数组越界异常");
}catch (NullPointerException e){
e.printStackTrace();
System.out.println("捕捉到一个空指针异常");
}
System.out.println("hehe");
}
}
编译并运行该代码,输出如下:
如果多个异常的处理方式是完全相同, 也可以写成这样:
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,2,3};
try{
array = null;
System.out.println(array[2]);
}catch(ArrayIndexOutOfBoundsException | NullPointerException e){
e.printStackTrace();
System.out.println("捕捉到一个数组越界异常或者空指针异常");
}
System.out.println("hehe");
}
}
(4)也可以用一个 catch 捕获所有异常(不推荐)
public class TestDemo {
public static void main(String[] args) {
int[] array = {1,2,3};
try{
array = null;
System.out.println(array[2]);
}catch(Exception e){
e.printStackTrace();
System.out.println("发生异常");
}
System.out.println("hehe");
}
}
编译并运行该代码,输出如下:
如果没有e.printStackTrace();这条语句,我们根本不知道发生了什么异常
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestDemo {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
try{
int n = scanner.nextInt();
System.out.println(10/n);
}catch(InputMismatchException e){
e.printStackTrace();
System.out.println("输出错误!");
}catch(ArithmeticException e){
e.printStackTrace();
System.out.println("算术异常!");
}finally{
//finally一般用作资源的关闭,也可以不写finally,只写scanner.close();
scanner.close();
}
}
}
import java.util.InputMismatchException;
import java.util.Scanner;
public class TestDemo {
public static void main(String[] args) {
try (Scanner scanner = new Scanner(System.in)) {
int n = scanner.nextInt();
System.out.println(10 / n);
} catch (InputMismatchException e) {
e.printStackTrace();
System.out.println("输出错误!");
} catch (ArithmeticException e) {
e.printStackTrace();
System.out.println("算术异常!");
}
}
}
tip:
IDEA 能自动检查我们的代码风格, 并给出一些更好的建议. 如我们之前写的代码, 在 try 上有一个 "加深底色" , 这时 IDEA 针对我们的
代码提出了一些更好的建议此时把光标放在 try 上悬停, 会给出原因. 按下 alt + enter, 会弹出一个改进方案的弹窗. 我们选择其中的
replace with 'try' with resources此时我们的代码就自动被 IDEA 调整成上面的样子
(7)异常会沿着异常的信息调用栈进行传递
public class TestDemo {
public static void func() {
int[] arr = {1, 2, 3};
System.out.println(arr[100]);
}
public static void main(String[] args) {
try {
func();
} catch (ArrayIndexOutOfBoundsException e) {
e.printStackTrace();
}
System.out.println("after try catch");
}
}
编译并运行该代码,输出如下:
如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止
3.异常处理流程
- 程序先执行 try 中的代码
- 如果 try 中的代码出现异常, 就会结束 try 中的代码, 看和 catch 中的异常类型是否匹配.
- 如果找到匹配的异常类型, 就会执行 catch 中的代码
- 如果没有找到匹配的异常类型, 就会将异常向上传递到上层调用者.
- 无论是否找到匹配的异常类型, fifinally 中的代码都会被执行到(在该方法结束之前执行).
- 如果上层调用者也没有处理的了异常, 就继续向上传递.
- 一直到 main 方法也没有合适的代码处理异常, 就会交给 JVM 来进行处理, 此时程序就会异常终止
4.抛出异常
public class TestDemo {
public static int divide(int x, int y) {
if (y == 0) {
throw new ArithmeticException("抛出除 0 异常");
//我们可以根据实际情况来抛出需要的异常. 在构造异常对象同时可以指定一些描述性信息
}
return x / y;
}
public static void main(String[] args) {
System.out.println(divide(10, 0));
}
}
5.异常说明
public class TestDemo {
public static int divide(int x, int y) throws ArithmeticException {
if (y == 0) {
throw new ArithmeticException("抛出除 0 异常");
//我们可以根据实际情况来抛出需要的异常. 在构造异常对象同时可以指定一些描述性信息
}
return x / y;
}
public static void main(String[] args) {
System.out.println(divide(10, 0));
}
}
6.关于finally的注意事项
public class TestDemo {
public static int func() {
try {
return 10;
}catch(ArithmeticException e){
e.printStackTrace();
}finally{
return 20;
}
}
public static void main(String[] args) {
System.out.println(func());
}
}
编译并运行该代码,输出如下:
20
四、Java异常体系
- 顶层类 Throwable 派生出两个重要的子类, Error 和 Exception
- 其中 Error 指的是 Java 运行时内部错误和资源耗尽错误. 应用程序不抛出此类异常. 这种内部错误一旦出现, 除了告知用户并使程序终止之外, 再无能无力. 这种情况很少出现.
- Exception 是我们程序猿所使用的异常类的父类.
- 其中 Exception 有一个子类称为 RuntimeException , 这里面又派生出很多我们常见的异常类NullPointerException ,IndexOutOfBoundsException 等
-
Java 语言规范将派生于 Error 类或 RuntimeException 类的所有异常称为 非受查异常 , 所有的其他异常称为 受查 异常
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class TestDemo {
public static String readFile() {
// 尝试打开文件, 并读其中的一行.
File file = new File("d:/test.txt");
// 使用文件对象构造 Scanner 对象.
Scanner sc = new Scanner(file);
return sc.nextLine();
}
public static void main(String[] args) {
System.out.println(readFile());
}
}
编译并运行该代码,输出如下:
编译出错了,显式处理的方式有两种:
(1)在方法上加上异常说明, 相当于将处理动作交给上级调用者
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class TestDemo {
public static String readFile() throws FileNotFoundException {
// 尝试打开文件, 并读其中的一行.
File file = new File("d:/test.txt");
// 使用文件对象构造 Scanner 对象.
Scanner sc = new Scanner(file);
return sc.nextLine();
}
public static void main(String[] args) throws FileNotFoundException {
System.out.println(readFile());
}
}
(2)使用 try catch 包裹起来
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class TestDemo {
public static String readFile(){
File file = new File("d:/test.txt");
Scanner sc = null;
try {
sc = new Scanner(file);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return sc.nextLine();
}
public static void main(String[] args) {
System.out.println(readFile());
}
}
五、自定义异常类
class NameException extends RuntimeException{
public NameException(String name){
super(name);
}
}
class PasswordException extends RuntimeException{
public PasswordException(String password){
super(password);
}
}
public class TestDemo {
private static final String name = "xiaoxiao";
private static final String passwords = "123456";
public static void login(String name,String password) throws NameException,PasswordException{
if(!TestDemo.name.equals(name)){
//System.out.println("用户名错误!");
throw new NameException("用户名错误!");//如果这里括号里什么都不加,上面就不用重写构造方法
}
if(!TestDemo.passwords.equals(password)){
//System.out.println("密码错误!");
throw new PasswordException("密码错误!");
}
}
public static void main(String[] args) {
try{
login("xiaoxiao","12356");
}catch(NameException e){
e.printStackTrace();
System.out.println("用户名错误!");
}catch(PasswordException e){
e.printStackTrace();
System.out.println("密码错误!");
}
}
}
编译并运行该代码,输出如下:
注意:
- 自定义异常通常会继承自 Exception 或者 RuntimeException
- 继承自 Exception 的异常默认是受查异常
- 继承自 RuntimeException 的异常默认是非受查异常.
因此,如果希望写一个检查性异常类,则需要继承 Exception 类;如果你想写一个运行时异常类,那么需要继承 RuntimeException 类
最后,我们看一条题:
使用while循环建立类似“恢复模型”的异常处理行为,它将不断重复,知道异常不再抛出
public class TestDemo {
public static void main(String[] args) {
int i = 0;
while(i < 10){
try{
throw new Exception();
} catch (Exception e) {
e.printStackTrace();
System.out.println("尝试连接网络第"+i+"次......");
i++;
}
}
System.out.println("终于有网了!");
}
}
编译并运行该代码,输出如下: