Правильное использование исключений Java

#java #exception #exception-handling

#java #исключение

Вопрос:

У меня есть несколько вопросов о наилучшем использовании исключений в Java.

Рассмотрим приведенный ниже код:

 private String doHttpGetRequest(String url) throws IOException {
...
}

private Photo processJson(String json) throws JSON Exception{
...
}

private Photo getPhoto() throws IOException, JSON Exception {
    String url = "http://...";
    String response = doHttpGetRequest(url);
    Photo photo = processJson(response);
    photo.downloadImage();
    return photo;
}

public static void main(String args[]) {
    Photo p = null; 
    try {
        p = getPhoto();
    } catch( JSONException j ) {
        // Re-try a few times, then display user alert
    } catch( IOException e ) {
        // Re-try a few times, then display user alert
    }

    if( p!=null)
        // now display photo
}
  

Этот код предназначен для приложения Android, где соединение может быть не очень надежным, поэтому я хотел бы несколько раз повторить метод getPhoto (), чтобы посмотреть, сработает ли он, затем сбой и оповещение пользователя.

Мои вопросы:

  1. Я открываю InputStream в doHttpGetRequest(), и этот метод выдает исключение IOException. Как мне закрыть InputStream, если он вызвал исключение? Без блока finally в методе, который позволил бы мне закрыть ресурсы, я в замешательстве.

  2. В main () меня интересует только то, работает getPhoto () или нет. Было бы лучше окружить инструкции внутри getPhoto () блоком try / catch и перехватить исключение JSONException, создавая новое исключение IOException при его обнаружении? Это привело бы к необходимости перехватывать только один тип исключения в main(), что привело бы к упрощению кода и отсутствию дублирующей функциональности.

  3. Если p.getPhoto() выдает исключение, я хочу повторить попытку (возможно, дважды), а затем отобразить предупреждение пользователя, если это не удается. Существует ли общая структура программирования для этого?

Комментарии:

1. Если это код для приложения Android, то почему существует main метод? Или вы пишете свой код для рабочего стола с намерением позже перенести его на Android?

2. Этот код является просто демонстрационным кодом для целей вопроса.

Ответ №1:

  1. doHttpGetRequest() должен отвечать за закрытие inputstream. Finally-блок там был бы пригоден для закрытия (почему вы не хотите блокировать finally?).

  2. Нужно ли передавать какие-либо исключения в метод main. Вы могли бы окружать doHttpGetRequest(url) с помощью try{} и окружать processJson другим (я полагаю, что этот не выигрывает от повторных попыток). Это сделало бы main() метод более чистым.

  3. Если вы согласитесь с моим предложением на 2. вы могли бы повторить попытку следующим образом:

     String response = null;
    for (int i = 0; i < RETRY_COUNT amp;amp; response == null; i  ){
      try {
        response = doHttpGetRequest(url);
      } catch (IOException e){
        // Possibly log the error here
      }
    }
    if (response  == null) return null;
      

РЕДАКТИРОВАТЬ: Исправлена ошибка

Ответ №2:

  1. Вы можете перехватить исключение, очистить и повторно создать его. Почему вы избегаете использования finally block? Похоже, что это очень хорошо бы туда вписалось.

  2. Вероятно, потребуется больше кода, чем для обработки этих двух исключений по отдельности.

  3. Поместите это в for цикл, continue если было исключение и break если нет.

Ответ №3:

  1. Вам все равно следует использовать блокировку try-finally, чтобы вы могли закрыть ресурсы. Блок используется следующим образом:

     InputStream is;
    try {
        is = openStream(); //This is a sample code, which 
        //Do something with the stream
    } finally {
        if (is != null) is.close();
    }
      

    Таким образом, если генерируется исключение, поток в любом случае будет закрыт, и исключение будет обработано в другом месте.

  2. Я предлагаю вам создать свое собственное исключение (скажем, PhotoReadingException) и запустить его из getPhoto(), но установить его причину в исходное исключение с помощью initCause(Throwable cause) метода.

  3. Конечно, вы можете использовать цикл и переменную, которая показывает, была ли операция успешной.

     Photo p = null; 
    Exception ex = null;
    for (int tries = 0; tries < MAX_TRIES; tries  ) {
        try {
            p = getPhoto();
            break;
        } catch (PhotoReadingException photoException) {
            ex = photoException;
        }
    }
    if (ex != null) reportException(ex);
      

Комментарии:

1. Обычным способом установления причины исключения является передача исключения cause вверх по цепочке конструкторов.

2. @Stephen C Это возможно, если вы выводите свое исключение из java.lang.Exception . Однако, IOException например, в Java 1.5 нет такого конструктора. initCause однако он все еще доступен. Поскольку в OP упоминалось о написании приложения для Android, а Android до версии 2.3 основан на библиотеках Java 1.5, я решил упомянуть способ, который всегда работает.