一、异常的背景
异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。
例如:
- 代码少了一个分号
System.out.println("he")
//执行结果
Error:(5, 33) java: 需要';'
- 除以 0
System.out.println(10 / 0);
//执行结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
- 数组下标越界
int[] arr = {1,2,3};
system.out.print1n(arr[100]);
//执行结果
Exception in thread "main" java.1ang.ArrayIndexoutofBoundsException: 100
异常发生的原因有很多,通常包含以下几大类:
- 用户输入了非法数据。
- 要打开的文件不存在。
- 网络通信时连接中断,或者JVM内存溢出。
异常的三种类型:
- 检查性异常:最具代表的检查性异常是用户错误或问题引起的异常,这是程序员无法预见的。例如要打开一个不存在文件时,一个异常就发生了,这些异常在编译时不能被简单地忽略。
- 运行时异常: 运行时异常是可能被程序员避免的异常。与检查性异常相反,运行时异常可以在编译时被忽略。
- 错误: 错误不是异常,而是脱离程序员控制的问题。错误在代码中通常被忽略。例如,当栈溢出时,一个错误就发生了,它们在编译也检查不到的。
二、防御式编程
LBYL: (Look Before You Leap)在操作之前就做充分的检查。
例如:
boolean ret = login();
if (!ret) {
//登陆失败的处理
return;
}
ret = startMatch();
if (!ret) {
//处理匹配失败
return;
}
ret = enterRoom();
if (!ret) {
//处理进入房间失败
return;
}
ret = chooseHero();
if (!ret) {
//处理选择英雄失败
return;
}
EAFP:(lt’s Easier to Ask Forgiveness than Permission)“事后获取原谅比事前获取许可更容易”。也就是先操作,遇到问题再处理。
例如:
try {
登陆游戏();
开始匹配();
游戏确认();
选择英雄();
载入游戏画面();
...
} catch (登陆游戏异常) {
处理登陆游戏异常;
} catch (开始匹配异常) {
处理开始匹配异常;
} catch (游戏确认异常) {
处理游戏确认异常;
} catch (选择英雄异常) {
处理选择英雄异常;
} catch (载入游戏画面异常) {
处理载入游戏画面异常; }
......
三、捕获异常
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
try{
有可能出现异常的语句 ;
}[catch (异常类型 异常对象) {
} ... ]
[finally {
异常的出口
}]
Catch 语句包含要捕获异常类型的声明。当保护代码块中发生一个异常时,try 后面的 catch 块就会被检查。
如果发生的异常包含在 catch 块中,异常会被传递到该 catch 块,这和传递一个参数到方法是一样。
1、示例1:如果代码中出现了异常,并且没有使用try catch 异常就会由JVM自己来处理,程序就会被直接终止。
public class Exception {
public static void main(String[] args) {
System.out.println(10 / 0);
}
}
//执行结果
Exception in thread "main" java.lang.ArithmeticException: / by zero
at homework.Exception.main(Exception.java:5)
Process finished with exit code 1
2、示例2:代码中可以使用try把可能抛出异常的代码给包裹起来,使用catch来处理这样的异常。
public class Exception {
public static void main(String[] args) {
try{
double ret;
ret = 10 / 0;
System.out.println(ret);
}catch(ArithmeticException e){
System.out.println("除数不能为0.");
}
}
}
//执行结果
除数不能为0.
注意:try catch 的执行顺序:
(1)先执行try 中的代码(按顺序执行);
(2)执行try 代码的过程中,如果出现异常,就会进入到catch执行,try中剩下的代码就不再执行了;
(3)当catch 也执行完毕之后,就会继续执行后续的代码,程序没有异常终止。
3、示例3:catch 中的异常的类型需要和抛出的异常类型匹配,才能够正确的处理,否则执行不到catch中的逻辑。
public class Exception {
public static void main(String[] args) {
try {
int[] a= {1,2,3};
System. out. println(a[100]);
}catch (NullPointerException e){
System. out. println("catch中的代码");
}
System.out.println("hello" );
}
}
//执行结果
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 100
at homework.Exception.main(Exception.java:8)
注意:
- 如果try 中抛出的异常的类型和catch 中声明的类型不匹配,此时catch 中的代码就不会被执行到。
- 使用try catch的时候,必须要非常明确的知道,,try中都会抛出哪些异常。
4、示例4:如果 try 中可能抛出多种异常的话,也就需要多个catch 语句来进行处理。
public class Exception {
public static void main(String[] args) {
try {
int[] a = {1, 2, 3};
System.out.println(a[10]);
System.out.println("try中异常之后的代码");
}catch (NullPointerException e) {
System.out.println("catch空指针异常");
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("catch下标越界异常");
}
}
}
5、示例5:可以使用一个catch语句来捕获多个异常。
public class Exception {
public static void main(String[] args) {
try {
int[] a = {1, 2, 3};
System.out.println(a[10]);
System.out.println("try中异常之后的代码");
}catch (NullPointerException |ArrayIndexOutOfBoundsException e) {
System.out.println("catch异常");
}
}
}
6、示例6:finally表示最后的善后工作,例如释放资源。
public class Exception {
public static void main(String[] args) {
try {
int[] a = {1, 2, 3};
System.out.println(a[10]);
System.out.println("try中异常之后的代码");
}catch (NullPointerException |ArrayIndexOutOfBoundsException e) {
System.out.println("catch异常");
}finally {
System.out.println("finally code");
}
}
}
注意:无论是否存在异常,finally 中的代码一定都会执行到。
7、示例7 使用 try 负责回收资源
try (Scanner sc = new Scanner(System.in)) {
int num = sc.nextInt();
System.out.println("num = " + num);
} catch (Exception e) {
e.printStackTrace();
}
8、示例8:如果本方法中没有合适的处理异常的方式,就会沿着调用栈向上传递。
public static void main(string[] args) {
try {
func();
}catch (ArrayIndexOutOfBoundsException e) {
e.printstackTrace(;
}
System.out.println("after try catch") ;
}
public static void func(){
int[]arr = {1,2,3};
System.out.println(arr[100]);
}
//直接结果
java. lang.ArrayIndexoutofBoundsException : 100
9、示例9:如果向上一直传递都没有合适的方法处理异常, 最终就会交给 JVM 处理, 程序就会异常终止(和我们最开始未使用 try catch 时是一样的)。
四、抛出异常
throw主动抛出异常,被抛出的异常,其实就是一个对象。
public class Exception {
public static void main(String[] args) {
System.out.println(divide(10,0));
}
public static int divide(int x,int y) {
if (y == 0) {
throw new ArithmeticException("抛出除О异常");
}
return x / y ;
}
}
五、异常体系
- 所有的异常类是从 java.lang.Exception 类继承的子类。
- Exception 类是 Throwable 类的子类。除了Exception类外,Throwable还有一个子类Error 。
- Java 程序通常不捕获错误。错误一般发生在严重故障时,它们在Java程序处理的范畴之外。
- Error 用来指示运行时环境发生的错误。例如,JVM 内存溢出。一般地,程序不会从错误中恢复。
- 异常类有两个主要的子类:IOException 类和 RuntimeException 类。
- 受查异常:如果某个方法中抛出了这个异常,那么就必须对这个异常进行显式的处理(显式处理包含两种方案:1、直接try catch,2、使用throws声明可能会抛出这个异常)
- 非受查异常:(Error、RuntimeException)可以不显式处理。
六、异常方法
方法 | 说明 |
---|---|
public String getMessage() | 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。 |
public Throwable getCause() | 返回一个Throwable 对象代表异常原因。 |
public String toString() | 使用getMessage()的结果返回类的串级名字。 |
public void printStackTrace() | 打印toString()结果和栈层次到System.err,即错误输出流。 |
public StackTraceElement [] getStackTrace() | 返回一个包含堆栈层次的数组。下标为0的元素代表栈顶,最后一个元素代表方法调用堆栈的栈底。 |
public Throwable fillInStackTrace() | 用当前的调用栈层次填充Throwable 对象栈层次,添加到栈层次任何先前信息中。 |
七、自定义异常
实现一个简单的控制台版用户登陆程序, 程序启动提示用户输入用户名密码. 如果用户名和密码出错, 使用自定义异常的方式来处理:
package homework.exception;
import java.util.Scanner;
public class Login {
public static String username;
public static String password;
public static void main(String[] args) throws PasswordException, NameException {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入用户名:");
String username = scanner.next();
System.out.println("请输入密码:");
String password = scanner.next();
login(username,password);
System.out.println("登陆成功!");
}
public static void login(String username, String password) throws PasswordException, NameException {
if(!Login.username.equals("hello")){
throw new NameException("用户名错误!");
}
if(!Login.password.equals("abc")){
throw new PasswordException("密码错误");
}
}
}
package homework.exception;
public class NameException extends Exception {
public NameException(String message){
super(message);
}
}
package homework.exception;
public class PasswordException extends Exception{
public PasswordException(String message){
super(message);
}
}