情報学部 菅沼ホーム 全体目次 演習解答例 付録 索引

第9章 その他

  1. 9.1 例外処理
    1. (プログラム例 9.1 ) 例外処理
  2. 9.2 マルチスレッド
    1. (プログラム例 9.2 ) マルチスレッド

9.1 例外処理 

  Java コンパイラでは,各コンストラクタとメソッドをチェックし,生成される例外の種類を確認します.プログラムでは,RuntimeException クラス及びそのサブクラスを除く,Exception クラスの全サブクラスに対して(ユーザが作成した独自の例外も含む),捕捉(その例外に対する処理を行う)または宣言(呼び出されたメソッドに例外を引き渡す)する必要があります.例えば,下に示す例において IOException や Negative に対する処理は必ず行わなければなりませんが,ArithmeticException や ArrayIndexOutOfBoundsException に対する処理は必ずしも必要ありません.処理を記述しない場合は,規定の例外ハンドラが呼び出され,例外メッセージが表示され,プログラムの実行が停止されます.

  例外が発生すると,try ブロックの後ろに記述された catch ブロックにその例外に対応した catch ブロックが存在するか否かを検索します.もし存在すれば,その catch ブロック内に記述された処理を実行します.もし存在しなかった場合は( try ブロックが記述されていない場合も含む),そのメソッドを呼び出したメソッドを遡って順に検索します.main メソッドまで遡っても対応する catch ブロックが存在しない場合は,規定の例外ハンドラが呼び出され,例外メッセージが表示され,プログラムの実行が停止されます.ただし,メソッドを遡るためには,以下の例に示すように,throw または throws によって,対応する例外が投げられて(引き渡されて)いる必要があります.

(プログラム例 9.1 ) 例外処理

001	/****************************/
002	/* 例外処理                 */
003	/*      coded by Y.Suganuma */
004	/****************************/
005	import java.io.*;
006	
007	/******************/
008	/* 独自の例外処理 */
009	/******************/
010	class Negative extends Exception {
011		double x, y;
012	
013		Negative(String str, double x, double y) {
014			super(str);
015			this.x = x;
016			this.y = y;
017		}
018	
019		void message(int sw) {
020			if (sw == 0)
021				System.out.println("    1 番目の値を正にして再実行しました");
022			else
023				System.out.println("    データを修正してください");
024		}
025	}
026	
027	/**************************************/
028	/* mainメソッドを含むクラスTestの定義 */
029	/**************************************/
030	public class Test {
031				// main
032		public static void main (String[] args) throws IOException
033		{
034						// try ブロック
035			try {
036				BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
037				System.out.print("0:平方根,1:除算,2:配列? ");
038				int sw = Integer.parseInt(in.readLine());
039								// (x + y) の平方根
040				if (sw == 0) {
041					System.out.print("1 つ目のデータは? ");
042					double x = Double.parseDouble(in.readLine());
043					System.out.print("2 つ目のデータは? ");
044					double y = Double.parseDouble(in.readLine());
045					sq(x, y);
046				}
047								// x / y
048				else if (sw == 1) {
049					System.out.print("1 つ目のデータは? ");
050					int x = Integer.parseInt(in.readLine());
051					System.out.print("2 つ目のデータは? ");
052					int y = Integer.parseInt(in.readLine());
053					div(x, y);
054				}
055								// 配列への代入
056				else
057					input();
058			}
059						// 独自の例外に対する処理
060			catch (Negative ng)
061			{
062				System.out.println(ng.getMessage());
063				ng.printStackTrace();
064				if (ng.y > 0.0) {
065					ng.message(0);
066					try {
067						sq(-ng.x, ng.y);
068					}
069					catch (Negative ng1) {}
070				}
071				else
072					ng.message(1);
073			}
074						// 0 による除算に対する処理
075			catch (ArithmeticException ar)
076			{
077				System.out.println(ar.getMessage());
078				ar.printStackTrace();
079				System.out.println("0 による除算");
080			}
081						// finally ブロック(常に実行)
082			finally
083			{
084				System.out.println("---method main---");
085			}
086		}
087				// (x + y) の平方根
088		static void sq(double x, double y) throws Negative
089		{
090			if (x < 0.0 && y < 0.0)
091				throw new Negative("両方とも負", x, y);
092			else if (x < 0.0 || y < 0.0)
093				throw new Negative("片方が負", x, y);
094	
095			double z = Math.sqrt(x+y);
096			System.out.println("平方根 " + z);
097		}
098				// x / y
099		static void div(int x, int y)
100		{
101			try {
102				int z = x / y;
103				System.out.println("除算 " + z);
104			}
105						// 0 による除算に対する処理
106			catch (ArithmeticException ar)
107			{
108				throw ar;
109			}
110			finally
111			{
112				System.out.println("---method div---");
113			}
114		}
115				// 何番目の要素に代入するのか
116		static void input() throws IOException
117		{
118			BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
119			System.out.print("何番目の要素に代入? ");
120			int n = Integer.parseInt(in.readLine());
121			try {
122				set(n);
123			}
124						// 配列の外への代入
125			catch (ArrayIndexOutOfBoundsException e)
126			{
127				System.out.println(e.getMessage());
128				e.printStackTrace();
129				System.out.println("要素番号が不適当");
130			}
131			finally
132			{
133				System.out.println("---method input---");
134			}
135		}
136				// 代入の実行
137		static void set(int n) throws ArrayIndexOutOfBoundsException
138		{
139			int a[] = new int [5];
140			a[n] = 10;
141			System.out.println("a[" + n + "] = 10");
142		}
143	}
		
010 行目~ 025 行目

  ユーザ独自の例外の定義です.必ず,Exception クラスを継承する必要があります.014 行目では,メッセージを引数として,スーパークラスのコンストラクタを呼んでいます.

032 行目

  IOException は,RuntimeException クラスのサブクラスではないため,捕捉または宣言する必要があります.IOException は,116 行目の input メソッドにおいても発生する可能性がありますが,throws によって投げられています.従って,main メソッドにおいて対応する処理を行う必要がありますが,main メソッドにおいても throws によって投げています.従って,このプログラムの場合は,規定の例外ハンドラの処理に任されることになります.もし,main メソッドに,
	catch (IOException io)
	{
		・・・(何も処理を行わなくても良い)
	}			
のような catch ブロックを記述しておけば,この行の throws 以下は必要ありません.

035 行目~ 058 行目

  この try ブロックの中では,Negative,ArithmeticException,ArrayIndexOutOfBoundsException,または,IOException 例外が発生する可能性があります.先に述べたように,IOException に関しては,032 行目の throws によってシステムに投げているため,このプログラムでは具体的な処理を行っていません.

060 行目~ 073 行目

  ユーザ独自の例外 Negative が発生した場合は,この catch ブロックで捕捉し,処理を実行します.062 行目では,Throwable クラス( Exception クラスのスーパークラス)のメソッド getMassage を使用し,014 行目においてスーパークラスに渡したメッセージを出力しています.printStackTrace メソッドは,getMessage メソッドと同様,Throwable クラスのメソッドであり,例外が発生した時点におけるスタック情報を出力します.なお,064 行目~070 行目では,1 番目の変数だけが負である場合,それを正の値に修正し,平方根の計算を再実行しています.

075 行目~ 080 行目

  ArithmeticException 例外が発生した場合は,この catch ブロックで捕捉し,処理を実行します.メソッド div の中に記述された catch ブロック内で処理可能ですが,107 行目の throw 文によってこのメソッドに投げられているため,ここでの処理が必要になります.

082 行目~ 085 行目

  finally ブロックは,例外が起きても起きなくても,常に実行されます.必要が無ければ,記述しなくても構いません.

088 行目~ 097 行目

  平方根を計算するメソッドです.指定された条件の時,ユーザ独自の例外 Negative を発生させ,その例外を,088 行目の throws によって,このメソッドを呼び出したメソッド(この例では,main メソッド)に投げています.その結果,main メソッドの 060 行目~ 073 行目において,具体的な処理が実行されます.なお,例外を発生させたメソッドの中に,try 及び catch ブロックを記述し,その処理を行わせることは出来ません.

099 行目~ 114 行目

  除算を行うメソッドです.102 行目の演算によって例外が発生した場合は,108 行目の throw 文によって,呼び出したメソッド(この例では,main メソッド)に投げられます.

116 行目~ 135 行目

  IOException に関しては,throws によって,呼び出したメソッド(この例では,main メソッド)に投げているため,try ブロックには入れてありません.また,ArrayIndexOutOfBoundsException に関しては,set メソッドの 140 行目で発生する可能性があるのですが,set メソッドが 137 行目の throws によって,呼び出したメソッド(この例では,input メソッド)に投げているため,ここで処理を行っています.このメソッドの catch ブロックにおいて ArrayIndexOutOfBoundsException に対する処理を行っているため,さらに上のメソッドに投げる必要はありません.

137 行目~ 142 行目

  大きさが 5 である配列に対して,a[n] = 10 の処理を行っています.従って,n の値が 0 未満,または,5 以上の時は,この文の実行により ArrayIndexOutOfBoundsException 例外が発生します.ただし,その処理は,137 行目の throws によって,呼び出したメソッド(この例では,input メソッド)に投げているため,ここでは何も行っていません.

  このプログラムを実行すると,各入力データに対して,以下に示すような結果が得られます.なお,以下に示す結果には入力促進文も含まれています.
+++++ 平方根,例外発生せず
	0:平方根,1:除算,2:配列? 0
	1 つ目のデータは? 1
	2 つ目のデータは? 2
	平方根 1.7320508075688772
	---method main---
+++++ 平方根,1 番目のデータが負
	0:平方根,1:除算,2:配列? 0
	1 つ目のデータは? -1
	2 つ目のデータは? 2
	片方が負
	Negative: 片方が負
	        at Test.sq(Test.java:93)
	        at Test.main(Test.java:45)
	    1 番目の値を正にして再実行しました
	平方根 1.7320508075688772
	---method main---
+++++ 平方根,2 つのデータが負
	0:平方根,1:除算,2:配列? 0
	1 つ目のデータは? -1
	2 つ目のデータは? -2
	両方とも負
	Negative: 両方とも負
	        at Test.sq(Test.java:91)
	        at Test.main(Test.java:45)
	    データを修正してください
	---method main---
+++++ 除算,例外発生せず
	0:平方根,1:除算,2:配列? 1
	1 つ目のデータは? 12
	2 つ目のデータは? 3
	除算 4
	---method div---
	---method main---
+++++ 除算,例外発生
	0:平方根,1:除算,2:配列? 1
	1 つ目のデータは? 12
	2 つ目のデータは? 0
	---method div---
	/ by zero
	java.lang.ArithmeticException: / by zero
	        at Test.div(Test.java:102)
	        at Test.main(Test.java:53)
	0 による除算
	---method main---
+++++ 配列,例外発生せず
	0:平方根,1:除算,2:配列? 2
	何番目の要素に代入? 3
	a[3] = 10
	---method input---
	---method main---
+++++ 配列,例外発生
	0:平方根,1:除算,2:配列? 2
	何番目の要素に代入? -1
	-1
	java.lang.ArrayIndexOutOfBoundsException: -1
	        at Test.set(Test.java:140)
	        at Test.input(Test.java:122)
	        at Test.main(Test.java:57)
	要素番号が不適当
	---method input---
	---method main---
		

9.2 マルチスレッド

  Java には,スレッドと呼ばれる処理単位を複数同時に動作させる機能があります.詳細に関しては,プログラム例にしたがって説明します.なお,C/C++ にも,子プロセスを生成するための関数 forkspawnlspawnv などが存在します.詳細については,それらの関数に対する説明を参照して下さい.

(プログラム例 9.2 ) マルチスレッド

  スレッドを作成する最も簡単な方法は,Java が提供している Thread クラスを継承することです.次の例は,この方法による簡単なプログラム例です.しかし,場合によっては,この方法を採用できない場合があります.例えば,後の章で述べる Window を作成する場合,Frame クラスなどを継承しなければならない場合が多く,かつ,多重継承ができないため,この方法を採用できません.その際に使用できる方法が Runnable インタフェースを利用することです.次の例の 7 行目を以下のようにすることによって可能です.
class Test implements Runnable {   // Runnableインタフェースの利用		
01	/****************************/
02	/* マルチスレッド           */
03	/*      coded by Y.Suganuma */
04	/****************************/
05	import java.io.*;
06	
07	class Test extends Thread {   // Threadクラスの継承
08		static int total_1 = 0, total_2 = 0;
09					// main method
10		public static void main (String[] args) throws IOException
11		{
12						// 自分自身のオブジェクト
13			Test test = new Test();
14						// test型のオブジェクトの生成
15						// 同じオブジェクトから生成するため,異なる名前を付加(***と---)
16			Thread t1 = new Thread(test, "***");
17			Thread t2 = new Thread(test, "---");
18						// スレッドの実行開始
19			t1.start();
20			t2.start();
21						// スレッドの状況のチェック
22			int k = 0;
23			while (t1.isAlive() || t2.isAlive()) {
24				k++;
25				System.out.println("k " + k);
26			}
27						// スレッドの終了後の処理
28			System.out.println("   終了しました(total_1 " + total_1 + " total_2 " + total_2 + ")");
29						// スレッドの終了
30			t1 = null;
31			t2 = null;
32		}
33					// スレッドの実行内容の記述(スレッドがスタートすると,
34					// このメソッドに記述されたことが実行されます)
35		public void run()
36		{
37			System.out.println(Thread.currentThread().getName() + " が実行中");
38			if (Thread.currentThread().getName().equals("***")) {
39				for (int i1 = 0; i1 < 10; i1++) {
40					total_1++;
41					System.out.println("total_1 " + total_1);
42				}
43			}
44			if (Thread.currentThread().getName().equals("---")) {
45				for (int i1 = 0; i1 < 10; i1++) {
46					total_2++;
47					System.out.println("total_2 " + total_2);
48				}
49			}
50		}
51	}
		
  このプログラムでは,2 つのスレッドが生成されスタートすると,各スレッドは run メソッドに記述された内容(カウンタを 10 まで数える)を実行します.スレッドが実行されている間,main では,while 文が実行されカウンタ k が増加していきます.実際,このプログラムを実行すると,例えば,以下のような結果が得られます.なお,結果は,常に下のようになるわけではありません.状況によって実行順序は変化します.
k 1
*** が実行中
--- が実行中
k 2
total_1 1
total_2 1
k 3
total_1 2
total_2 2
k 4
total_1 3
total_2 3
k 5
total_1 4
total_1 5
total_1 6
total_1 7
total_2 4
total_2 5
total_2 6
total_2 7
total_2 8
total_2 9
total_2 10
total_1 8
total_1 9
k 6
k 7
k 8
k 9
k 10
k 11
k 12
total_1 10
k 13
   終了しました(total_1 10 total_2 10)
		

  次のプログラムは,上の例を多少複雑にして,スレッドの実行順序を制御した場合の例です.最初のスレッド *** がスタートしカウントを始めますが,10 カウントしたところでスレッド --- の実行が終了するまで待ちます.そして,スレッド --- の実行終了後に再びカウントを継続します.

/**********************************/
/* マルチスレッド(実行順序制御) */
/*      coded by Y.Suganuma       */
/**********************************/
import java.io.*;

class Test extends Thread {   // Threadクラスの継承
	static int total_1 = 0, total_2 = 0;
				// main method
	public static void main (String[] args) throws IOException
	{
					// 自分自身のオブジェクト
		Test test = new Test();
					// Threadオブジェクトの生成
		Thread t1 = new Thread(test, "***");
		t1.setPriority(6);   // プライオリティを少し高くしておきます
		Thread t2 = new Thread(test, "---");
					// スレッドの実行開始
		t1.start();
		t2.start();
					// スレッドの状況のチェック
		int k = 0;
		while (t1.isAlive() || t2.isAlive()) {
			k++;
			System.out.println("k " + k + " " + Math.sqrt((double)k));
		}
					// スレッドの終了後の処理
		System.out.println("   終了しました(total_1 " + total_1 + " total_2 " + total_2 + ")");
	}
				// スレッドの実行内容の記述(スレッドがスタートすると,
				// このメソッドに記述されたことが実行されます)
				// スレッド間の同期をとるためにsynchronized指定が必要
				// 同期をとれば,あるスレッドの実行中に他のスレッドが割り込むことはない
	public synchronized void run()
	{
		System.out.println(Thread.currentThread().getName() + " が実行中");
		if (Thread.currentThread().getName().equals("***")) {
			for (int i1 = 0; i1 < 20; i1++) {
				total_1++;
				System.out.println("total_1 " + total_1);
				if (total_1 == 10) {
					try {
						this.wait();   // スレッドを待ち状態にする
					}
					catch (InterruptedException e) {}
				}
			}
		}
		if (Thread.currentThread().getName().equals("---")) {
			for (int i1 = 0; i1 < 20; i1++) {
				total_2++;
				System.out.println("total_2 " + total_2);
			}
			this.notify();   // 待ち状態のスレッドを元に戻す
		}
	}
}
		
  このプログラムを実行すると以下のような結果が得られます.
*** が実行中
total_1 1
k 1 1.0
total_1 2
total_1 3
k 2 1.4142135623730951
total_1 4
k 3 1.7320508075688772
total_1 5
k 4 2.0
total_1 6
k 5 2.23606797749979
total_1 7
k 6 2.449489742783178
total_1 8
k 7 2.6457513110645907
total_1 9
k 8 2.8284271247461903
total_1 10
k 9 3.0
--- が実行中
k 10 3.1622776601683795
total_2 1
k 11 3.3166247903554
total_2 2
k 12 3.4641016151377544
total_2 3
k 13 3.605551275463989
total_2 4
k 14 3.7416573867739413
total_2 5
k 15 3.872983346207417
total_2 6
k 16 4.0
total_2 7
k 17 4.123105625617661
total_2 8
k 18 4.242640687119285
total_2 9
k 19 4.358898943540674
total_2 10
k 20 4.47213595499958
total_2 11
k 21 4.58257569495584
total_2 12
k 22 4.69041575982343
total_2 13
total_2 14
k 23 4.795831523312719
total_2 15
k 24 4.898979485566356
total_2 16
k 25 5.0
total_2 17
k 26 5.0990195135927845
total_2 18
k 27 5.196152422706632
total_2 19
k 28 5.291502622129181
total_2 20
k 29 5.385164807134504
total_1 11
k 30 5.477225575051661
total_1 12
k 31 5.5677643628300215
total_1 13
k 32 5.656854249492381
total_1 14
k 33 5.744562646538029
total_1 15
total_1 16
k 34 5.830951894845301
total_1 17
k 35 5.916079783099616
total_1 18
k 36 6.0
total_1 19
k 37 6.082762530298219
total_1 20
k 38 6.164414002968976
   終了しました(total_1 20 total_2 20)
		

情報学部 菅沼ホーム 全体目次 演習解答例 付録 索引