22. 异常(Eception)
1. 现实生活的病
现实生活中万物在发展和变化会出现各种各样不正常的现象。
1)例如:人的成长过程中会生病。
|——病
|——不可治愈(癌症晚期)
|——可治愈
|——小病自行解决(上火,牙痛)
|——去医院(感冒,发烧)
同时我们的java也可以诊断和处理这些异常
注意:
除了RunntimeException和它的子类以外,其他的都是编译时异常,我们可以在api文档中查找,并不需要死记硬背
2. java异常体系图
|——Throwable (实现类描述java的错误和异常)
|——Error (错误)一般不通过代码去处理。
|——Exceprion (异常)
|——RuntimeException (运行时异常)jvm不强制要求进行处理
|——编译时异常 jvm强制要求进行处理
1)苦恼1:为什么Error错误一般不需要代码去处理呢?
因为有些错误程序员是很难避免的,例如内存溢出,jvm默认管理的内存为64G如果超出这个范围就会报错
//定义一个1G内存的数组
byte[] buf = new byte[2014*2014*2014];
System.out.println(buf);
报错:
错误(Error):
它指的是一个合理的应用程序不能截获的严重的问题。大多数都是反常的情况。错误是JVM的一个故障(虽然它可以是任何系统级的服务)。
所以,错误是很难处理的,一般的开发人员是无法处理这些错误的。比如内存溢出.
2)苦恼2:为什么编译时异常jvm强制要求处理,而运行时异常jvm不强制要求处理呢?
因为运行时异常可以通过程序员良好的编程习惯而避免,程序员可以直接找到问题代码进行处理,防止这种情况发生
例如:
当我们执行除法运算的时候我们只需要在方法里面加一个if判断(被除数不能为0),就可以避免被除数为0的情况
而不需要抛出处理或者try...catch
注意:
a.运行时异常编译器不会检查程序员是否处理该异常,所以我们应该尽量避免这种异常的出现
3.自行处理(try...catch)
1)单个异常处理
例如:
class Demo2
{
public static void main(String[] args)
{
div(4,0);
}
public static void div(int a , int b){
try{
System.out.println(a/b);//可能出现的异常
}catch(ArithmeticException e){//异常匹配
e.printStackTrace();
System.out.println("除数不能为0");
}
System.out.println("运算除法");
}
}
注意:
a.如果不用处理异常,在程序运行到病态代码时,后面的代码不会执行。如果处理了,后面的代码会正常执行
b.如果我们需要一次处理多个异常我们需要在后面多添加几个catch语句
2)多个异常处理
例如:
class Demo2
{
public static void main(String[] args)
{
int[] arr = { 1, 2 };
arr = null;
div(4,0,arr);
}
public static void div(int a , int b, int[] arr){
try{
System.out.println(arr[1]);//会报空指针异常
System.out.println(a/b); //运算异常
}catch(ArithmeticException e){//异常匹配
e.printStackTrace();
System.out.println("除数不能为0");
}catch(NullPointerException e){
e.printStackTrace();
System.out.println("数组为空");
}
System.out.println("运算除法");
}
}
注意:
a.try中多个异常同时出现,只会处理第一条出现异常的语句,剩余的异常不再处理。
苦恼:如果是这样,难道我要记住所有的预定义异常吗?
其实我们可以用多态的方式来捕获异常,进行处理
例如:
class Demo2
{
public static void main(String[] args)
{
div(4,0);
}
public static void div(int a , int b){
try{
System.out.println(a/b); //运算异常
}catch(Exception e){//异常匹配
e.printStackTrace();
System.out.println("除数不能为0");
}
}
}
注意:
因为Exception是所有异常的父类所以无论try语句块中出现何种错误,都会进行处理,这样导致的是,我们没办法对特定异常进行特定的处理。
所以我们一般在catch(Exception e)前捕获一些我们知道可能要出现异常的代码,而把catch(Exception e)写在后面防止我们不知道的异常发生
3.抛出异常(throw throws)
1)语法
throws 异常名 声明异常
throw new 异常名 抛出异常
2)代码:
class Demo2
{
public static void main(String[] args)
{
try{
div(4,0);
}catch(Exception e){
e.printStackTrace();
}
}
public static void div(int a , int b) throws Exception{
if(b == 0){
throw new Exception("除数为0");
}
}
}
注意:
a.如果调用抛出异常的方法时,调用者必须处理异常或者抛出代码.
苦恼:main方法抛出异常谁去处理呢?
因为程序运行时jvm会调用main方法,所以如果main方法抛出异常jvm会自动处理
代码:
public static void main(String[] args) throws Exception
{
div(4,0);
}
在实际的开发中预定义的异常并不能帮我们解决所有问题,这时我们就需要自定义异常了
4.自定义异常
实例代码:
/*
需求:模拟自己去吃木桶饭,如果没有带够10块,就抛出异常
带够就吃木桶饭
Exception的构造方法:
1. Exception() 构造详细消息为 null 的新异常。
2. Exception(String message) 构造带指定详细消息的新异常。
3. Exception(String message, Throwable cause) 构造带指定详细消息和原因的新异常。
4. Exception(Throwable cause)
根据指定的原因和 (cause==null ? null : cause.toString()) 的详细消息构造新异常(它通常包含 cause 的类和详细消息)。
*/
class MoneyException extends Exception{
public MoneyException(String message){
//调用Exception一个参数的构造方法
super(message);
}
}
class Demo1{
public static void main(String[] args) {
int money = 5;
try{
eat(money);
}catch(MoneyException e){
e.printStackTrace();
System.out.println("跟我洗一个月的碗吧!");
}
}
public static void eat(int money)throws MoneyException{
if(money<10){
throw new MoneyException("吃霸王餐");
}
System.out.println("吃香喷喷的饭吧!");
}
}
5. finally语句
3)定义:
无论程序正常还是异常,都会执行finally,除非jvm停止(System.exit(0))
2)运用:
因为我们不能对一个文件同时进行修改和删除,当一个人在删除文件时代码报错,这个时候我们就要释放资源(finally一般用于释放资源)
3)语法:
a.try{ // 可能发生异常的代码 } catch( 异常类的类型 e ){ // 当发生指定异常的时候的处理代码 }catch...
比较适合用于专门的处理异常的代码,不适合释放资源的代码
b.try{ } catch(){} finally{ // 释放资源的代码 }
比较适合用于既要处理异常又有资源释放的代码
C.try{ }finally{ // 释放资源 }
比较适合处理的都是运行时异常且有资源释放的代码。