上海建站费用,福建seo外包,百度网站的优势,深圳东门街道办事处电话目录 异常的概念和体系结构
异常的概念
异常的体系结构
异常的分类
1.编译时异常
2.运行时异常
异常的处理
防御式编程
LBYL
EAFP
异常的抛出
异常的捕获
异常声明throws
try-catch捕获并处理
finally
异常的处理流程 异常的概念和体系结构
异常的概念 在Java中…目录 异常的概念和体系结构
异常的概念
异常的体系结构
异常的分类
1.编译时异常
2.运行时异常
异常的处理
防御式编程
LBYL
EAFP
异常的抛出
异常的捕获
异常声明throws
try-catch捕获并处理
finally
异常的处理流程 异常的概念和体系结构
异常的概念 在Java中将程序执行过程中发生的不正常行为称为异常。比如经常遇到的 1.算数异常 System.out.println(10 / 0); //执行结果 Exception(异常) in thread main java.lang.ArithmeticException(算数异常): / by zero 2.数组越界异常 int arr[] {1, 2, 3}; System.out.println(arr[100]); //执行结果 Exception in thread main java.lang.ArrayIndexOutOfBoundsException(数组越界异常): Index 100 out of bounds for length 3 3.空指针异常 int[] arr null; System.out.println(arr.length); //执行结果 Exception in thread main java.lang.NullPointerException(空指针异常): Cannot read the array length because arr is null 从上述过程中可以看到Java中不同类型的异常都有对应的类来进行描述。 注意当程序出现异常之后将不会继续执行异常之后的代码。 异常的体系结构 异常种类繁多为了对不同异常或者错误进行很好的分类管理Java内部维护了一个异常的体系结构。 从上图可以看到 1.Throwable: 是指异常的顶层类其派出两个重要的子类Error和Exception 2.Error指的是Java虚拟机无法解决的严重问题比如JVM的内部错误资源耗尽等典型代表StackOverflowError栈溢出错误和OutOfMemoryError类内存不足错误一旦发生回力乏术 3.Exception异常产生之后程序员可以通过代码进行处理使程序继续执行。 异常的分类
异常可能在编译时发生也可能在程序运行时发生根据发生时机的不同可以将异常分为
1.编译时异常 在程序编译期间发生的异常称为编译时异常也称受检查异常即在编译期间一定要处理否则代码不通过 public class Person {private String name;private String gender;int age;//想要让该类支持深拷贝覆写Object类的clone方法即可Overridepublic Person clone() {return (Person)super.clone();}
}//编译时报错
//Error:java:未报告的异常错误java.lang.CloneNotSupportedException;
//必须对其进行捕获或声明以便抛出
2.运行时异常 在程序执行期间发生的异常称为运行时异常也称为非受检查异常。 RunTimeException以及其子类对应的异常都称为运行时异常。比如NullPointerException,ArrayIndexOutOfBoundsException,ArithmeticException 注意编译时出现语法性错误不能称之为异常。例如拼写错误等。此时编译过程中就会出错这是编译期出错。而运行时指的是程序已经编译通过得到class文件了再由JVM执行过程中出现的错误。 异常的处理
防御式编程 错误在代码中是客观存在的。因此我们要让程序出现问题的时候及时通知程序员。 主要的方式 LBYL
Look Before You Leap.在操作之前就做充分的检查。即事前防御型
boolean ret false;
ret 登陆游戏();
if(!ret) {//处理登陆游戏时的错误;return;
}
ret 开始匹配();
if(!ret) {//处理匹配错误;return;
}
ret 游戏确认();
if(!ret) {//处理游戏确认错误return;
}
ret 选择英雄();
if(!ret) {//处理选择英雄错误return;
}
... 缺陷正常流程和错误处理流程的代码混在一起代码整体显得较乱。 EAFP
Its Easier to Ask Forgiveness than Permission. 事后获取原谅比事前获取许可更容易. 也就是先操作, 遇到问题再处理. 即事后认错型
try {登录游戏();开始匹配();游戏确认();选择英雄();...
} catch(登陆游戏时异常) {//处理登陆游戏时异常
} catch(开始匹配时异常) {//处理开始匹配时异常
} catch(游戏确认时异常) {//处理确认游戏时异常
} catch(选择英雄时异常) {//处理选择英雄时异常
}
... 优势正常流程和错误流程是分离开的程序员更关注正常流程代码更清晰容易理解代码异常处理的核心思想就是EAFP. 在Java中异常处理的五个关键字throw, try, catch, final, throws 异常的抛出
在编写程序时如果程序出现错误此时就需要将错误的信息告知给调用者比如参数检测
在Java中可以借助throw关键字抛出一个指定的异常对象将错误信息告知给调用者。具体语法如下 throw new XXXException(异常产生的原因) 抛出异常的方式1.某段程序触发 2.通过throw关键字抛出异常。 throw一般用于抛出自定义的异常。 举个例子实现一个获取数组中任意位置元素的方法。 注意事项 1.throw必须写在方法体内部 2.抛出的对象必须是Exception或者Exception的子类对象 3.如果抛出的是RunTimeException或者RunTimeException的子类则可以不用处理可以交给JVM来处理 4.如果抛出的是编译时异常用户必须处理否则无法通过编译 5.异常一旦抛出其后的代码就不会执行 异常的捕获 异常的捕获也就是异常具体的处理方式主要有两种异常声明throws以及try-catch捕获处理。 异常声明throws 处在方法声明时参数列表之后当方法中抛出编译时异常用户不想处理该异常此时就可以使用throws将异常抛给方法的调用者来处理。即当前方法不处理异常提醒方法的调用者处理异常。 有以下几种方式可以在方法中不处理异常,而是将异常抛出给调用者处理: 1.不添加异常处理代码直接不添加try-catch等异常处理代码,让异常在方法内部抛出。 public void method() { // 操作可能抛出异常 throw new Exception(); } 2.使用throws声明抛出异常在方法声明中使用throws关键字声明可能抛出的异常类型 public void method() throws Exception { // ... } 3.重新抛出异常捕获异常后使用throw语句重新抛出 public void method() { try { // ... } catch (Exception e) { throw e; } } 4.抛出未检查异常对于运行时异常,可以直接抛出而不用声明 public void method() { throw new RuntimeException(); } 主要语法格式 修饰符 返回值类型 方法名(参数列表)throws 异常类型1 异常类型2...{ } 举个栗子加载指定的配置文件config.ini
public class Config {File file;/*FileNotFoundException:编译时异常表明文件不存在此处不处理也没有能力处理应该将错误信息报告给调用者让调用者检查文件名字是否给错误了*/public void OpenConfig(String filename) throws FileNotFoundException {if(filename.equals(config.ini)) {throw new FileNotFoundException(配置文件名字不对);}//打开文件}public void readConfig() {}
} 注意事项 1.throws必须跟在方法参数列表之后 2.声明的异常必须是Exception 或者Exception的子类 3.方法内部如果抛出了多个异常throws之后必须跟多个异常类型中间用逗号隔开如果抛出多个异常类型具有父子关系直接声明父类即可。 public class Config {File file;//public void OpenConfig(String filename)throws IOException, FileNotFoundException {//FileNotFoundException继承自IOExceptionpublic void OpenConfig(String filename) throws IOException {if(filename.endsWith(.ini)) {throw new IOException(文件不是.ini文件);}if(filename.equals(config.ini)) {throw new FileNotFoundException(配置文件名字不对);}//打开文件}public void readConfig() {}
}4.调用声明抛出异常的方法时调用者必须对该异常进行处理或者继续使用throws抛出异常实际上未被程序员处理实际上还是交给了JVM public static void main(String[] args) throws IOException {Config config new Config();config.OpenConfig(config.ini);
}
try-catch捕获并处理 throws对异常并没有真正的处理而是将异常报告给抛出异常方法的调用者由调用者进行处理就需要try-catch. 语法格式如下
try{//将可能出现异常的代码放在这里
} catch(要捕获的异常类型 e){/*如果try中的代码抛出异常了此处catch捕获时的异常类型与try中抛出的异常类型
一致时或者是try中抛出异常的基类型时就会被捕捉到*///对异常就可以正常处理处理完成后就会跳出try-catch结构继续执行后续代码
} [{catch(异常类型 e) {//对异常进行处理
} finally {//此处代码一定会被执行到
}]//后续代码
//当异常被捕获到时异常就被处理了这里的后续代码一定会被执行到
//如果捕获了由于捕获时的类型不对那就没有被捕获到这里的代码就不会执行 注意 1. [ ]中表示可选项可以添加也可以不添加 2.try中的代码可能会抛出异常也可能不会 让我们赓续上一个例子用try-catch来写一下这种异常捕获形式
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;public class Config {File file;public void openConfig(String filename) throws FileNotFoundException {if(filename.equals(config.ini)) {throw new FileNotFoundException(配置文件名字不对);}//打开文件}public void readConfig() {}public static void main(String[] args) {Config config new Config();try {//可能出现异常的代码config.openConfig(config.ini);System.out.println(文件打开成功);} catch(IOException e) {//异常的处理方式//System.out.println(e.getMessage());//只打印出异常信息//System.out.println(e);//打印异常类型异常信息e.printStackTrace();//打印信息最全面}//一旦异常被捕获处理了此处的代码会执行System.out.println(异常如果被处理了这里的代码也可以执行);}
}关于异常的处理方式 异常的种类有很多我们要根据不同的业务场景来决定 对于比较严重的问题应该让程序直接崩溃防止造成更严重的后果 对于不太严重的问题可以记录错误日志并通过监控报警程序及时通知程序员 对于可能会恢复的问题(和网络相关的场景)可以进行重试 在我们当前的代码中采取的是经过简化的第二种方式我们记录的错误日志时出现异常的方法调用信息能很快的让我们找到出现异常的位置以后工作中我们会采取更完备的方式来记录异常信息。 注意事项 1.try块内抛出异常位置之后的代码将不会被执行 2.如果抛出异常类型与catch时异常类型不匹配即异常不会被成功捕获也就不会被处理继续向外抛直到JVM收到后中断程序-异常是按照类型来捕获的 3.try中可能会抛出多个不同的异常则必须用多个catch来捕获-即多种异常多次捕获虽然可以捕获多种异常但是同一时刻只能抛出一个异常 如果多个异常方式处理方式完全相同也可以这样不太推荐 catch(NullPointerException | ArrayIndexOutOfBoundsException e){...} 如果异常之间具有父子关系一定是子类异常在前catch父类在后catch否则语法错误 public class Test2 {public static void main(String[] args) {int[] arr {1,2,3};try {System.out.println(before);arr null;System.out.println(arr[0]);System.out.println(after);} catch(Exception e) {//Exceiom可以捕获到所有异常e.printStackTrace();} catch (NullPointerException e) {//永远都可以被执行到e.printStackTrace();}System.out.println(after try catch);}
}4.可以通过一个catch捕获所有的异常即多个异常一次捕获不推荐即不能用父类接收所有的异常子类这样是不精准的可以放在后面捕获殿后 备注catch进行类型匹配的时候不光会匹配相同类型的异常对象也会捕捉目标异常类型的子类对象 finally 写程序时有些特定的代码不论程序是否会发生异常都需要执行比如程序中打开资源网络连接数据库连接IO流等在程序正常或异常退出时必须要对资源进行回收。另外因为异常引发的程序跳转有的可能执行不到finally就是用来解决该问题的。 语法格式
try{//可能会发生异常的代码//如果这里有return,仍会执行finally,finally执行时机在return之前
}catch(异常类型 e){//对捕获的异常进行处理
}finally{//此处的语句无论是否发生异常都会被执行到
} finally执行时机是在方法返回之前try或者catch中如果有return会在这个return之前执行finally.但是如果finally中也存在return语句那就会执行finally中的return从而不会执行到try中的return。一般不建议在finally中写return 异常的处理流程 调用栈 方法之间是存在相互调用的关系的这种调用关系我们可以用调用栈来描述在JVM中有一块内存空间称为“虚拟机栈”专门存储方法之间的调用关系。当代码中出现异常的时候我们就可以使用e.printStackTrace()的方式查看异常代码的调用栈 如果本方法中没有合适的异常处理方式就会沿着调用栈向上传递举个例子
public class Test3 {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]);}
}执行结果 异常处理流程总结 1.程序先执行try中的代码 2.如果try中的代码出现异常就会结束try中的代码看和catch中的异常类型是否匹配 3.如果找到匹配的异常类型就会执行catch中的代码 4.如果没有找到匹配的异常类型就会把异常向上传递到上层调用者 5.无论是否找到匹配的异常类型finally中的代码都会被执行到(在该方法结束之前执行 6.如果上层调用者也没有处理了的异常就继续向上传递 7.一直到main方法也没有合适的代码处理异常就会交给JVM处理此时程序就会异常终止