java什么时候用异常_深入理解Java异常的使用场景

最近在项目代码中,遇见异常滥用的情形,分析下会带来哪些后果。

1. 代码可读性变差,业务逻辑难以理解

异常流与业务状态流混在一起,无法从接口协议层面理解业务代码,只能深入到方法(Method)内部才能准确理解返回值的行为

可看一下代码:

public UserProfile findByID(longuser_id) {

Map cond = new HashMap();

cond.put("id", user_id);

UserProfile userInfo= null;try{

userInfo= DBUtil.selecta(UserProfile.class, "user_info", cond);

}catch(Throwable e) {

log.error(e,"UserProfile findByID");

}returnuserInfo;

}

DAO层负责数据库的基本操作,该方法返回值为查询结果用户对象数据。代码强行抓了所有的异常,并以null返回,后来人无法确认null是代表该用户不存在还是出现异常。

2. 代码健壮性变差,异常信息被随意捕捉,甚至被吃掉

同样上述代码,首先抓了Throwable这个所有异常,包括Error(后文会介绍异常体系)。代码内部隐藏了问题,只是打印了一行日志,并且让程序可以正常继续往后走,带来的不确定性和风险都很大,这也极大的影响代码的健壮。

3. 破坏架构的分层清晰,职责单一的原则,为系统扩展带来很大阻碍

随着系统的发展,往往会沉淀出一些平台系统,比如调用监控。会负责统一采集系统的各类信息,因为这样的错误异常处理,将很难统一分离出异常信息。

那我们在实际编写代码的如何正确考虑异常的使用呢?

首先了解下Java异常的设计初衷。

Exceptions are the customary way in Java to indicate to a calling method that an abnormal condition has occurred. 一个很重要的概念——不正常情形。Java异常旨在处理方法调用时不正常情形,但我们该如何理解“不正常情形”?

看下图,JDK给我们定义了以下异常体系:

f7da1533321f254be7578a5d63219244.png

根节点是Throwable,代表Java内所有可以Catch的异常都继承此,向下有两类,Exception和Error,日常用到较多的都是Exception,Error一般留给JDK内部自己使用,比如内存溢出OutOfMemoryError,这类严重的问题,应用进程什么都做不了,只能终止。用户抓住此类Error,一般无法处理,尽快终止往往是最安全的方式,既然什么都干不了就没必要抓住了。Exception是应用代码要重点关心的,其下又分为运行时异常RuntimeException和编译时异常,各自区分就不在详述了。

了解异常的结构后,接下来解决两个重要问题,何时抛异常和抛什么异常,何时抓异常和抓什么异常 何时会有异常抛出,总结起来有以下三个典型的场景:

调用方(Client)破坏了协议

说白了就是调用方法时没有按照约定好的规范来传参数,典型的比如参数是个非空集合却传入了空值。这种破坏协议的还可以细分两类,一类是调用方从接口形式上不易觉察的规则但需要在出现时给调用方些强提示,带些信息上去,这时异常就是特别好的方式;另一类是调用方可以明确看到的规则,正常情况会正常处理协议,不会产生破坏,但可能因为bug导致破坏协议。

public voidmethod(String[] args) {int temperature = 0;if (args.length > 0) {try{

temperature= Integer.parseInt(args[0]);

}catch(NumberFormatException e) {throw newIllegalArgumentException("Must enter integer as first argument, args[0]="+args[0],e);

}

}//其他代码

}

要求传入整数,但转换数字时出错,此时可抛出特定异常并附上提示信息

(Method)知道有问题,但自己处理不了

这里"有问题",可能是调用方破坏了协议,但是单独提出来是要从被调用方出发考虑,比如Method内部有读取文件操作,但发现文件并不存在

1 public static voidmain(String[] args) {2 if (args.length == 0) {3 System.out.println("Must give filename as first arg.");4 return;5 }6 FileInputStream in = null;7 try{8 in = new FileInputStream(args[0]);9 }10 catch(FileNotFoundException e) {11 System.out.println("Can't find file: " + args[0]);12 return;13 }14 //其他代码

15 }

FileInputStream在创建时抛出了FileNotFoundException,显然出现该问题时FileInputStream是处理不了的。

预料不到的情形

空指针异常、数组越界是这种典型的场景,一般是由于有代码分支被忽略

了解了何时会出现异常,但是需要抛出异常时是选择编译时异常还是运行时异常呢? 很多人可能会说,很简单啊,需要调用方catch的就编译时,否则运行时。问题来了,什么时候需要调用方catch?

分析编译时和运行时对代码编写的影响,可以总结出来区分时考虑的点有:调用方能否处理、严重程度、出现的可能性。

调用方能处理->编译时

调用方不能处理->运行时

严重程度高->运行时

出现可能性低->运行时

本人细化了这个分类的考虑过程如下:

9e46015f189e042f00bb4a0f30cc4796.png

首先从调用方开始考虑,如果是调用方破坏了协议,则抛出运行时异常,这类异常一般出现可能性较低,调用方已知,所以没必要强制调用方抓此异常。

然后如果问题出现被调用方,无法正常执行完成工作,这时候考虑该问题调用方是否可以处理,如果能处理,比如文件找不到、网络超时,则抛出编译时异常,否则比如磁盘满,抛运行时异常

解决了何时抛异常和抛什么异常,接下来是调用这些有异常的代码时,何时catch和catch什么异常呢? 攻守不分离... 免不了俗,总结一下几点供大家探讨:

不要轻易抓Throwable,图省事可能会带来巨大的隐患

1 try{2 someMethod();3 } catch(Throwable e) {4 log.error("method has failed", e);5 }

应该尽量只去抓关注的异常,明确catch的都是什么具体的异常

自己处理不了,不要抓

比如上文DB可能会有异常,在DAO层是处理不了这种问题的,交由上层处理。抓异常宜晚不宜早,抛异常宜早不宜迟。

切忌抓了,又把异常吞掉,不留下一丝痕迹

抓住异常,打行日志完事儿,不是一个好习惯。

切忌抓异常了将异常状态流和业务状态流混在一起,这样你算是彻底抛弃了Java的异常机制

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
责任链模式是一种行为型设计模式,它允许你将请求沿着处理者链进行发送,直到其中一个处理者能够处理它为止。每个处理者都有一个对下一个处理者的引用,形成了一个链式结构。 这种模式的优点在于它可以动态地组合处理者链,而不需要修改现有代码。它还允许您以灵活的方式为请求选择处理者。 下面是一个简单的Java示例,演示了如何使用责任链模式。我们将创建一个处理器链,每个处理器都能处理特定类型的请求: 首先,我们需要定义一个请求类,它将包含要处理的数据: ```java public class Request { private String requestType; private String requestData; public Request(String requestType, String requestData) { this.requestType = requestType; this.requestData = requestData; } public String getRequestType() { return requestType; } public String getRequestData() { return requestData; } } ``` 接下来,我们需要定义一个处理器接口,它将定义处理请求的方法: ```java public interface RequestHandler { void handleRequest(Request request); } ``` 然后,我们将实现几个具体处理器类,分别处理不同类型的请求: ```java public class EmailHandler implements RequestHandler { private RequestHandler nextHandler; public void setNextHandler(RequestHandler handler) { nextHandler = handler; } public void handleRequest(Request request) { if (request.getRequestType().equals("email")) { System.out.println("EmailHandler handles request: " + request.getRequestData()); } else if (nextHandler != null) { nextHandler.handleRequest(request); } else { System.out.println("No handler found for request: " + request.getRequestType()); } } } public class FaxHandler implements RequestHandler { private RequestHandler nextHandler; public void setNextHandler(RequestHandler handler) { nextHandler = handler; } public void handleRequest(Request request) { if (request.getRequestType().equals("fax")) { System.out.println("FaxHandler handles request: " + request.getRequestData()); } else if (nextHandler != null) { nextHandler.handleRequest(request); } else { System.out.println("No handler found for request: " + request.getRequestType()); } } } public class LetterHandler implements RequestHandler { private RequestHandler nextHandler; public void setNextHandler(RequestHandler handler) { nextHandler = handler; } public void handleRequest(Request request) { if (request.getRequestType().equals("letter")) { System.out.println("LetterHandler handles request: " + request.getRequestData()); } else if (nextHandler != null) { nextHandler.handleRequest(request); } else { System.out.println("No handler found for request: " + request.getRequestType()); } } } ``` 最后,我们将使用这些处理器类创建一个处理器链,并将请求发送到该链: ```java public class ChainDemo { private static RequestHandler getChainOfHandlers() { RequestHandler emailHandler = new EmailHandler(); RequestHandler faxHandler = new FaxHandler(); RequestHandler letterHandler = new LetterHandler(); emailHandler.setNextHandler(faxHandler); faxHandler.setNextHandler(letterHandler); return emailHandler; } public static void main(String[] args) { RequestHandler chain = getChainOfHandlers(); chain.handleRequest(new Request("email", "Email request")); chain.handleRequest(new Request("fax", "Fax request")); chain.handleRequest(new Request("letter", "Letter request")); chain.handleRequest(new Request("unknown", "Unknown request")); } } ``` 当我们运行该程序时,它将输出以下结果: ``` EmailHandler handles request: Email request FaxHandler handles request: Fax request LetterHandler handles request: Letter request No handler found for request: unknown ``` 这个例子演示了如何使用责任链模式来处理请求。在这个例子中,我们创建了一个处理器链,每个处理器都可以处理特定类型的请求。当请求到达链的顶部时,它将沿着链向下传递,直到找到可以处理它的处理器为止。如果没有处理器能够处理请求,则会输出一条错误消息。 总之,责任链模式可以让您以灵活和可扩展的方式组织代码,并将请求发送到处理器链中,直到找到可以处理它的处理器为止。这种模式可以用于许多不同的场景,例如请求处理、日志记录和异常处理。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值