异常:就是程序运行时候出现不正常情况。
异常的由来:Java是纯面向对象语言,异常就是Java用面向对象的思想将不正常的情况进行了封装。
比如如果访问了数组不存在的下标,java就把这个错误信息封装到ArrayIndexOutOfBoundsException(数组下标越界异常)这个类里面。
所有的异常都继承自Throwable接口。
异常的分类:
Error:非常严重的错误,联想到人就是得了不治之症。
一般很少见,也很难通过程序解决。它可能源于程序的bug,但一般更可能源于环境问题,如内存耗尽。错误在程序中无须处理,而有运行环境处理。
Exception:并不是非常严重的错误,联想到人得了感冒这种疾病,通过吃药可以治疗。
编译时异常(非运行时异常,你在写代码时候就需要处理的异常):
程序正确,但因为外在的环境条件不满足引发。
比如读取一个硬盘上图片,读取的代码逻辑是正确的,但是这个图片所在的硬盘可能出现坏道,就没法读取这个文件,未雨绸缪提前检测可能会有读取硬盘问题。
运行期异常(RuntimeException,代码运行时候抛出的异常 ):
这意味着程序存在bug,如数组越界,0被除,空指针....这类异常需要更改程序来避免,Java编译器强制要求处理这类异常。
如果抛出运行时异常,是要根据抛出的异常的信息去分析程序的错误,在写代码时候绝大部分情况下不去try-catch运行时异常。
编译时异常在写代码时候必须要处理这个异常,不处理就报错。
处理的方法有两种:
1、try-catch捕获这个异常,自己处理了这个异常
2、throws抛出异常,我不处理这个异常,抛出异常
异常的处理:
try-catch 或者try-catch-finally
try {
需要检查的代码(可能会抛出异常,也可能不会抛出异常)
} catch(异常的类型 异常类型的变量) {
捕获异常后要处理异常
} finally {
一定会被执行的代码(不管异常抛不抛出都会执行)
}
多重捕获块(多个catch):用于可能有多个异常的情况
try {
需要检测的代码(可能会抛出异常,也可能不会抛出异常)
} catch (异常的类型1 异常类型的变量1) {
捕获异常后处理异常
} catch (异常的类型2 异常类型的变量2) {
捕获异常后处理异常
} catch (异常的类型3 异常类型的变量3) {
捕获异常后处理异常
} finally {
一定会被执行的代码(不管异常抛不抛出都会执行,例如数据库释放连接)
}
自定义异常:
1、所有的异常的都是Throwable的子类
2、如果写一个运行时异常,需要继承RuntimeException
3、如果要写一个编译时异常,继承Exception
案例:模拟银行转账,可以实现存钱和取钱的功能
取钱时候如果余额不够就抛出异常 MeiQianException
1.构造一个AccountMoney类
定义一个money变量,构造方法,存钱方法,取钱方法
注意取钱方法要在钱不够时抛出异常
public class AccountMoney extends Exception{
private double money;
public double getMoney() {
return money;
}
public void setMoney(double money) {
this.money = money;
}
public AccountMoney() {
}
public AccountMoney(double money) {
this.money = money;
}
public void cunqian(double money) {
this.money += money;
}
public void quqian(double money) throws Exception {
if (this.money >= money) {
this.money -= money;
} else {
double needmoney = money - this.money;
throw new Exception("余额不足!还差:" + needmoney);
}
}
}
2.编写执行存取钱的测试代码的类
注意执行取钱时要判断有没有异常,如果捕获到异常要执行catch
public class ExceptionDemo {
@Test
public void test1() {
AccountMoney user = new AccountMoney(1000.0);
user.cunqian(500);
System.out.println(user.getMoney());
try {
user.quqian(1600);
} catch (Exception e) {
e.printStackTrace();
}
}
}
Finally:
必须执行的代码块,不管是否有异常产生,即使发生OutOfMemoryError也会执行,通常用于执行善后清理工作。
如果finally代码块没有执行,那么有三种情况可能:
1、没有进入try代码块。
2、进入try代码块,但是代码运行中出现了死循环或死锁情况。
3、进入try代码块,但是执行了System.exit()操作。
finally是在return表达式运行后执行的,此时return的结果已经暂存起来,待finally代码块执行结束后再将之前暂存的结果返回
public static void main(String[] args) {
int result = finallyNotWork();
System.out.println(result);// 10001
}
public static int finallyNotWork() {
int temp = 10000;
try {
throw new Exception();
} catch (Exception e) {
return ++temp;
} finally {
temp = 99990;
}
}
finally代码块的职责不在于对变量进行赋值等操作,而是清理资源、释放连接、关闭管道流等操作。
相对在finally代码块中赋值,更加危险的做法是在finally块中使用return操作,这样的代码会使返回值变得非常不可控,警示代码如下:
public class TryCatchFinally {
static int x = 1;
static int y = 10;
static int z = 100;
public static void main(String[] args) {
int value = finallyReturn();
System.out.println("value=" + value);
System.out.println("x=" + x);
System.out.println("y=" + y);
System.out.println("z=" + z);
}
private static int finallyReturn() {
try {
// ...
return ++x;
} catch (Exception e) {
return ++y;
} finally {
return ++z;
}
}
}
// 执行结果如下:
// value=101
// x=2
// y=10
// z=101