关于java:Java异常处理的20个最佳实践告别系统崩溃

6次阅读

共计 6121 个字符,预计需要花费 16 分钟才能阅读完成。

引言
在 Java 编程中,异样解决是一个至关重要的环节,它不仅波及到程序的稳定性和安全性,还关系到用户体验和系统资源的正当利用。正当的异样解决可能使得程序在面对不可预知谬误时,可能优雅地复原或者给出明确的反馈,而不是简略地解体退出。
文章开始前,咱们先看下思维导图相熟下有哪些异样

注释
1、尽量不要捕捉 RuntimeException(Unchecked Exception)
阿里巴巴 Java 开发手册)上这样规定:

尽量不要 catch RuntimeException,比方 NullPointerException、IndexOutOfBoundsException 等等,应该用预查看的形式来躲避。

正例
if (obj != null) {
//…
}
” + (a / b));
}
反例
try {
obj.method();
} catch (NullPointerException e) {
//…
}
如果有些异样预查看不进去呢?比如说 NumberFormatException,尽管也属于 RuntimeException,但没方法预查看,所以还是应该用 catch 捕捉解决。
2、切勿在代码中应用异样来进行流程管制
异样该当是在真正的异常情况下应用,而不是用来管制程序流程。
public class Demo {

public static void main(String[] args) {
    String input = "1,2,3,a,5";
    String[] values = input.split(",");
    for (String value : values) {
        try {int num = Integer.parseInt(value);
            System.out.println(num);
        } catch (NumberFormatException e) {System.err.println(value + "is not a valid number");
        }
    }
}

}

}
3、正当利用 finally 块
确保在 finally 块中开释资源,比方敞开文件流或数据库连贯,无论是否产生异样。
FileInputStream file = null;
try {

file = new FileInputStream("someFile.txt");
// 应用文件流

} catch (IOException e) {

e.printStackTrace();

} finally {

if (file != null) {
    try {file.close();
    } catch (IOException e) {e.printStackTrace();
    }
}

}
4、不要在 finally 块中应用 return
这会导致 try 块中的 return 语句被疏忽。
public int notGood() {

try {
    // 假如这里有逻辑代码
    return 1;
} finally {return 2;}

}
5、防止疏忽异样
即便认为某些异样不重要也不应该齐全疏忽它们,至多要记录下来。
反例
public void doNotIgnoreExceptions() {

try {} catch (NumberFormatException e) {// 没有记录异样}

}
正例
public void logAnException() {

try {} catch (NumberFormatException e) {log.error("哦,谬误居然产生了:" + e);
}

}
6、捕捉具体的子类而不是捕捉 Exception 类
对不同类型的异样给出不同的解决逻辑。
反例
try {
someMethod();
} catch (Exception e) {// 谬误形式
LOGGER.error(“method has failed”, e);
}
正例
try {

// 某些可能产生异样的操作

} catch (FileNotFoundException e) {

// 文件未找到的解决逻辑

} catch (IOException e) {

// IO 异样的解决逻辑

}
7、将所有相干信息尽可能地传递给异样
有用的异样音讯和堆栈跟踪十分重要,如果你的日志不能定位异样地位,那要日志有什么用呢?
try {

// 某些可能产生异样的操作

} catch (IOException | SQLException e) {
// Log exception message and stack trace

LOGGER.debug("Error reading file", e);

}
应该尽量把 String message, Throwable cause 异样信息和堆栈都输入。
8、应用自定义异样传递更多信息
当内置的异样类型不能满足需要时,能够创立自定义异样。
public class MyException extends Exception {

public MyException(String message) {super(message);
}

}

public void doSomething() throws MyException {

// 某些逻辑
throw new MyException("特定错误信息");

}
9、自定义异样时不要失落堆栈跟踪
在捕捉一个异样并抛出另一个异样时,保留原始异样的信息。
catch (NoSuchMethodException e) {
// 谬误形式
throw new MyServiceException(“Some information: ” + e.getMessage());
}
这毁坏了原始异样的堆栈跟踪,正确的做法是:
catch (NoSuchMethodException e) {
// 正确形式
throw new MyServiceException(“Some information: ” , e);
}
10、优先应用规范异样
在可能的状况下,应优先应用 Java 规范库中定义的异样。
public void setValue(int value) {

if (value < 0) {throw new IllegalArgumentException("值不能为负"); // 应用规范异样
}
// 设置值的逻辑

}
11、不要在生产环境中应用 printStackTrace()
在 Java 中,printStackTrace() 办法用于将异样的堆栈跟踪信息输入到规范谬误流中。这个办法对于调试和排错十分有用。但在生产环境中,不应该应用 printStackTrace() 办法,因为它可能会导致以下问题:

printStackTrace() 办法将异样的堆栈跟踪信息输入到规范谬误流中,这可能会裸露敏感信息,如文件门路、用户名、明码等。
printStackTrace() 办法会将堆栈跟踪信息输入到规范谬误流中,这可能会影响程序的性能和稳定性。在高并发的生产环境中,大量的异样堆栈跟踪信息可能会导致系统解体或出现意外的行为。
因为生产环境中往往是多线程、分布式的简单零碎,printStackTrace() 办法输入的堆栈跟踪信息可能并不残缺或精确。

在生产环境中,应该应用日志零碎来记录异样信息,例如  log4j、slf4j、logback 等。日志零碎能够将异样信息记录到文件或数据库中,而不会裸露敏感信息,也不会影响程序的性能和稳定性。同时,日志零碎也提供了更多的性能,如级别管制、滚动日志、邮件告诉等。
// 例如,能够应用 logback 记录异样信息,如下所示:
try {

// some code

} catch (Exception e) {

logger.error("An error occurred:", e);

}
12、不要捕捉 Throwable
Throwable 是 exception 和 error 的父类,如果在 catch 子句中捕捉了 Throwable,很可能把超出程序处理能力之外的谬误也捕捉了。
public void doNotCatchThrowable() {

try {} catch (Throwable t) {// 不要这样做}

}
13、利用 try-with-resources 主动治理资源
Java 7 引入的 try-with-resources 语句能够主动治理资源,缩小代码冗余。
try (FileInputStream input = new FileInputStream(“file.txt”)) {

// 应用资源

} catch (IOException e) {

e.printStackTrace();

}
14、为异样提供具体的上下文信息
在抛出异样时,提供足够的上下文信息,以帮忙定位和解决问题。
public void loadConfiguration(String path) throws IOException {

try {// 加载配置逻辑} catch (IOException e) {throw new IOException("加载配置文件失败,门路:" + path, e);
}

}
15、不要记录了异样又抛出了异样
这纯属画龙点睛,并且容易造成错误信息的凌乱。
反例:
try {
} catch (NumberFormatException e) {

log.error(e);
throw e;

}
要抛出就抛出,不要记录,记录了又抛出,等于多此一举。
正例:
public void wrapException(String input) throws MyBusinessException {

try {} catch (NumberFormatException e) {throw new MyBusinessException("错误信息形容:", e);
}

}
16、finally 块中不要抛出任何异样
try {
someMethod(); //Throws exceptionOne
} finally {
cleanUp(); // 如果 finally 还抛出异样,那么 exceptionOne 将永远失落
}
finally 块用于定义一段代码,无论 try 块中是否出现异常,都会被执行。finally 块通常用于开释资源、敞开文件等必须执行的操作。
如果在 finally 块中抛出异样,可能会导致原始异样被覆盖。比如说上例中,一旦 cleanup 抛出异样,someMethod 中的异样将会被笼罩
17、只抛出和办法相干的异样
相关性对于放弃代码的整洁十分重要。一种尝试读取文件的办法,如果抛出 NullPointerException,那么它不会给用户提供有价值的信息。相同,如果这种异样被包裹在自定义异样中,则会更好。NoSuchFileFoundException 则对该办法的用户更有用。
public class Demo {

public static void main(String[] args) {
    try {int result = divide(10, 0);
        System.out.println("The result is:" + result);
    } catch (ArithmeticException e) {System.err.println("Error:" + e.getMessage());
    }
}

public static int divide(int a, int b) throws ArithmeticException {if (b == 0) {throw new ArithmeticException("Division by zero");
    }
    return a / b;
}

}
18、尽早验证用户输出以在申请解决的晚期捕捉异样
再好的异样捕捉不如没有异样,所以咱们在业务开发中,能够提前验证用户输出,以在申请解决的晚期就捕捉解决异样
举个例子,咱们用 JDBC 的形式往数据库插入数据,那么最好是先 validate 再 insert,而不是 validateUserInput、insertUserData、validateAddressInput、insertAddressData。
Connection conn = null;
try {

// Connect to the database
conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");

// Start a transaction
conn.setAutoCommit(false);

// Validate user input
validateUserInput();

// Insert user data
insertUserData(conn);

// Validate address input
validateAddressInput();

// Insert address data
insertAddressData(conn);

// Commit the transaction if everything is successful
conn.commit();

} catch (SQLException e) {

// Rollback the transaction if there is an error
if (conn != null) {
    try {conn.rollback();
    } catch (SQLException ex) {System.err.println("Error:" + ex.getMessage());
    }
}
System.err.println("Error:" + e.getMessage());

} finally {

// Close the database connection
if (conn != null) {
    try {conn.close();
    } catch (SQLException e) {System.err.println("Error:" + e.getMessage());
    }
}

}
19、一个异样只能蕴含在一个日志中
反例
log.debug(“Using redis one”);
log.debug(“Using redis two”);
在单线程环境中,这样看起来没什么问题,但如果在多线程环境中,这两行紧挨着的代码两头可能会输入很多其余的内容,导致问题查起来会很好受。应该这样做:
LOGGER.debug(“Using redis one, Using redis two”);
20、对于不打算解决的异样,间接应用 try-finally,不必 catch
try {
method1(); // 会调用 Method 2
} finally {
cleanUp(); //do cleanup here
}
如果 method1 正在拜访 Method 2,而 Method 2 抛出一些你不想在 Method 1 中解决的异样,然而依然心愿在产生异样时进行一些清理,能够间接在 finally 块中进行清理,不要应用 catch 块。
总结
无效的异样解决是高质量 Java 利用开发的基石。也是 Java 面试中被问频率很高的问题,通过遵循上述 20 个最佳实际,开发者不仅可能编写出更加强壮和可保护的代码,还能应答面试中面试官的各种对于异样的问题。
最初说一句 (求关注,求赞,别白嫖我)
最近无意间取得一份阿里大佬写的刷题笔记,一下子买通了我的任督二脉,进大厂原来没那么难。
这是大佬写的,7701 页的 BAT 大佬写的刷题笔记,让我 offer 拿到手软
本文,已收录于,我的技术网站 aijiangsir.com,有大厂残缺面经,工作技术,架构师成长之路,等教训分享
求一键三连:点赞、分享、珍藏
点赞对我真的十分重要!在线求赞,加个关注我会非常感激!

正文完
 0