A - 解释
SQLException 是java.lang.Exception 的子类型,它也实现了Iterable<Throwable> 类。程序员更喜欢抛出 Exception 类的不同子类型,因为在更高级别上,他们希望捕获确切的子异常类,以便他们可以确保在某些确切情况下抛出特定的异常。因此,他们可以知道异常的确切来源。
B - 示例
假设您编写了一个引发多个异常的方法。假设您获取一个 json 字符串并对其进行解析,然后将其保存在数据库中。考虑以下方法;
public boolean persistCustomer(String jsonString) throws SQLException, IOException {
Connection conn = getConnection();
PreparedStatement preparedStatement = null;
ObjectMapper objectMapper = new ObjectMapper();
try {
Customer customer = objectMapper.readValue(jsonString, Customer.class);
preparedStatement = conn.prepareStatement(PERSIST_CUSTOMER);
preparedStatement.setString (1, customer.getName());
preparedStatement.setInt (2, customer.getAge());
preparedStatement.setBoolean (3, customer.getIsRegular());
preparedStatement.executeUpdate();
return true;
} catch (IOException e) {
throw e;
} finally {
try {
if (preparedStatement != null)
preparedStatement.close();
if (conn != null)
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
在此方法中,我们将 JSON 转换为 Customer 类,并将客户类持久化到数据库中。
以下行抛出SQLException;
preparedStatement = conn.prepareStatement(PERSIST_CUSTOMER);
preparedStatement.setString (1, customer.getName());
preparedStatement.setInt (2, customer.getAge());
preparedStatement.setBoolean (3, customer.getIsRegular());
preparedStatement.executeUpdate();
prepareStatement()、setter 和 executeUpdate() 方法,它们都在抛出 SQLException 的。而且,我们将 String 中的 JSON 转换为 Customer 对象的那一行,除了 SQLException 之外,也抛出了几个异常。
Customer customer = objectMapper.readValue(jsonString, Customer.class);
readValue() 方法抛出 JsonParseException、JsonMappingException 和 IOException。所有这些都可以使用IOException 捕获,因为与 JSON 相关的异常扩展了IOException。
我将提供两个不同的示例,以便清楚地理解为什么我们需要不同类型的异常。
C - 坏习惯:使用异常捕获所有异常
public class BadPracticeExample {
public static void main(String[] args) {
MySQLUtil dbUtil = new MySQLUtil();
String jsonString = "{\"name\":\"Levent\",\"age\":31,\"isRegular\":true}";
try {
dbUtil.persistCustomer(jsonString);
} catch (Exception e) {
System.out.println("A problem occured");
}
}
}
如您所见,它捕获了异常,但如果我们需要针对两种不同的问题来源进行特殊异常处理,我们该怎么办? persistCustomer 可以抛出 IOException 或 SQLException ,如果我们需要执行不同的任务集来处理这些问题怎么办?我想在发生 SQLException 时向数据库管理员发送电子邮件,并且我想在发生 JSON 解析问题时继续,在捕获 IOException 的情况下?
在这种情况下,您不能这样做。这是上面代码 sn-p 的输出,我们只确定发生了异常,但我们不知道它的来源;
A problem occured
D - 良好实践示例 I:捕获的 SQL 异常
public class GoodPracticeExample {
public static void main(String[] args) {
MySQLUtil dbUtil = new MySQLUtil();
String jsonString = "{\"name\":\"Levent\",\"age\":31,\"isRegular\":true}";
try {
dbUtil.persistCustomer(jsonString);
} catch (SQLException e) {
System.out.println("SQL Exception catched, SQL State : " + e.getSQLState());
System.out.println("Error Code : " + e.getErrorCode());
System.out.println("Error Message : " + e.getMessage());
} catch (IOException e) {
System.out.println("Cannot parse JSON : " + jsonString);
System.out.println("Error Message : " + e.getMessage());
}
}
}
如您所见,我们同时捕获了 JSON 和 SQL 问题,在此示例中,子方法试图在没有表的情况下持久化 DB。输出如下;
SQL Exception catched, SQL State : 42000
Error Code : 1142
Error Message : INSERT command denied to user 'levent'@'example.com' for table 'CUSTOMER'
所以我们已经捕获了 SQL 异常,并且我们拥有发送警报电子邮件所需的所有参数。我们可以在 SQLException catch 块上添加额外的处理程序或实用程序方法。
D - 良好实践示例 II:IOExceptoin 遇到解析错误
public class GoodPracticeExample {
public static void main(String[] args) {
MySQLUtil dbUtil = new MySQLUtil();
String jsonString = "{\"Zname\":\"Levent\",\"age\":31,\"isRegular\":true}";
try {
dbUtil.persistCustomer(jsonString);
} catch (SQLException e) {
System.out.println("SQL Exception catched, SQL State : " + e.getSQLState());
System.out.println("Error Code : " + e.getErrorCode());
System.out.println("Error Message : " + e.getMessage());
} catch (IOException e) {
System.out.println("Cannot parse JSON : " + jsonString);
System.out.println("Error Message : " + e.getMessage());
}
}
}
如果你注意到了,我已经破坏了 JSON 以导致 IOException。现在在 json 字符串中,写入了“Zname”而不是“name”,这将导致 Jackson Parser 失败。让我们检查一下输出这段代码。
Cannot parse JSON : {"Zname":"Levent","age":31,"isRegular":true}
Error Message : Unrecognized field "Zname" (class com.divilioglu.db.utils$Customer), not marked as ignorable (3 known properties: "isRegular", "name", "age"])
at [Source: (String)"{"Zname":"Levent","age":31,"isRegular":true}"; line: 1, column: 11] (through reference chain: com.divilioglu.db.utils.MySQLUtil$Customer["Zname"])
如您所见,我们捕捉到了具体场景,我们确信,这来自 dbUtil.persistCustomer() 方法中的行,如下所示;
Customer customer = objectMapper.readValue(jsonString, Customer.class);
E - 结论
因此,通过扩展现有的异常类来创建新的异常是一种最佳实践。在开始编写代码时,您可能会认为这是一种矫枉过正,您不需要额外的异常类,但是当您需要区分问题的根源并独立处理它们时,您将需要它们。
在上面演示的这个示例中,我可以独立捕获IOException 和SQLException,并且两个异常的来源都来自同一个方法。我想区分两者,以便我可以独立处理它们。如果你只是用基类 Exception 类包装所有的 Exceptions,你就无法拥有这种灵活性。