11章 例外 | |
1.例外とは | |
例外とは予期しなかった処理のことをいう。例えば、配列の中身の平均値を求めるメソッドを作ろうとした場合、かきのようになる。 | |
class class Test{ public static void main(String args[ ] ){ int array[ ] = new int[5]; array[0] = 10; public static int average(int[ ] array){ return total / array.length; |
|
C:\java>javac Test.java C:\java>java Test average=6 C:\java> |
|
ところが、配列の長さが0だった場合はaverageメソッドの中で「0で割る」という計算が発生してしまいエラーになってしまう。 | |
class class Test{ public static void main(String args[ ] ){ // int array[ ] = new int[5]; int array [ ] = new int [0]; /* array[0] = 10; array[1] = 5; array[2] = 2; array[3] = 4; array[4] = 9; /* System.out.println("average="+average(array)); } public static int average( int[ ] array ){ return total / array.length; |
|
C:\java>javac Test.java C:\java>java Test Exception in thread "main" java.lang.ArithmeticException: / by zero at Test.average(Test.java:20) at Test.main(Test.java:12) C:\java> |
|
このように予期せぬエラーが発生するとJavaではそのスレッドが終了してしまう。この例のように単一スレッドの場合はプログラムが終了してしまう。 このエラーメッセージにある「Exception」というのが例外をあらわしており、「in thread "main"」がスレッドをあらわしている。そしてその後の「java.langArithmeticException:/ by zero」が例外の内容をあらわしていて、ここではゼロで割ったという例外であることがわかる。次の行からどの部分で例が起ったかをあらわしている。Test.javaの20行目、Testクラスのaverageメソッドで起っていることがわかり、さらに次の行を見ると、Test.averageメソッドはTest.javaの12行目(Testクラスのmainメソッド)から呼び出されていることがわかる。 |
|
2.例外が起きたら | |
例外が起きた場合に、アプリケーションが終了してしまっては困る。そこでプログラマーは例外を予測して、例外が起きた場合に適当な処理をさせる必要がある。 例えば平均を求めるメソッドの場合、引数で与えられた配列がnewし忘れたなどで、配列の実体がなかった場合などは、 NullPointerExceptionという名前の例外が発生する。平均を求めるメソッドでは、この2つの例外が発生する可能性があるので、例外が起きた場合専用の処理をするようにする。 |
|
public class Test{ public static void main(String args[ ] ){ // int array[ ] = new int[5]; int array [ ] = new int [0]; System.out.println("average="+average(array)); public static int average( int[ ] array ){ return total / array.length; } } |
|
C:\java>javac Test.java C:\java>java Test 配列が空です average=0 C:\java> |
|
上記のように、例外が発生すると思われる部分を | |
try{ // 例外が発生する可能性のあるブロック }catch(例外の種類1 param1){ // 例外の種類1が発生した場合に実行される部分 }catch(例外の種類2 param1){ // 例外の種類2が発生した場合に実行される部分 }finally{ // 例外が起きても起きなくても実行される部分 } |
|
上記の構文を用いた場合、Jvmはtryブロックを実行する。もしtryブロックの中で例外が発生しなければ、catchブロックは実行せずに、finallyブロックを実行する。 もし、tryブロック実行中に例外が発生したら、発生した場所から例外の内容に応じてcatchブロックが実行される。この場合、tryブロックはすべてが実行されるわけではなく、例外が発生した部分よりも下の部分は実行されずにcatchブロックにジャンプする。上記の例で、averageメソッドの引数にnullが渡された場合、for分のarray.lengthでNullPointerExceptionという例外が発生する。したがって、forの中身は実行されずにNullPointerExceptionをキャッチしているブロックに処理が移行する。 なお、finallyブロックは省略することができる。catchブロックは1つ以上必要になる。 |
|
3.例外の種類 | |
ClassNotFoundException | 呼び出そうとしたクラスのクラスファイルが無かった場合 |
CloneNotSupportedException | オブジェクトを複製するためにObjectクラスのcloneメソッドが呼び出されたが、そのオブジェクトのクラスがCloneableインターフェイスを実装していないことを示すためにスローされる。 |
DataFormatException | データ形式エラーが発生したことを通知する |
IOException | なんらかの入出力例外の発生を通知するシグナルを発生させる。 サブクラスとしてEOFException,FileNotFoundExceptionなどがある。 |
PrinterException | 印刷システムに例外的な状態が発生したことを示すために使用される。 サブクラスとしてPrinterAbortException,PrinterIOExceptionがある。 |
RuntimeException | JVMの通常の処理でスローすることができる各種の例外のスーパークラス。 |
ArithmeticException | RuntimeExceptionのサブクラス。 算術計算で例外的条件が発生した場合にスローされる。例えば、「ゼロで除算される」整数はこのクラスのインスタンスをスローする。 |
ArrayStoreException | RuntimeExceptionのサブクラス。 不正な型のオブジェクトをオブジェクトの配列に格納しようとしたことを示すためにスローされる。 |
ClassCastException | RuntimeExceptionのサブクラス。 あるオブジェクトを継承関係にないクラスにキャストしようとしたことを示すためにスローされる。 |
IllegalArgumentException | RuntimeExceptionのサブクラス。 不正な引数、または不適切な引数をメソッドに渡したことを示すためにスローされる。 |
IndexOutOfBoundsException | RuntimeExceptionのサブクラス。 ある種のインデックス(配列、文字列、ベクトルなど)が範囲外であることを示すためにスローされる。サブクラスにArrayIndexOutOfBoundsException,StringIndexOutOfBoundsExceptionがある。 |
NegativeArraySizeException | RuntimeExceptionのサブクラス。 負のサイズを持った配列をアプリケーションが作成しようとした場合にスローされる。 |
NoSuchElementException | RuntimeExceptionのサブクラス。 この列挙にそれ以上の要素がないと、EnumerationのnextElementメソッドによってスローされる。 |
NullPointerException | RuntimeExceptionのサブクラス。 オブジェクトが必要な場合に、アプリケーションがnullを使おうとするとスローされる。 |
SecurityException | RuntimeExceptionのサブクラス。 セキュリティマネージャによってスローされ、セキュリティ違反を示す。 サブクラスにAccessControlException,RMISecurityExceptionがある。 |
NumberFormatException | IllegalArgumentExceptionのサブクラス。 アプリケーションが文字列を数値型に変換しようとしたとき、文字列の形式が正しくない場合にスローされる。 |
4.catchブロックの検索 | |
tryブロック内で例外が発生した場合は、tryブロックの次に記述されているcatchブロックに対応する例外処理が書かれているかを検索する。しかし、対応する例外処理が書かれていなければ(対応する例外をキャッチするcatchブロックがない場合)、そのtryブロックを囲っているさらに上のtryブロックにかかれているcatchブロックを検索する。つまり、例外処理が起きたメソッドを呼び出している呼び出し元のtry〜catchブロックなどを探す。そこにも発生した例外をキャッチするcatchブロックが無ければ、さらに呼び出し元を検索する。 可能な限りさかのぼってもキャッチされない場合は、そのスレッドはJVMによって強制的に終了されてしまう。もし単一スレッドであった場合は、アプリケーションが終了する。 |
|
public class Test{ public static void main(String args[ ] ){ try{ a( ); }catch (NullPointerException e){ System.err.println(e); } System.out.println("アプリケーションは正常に終了しました"); } public static void a( ){ public static void b( ){ int array[ ] = new int[2]; // ArrayIndexOutOfBoundsExceptionを発生 array[3] = 10; } } |
|
C:\java>javac Test.java
C:\java>java Test |
|
例えば、NullPointerExceptionはRuntimeExceptionのサブクラスで、RuntimeExceptionはExceptionのサブクラスなので下記のように記述する。 | |
public class Test{ public static void main(String args[ ] ){ try{ int.array[ ] = null; array[0] = 10; }catch(NullPointerException e{ System.err.println("NullPointerException"); }catch(RuntimePointerException e{ System.err.println("RuntimeException"); }catch(Exception e){ System.err.println("Exception"); } } |
|
C:\java>javac Test.java
C:\java>java Test |
|
ところが、下記の例では、スーパークラスのExceptionから記述しようとしたため、すべての例外は最初のExceptionクラスのcatchブロックでキャッチされてしまう。キャッチブロックは一回の例外で一度しか処理しないため、その下のRuntimeExceptionやExceptionやNullPointerExceptionではキャッチされない。したがって、RuntimeExceptionやNullPointerExceptionのキャッチブロックは処理されることがないため、コンパイルエラーになる。 | |
public class Test{ public static void main(String args[ ] ){ try{ int.array[ ] = null; array[0] = 10; }catch(Exception e{ System.err.println("Exception"); }catch(RuntimeException e{ System.err.println("RuntimeException"); }catch(NullPointerException e){ System.err.println("NullPointerException"); } } } |
|
C:\java>javac Test.java Test.java:8 catch not reached. }catch(RuntimeException e){ ^ Test.java:10: catch not reached. }catch(NullPointerException e){ ^ 2 errors C:\java> |
|
5.throw | |
例外は受け取るだけでなく、呼び出し元に例外処理を任せることもできる。これを「例外を投げる」という。例えば、そのキャッチブロックで例外処理をしただけではダメで、呼び出しもとのキャッチブロックでも例外処理をしなければならないような場合に使う。 下記の例ではaメソッド内で例外処理をした後、throwステートメントを用いて、例外を上に投げている。 |
|
public class Test{ public static void main(String args[ ] ){ try{ a( ); }catch(RuntimeException e){ System.err.println("mainメソッド内で例外を処理しました("+e+")"); } System.out.println("アプリケーションは正常に終了しました"); } public static void a( ) try{ int array[ ] = new int[2]; // ArrayIndexOutOfBoundsExceptionを発生 array[3] = 10; }catch(RuntimeException e){ System.err.println("aメソッド内で例外を処理しました("+e+")"); throw e; } } } |
|
C:\java>javac Test.java C:\java>java Test aメソッド内で例外を処理しました(java.lang.ArrayIndexOutOfBoundsException) aメソッド内で例外を処理しました(java.lang.ArrayIndexOutOfBoundsException) アプリケーションは正常に終了しました C:\java> |
|
上記の例では、生成されたRuntimeExceptionのインスタンスをthrowで上に投げている。throwステートメントは生成されたExceptionを投げるだけでなく、プログラマーが明示的にnewした例外などを上に投げることもできる。投げることができるのは、Throwableクラスのサブクラスだけである。Stringクラスなどのインスタンスは投げることができない。ThrowableクラスのサブクラスはExceptionクラス(すべての例外のスーパークラス)と、Errorクラスである。 | |
public class Test{ public static void main(String args[ ] ){ int array[ ] = new int[2]; try{ a(array, 10); }catch(RuntimeException e){ System.err.println("mainメソッド内で例外を処理しました("+e+")"); } } public static int a(int array[ ], int i ){ System.out.println("aメソッドは正常に終了します"); |
|
C:\java>javac Test.java C:\java>java Test ArrayIndexOutOfBoundsExceptionを投げます mainメソッド内で例外を処理しました(java.lang.ArrayIndexOutOfBoundsException) C:\java> |
|
6.throws | |
例外を投げる可能性があるメソッドを作成した場合は、他のプログラマーが見てもすぐに例外が投げられる可能性があることがすぐわかるように、明示しておくべきである。明示するためには、例外を投げる可能性があるコンストラクタやメソッドの宣言時にthrowステートメントを用いる。 | |
public class Test{ public static void main(String args[ ] ){ int array[ ] = new int[2]; try{ a(array, 10); }catch(RuntimeException e){ System.err.println("mainメソッド内で例外を処理しました("+e+")"); } } public static int a(int array[ ], int i ) throws ArrayIndexOutOfBoundsException{ System.out.println("aメソッドは正常に終了します"); |
|
C:\java>javac Test.java
C:\java>java Test C:\java> |
|
投げる例外が複数ある場合は、カンマ区切で列挙する。 | |
throwステートメントは記述しなくても問題ないが、特に複数人でプログラミングする場合など、自分以外の人にわかりやすくするために記述することが好ましい。また、1人でプログラムしていても、しばらくたつと忘れてしまう場合もあるため、記述することを勧める。 | |
7.エラーと例外 | |
throwできるクラスはThrowableクラスのサブクラスだけである。ThrowableクラスにはErrorクラスとExceptionクラスの2つがある。一般にアプリケーションの使い方をユーザーが間違えた場合、ファイルをセーブしようとしたのにディスクがいっぱい・通信の失敗など一般に予期することができるようなエラーはJavaではエラーではなく例外として処理する。逆にJVMの重大なエラーを示す。例えばメモリー不足、スタックオーバー、内部エラー、不正アクセスなど、プログラマーやユーザーのせいではないエラークラスとする。したがって、一般にはエラークラスのサブクラスを作成することはできるが一般にはしない。 Throwableクラス、Errorクラス、Exceptionクラスのコンストラクタとメソッドを紹介する。これらのサブクラスを作る方法は、必要なメソッドをオーバーライドする必要がある。 |
|
Throwableクラスのメソッドの概要 Throwable( ) エラーメッセージ文字列としてnullを指定してThrowableを新しく構築する Throwable(String message ) 指定されたエラーメッセージを持つ、Throwableを新しく構築する |
|
Throwableクラスのメソッドの概要 | |
Throwable | fillInStackTrace( ) 実行スタックトレースを埋め込む。 |
String | getLocalizedMessage( ) Throwableの地域対応された記述を作成する。 |
String | getMessage( ) このThrowableの地域対応された記述を作成する。 |
Void | printStackTrace( ) Throwableとそのバックトレースを標準エラーストリームに出力する。 |
Void | printStackTrace(PrintStream s) Throwableとそのバックトレースを指定された印刷ストリームに出力する。 |
Void | getStackTrace(PrintWriter s) このThrowableとそのバックトレースを指定されたプリントライターに出力する。 |
String | toString( ) Throwableオブジェクトの短い記述を返す。 |
Errorクラスのコンストラクタの概要 Error( ) 詳細メッセージを指定しないでErrorを構築する。 Error(String s) 指定された詳細メッセージを持つErrorを構築する。 |
|
Errorクラスのコンストラクタの概要 Exception( ) 詳細メッセージを指定しないでExceptionを構築する。 Exception(String s) 指定された詳細メッセージを持つExceptionを構築する。 |
|
Throwable,Error,Exception
public Throwable( ) エラーメッセージ文字列としてnullを指定してThrowable,Error,Exceptionを新しく構築する。 |
|
Throwable,Error,Exception
public Throwable(String message) 指定されたエラーメッセージを持つ、Throwable,Error,Exceptionを新しく構築する。 |
|
getMessage
public string getMessage( ) このThrowableオブジェクトのエラーメッセージ文字列を返す。 |
|
toString
public String toString( ) Throwable オブジェクトの短い記述を返す。このThrowable,Error,Exceptionオブジェクトがエラーメッセージ文字列付きで作成された場合、結果は次の3つの文字列を連結したものとなる。 |
|
printStackTrace public void printStackTrace( ) Throwable,Error,Exceptionとそのバックトレースを標準エラーストリームに出力する。 |
|
8.独自の例外 | |
Exceptionクラスを継承して独自の例外クラスを作成することもできる。これはアプリケーション特有の例外を作成したい場合などに使用される。 独自のエラークラスを投げるメソッドでは、必ずキャッチするか、throwsステートメントで、独自の例外を投げる可能性があることを明示しなければならない。 |
|
// 独自の例外クラス class MyException extends Exception{ public MyException( ){ super( ); } } public class Test{ public static void a( ) throws MyException{ |
|
C:\java>javac Test.java
C:\java>java Test |
|
上記の例で、throw MyExceptionやmainメソッド内のcatchを忘れると、コンパイルエラーになる。 ただし、独自の例外がRuntimeExceptionのサブクラスの場合は、この制限は適用されない。 |
|
// 独自の例外クラス class MyException extends Exception{ public MyException( ){ super( ); } } public class Test{ public static void a( ){ |
// 独自の例外クラス class MyException extends RuntimeException{ public MyException( ){ super( ); } } public class Test{ public static void main(String args[ ]){ try{ a( ); } catch(MyException e ){ System.err.println(e); } } public static void a( ){ |
C:\java>javac Test.java Test.java:11: Exception MyException is never thrown in the body of the corresponding try statment. }catch(MyException e){ Test.java:17: Exception MyException must be caught, or it must be declared in the throws clause of this method. throw new MyException( ); 2 errors C:\java> |
C:\java>javac Test.java
C:\java> |
Author: switch over
A big outdoor sports lover & A IT engineer.
9.インターフェース
|
12.マルチスレッド
12章 マルチスレッド | |
1.スレッドとは | |
一つのプロセスで、同時に複数の処理をすることができる。例えば、ブラウザでインターネットからファイルを取得する仕事が動き、さらにユーザからの操作を受け付ける仕事があり・・・これらが同時に動いている。この仕事一つ一つをスレッドという。 複数スレッドが同時に処理することができるプログラムをマルチスレッドプログラムという。 |
|
2.スレッドのライフサイクル | |
同時にスレッドが動くといっても実際はCPUが一つなので、一つ目のスレッドが動いたら二つ目のスレッドが動く、というように短時間の間に交互に動く。これをタイムディビジョン方式という。 | |
![]() 現在、実行されているスレッド 待機状態 通信の返事が返ってくるまで待つなどキーボード入力を待ったり、イベントが終了するまで待機している状態。または、何もすることがない状態。 実行可能状態 本来は、すぐに実行になりたいが別のスレッドが実行中の場合は、一時待つ。 |
|
3.スレッドの作成 〜Threadクラス〜 | |
スレッドを作るには、java.langパッケージのThredクラスを継承すれば作られる。さらに、スレッドの中身はrunメソッドをオーバーライドして記述する。そして、スレッドクラスのstartメソッドを呼び起こせば、新しいスレッドが作成されrunメソッドが別スレッドとして実行される。 | |
Test.java// 独自のスレッドクラス class MyThread extends Thread{ public MyThread( ){ } // このrunメソッドがスレッド本体 public void run( ){ System.out.println(“MyThread Start!”); // 10回500ミリ秒毎n”Hello”と表示する public class Test { // スレッドを開始 // 1秒毎に”Hi !”と10回表示する |
|
C:\java>javac Test.java
C:\java>java Test |
|
4.Threadクラスのメソッド | |
●コンストラクタ概要 | |
Thread( ) 新しいThreadオブジェクトを割り当てる |
|
Thread(Runnable target ) 新しいThreadオブジェクトを割り当てる |
|
Thread(Runnable target,String name) nameという名前の新しいThreadオブジェクトを割り当てる |
|
Thread(String name) nameという名前の新しいThreadオブジェクトを割り当てる |
|
●メソッド概要 | |
String getName( ) このスレッドの名前を返す |
|
boolean isAlive( ) このスレッドが生存しているかどうかを判定する |
|
void join(long millis) このスレッドが終了するのを、最高でmillisミリ秒待機する |
|
void run( ) このスレッドが別個のRunnable実行オブジェクトを使用して作成された場合、そのRunnableオブジェクトのrunメソッドが呼び出される |
|
void setName(String name) このスレッドを、デーモンスレッドまたはユーザスレッドとしてマークする |
|
void setPriority(int newPriority) このスレッドの優先順位を変更する |
|
static void sleep(long millis) 現在実行中のスレッドを指定されたミリ秒数の間、スリープ(一時的に実行を停止)させる |
|
void start( ) このスレッドの実行を開始 |
|
static void yield( ) 現在実行中のスレッドオブジェクトを一時的に休止させ、ほかのスレッドが実行できるようにする |
|
5.スレッドの作成 〜Runnableインターフェース〜 | |
Test.java// 独自のクラス class MyRunnable implements Runnable{ // このrunメソッドがスレッド本体 public void run( ){ System.out.println(“MyRunnable Start!”); // 10回500ミリ秒毎n”Hello”と表示する public class Test { // スレッドを開始 // 1秒毎に”Hi !”と10回表示する |
|
C:\java>javac Test.java
C:\java>java Test |
|
6.同期 | |
ロックとは、一つ目のスレッドがあるメソッドをロックすると別のスレッドはそのメソッド内に入れなくなる。そのメソッドを出るときにロックを解除することで別スレッドがそのメソッドに入ることができるようにする。 メソッドを作成するときに「Synchronized」修飾子をつける。javaではこれだけでロックをかける(同期をとる)ことができる。 |
|
Test.java// 口座クラス class Account{ private int balance = 0;
synchronized void deposit ( int amount ){ // 顧客クラス // コンストラクタ // 10円ずつ貯金することを1000回繰り返す class Test{ // 10人の顧客を作る // 10人の顧客が一つの口座に振り込み処理を } // 10人のスレッドが終わるのを待つ // 残高表示 |
Test.java// 口座クラス class Account{ private int balance = 0; void deposit ( int amount ){ int getBalance( ){ // 顧客クラス // コンストラクタ // 10円ずつ貯金することを1000回繰り返す class Test{ // 10人の顧客を作る // 10人の顧客が一つの口座に振り込み処理を } // 10人のスレッドが終わるのを待つ // 残高表示 |
C:\java>javac Test.java
C:\java>java Test |
C:\java>javac Test.java
C:\java>java Test |
メソッドにsynchronized修飾子をつけて同期をとる方法ではなく、オブジェクトにロックをかけて同期をとる方法 | |
Test.java// 口座クラス class Account{ private int balance = 0; void deposit ( int amount ){ int getBalance( ){ // 顧客クラス // コンストラクタ // 10円ずつ貯金することを1000回繰り返す class Test{ // 10人の顧客を作る // 10人の顧客が一つの口座に振り込み処理を開始する } // 10人のスレッドが終わるのを待つ // 残高表示 |
|
C:\java>javac Test.javaC:\java>java Test 残高:100000 C:\java> |
|
この方法は、accountにロックをかけている。一つ目のスレッドがaccountにロックをかけると、別のスレッドはロックが解除されるまで、synchronizedで囲まれたブロック(synchronizedブロック)には、入ることができない。 | |
7.デットロック | |
デットロックとは、マルチスレッドプログラミングで起こりやすいバグの一つ。一つ目のスレッドがXというオブジェクトのロックが解除されるのを待つ。二つ目のスレッドはYというオブジェクトをロックしており、Xというオブジェクトの解放を待つ。このような場合、両方のスレッドはオブジェクトの解放を永遠に待ち続けてしまうため、プログラムは進まなくなり、いわゆる暴走の状態に陥る。このように、複数のスレッドがお互いにロックの解放を永久に待ち続けてしまうことをデットロックという。 | |
Test.java class X{ int x; } class Y{ class A extends Thread{ A(X x, Y y){ void func1( ){ void func2( ) { public void run( ){ class Test{ A a[ ] = new A[10]; |
|
C:\java>javac Test.javaC:\java>java Test func1:start func1:start func1:start func1:start func1:start func1:start func1:start func1:start func2:start func1:start func1:start func2:start ←ここでデットロックが発生
|
|
多くの場合、ロックするオブジェクトを同じ順番でロックすれば、デットロックは防げる。例えば上記では、func2のsychronizedでロックする順番をfunc1と同じように「x→y」の順番でロックすればデットロックは防げる。言い換えるとオブジェクトに優先順位を与えるということになる。 このようにマルチスレッド特有のバグが発生した場合の多くは、バグを再現させることすら難しく、デバッグ作業は大変な作業になってしまう。バグが起きないようにあらかじめちゃんと設計してからプログラミングする必要がある。 |
|
8.スレッド間の通信 | |
スレッドから一時的にロックを解放し、他のスレッドにsynchronizedメソッドもしくはsynchronizedブロックを実行するチャンスを与えることができる。例えば、一つ目のスレッドはメッセージを受信するスレッドで、二つ目のスレッドはメッセージを与えるスレッドである。メッセージ受信のスレッドは、メッセージボックスにメッセージがなければメッセージが格納されるまですることがない。このような場合ほかのスレッド(メッセージ送信スレッド)に制御を移す。逆にメッセージ送信スレッドは、メッセージボックスがいっぱいならば、メッセージ受信スレッドがメッセージを処理し終わるまで待たなければならない。 このように、他のスレッドが処理し終わるまで待つ場合は「wait」メソッドを使用し、他のスレッドに制御を与える場合は「notify」または「notifyAll」メソッドを使用する。notifyAllメソッドはwaitしているすべてのメソッドに対して通知をするが、notifyメソッドは、いずれか一つのスレッドに対してのみ通知する。どのスレッドに通知されるかは、JVMが勝手に決めていいことになっている。 |
|
Test.java import java.util.*;
// メッセージボックス // メッセージボックスにメッセージを入れる // メッセージを入れる // メッセージを入れ終わったら別のスレッドを起こす // 以下例外処理 // メッセージ取り出し // メッセージ取り出し // 取り出したら別のスレッドを起こす // 以下例外処理 // メッセージを取り出し、表示するスレッド class Test{ public static void main(String args[ ]){ MessageSender sender = new MessageSender( ); try{ |
|
C:\java>javac Test.javaC:\java>java Test 0.2894081823761079 0.20714542091971344 中略 0.519766868805261 C:\java> |
|