Java是否应该用异常来返回错误信息
调用Java方法时,被调用的方法如果出现了执行失败的情况,或者其他需要提示给调用者的信息,通行的做法是以下两种:
- 创建一个“结果”类,在该类中标识其是否执行成功以及失败时的失败信息
代码如下:
public Result doSomething() {
// do something...
if(success) {
Result result = new Result();
result.setSeccuss(true);
result.setObject(obj);
return result;
} else {
Result result = new Result();
result.setSeccuss(false);
return result;
}
}
- 执行失败时抛出异常
public SomeClass doSomething() throws SomeException {
// do something...
if(success) {
return new SomeClass();
} else {
throw new SomeException();
}
}
那么应该选择哪种方式呢?
效率对比
从执行效率的角度来讲,第一种方式主要的缺点是每次执行都会创建一个结果对象,不论成功还是失败;而第二种方式的缺点是当执行失败抛出异常时,会创建一个异常类,并且调用方不得不在try
语句块中调用方法,以下是通过一段简易测试代码测试出来的两种方式的执行效率对比:
循环次数n=100000000
———- 全部成功
method1 不放try中,耗时:652
method1 放在try中,耗时:1344
method2 放在try中,耗时:382
———- 全部失败
method1 不放try中,耗时:645
method1 放在try中,耗时:839
method2 放在try中,耗时:74586
结论
在执行成功时,第一种方式由于多创建了一个结果对象因此比第二种方式多用了大约二分之一的时间(如果放在try
语句块中则会比第二种方式多三倍时间),但当执行失败时,第二种方式比第一种方式所消耗的时间多出了2个数量级,也就是达到了百倍的时间损耗。
建议
- 如果失败的情形极为罕见,则显然选择第二种方式更加划算,例如读写数据库的操作,如果不发生一些紧急事件,预期每一次读写操作都应该是正常的,失败的情况应是极少数,可以选择第二种方式来提示失败信息。
- 而如果失败的情形出现的次数并不那么罕见,甚至成为了一种正常的业务状态时,例如登录操作,用户可能输入正确的密码也可能输入错误的密码,或者用户的状态已被锁定等等,这时显然就应该以第一种方式来返回失败信息。
附-测试代码
public class ExceptionTest {
public static ResultWithoutException method1(boolean success) {
if (success) {
return new ResultWithoutException(true, new Object());
}
return new ResultWithoutException(false, null);
}
public static Object method2(boolean success) throws SomeException {
if (success) {
return new Object();
}
throw new SomeException();
}
public static void main(String[] args) {
int n = 100000000;
System.out.println("循环次数n=" + n);
long l;
System.out.println("---------- 全部成功");
l = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
method1(true);
}
l = System.currentTimeMillis() - l;
System.out.println("method1 不放try中,耗时:" + l);
l = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
try {
method1(true);
} catch (Exception e) {
}
}
l = System.currentTimeMillis() - l;
System.out.println("method1 放在try中,耗时:" + l);
l = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
try {
method2(true);
} catch (SomeException e) {
}
}
l = System.currentTimeMillis() - l;
System.out.println("method2 放在try中,耗时:" + l);
l = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
method1(false);
}
System.out.println("---------- 全部失败");
l = System.currentTimeMillis() - l;
System.out.println("method1 不放try中,耗时:" + l);
l = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
try {
method1(true);
} catch (Exception e) {
}
}
l = System.currentTimeMillis() - l;
System.out.println("method1 放在try中,耗时:" + l);
l = System.currentTimeMillis();
for (int i = 0; i < n; i++) {
try {
method2(false);
} catch (SomeException e) {
}
}
l = System.currentTimeMillis() - l;
System.out.println("method2 放在try中,耗时:" + l);
}
}
class ResultWithoutException {
private boolean success;
private Object obj;
public ResultWithoutException(boolean success, Object obj) {
this.success = success;
this.obj = obj;
}
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
}
class SomeException extends Exception {
public SomeException() {
}
public SomeException(String message) {
super(message);
}
public SomeException(String message, Throwable cause) {
super(message, cause);
}
public SomeException(Throwable cause) {
super(cause);
}
public SomeException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}