当程序报错的时候我们给控制台返回什么呢?本篇文章是学习设计模式之美“程序报错该返回什么?”内容作的学习笔记。
返回错误码实例
- ①直接返回错误码到参数中
- ②将错误码定义为全局变量
// 错误码的返回方式一:pathname/flags/mode为入参;fd为出参,存储打开的文件句柄。
int open(const char *pathname, int flags, mode_t mode, int* fd) {
if (/*文件不存在*/) {
return EEXIST;
}
if (/*没有访问权限*/) {
return EACCESS;
}
if (/*打开文件成功*/) {
return SUCCESS; // C语言中的宏定义:#define SUCCESS 0
}
// ...
}
//使用举例
int fd;
int result = open(“c:\test.txt”, O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO, &fd);
if (result == SUCCESS) {
// 取出fd使用
} else if (result == EEXIST) {
//...
} else if (result == EACESS) {
//...
}
// 错误码的返回方式二:函数返回打开的文件句柄,错误码放到errno中。
int errno; // 线程安全的全局变量
int open(const char *pathname, int flags, mode_t mode){
if (/*文件不存在*/) {
errno = EEXIST;
return -1;
}
if (/*没有访问权限*/) {
errno = EACCESS;
return -1;
}
// ...
}
// 使用举例
int hFile = open(“c:\test.txt”, O_RDWR, S_IRWXU|S_IRWXG|S_IRWXO);
if (-1 == hFile) {
printf("Failed to open file, error no: %d.\n", errno);
if (errno == EEXIST ) {
// ...
} else if(errno == EACCESS) {
// ...
}
// ...
}
返回NULL值实例
- 缺点:①产生与NULL有关的判断逻辑,写起来繁琐,可读性也不好 ②忘记做NULL值判断时,可能会
抛出空指针异常 - 应用:查找不到的情况不属于异常,这种情况下返回NULL或是-1更合理
public class UserService {
private UserRepo userRepo; // 依赖注入,通过构造函数/函数参数的方式注入类中
public User getUser(String telephone) {
// 如果用户不存在,则返回null
return null;
}
}
// 使用函数getUser()
User user = userService.getUser("18917718965");
if (user != null) { // 做NULL值判断,否则有可能会报NPE
String email = user.getEmail();
if (email != null) { // 做NULL值判断,否则有可能会报NPE
String escapedEmail = email.replaceAll("@", "#");
}
}
返回空字符串或空集合实例
- ①空字符串 ②空集合 用这两种情况来替代NULL值,就不需要做NULL值判断了,前提是它们的返回类型就是集合或是字符串
// 使用空集合替代NULL
public class UserService {
private UserRepo userRepo; // 依赖注入
public List<User> getUsers(String telephonePrefix) {
// 没有查找到数据
return Collections.emptyList();
}
}
// getUsers使用示例
List<User> users = userService.getUsers("189");
for (User user : users) { //这里不需要做NULL值判断
// ...
}
// 使用空字符串替代NULL
public String retrieveUppercaseLetters(String text) {
// 如果text中没有大写字母,返回空字符串,而非NULL值
return "";
}
// retrieveUppercaseLetters()使用举例
String uppercaseLetters = retrieveUppercaseLetters("wangzheng");
int length = uppercaseLetters.length();// 不需要做NULL值判断
System.out.println("Contains " + length + " upper case letters.");
Redis地址格式错误时,希望程序终止
// address格式:"192.131.2.33:7896"
public void parseRedisAddress(String address) {
this.host = RedisConfig.DEFAULT_HOST;
this.port = RedisConfig.DEFAULT_PORT;
if (StringUtils.isBlank(address)) {
return;
}
String[] ipAndPort = address.split(":");
if (ipAndPort.length != 2) {
throw new RuntimeException("...");
}
this.host = ipAndPort[0];
// parseInt()解析失败会抛出NumberFormatException运行时异常
this.port = Integer.parseInt(ipAndPort[1]);
}
抛出方式
1、直接吞掉。具体的代码示例如下所示:
public void func1() throws Exception1 {
// ...
}
public void func2() {
//...
try {
func1();
} catch(Exception1 e) {
log.warn("...", e); //吐掉:try-catch打印日志
}
//...
}
2、原封不动地 re-throw。具体的代码示例如下所示:
public void func1() throws Exception1 {
// ...
}
public void func2() throws Exception1 {//原封不动的re-throw Exception1
//...
func1();
//...
}
3、包装成新的异常 re-throw。具体的代码示例如下所示:
public void func1() throws Exception1 {
// ...
}
public void func2() throws Exception2 {
//...
try {
func1();
} catch(Exception1 e) {
throw new Exception2("...", e); // wrap成新的Exception2然后re-throw
}
//...
}
- 如果 func1() 抛出的异常是可以恢复,且 func2() 的调用方并不关心此异常,我们完全可以在 func2() 内将 func1() 抛出的异常吞掉;
- 如果 func1() 抛出的异常对 func2() 的调用方来说,也是可以理解的、关心的 ,并且在业务概念上有一定的相关性,我们可以选择直接将 func1 抛出的异常 re-throw;
- 如果 func1() 抛出的异常太底层,对 func2() 的调用方来说,缺乏背景去理解、且业务概念上无关,我们可以将它重新包装成调用方可以理解的新异常,然后 re-throw。
学完啦~