プログラミング技法 10.
パッケージ,例外処理など
1.例外処理
プログラムが正常に期待される以外の処理が行われたときに,例外が発生(エラーの情報の 発生)する処理をプログラムにさせること 例外に対応した処理をプログラムにさせること ※ 例外もオブジェクトである ※ 多数の例外がある(例外クラスが存在し,例外にあわせてオブジェクトが生成される) 例外処理の方法 (通常の処理)A の実行中に → (1)例外が発生しなかった → 処理Bへ (2)例外が発生 ⇒ 例外オブジェクトが e へ代入 (JVM が自動的に発生) (3) 例外オブジェクトを処理 X あるいは Y が実行 ※例外オブジェクトの型は,処理 A で発生しうる型を設定する 例外の種類 ・実行時例外 ← (try.Catch 文で)例外処理が行われなくてもコンパイルが可能 ⇒プログラムの異常終了の場合あり ・チェック例外 ← 例外処理が行われないとコンパイルができない 自分で例外を作成し,戻す場合 ⇒ throws 文が必要 (表現方法) throw ステートメントにより,明示的に例外を生成できる.static void sleep throws interruptedException ここで,例外を投げる場合の処理 try{ 通常の処理 A }catch( 例外オブジェクトの型 変数(引数)e1 ){ 例外発生時の処理 X }catch( 例外オブジェクトの型 変数(引数)e2 ) { 例外発生時の処理 Y }finally{ finally ブロック処理 B } ①エラーが発生した場合,例外オブジェクトが投げられる 省略可,catch の処理後に必ず処理 ②エラーによって,作成され た例外オブジェクトが該当す る例外処理(引数の型)を選択 し,実行する
プログラミング技法 プログラムに明示的に例外を生成する場合 例外用のクラス (スーパークラスとなる)Exception クラス(サブクラス) → すべての例外処理のクラスの親(throwable クラスの継承) ※すべての例外 および エラークラスのスーパークラス → Throwable クラス 具体的な例外処理のクラス(親は Exception クラス) ArithmeticException(計算に関する)
Array Index Out of Bounds Exception(配列要素外)
※サブクラスの例外は Exception クラスを含んでいるので,Exception でも例外を受け取れる しかし,Exception を引数とすることで必ず例外処理を得ることができる try{ ・・・・ } catch(親クラス(例えば Exception) e ); // ** 先にすべての例外を受け取る ** ・・・・ } // いずれの catch 文でもコンパイルエラーとなる catch(子クラス e){ // 先にすべての例外を受け取る処理のため意味がない ・・・・ } ① 引数として受け取った例外オブジェクトを投げる
catch (ExceptionType param ){ // 引数で受けた例外オブジェクトの投入
・・・・ // ExceptionType は何か例外のオブジェクトの型 throw param; ・・・・ } (例) try{ int i=1, j=0, k; k = i / j ; // j = 0 なので 演算処理のエラーとなる } // ArithmeticException の例外が発生する catch(ArithmeticException e) { //引数で例外オブジェクトを受け取る throw e; // その例外オブジェクトを投げて次へ進む } ① 例外オブジェクトを作成して投げる(意図的に例外を発生させる) throw new ExceptionType(args);
// ExceptionType は 何か例外の型を示す ※ ある例外のインスタンスを作成し、投げる(戻す)
(例) try{
System.out.println("try");
throw new ArithmeticException( ); // ここで例外が生じていなくても // 例外オブジェクトを発生させる } ↓
catch(Exception e){ // そして、ここでつかまる。 System,out,println("catch");
プログラミング技法 ※ catch ブロックには Throwable クラス(全ての例外の親クラス)の型の引数が入る(当然) ・Error クラス ← Throwable の継承(拡張) 継承(サブクラス)→ JVM で検出される可能性のある問題に対応するクラス 形式のエラー,メモリ不足,内部エラー スタックのオーバーフローなど. ・Exception クラス ← Throwable の継承 実行時に発生する可能性がある問題に対応するクラス Exception 内のクラスの例 ① ClassNotFoundException クラスが見つからない ② NoSuchFieldException フィールドが見つからない ③ NoSuchMethodException メソッドが見つからない ④ RunTimeException 実行時例外発生 RunTimeException のサブクラス
(A)Array Index Out Of Bounds Exception
配列の操作でインデックスのない要素を指す場合 (B)ArithmeticException 算術例外(0 で割った) (C)Null PointerException 空のオブジェクトのフィールド or メソッドにアクセスする場合 (D)NumberFormatException Integer.parseInt(args[0])で args[0] が文字だった場合など Throwable
Error
Exception…
①
②
③
④
…
(A)
(B)
(C)
…
Runtime Exception JVM で検出される可能性のある問題を表すク ラス (形式のエラー,メモリ不足,スタックのオー バーフロー など)プログラミング技法
2.パッケージ
クラスライブラリ (ある目的に作成された)クラスインターフェースの集まり パッケージ クラスライブラリが提供されている場所,クラスライブラリのまとまりを指す パッケージに登録する方法 ソースファイルにかかれているクラス,インターフェースをパッケージに属させる方法 ソースファイルの先頭で package パッケージ名 を宣言する. ソースファイルの頭に上の宣言文を書く (この宣言文がないと,デフォルトのパッケージに属する) パッケージ名を tg とすると,パッケージ階層構造(実際はフォルダ)を用いることもでき, package tg.a.b・・・ と,表現できる. この場合,tg−a−b のフォルダ(パッケージ)の下にソースファイルをおく. (既存の)クラスファイルの参照方法 クラスファイルの検索先(ディレクトリ)を指示する環境変数に設定する ・システムのデフォルトの位置 ・CLASSPATH で指定された位置 (ex) クラスの場所 /home/taro/class ならば CLASSPATH に .;¥home¥taro¥classes を記述する クラスファイルには存在するディレクトリ構造全体をアーカイブして1つのファイル にしても利用できる (jar, zip ファイル) パッケージの作成(CLASSPATH の設定) CLASSPATH の設定方法set CLASSPATH = .; C=¥ home¥mylogin¥java book フォルダ javabook の下に tg ディレクトリを作成する場合 上記のフォルダ構造の場合では, Turtle.java, TurtleFrame.java の先頭に package tg; と入れてコンパイルする.
Turtle, TurtleFrame の限定名は tg.Turtle, tg.TurtleFrame となる.
<javabook> <tg> Turtle.java TurtleFrame.java
プログラミング技法 利用例 <X> <tg> Test.java A.java B.java A.class B.class // Test.java package p; class Test{
public static void main( String args[]){ A a= new A(); a.f(); B b= new B(); b.f(); } // A.java package p; class A{ void f( ){ System.out.println(“A”); } } // B.java package p; class B{ void f( ){ System.out.println(“B”); } } 実行例 コンパイル フォルダ<X> (<p>の親フォルダ)の上で)
javac p¥Test.java あるいは javac p¥*.java
※コンパイルにより, class ファイルは フォルダ p に格納される 実行
プログラミング技法
3.プログラム作成時の注意
(1)クラスの構成※Turtle や TurtleFrame もクラスである.(しかし,これらは(main メソッドに)組み込まれて 使われるものなのでこれらのクラスには main メソッドは存在しない 例えば, Tutle クラス{ double x y; // (インスタンス) 変数(x,y 座標) double angle; // (インスタンス) 変数(角度)
public void fd(inta){ // メソッド //処理
}
public void rt(inta){ // メソッド //処理 } …… } (2)プログラムの作成では一般式で表現して処理を記述する 処理を一般式で表現する.(とくに,繰り返し処理) (ex) 3∼6 角形まで描く ⇒ k 角形を描く処理に一般化する (そして,k=3∼6 に変化させる) (3)for 文の記述 for(初期設定,繰り返し条件,後処理){・・・} 記述の特徴 ・初期設定は for 文の外に出せる
for(i=0; i<10; i++){ } → i=0; for( ; i<10; i++){ } ・繰り返し条件を記述しない場合はその条件は True と見なされる
for(i=0; ; i++){ } は i=0; while(true){ i++; } と同じ
・初期設定は複数できるが,繰り返し条件(の判断)は1つの boolean 型しかできない for(i=0, S=0; i<10; i++){ }
上の “i=0, S=0” が初期化, “i<10” が繰り返し条件 である.
記述を容易にし,処理の全体が見渡せるよう main メソッド内のよりはメソッドを利用する class クラス名{
// 変数 (フィールド)
void main( ){ // main メソッド(main メソッドは 2 つ以上存在できない) f( ); // 主となる処理を記述する } // (自分クラスの中のメソッドならばメソッド名だけで良い) void f( ){ // その他のメソッド (同じ処理を複数回使う場合は // メソッドを自分で作っておくと良い) } }
プログラミング技法 (4)スコープ ※ ただし,同じメソッド内で同一名の変数は利用できない (5)プリシティブ値の型変換 誤差が含まれる可能性がある(実数値は正確な値を表現できない場合がある) (解決策) 整数値を利用する演算とキャストの利用 for(int i=0; i<10; i++){
// 処理が記述される }
//---// int i;
for( i=0; i<10; i++ ){ // 処理が記述される } ……… int i はこの範囲で有効 int i はこの範囲で(for 文の 外でも)有効 問題となるプログラム例 for(double x=0; x<1; x+=0.1){ System.out.println(x); } 実行結果 0.0 0.1 0.2 0.3 0.399・・・ 0.499・・・ ・・・ ※ 0.4, 0.5 など意図する値が得られていない (0.1 にも誤差を含んでいるため) 改良したプログラム double x;
for(int i=0; i<10; i++){ x=(double)i/10; System.out.println(x); } 実行結果 0.0 0.1 0.2 0.3 0.4 0.5 ・・・ ※ くり返し処理のデータに整数を利用した (整数 i には誤差は含まない)
プログラミング技法
4.実数計算における浮動小数への取り扱い時に生じる問題
(1)オーバーフロー 表現できる数値の範囲を超えてしまったとき,正しい値を表現できなくなる現象 (ex) 単精度 -1.11・・・1(2)×2 127 ∼ 1.11・・・1 (2)×2 127 では 1.0×2128は表現不可である.表現しようとすると異なる値が表現される (2)アンダーフロー 演算結果が 0 に近づき,表現可能な最小値よりも小さい値になった場合 (ex) 単精度では 0 の次に表示可能な小さい値は 0.00・・・1(2)×2 -126 (1 は小数点以下 24 桁 目)である.すなわち,1.0(2)×2 -149 を意味する. 例えば,1.0(2)×2 -100×1.0 (2)×2 -50 や (1.0(2)×2 -149 )/2 の演算では 1.0(2)×2 -150 となり,表示できないため,正しい解は表せない. コ ン ピ ュ ー タ に よ っ て は , 1.0(2)× 2 -150 は 0 と 扱 う も の も あ る が , ((1.0(2)× 2-149 )/2)*2 の演算では初めの括弧の演算で 0 となるため,結果は 0 となり,コンピュ ータで表示できるものでも異なる結果が得られる. (3)桁落ち 単精度でほぼ同じ値の引き算を行ったときに,有効桁が減少して誤差が大きくなる現象 (ex) 1.00001-1.0 = 0.00001 の計算で正しく 0.00001 が表現できないこと (4)情報落ち 大きな数値と小さな数値の加算を行ったとき,小さな値の情報が消されてしまう現象 (ex) 十進法では 3355432(10) + 1(10) = 33554433(10) 3355432(10) を浮動小数点表示すると, 1000・・・0(2) (00 が 25 桁ある) = 1.000×2 25 (00・・・23 桁ある) 1 を加える際,1 は 0.0・・・1×1025(小数点第 25 位が 1)となるので浮動小数点が表記で きる 23 桁以下に値が埋もれる このため, 3355432(10) + 1(10) = 1.000×2 25 + 0.0・・・0(23 桁)×1025 となり,正しい解が得られない. (5){ }の省略 { }の中が 1 行のみならば{ }はいらない. (ex) (6)実数型利用時における判断式の取り扱い 例えば double for(double x=0; x<=2.0; x+=0.1){ System.out.println(x); } System.out.println(x); if(x>0){ x++; } //--- for(x=0, S=0; x<10; x++){ S+= x; } if(x>0) x++; あるいは, if(x>0) x++; //--- for(x=0, S=0; x<10; x++) S+= x;プログラミング技法 のプログラムにおいて,for 文の判断式 x<=2.0 の取り扱いでは x=2.0 が正しく判断され ない可能性が高い(浮動数小数点では x=2.0 になるとは限らない.) 上のプログラムを実行すると以下の結果が得られる. 0.0 0.1 0.2 0.30000000000000004 0.4 0.5 0.6 0.7 0.7999999999999999 0.8999999999999999 0.9999999999999999 1.0999999999999999 1.2 1.3 1.4000000000000001 1.5000000000000002 1.6000000000000003 1.7000000000000004 1.8000000000000005 1.9000000000000006 2.0000000000000004 x に代入した値 0.0 0.1 0.2 0.3 ← x=0.3 に近いが,値を越えている 0.4 0.5 0.6 0.7 0.8 ← x=0.8 に近いが,値を 0.8 より小 0.9 ← x=0.9 に近いが,値を 0.9 より小 1.0 ← x=1.0 に近いが,値を 1.0 より小 1.1 ← x=1.1 に近いが,値を 1.1 より小 1.2 1.3 1.4 ← x=1.4 に近いが,値を 1.4 より大 1.5 1.6 1.7 1.8 1.9 2.0 ← x<= 2.0 の条件を越える範囲外 処理終了(予定なら 2.1 で繰り返しを抜ける)
第11回
例外処理,パッケージに関する デモ
①
パッケージに関するデモ
(資料参照のこと)フォルダ<p>の中に Test.java, A.java, B.java がある.
// Test.java package p; class Test{
public static void main( String args[]){ A a= new A(); a.f(); B b= new B(); b.f(); } } // A.java package p; class A{ void f( ){ System.out.println("A"); } } // B.java package p; class B{ void f( ){ System.out.println("B"); } }
フォルダ<p> の階層の上で
E:¥>javac p¥*.java E:¥>java p.Test A B E:¥>cd p E:¥p>javac *.java E:¥p>java TestException in thread "main" java.lang.NoClassDefFoundError: Test (wrong name: p/T est) at java.lang.ClassLoader.defineClass0(Native Method) at java.lang.ClassLoader.defineClass(ClassLoader.java:486) …… E:¥>java p.Test A B コンパイルと実行にはパッケージ名 p が必要 ただし,階層の表現が異なるので注意 コンパイルでは ¥ 実行では . パッケージ p 内では コンパイルができるが,実 行はできない パッケージ p の上の階層(フォルダ)から実行す ればできる.
②例外処理のデモ
実行時の引数に複数の整数を並べ,プログラム内では配列 x[ ] に引数の整数を格納する. 格納は for 文を用い,一つずつ整数に変換して代入し,代入した値を画面に表示する. ////////////////////////////////////////////////// // Class demo_reigai.java ////////////////////////////////////////////////// public class demo_reigai{public static void main(String args[]){ // 引数よりデータの格納 int elementNum=args.length; if(elementNum< 1){ System.out.println(" 引数が不足しています ¥n [ データ1 データ2 データ3 ...] で入力してください."); System.exit(1); } //データの格納 ( 配列)
int x[]=new int[6]; // 配列の要素数は 6 個まで try{
for(int i=0; i< elementNum; i++){ x[i]=Integer.parseInt(args[i]);
System.out.println("i=" + i + " x[i]=" + x[i]); } }catch(NumberFormatException e){ System.out.println("引数で与えたデータのフォーマットが間違っている"); <<実行例>> E:¥demo_reigai>java demo_reigai 1 2 3 4 5 i=0 x[i]=1 i=1 x[i]=2 i=2 x[i]=3 i=3 x[i]=4 i=4 x[i]=5 E:¥demo_reigai>java demo_reigai 1 2 3 4 5 6 7 i=0 x[i]=1 i=1 x[i]=2 i=2 x[i]=3 i=3 x[i]=4 i=4 x[i]=5 i=5 x[i]=6 配列の要素数が越える処理を行なった。 E:¥demo_reigai>java demo_reigai 1 2 e 4 5 i=0 x[i]=1 i=1 x[i]=2 引数で与えたデータのフォーマットが間違っている
正常終了
配列の要素数を
越えている場合
整数でないデータを
引数にしている場合
③実数計算における浮動小数への取り扱いに関するデモ
public class sample { /* Main メソッド */
public static void main(String[] args){
/* 実行時引数の値を実数型変数に代入 * (1)実行時引数は args[] の配列に文字列として入る * (2)文字列なので実数にするため,クラスメソッドで変換する */ double x; for(x=0; x<=2.0; x+=0.1){ System.out.println(x); } System.out.println(x); } }
public class sample2 { /* Main メソッド */
public static void main(String[] args){ /* 実行時引数の値を実数型変数に代入 * (1)実行時引数は args[] の配列に文字列として入る * (2)文字列なので実数にするため,クラスメソッドで変換する */ double x; int i;
for(i=0; i<=20; i++){ // 変化させるデータは整数 x=(double)i/10; // 1/10 にして、実数にする System.out.println(x); } x=(double)i/10; System.out.println(x); } } E:¥>java sample 0.0 0.1 0.2 0.30000000000000004 0.4 0.5 0.6 0.7 0.7999999999999999 0.8999999999999999 0.9999999999999999 1.0999999999999999 1.2 1.3 1.4000000000000001 1.5000000000000002 1.6000000000000003 1.7000000000000004 1.8000000000000005 1.9000000000000006 2.0000000000000004 E:¥>java sample2 0.0 0.1 0.2 0.3 0.4 0.5 0.6 0.7 0.8 0.9 1.0 1.1 1.2 1.3 1.4 1.5 1.6 1.7 1.8 1.9 2.0 2.1