Некоторые вещи иногда работают не так, как подсказывает интуиция. Это утверждение можно отнести к обработке исключений в Java. Далее — ситуации и примеры кода, которые отражают некоторые имеющиеся нюансы.
Здесь всё просто и интуитивно понятно.
Здесь тоже всё элементарно. Блок finally отрабатывает на каждой итерации, и после отлова исключения, то есть в принципе всегда, было исключение или его не было, вышли мы из метода, или нет.
А вот этот вариант показывает тот момент, на котором я засыпался месяца три тому на собеседовании:
Блок finally исполняется всегда, кроме того, он может переопределить результат возврата из метода, либо отменить возврат.
Кажется, да:
Не работает!
Даже нормальном завершении программы, в потоках типа «daemon» блок finally не отрабатывает.
И да, как было замечено, при выходе из блока try-catch через System.exit(0) блок finally также не отрабатывает.
up: вместо System.exit(int) можно использовать Runtime.getRuntime().halt(int)
Мой оригинал
Самый простой случай, исключение случается:
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)
Мой оригинал