第71回 Javaの例外処理の構造(文法的なお話) その2

それでは、try{ } catch { } の構造を使うと、
どのようにプログラムが流れていくのか見てみよう。

public class Hoge {

	public static void main(String[] args) {
		try {
			badMethod();
			System.out.println("例外起こらないでー");
		} catch (Exception e) {
			System.out.println("例外処理った");
			e.printStackTrace();
		}
		System.out.println("ここまでこれるかな");
	}

	private static void badMethod() throws Exception {
		throw new Exception(); 
	}
}

結果:

例外処理った
java.lang.Exception
	at Hoge.badMethod(Hoge.java:16)
	at Hoge.main(Hoge.java:6)
ここまでこれるかな

おわかりだろうか?

System.out.println("例外起こらないでー");

の部分が実行されていないのである。
tryの中で例外が発生すると(今回は、badMethod();の部分)それより以下の行は処理されない(すっとばされるのである)。

finally

ちなみにfinallyというものが存在する。

catchは例外が起こったときに処理される部分であるが、
finallyは、例外が起こったときも起こらなかったときも、最後に必ず実行されるのである。


それでは、例外が起こった場合からみてみよう。

public class Hoge {

	public static void main(String[] args) {
		try {
			badMethod();
			System.out.println("例外起こらないでー");
		} catch (Exception e) {
			System.out.println("例外処理った");
			e.printStackTrace();
		} finally {
			System.out.println("Hey!! みんなのfinally");
		}
		System.out.println("ここまでこれるかな");
	}

	private static void badMethod() throws Exception {
		throw new Exception(); 
	}
}

結果:

例外処理った
java.lang.Exception
	at Hoge.badMethod(Hoge.java:18)
	at Hoge.main(Hoge.java:6)
Hey!! みんなのfinally
ここまでこれるかな

では、次に例外が起こらなかった場合である。

public class Hoge {

	public static void main(String[] args) {
		try {
			badMethod();
			System.out.println("例外起こらないでー");
		} catch (Exception e) {
			System.out.println("例外処理った");
			e.printStackTrace();
		} finally {
			System.out.println("Hey!! みんなのfinally");
		}
		System.out.println("ここまでこれるかな");
	}

	private static void badMethod() throws Exception {
		// throw new Exception();  コメントアウトした 
	}
}

結果:

例外起こらないでー
Hey!! みんなのfinally
ここまでこれるかな

このように、どちらの場合もfinallyの中は実行されているのがわかる。


finallyは必要なのか?

それでは、

try{
 A
} catch {
 B
} finally {
 F
}
 D

と書くのも、

try {
 A
} catch {
 B
}
F
D

と書くのも同じように思えるが、
次のような違いがあるようだ。

Javaの道>掲示板(finallyの使い道)


なるほど。例外をさらに上に投げるときにfinallyは使えるらしい。
やってみよう。

public class Hoge {

	public static void main(String[] args) {
		try {
			giveUp();
			System.out.println("main() : 例外起こらないでー");
		} catch (Exception e) {
			System.out.println("main() : 例外処理った");
			e.printStackTrace();
		} 
	}

	private static void badMethod() throws Exception {
		throw new Exception();
	}
	
	private static void giveUp() throws Exception {
		try{
			badMethod();
		} catch (Exception e) {
			System.out.println("giveUp() : catchした!");
			throw e;
		} finally {
			System.out.println("giveUp() : finally!!");
		}
		System.out.println("giveUp() : 例外処理後");
	}
	
}

新たに、giveUpメソッドを用意し、
そのgiveUpメソッドがbadMethodを呼んでいるが、自分では結局処理せず、
呼び出し元に任せている(throwしている)。


結果:

giveUp() : catchした!
giveUp() : finally!!
main() : 例外処理った
java.lang.Exception
	at Hoge.badMethod(Hoge.java:15)
	at Hoge.giveUp(Hoge.java:20)
	at Hoge.main(Hoge.java:6)

たしかに、呼び出し元に投げているにもかかわらず、
投げる前に、finallyが実行されている。



では、Error.pmはどうかというと、

use strict;
use Error qw(:try);

sub bad_sub {
  die 'OH';
}

sub give_up {
  try {
    bad_sub();
  } otherwise {
    my $e = shift;

    print 'give_up() :  catchした', "\n";
    die $e;
  } finally {
    print 'geve_up() :  finally', "\n";
  };
}


try {
  give_up();
} otherwise {
  my $e = shift;

  print 'main() : 例外処理った', "\n";
  print $e;
};

結果:

give_up() :  catchした
geve_up() :  finally
main() : 例外処理った
OH at e.pl line 5.

確かに、Error.pmもそのような挙動をする。



ちなみに、上に投げる方法は、tryを書かずに

use strict;
use Error qw(:try);

sub bad_sub {
  die 'OH';
}

sub give_up {
  bad_sub();
  print 'ここは通らない', "\n";
}


try {
  give_up();
} otherwise {
  my $e = shift;

  print 'main() : 例外処理った', "\n";
  print $e;
};

結果:

main() : 例外処理った
OH at e.pl line 5.

このようにも書けるが、これはあまりよいとは思えないような気がする。
が、どうなんだろう.....