Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Java: Общие вопросы > Исключения – советы по использованию


Автор: AntonSaburov 24.1.2008, 13:53
Исключения – советы по использованию

(Оригинал на сайте - http://antonsaburov.narod.ru/articles/Exceptions.html)

Обработка исключений – это встроенная возможность языка JAVA. Концепция обработки исключений позволяет сделать код более надежным и позволяет лучше читать и сопровождать его. Давайте посмотрим на конструкцию для обработки исключения:

Код

try {

   // code which could potentially
   // throw an exception

} catch (TheException1 e) {

   // code to handle exceptional condition

} catch (TheException2 e) {

   // code to handle next exceptional condition

} finally {

   // code to run whether an exceptional 
   // condition happened or not
    
}


В основном, код, который может вызвать исключение помещается в такую конструкцию. Блок catch описываемый для определенного исключения будет вызываться – т.е. если случится исключение TheException1, тогда будет вызван блок внутри его catch. Однако блок finally будет вызываться всегда, даже если какой-либо блок catch содержит return.
Таким образом думаю понятно, что блок try должен быть всегда. И всегда должен быть хотя бы один из блоков catch или finally.
Исходя из того, что блоков catch может быть много, система в случае исключения будет искать первый подходящий тип исключения. Причем с учетом наследования. Т.е. если вы ловите исключение IOException, то исключение FileNotFoundException, которрое является подклассом IOException будет обрабатываться в блоке try, который содержит IOException. Думаю, что это несложно.

Первый совет по обработке исключений: старайтесь обрабатывать специфические исключения. Т.е. если вы создали метод, который будет генерировать исключение FileNotFoundException – не декларируйте, что метод взывает исключение IOException. Нехорошо заставлять пользователя вашим классом обрабатывать исключение более высокого уровня, чем надо в действительности.

Второй совет: не делайте пустых catch блоков. Иными словами не делйте такой код.

Код

try {
     ...
} catch (AnException e) {
}


Если даже логика вашего кода подразумевает полное отсуствие каких-либо действий при исключении – не поленитесь и напишите комментарий. Иначе пользватели вашего кода будут в затруднении.

Еще один добрый совет: Если метод класса вызывает исключение – пишите на него документацию. Используйте тэг @throws.

Код

/**
 * Loads the class
 *
 * @param  name
 *         Class name
 *
 * @return  Resulting <tt>Class</tt> object
 *
 * @throws  ClassNotFoundException
 *          If class not found
 */
public Class loadClass(String name) throws ClassNotFoundException 
{
        ...
}


Наверняка вы знаете о RuntimeException и его подклассах. Эти исключения не требуют, чтобы их обрабатывали. И вообщем не всегда их обработка будет выглядет логичной и нужной.
Но в некоторых случаях это будет логично и понятно. Например если вы ожидаете от пользователя ввода целого числа, которе вводится как строка. Для преобразования скорее всего будет использован методв parseInt, который вызывает runtime исключение NumberFormatException. Обычно runtime исключения обрабатываются тогда, когда необходимо восстановление при таких исключениях. Как в выше приведенном примере с parseInt.


Новые возможности Java 1.5 – UncaughtExceptionHandler

Мы уже говорили о том, что есть non-runtime исключения, которые вы обязаны помещать в блок try/catch и runtime, которые не требуют специальной обработки. Но бывают случаи, когда требуется обрабатывать и их – например вы хотите выводить все сообщения в определенном формате в какое-то окно сообщений. Расставлять по всему коду блоки try/catch конечно не очень удобное занятие.
Существуют три способа сделать обработку runtime исключений.
1.    Вызвать метод setUncaughtExceptionHandler() у класса Thread.
2.    Определить свой класс ThreadGroup и переопределить метод uncaughtException().
3.    Вызвать статический метод класса Thread - setDefaultUncaughtExceptionHandler().

Методы setUncaughtExceptionHandler() и setDefaultUncaughtExceptionHandler() принимают в качестве аргумента класс, который реализует интерфейс UncaughtExceptionHandler.
Этот интерфейс имеет всего один метод 

Код

void uncaughtException(Thread t, Throwable e)


Рассмотрим в качесвте примера два класса.

Первый класс представляет из себя окно, которое будет «всплывать» при возникновении исключнения. Окно реализует интерфейс UncaughtExceptionHandler.
Надо обратить внимание на два момента:
1.    Это метод uncaughtException. Он реализует интерфейс 
2.    Метод addStackInfo. Вообщем-то в нем и происхожит основная работа.

Чтобы вы не пугались - EventQueue.invokeLater представляет из себя метод, который вызывается «в очереди» и что важно отметить делается асинхронно (в отдельном треде).

Все остальные вызовы достаточно очевидны – сделать окно видимым, вывести его на передний план, создать два потока для печати строк и вывести строки. Вообщем-то и все.

Код

import java.awt.*;
import java.io.*;
import javax.swing.*;

public class StackWindow extends JFrame implements Thread.UncaughtExceptionHandler 
{
  private JTextArea textArea;

  public StackWindow(String title, final int width, final int height) 
  {
    super(title);
    setSize(width, height);
    textArea = new JTextArea();
    JScrollPane pane = new JScrollPane(textArea);
    textArea.setEditable(false);
    getContentPane().add(pane);
  }

  public void uncaughtException(Thread t, Throwable e) 
  {
    addStackInfo(e);
  }

  public void addStackInfo(final Throwable t) 
  {
    EventQueue.invokeLater(new Runnable() 
    {
      public void run() {
        // Bring window to foreground
        setVisible(true);
        toFront();
        // Convert stack dump to string
        StringWriter sw = new StringWriter();
        PrintWriter out = new PrintWriter(sw);
        t.printStackTrace(out);
        // Add string to end of text area
        textArea.append(sw.toString());
      }
    });
  }
}


Второй класс сделан для тогоЮ чтобы продемонстрировать работу нашего окошка. Откомпилируйте и запускайте.

Код

import java.io.*;

public class DumpTest 
{
  public static void main(final String args[]) throws Exception 
  {
    Thread.UncaughtExceptionHandler handler =
      new StackWindow("Show Exception Stack", 400, 200);
    Thread.setDefaultUncaughtExceptionHandler(handler);
    new Thread() {
      public void run() {
        System.out.println(1 / 0);
      }
    }.start();
    BufferedReader br =
      new BufferedReader(new InputStreamReader(System.in));
    System.out.print("Press Enter for next exception");
    br.readLine();
    new Thread() {
      public void run() {
        System.out.println(args[0]);
      }
    }.start();
    System.out.print("Press Enter to end");
    br.readLine();
    System.exit(0);
  } 
}


При запуске вы увидите окошко с ошибкой. Его можно закрыть и в консольном окне (где запущено приложение) нажмите Enter (как предлагается). И снова увидите окно с ошибкой. Причем важно отметить, что обе ошибки – runtime, но программа нормально продолжает свою работу.

Автор: Samotnik 24.1.2008, 14:18
Очень хорошо, спасибо вам !!    smile 

Автор: Maksym 24.1.2008, 14:48
AntonSaburov
А как правильнее прицепить такого слушателя необработанных исключений к дефолтному (текущему) потоку (который в при запуске приложения jvm создает)?

Автор: AntonSaburov 24.1.2008, 15:54
По идее вызвать Thread.currentThread() - получим ссылку на текущий тред. И у него вызывать setUncaughtExceptionHandler().

Автор: Maksym 24.1.2008, 16:07
AntonSaburov
Ага. Сенкс.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)