在企业级开发中,异常的处理和学习中的异常处理差别还是挺大的;
在学习中我们了解的是异常的分类和处理异常的方法,而在真实工作中我们要注重异常处理是否得当,因为不得当的异常处理经常会导致程序出现这样那样的问题。
我们从四个方向讲解一下异常处理的核心知识:
- 异常的认识与异常的分类
- 异常的产生过程
- 异常的处理
- 多线程中的异常
异常的认识和异常的分类
首先确定,Java的异常机制的目的是帮助我们快速确定程序中的问题
异常的根类是java.lang.Throwable,其中他有两个子类:
java.lang.Error:指的是工程师不能处理只能避免的致命错误
java.lang.Exception:指的是使用不当操作可以避免
我们在开发中常说的异常是指的Exception类的异常,这一类异常要求我们在出现问题时要及时的给出问题的解决方案,及时更正代码、修复程序
那根据JDK业务错误异常不同我们异常还分为:
编译时异常(检查时异常):checked异常,在编译时期,就会检查,如果没有处理异常,则编译失败。
运行时异常:runtimpe异常。在运行时期,检查异常在编译时期,运行异常不会编译器检测不报错
总结:
在Java异常中我们必须要处理的是Exception类异常,根据处理阶段区分我们在编码时就必须要处理的异常是检查时异常;在运行过程中可能出现的异常是运行时异常;error是致命错误如:内存溢出、io断流、cpu异常等问题不是我们jvm所抛出的而是外部因素影响的问题我们只能规避遇到时只能借助外部系统帮助我们容错。
异常产生的过程
java的异常机制除外部系统错误导致,内部检查时异常和运行时异常都是在触发问题时创建了一个“预警类”也就是异常类,通过方法调用传递的逆向路径向上传导,从而达到在调用处获取异常信息的作用。
下面就以一个数组下标越界错误来展示,jdk中异常的传递过程;
过程解析:
1.我们定义了一段数组代码,故意产生异常:
int [] arr = {1,2,3};
int c = getElement(arr,3);
public static int getElement(int [] arr,int index){
int ele = arr[index];
return ele;
}
2.访问了数组中的3索引而数组是没有3索引的,这时候jVM就会检测出程序会出现异常
JVM会做两件事情:
(1)new ArrayIndexOutofBoundsException("3"):
(2)在getElement方法中,没有异常的处理逻辑(ry-.catch),那么JVM就会把异常对象抛出给方法的调用者main方法来处理这个异常
3.getElement方法把异常抛给调用的main方法处
4.main方法接收到了这个异常对象main方法也没有异常的处理逻辑继续吧对象抛出给main方法的调用者JVM处理
5.JVM接收到了这个异常对象,做了两件事情:
(1)把异常对象(内容,原因,位置)以红色的字体打印在控制台
(2)JVM会终止当前正在执行的java程序-->中断处理
异常的处理
在学习中我们学习的异常处理方式主要有以下几个:
1.异常捕获和处理
try{
}catch(Exception e){
}finally{
}
2.方法上异常的声明
public void method0()thorws Exception{...}
3.主动抛出异常
throw new ArraIndexOutOfBoundsException("XXX");
这几种异常处理的方式从规则上可以如下总结:
1.检查时异常或必要处理异常try-catch一定要做
2.当finally块出现时一定务必注意流程处理
3.注意异常覆盖和finally让main或其他方法非正常闭环问题
4.抛出异常注意继承问题中的范围界定
5.主动抛出异常注意代码业务的合理性不要牛头不对马嘴
其中,我们重点说明,finally代码块非正常退出问题:
因为在开发中,如果不注意此类问题,经常会导致关键异常没有传递,而被正常返回值所替代。
第一种:控制转移,逻辑替代
try{
throw new Exception("错误");
}catch(Exception e){
throw e;
}finally{
return true;//控制出现转移,直接返回true而不再返回catch块,吃掉了异常
}
第二种:“异常打架”
try{
throw new Exception("错误");
}catch(Exception e){
throw e;
}finally{
throw new Exception("错误2222");//控制转移,吃掉cathc的异常而抛出了finally的异常
}
总结:
在开发过程中一定要注意异常覆盖问题,因为没有向调用处抛出真实的问题会导致异常处理流程没有按既定规则行走,从而出现未知的、不可追寻的错误异常,导致维护成本升高。
同时,推荐使用try-with-resource语句
try-with-resource语句:
将创建资源的操作写在try()的括号中,那就不需要在finally中编写关闭资源的操作了,减少代码开发量,让代码变得更加美观。
目的:实现异常的处理减少finally覆盖问题,我们可以在异常抑制数组中找到会被覆盖的异常们(Suppressed数组)
要求:JDK >= 1.7
static String readFirstLineStrings(String path) throws IOException {
try (BufferedReader br = new BufferedReader(new FileReader(path))) {
return br.readLine();
}
}
在之前版本中处理的方式:
public static String getFirstLineStrings(String path) throws IOException {
BufferedReader r = new BufferedReader(new FileReader(path));
try {
return r.readLine();
}catch (IOException e){
System.out.println("读取中发生异常");
}finally {
if(r!=null)
r.close();
}
return null;
}
多线程中的异常
多线程异常问题,在普通开发模式中很少遇见,除非出现如:主线程要管理所有子线程进行情况,同时要根据子线程问题反馈综合事务闭环状态时会使用到父子线程异常管理机制。
首先,我们知道,线程的概念中有“边界”的划分,每个线程的边界其实都在run方法中,就如同主线程在main方法实例中,那我们怎么“跨越边界”获取子线程异常呢?
我们讲解两种方法,其实原理合一。
第一种方法:手动编写集中处理方法,但是在同一个线程类中
class MyThread extends Thread {
public MyThread(String threadName){
this.setName(threadName);
}
@Override
public void run() {
try{
System.out.println(3/0);
}catch (Exception e){
System.out.println("线程出现异常!");
e.printStackTrace();
}
}
}
class MyThread extends Thread {
public MyThread(String threadName){
this.setName(threadName);
}
public void threadException(Thread thread,Throwable throwable){
System.out.println(thread.getName()+"出现异常");
throwable.printStackTrace();
}
@Override
public void run() {
Throwable nowThrowable = null;
try{
System.out.println(3/0);
}catch (Exception e){
System.out.println("线程出现异常!");
nowThrowable = e;
}finally {
threadException(Thread.currentThread(),nowThrowable);
}
}
}
传统解决方案:
在每个线程内部run()方法内通过try catch捕获当前线程发生的异常。
缺点:
每个线程都需要编写重复冗余的try catch 代码。
新解决方案:
使用线程异常处理器UncaughtExceptionHandler
class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("线程:"+t.getName());
System.out.println("异常:"+e.toString());
}
}
public static void main(String[] args) {
MyThread t1 = new MyThread("线程1");
t1.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
try {
t1.start();
}catch (Exception e) {
System.out.println("!!主线程捕获!!");
e.printStackTrace();
}
}
使用:
public static void main(String[] args) {
MyThread t1 = new MyThread("线程1");
MyThread t2 = new MyThread("线程2");
MyThread t3 = new MyThread("线程3");
MyThread t4 = new MyThread("线程4");
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
try {
t1.start();
t2.start();
t3.start();
t4.start();
}catch (Exception e) {
System.out.println("!!主线程捕获!!");
e.printStackTrace();
}
}
标签:Exception,Java,企业级,try,MyThread,线程,new,异常
From: https://www.cnblogs.com/DQGonoes/p/16612043.html