快速掌握异常(含面试题)
目录
- * 异常的体系结构
- 1. 异常的分类
- 1.1 运行时异常
- 1.2 编译时异常
- 2. 异常的处理(含面试题)
- 2.1 防御式编程
- 1. 事前防御型---LBYL
- 2. 事后认错型---EAFP
- 2.2 抛出异常
- 2.3 捕获异常---真正开始处理异常
- 3.4 finally --- 回收资源
- 3. 自定义异常类
在Java中,将程序运行中不正常的行为称为异常,比如经常遇到的数组越界异常。Java中不同类型的异常都有与其对应的类来描述。
异常一旦抛出,后边的代码不会再执行。
* 异常的体系结构
由于异常过于多,为了方便分类,Java内部维护了一个异常体系结构,如下(只展示一小部分)
- Throwable:是异常体系的顶层类,其派生出了两个类,Error和Exception。
- Error:是JVM无法解决的问题, 如JVM资源耗尽等, 如StackOverflowError(栈溢出错误)。
- Exception:是程序员能用代码处理的问题,如NullPointerException(空指针异常)。
我们主要学习的是Exception类。
1. 异常的分类
Exception类分为运行时异常(非受查异常)和编译时异常(受查异常)。
1.1 运行时异常
运行时异常能通过编译,只在运行时JVM才会抛异常。
1.2 编译时异常
编译时异常不能通过编译,编写时就会抛出异常。注意区分语法错误和变异时异常。
自己写的类想实现克隆,实现了Cloneable接口且重写了toString方法和clone方法,在main方法中克隆时依然抛出了异常
那出现了异常怎么解决?下面学习异常的处理。
2. 异常的处理(含面试题)
2.1 防御式编程
1. 事前防御型—LBYL
操作之前就进行检查。基本不用。
boolean ret = false;
ret = 登陆游戏();
if (!ret) {
处理登陆游戏错误;
return;
}
ret = 开始匹配();
if (!ret) {
处理匹配错误;
return;
}
...
2. 事后认错型—EAFP
先操作,遇到问题再处理。处理异常的经典做法就是EAFP。
//方式一
try{
存放可能有异常的代码
}catch (捕获某个的异常类) {
}
...
//方式二
try{
存放可能有异常的代码
}catch (捕获某个的异常类 | 捕获某个的异常类 | ...) {
}
注:
- 可以用多个catch捕获多个异常,该语句能让程序员自己处理异常,而不是交给JVM。
- 虽然可以捕获多种异常,但同一时刻只能捕获一个异常。
- catch不能用父类(Exception)接收所有的异常子类,但可以先catch子类,父类作最后一个catch,作垫背。
- try()内部可以加Scanner等语句
2.2 抛出异常
抛出异常有两种方式:由程序触发/通过throw关键字手动抛出
手动抛出异常的语法:throw new XXXException(“异常产生的原因”);
try{
int a = 10/0;
}catch (ArithmeticException e){
e.printStackTrace();//打印错误信息在第几行
throw new ArithmeticException("算数异常");
}
//运行结果 --- 抛异常
Exception in thread "main" java.lang.ArithmeticException: 算数异常
at MyException.main(MyException.java:25)
注:
- throw必须在方法内部。
- 抛出的对象必须是Exception或Exception子类。
- 如果抛出的是运行时异常(RunTimeException)或RunTimeException的子类,不需要处理,直接交给JVM处理即可。
- 若抛出的是编译时异常,必须处理,否则无法通过编译。
2.3 捕获异常—真正开始处理异常
处理异常有两种方式,
- 声明异常,用throws关键字,在方法声明后面抛出异常(告诉方法调用者,调用该方法会抛出某个异常),但这个异常是交给JVM处理的。
public static void main(String[] args) throws CloneNotSupportedException {
MyExc myExc = new MyExc("Bob");
MyExc myExc1 = (MyExc)myExc.clone();
System.out.println(myExc1);
}
- 手动抛出异常,throw关键字,抛出的一般是自定义的异常,该异常由程序员自己处理。
public static void main(String[] args) {
try{
int a = 10/0;
}catch (ArithmeticException e){
e.printStackTrace();
throw new ArithmeticException("算数异常");
}
}
3.4 finally — 回收资源
不论程序是否发生异常,finally中的语句一定会被执行。比如打开文件,如果抛出异常会执行不到关闭文件的代码,但必须对资源进行回收,这时放在finally内部,就能关闭文件。
finally一般在return语句之前执行,finally内不要有return语句。不然会执行其内部的return语句,执行不了try语句的return。如下,
想要得到10,进入try语句,进入finally语句,直接return20,没有执行try中的return10。
public static int func() {
try {
return 10;
} finally {
return 20;
}
}
若没有catch异常,异常抛出,后面的代码不会执行(main不会打印),但finally内的语句依旧打印
public static void main(String[] args) {
try(Scanner scanner = new Scanner(System.in)) {
//存放可能出现异常的代码
int a = scanner.nextInt();
int b = a/0;
}finally {
//不论程序是否发生异常,语句一定会被执行
System.out.println("finally");
}
//异常若被捕或没有异常,语句会被执行
System.out.println("main");
}
//运行结果
finally
Exception in thread "main" java.lang.ArithmeticException: / by zero
at MyException.main(MyException.java:26)
【面试题】:
-
throw 和 throws 的区别?
-
finally中的语句一定会执行吗?
3. 自定义异常类
自定义异常类必须继承Exception类(受查异常)或者RunTimeException(非受查异常)。示例如下,
我想抛出用户名异常和密码异常,库中没有,就需要自己写。两个异常要继承Exception类或RunTimeException类。并在throw所在的方法内throws(声明异常),在main方法中处理异常。
class userException extends Exception {
public userException(String message) {
super(message);
}
}
class passwordException extends RuntimeException{
public passwordException(String message) {
super(message);
}
}
public class LogIn {
private String userName = "admin";
private String password = "123456";
public static void loginInfo(String userName, String password) throws userException,passwordException{
if (!userName.equals(userName)) {
throw new userException("用户名错误");
}
if (!password.equals(password)) {
throw new passwordException("密码异常");
}
System.out.println("登陆成功");
}
public static void main(String[] args) {
try{
loginInfo("admin", "123456");
}catch (userException e){
}catch (passwordException e){
}
}
}