Java
21 August 2009

Особенности обработки исключений

Некоторые вещи иногда работают не так, как подсказывает интуиция. Это утверждение можно отнести к обработке исключений в Java. Далее — ситуации и примеры кода, которые отражают некоторые имеющиеся нюансы.


Самый простой случай, исключение случается:

public class Test01 {
   static int doTest() throws Exception {
    for (int i = 0; i < 10; i++) {
      System.out.println("i = " + i);
      if (i == 3) {
        throw new Exception();
      }
    }
    return -1;
  }
  public static void main(String[] args) throws Exception {
    System.out.println("doTest() = " + doTest());
  }
}

Здесь всё просто и интуитивно понятно.

Исключения случаются, но мы их ловим:

public class Test02 {
   static int doTest() {
    for (int i = 0; i < 10; i++) {
      System.out.println("i = " + i);
      try {
        if (i == 3) {
          throw new Exception();
        }
      } catch (Exception e) {
        System.out.println("Exception!");
        return i;
      } finally {
        System.out.println("Finally block");
      }
    }
    return -1;
  }
  public static void main(String[] args) {
    System.out.println("doTest() = " + doTest());
  }
}

Здесь тоже всё элементарно. Блок finally отрабатывает на каждой итерации, и после отлова исключения, то есть в принципе всегда, было исключение или его не было, вышли мы из метода, или нет.

Исключения случаются, мы их ловим и… отменяем:

public class Test03 {
  static int doTest(int n) {
    for (int i = 0; i < n; i++) {
      System.out.println("i = " + i);
      try {
        if (i % 3 == 0) {
          throw new Exception();
        }
      } catch (Exception e) {
        System.out.println("Exception!");
        return i;
      } finally {
        System.out.println("Finally block");
        if (i % 3 == 0) {
          if (i < 5) {
            System.out.println("Cancel exception, please");
            continue;
          } else {
            System.out.println("OK, now everything is done");
            return 42;
          }
        }
      }
    }
    return -1;
  }
  public static void main(String[] args) {
    System.out.println("doTest(2) = " + doTest(2));
    System.out.println();
    System.out.println("doTest(10) = " + doTest(10));
  }
}


А вот этот вариант показывает тот момент, на котором я засыпался месяца три тому на собеседовании:
i = 0
Exception!
Finally block
Cancel exception, please
i = 1
Finally block
doTest(2) = -1

i = 0
Exception!
Finally block
Cancel exception, please
i = 1
Finally block
i = 2
Finally block
i = 3
Exception!
Finally block
Cancel exception, please
i = 4
Finally block
i = 5
Finally block
i = 6
Exception!
Finally block
OK, now everything is done
doTest(10) = 42

Блок finally исполняется всегда, кроме того, он может переопределить результат возврата из метода, либо отменить возврат.

И даже с многопоточностью?

import java.util.concurrent.*;
public class Test04 implements Runnable {
  public void run() {
    try {
      for (int i = 0; i < 10; i++) {
        System.out.println(Thread.currentThread() + ": " + i);
        TimeUnit.SECONDS.sleep(1);
      }
    } catch (InterruptedException e) {
      System.out.println("Interrupted!");
    } finally {
      System.out.println("I'm in the finally block!");
    }
  }
  public static void main(String[] args) throws Exception {
    Thread t = new Thread(new Test04());
    t.start();
    TimeUnit.SECONDS.sleep(5);
    System.out.println("main() finished");
  }
}

Кажется, да:
Thread[Thread-0,5,main]: 0
Thread[Thread-0,5,main]: 1
Thread[Thread-0,5,main]: 2
Thread[Thread-0,5,main]: 3
Thread[Thread-0,5,main]: 4
main() finished
Thread[Thread-0,5,main]: 5
Thread[Thread-0,5,main]: 6
Thread[Thread-0,5,main]: 7
Thread[Thread-0,5,main]: 8
Thread[Thread-0,5,main]: 9
I'm in the finally block!


А теперь — печеньки!

import java.util.concurrent.*;
public class Test05 implements Runnable {
  public void run() {
    try {
      for (int i = 0; i < 10; i++) {
        System.out.println(Thread.currentThread() + ": " + i);
        TimeUnit.SECONDS.sleep(1);
      }
    } catch (InterruptedException e) {
      System.out.println("Interrupted!");
    } finally {
      System.out.println("I'm in the finally block!");
    }
  }
  public static void main(String[] args) throws Exception {
    Thread t = new Thread(new Test05());
    // обратите внимание на следующую строчку
    t.setDaemon(true);
    t.start();
    TimeUnit.SECONDS.sleep(5);
    System.out.println("main() finished");
  }
}

Не работает!
Thread[Thread-0,5,main]: 0
Thread[Thread-0,5,main]: 1
Thread[Thread-0,5,main]: 2
Thread[Thread-0,5,main]: 3
Thread[Thread-0,5,main]: 4
main() finished
Thread[Thread-0,5,main]: 5


Даже нормальном завершении программы, в потоках типа «daemon» блок finally не отрабатывает.

И да, как было замечено, при выходе из блока try-catch через System.exit(0) блок finally также не отрабатывает.
up: вместо System.exit(int) можно использовать Runtime.getRuntime().halt(int)

Мой оригинал

+24
12.3k 42
Comments 29
Top of the day