IE盒子

搜索
查看: 151|回复: 1

开源项目中的 Java 异常处理示例

[复制链接]

3

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2023-1-12 10:12:38 | 显示全部楼层 |阅读模式

关于 Java 异常处理的 9 个技巧是:
1. 仅在异常情况下使用异常
2. 对可恢复的条件使用检查异常,对编程错误使用运行时异常
3. 避免不必要地使用检查异常
4. 支持使用标准异常
5. 抛出适合抽象的异常
6.记录每个方法抛出的所有异常
7. 在详细消息中包含故障捕获信息
8. 力求失败的原子性
9. 不要忽视异常
1. 仅在异常情况下使用异常
这一项主要是关于避免对普通控制流使用异常。
例如,不要使用异常来终止循环控制流:
try{
  Iterator<Foo> iter = ...;
  while(true) {
    Foo foo = i.next();
    ...
  }
} catch (NoSuchElementException e){
}应该使用对集合的常规迭代:
for(Iterator<Foo> iter = ...; i.hasNext();){
  Foo foo = i.next();
  ...
}我没有找到任何将异常用于常规控制流的示例。
2. 对可恢复的条件使用检查异常,对编程错误使用运行时异常
在大多数情况下,如果调用者可以恢复异常,则应使用受检异常。如果不是,则应使用运行时异常。运行时异常表示可以通过检查一些先决条件来防止的编程错误,例如数组边界和空值检查。
在下面的方法中,IllegalArgumentException 是一个 RuntimeException,它的用法表示编程错误。通过检查前提条件,通常可以避免编程错误。所以这是一个基于这个技巧的坏例子。可以通过检查前提条件来避免异常,即这里的“hasNext()”方法。(链接到源代码)
/**
* Convert a tag string into a tag map.
*
* @param tagString a space-delimited string of key-value pairs. For example, {@code "key1=value1 key_n=value_n"}
* @return a tag {@link Map}
* @throws IllegalArgumentException if the tag string is corrupted.
*/
public static Map<String, String> parseTags(final String tagString) throws IllegalArgumentException {
    // delimit by whitespace or '='
    Scanner scanner = new Scanner(tagString).useDelimiter("\\s+|=");

    Map<String, String> tagMap = new HashMap<String, String>();
    try {
        while (scanner.hasNext()) {
            String tagName = scanner.next();
            String tagValue = scanner.next();
            tagMap.put(tagName, tagValue);
        }
    } catch (NoSuchElementException e) {
        // The tag string is corrupted.
        throw new IllegalArgumentException("Invalid tag string '" + tagString + "'");
    } finally {
        scanner.close();
    }

    return tagMap;
}3. 避免不必要地使用检查异常
检查异常会强制调用者处理异常情况,因为如果没有,编译器会抱怨。过度使用受检异常会给调用者带来处理异常情况的负担。因此,必要时应使用已检查的异常。使用已检查异常的经验法则是,当异常不能通过检查前提条件来避免时,调用者可以采取一些有用的操作来处理异常。
常用的运行时异常本身就是不过度使用检查异常的例子。常见的运行时异常有:ArithmeticException、ClassCastException、IllegalArgumentException、IllegalStateException、IndexOutOfBoundExceptions、NoSuchElementException 和 NullPointerException。
在下面的方法中(源代码链接),当propertyName不是目标案例之一时,调用者无能为力,所以抛出了运行时异常。
/**
* Convert a tag string into a tag map.
*
* @param tagString a space-delimited string of key-value pairs. For example, {@code "key1=value1 key_n=value_n"}
* @return a tag {@link Map}
* @throws IllegalArgumentException if the tag string is corrupted.
*/
public static Map<String, String> parseTags(final String tagString) throws IllegalArgumentException {
    // delimit by whitespace or '='
    Scanner scanner = new Scanner(tagString).useDelimiter("\\s+|=");

    Map<String, String> tagMap = new HashMap<String, String>();
    try {
        while (scanner.hasNext()) {
            String tagName = scanner.next();
            String tagValue = scanner.next();
            tagMap.put(tagName, tagValue);
        }
    } catch (NoSuchElementException e) {
        // The tag string is corrupted.
        throw new IllegalArgumentException("Invalid tag string '" + tagString + "'");
    } finally {
        scanner.close();
    }

    return tagMap;
}3. 避免不必要地使用检查异常
检查异常会强制调用者处理异常情况,因为如果没有,编译器会抱怨。过度使用受检异常会给调用者带来处理异常情况的负担。因此,必要时应使用已检查的异常。使用已检查异常的经验法则是,当异常不能通过检查前提条件来避免时,调用者可以采取一些有用的操作来处理异常。
常用的运行时异常本身就是不过度使用检查异常的例子。常见的运行时异常有:ArithmeticException、ClassCastException、IllegalArgumentException、IllegalStateException、IndexOutOfBoundExceptions、NoSuchElementException 和 NullPointerException。
在下面的方法中(源代码链接),当propertyName不是目标案例之一时,调用者无能为力,所以抛出了运行时异常。
Override
public Object get(String propertyName) {
  switch (propertyName.hashCode()) {
    case 842855857:  // marketDataName
      return marketDataName;
    case -1169106440:  // parameterMetadata
      return parameterMetadata;
    case 106006350:  // order
      return order;
    case 575402001:  // currency
      return currency;
    case 564403871:  // sensitivity
      return sensitivity;
    default:
      throw new NoSuchElementException("Unknown property: " + propertyName);
  }
}4. 支持使用标准异常
最常重用的 Java 异常类如下。您可以在此处查看完整列表。
1. java.io.IOException
2. java.io.FileNotFoundException
3. java.io.UnsupportedEncodingException
4. java.lang.reflect.InvocationTargetException
5. java.security.NoSuchAlgorithmException
6. java.net.MalformedURLException
7. java.text. ParseException
8.java.net.URISyntaxException
9.java.util.concurrent.ExecutionException
10.java.net.UnknownHostException
前 10 名中没有一个是书中最常用的。但请注意,这些是按项目计算的,即如果一个类在项目中使用,则无论项目中有多少方法使用它,都只计算一次。所以这是由 # 个项目决定的,但由代码中的 # 个出现次数决定。
5. 抛出适合抽象的异常
抛出的异常应该与调用者执行的任务有关。本项介绍异常转换(捕获一个异常并抛出另一个异常)和异常链接(将一个异常包装在一个新的异常中,以保持异常的因果链)。
private void serializeBillingDetails(BillingResult billingResult,
        BillingDetailsType billingDetails) {

    try {
        final JAXBContext context = JAXBContext
                .newInstance(BillingdataType.class);
        final ByteArrayOutputStream out = new ByteArrayOutputStream();
        final Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty("jaxb.formatted.output", Boolean.FALSE);
        final BillingdataType billingdataType = new BillingdataType();
        billingdataType.getBillingDetails().add(billingDetails);
        marshaller.marshal(factory.createBillingdata(billingdataType), out);
        final String xml = new String(out.toByteArray(), "UTF-8");
        billingResult.setResultXML(xml.substring(
                xml.indexOf("<Billingdata>") + 13,
                xml.indexOf("</Billingdata>")).trim());
        billingResult.setGrossAmount(billingDetails.getOverallCosts()
                .getGrossAmount());
        billingResult.setNetAmount(billingDetails.getOverallCosts()
                .getNetAmount());
    } catch (JAXBException | UnsupportedEncodingException ex) {
        throw new BillingRunFailed(ex);
    }
}上述方法捕获 JAXBException 和 UnsupportedEncodingException,并重新抛出一个适合该方法抽象级别的新异常。新的 BillingRunFailed 异常包装了原始异常。所以这是异常链的一个很好的例子。异常链接的好处是保留有助于调试问题的低级异常。(链接到源代码)
6.记录每个方法抛出的所有异常
这是严重未充分使用的。大多数公共 API 都缺少 @throws Java 文档来解释抛出的异常。
这是一个很好的例子。(链接到源代码)
...
*
* @throws MalformedURLException The formal system identifier of a
* subordinate catalog cannot be turned into a valid URL.
* @throws IOException Error reading subordinate catalog file.
*/
public String resolveSystem(String systemId)
  throws MalformedURLException, IOException {
...这是一个糟糕的例子,它缺乏关于在什么情况下引发异常的信息。
* @throws Exception exception
*/
public void startServer() throws Exception {
    if (!externalDatabaseHost) {7. 在详细消息中包含故障捕获信息
private OutputStream openOutputStream(File file) throws IOException {
    if (file.exists()) {
        if (file.isDirectory()) {
            throw new IOException("File '" + file + "' exists but is a directory");
        }
        if (!file.canWrite()) {
            throw new IOException("File '" + file + "' cannot be written to");
        }
    } else {
        final File parent = file.getParentFile();
        if (parent != null) {
            if (!parent.mkdirs() && !parent.isDirectory()) {
                throw new IOException("Directory '" + parent + "' could not be created");
            }
        }
    }
    return new FileOutputStream(file, false);
}在此方法中,IOException 使用不同的字符串来传递不同的故障捕获信息。
8. 力求失败的原子性
第 8 条是关于失败的。一般规则是失败的方法不应该改变方法中对象的状态。为了尽早失败,一种方法是在执行操作之前检查参数的有效性。以下是遵循此提示的一个很好的示例。
/**
* Assigns a new int value to location index of the buffer instance.
* @param index int
* @param newValue int
*/
public void modifyEntry(int index, int newValue) {
        if (index < 0 || index > size - 1) {
            throw new IndexOutOfBoundsException();
        }

//        ((int[]) bufferArrayList.get((int) (index / pageSize)))[index % pageSize] =
        ((int[]) bufferArrayList.get((index >> exp)))[index & r] =
            newValue;
}9. 不要忽视异常
public static Bundle decodeUrl(String s) {
    Bundle params = new Bundle();
    if (s != null) {
        String array[] = s.split("&");
        for (String parameter : array) {
            String v[] = parameter.split("=");
            try {
                params.putString(URLDecoder.decode(v[0], "UTF-8"), URLDecoder.decode(v[1], "UTF-8"));
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        }
    }
    return params;
}在生产代码中几乎总是应该避免打印堆栈跟踪。这与忽略异常一样糟糕。这将写入标准错误流,这不是日志使用日志框架的地方。(链接到源代码)
如果本文对你有帮助,别忘记给我个3连 ,点赞,转发,评论,咱们下期见。收藏 等于白嫖,点赞才是真情。

回复

使用道具 举报

2

主题

9

帖子

18

积分

新手上路

Rank: 1

积分
18
发表于 2025-5-26 03:37:02 | 显示全部楼层
前排支持下了哦~
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表