最終更新日 |
1 準備 2 最初のトライ-再帰用法- 3 for,whileによる繰り返し
4 配列 5 Vector,Stack,配列 6 ArrayList 1(Fput) 7 ArrayList 2(Insert)
1 Java2SE1.4 サンマイクロシステムズのホームページ、Java 2 Platform, Standard Edition v 1.4.2 から、Java 2 SDK, Standard Edition 1.4.2_01 をXPにダウンロード、インストール。
2 環境変数 コントロール・パネル-システム-詳細設定-環境変数で、ユーザとシステムの環境変数のPathに、c:\j2sdk1.4.2\bin を加える(図では、最初にいれたj2sdk1.4.1_04\binとなっている)。再起動後に有効となる。
[2005/10 追記] 2005/10時点では、 Java2 SE5.0 Update5 となっている。このjdk1.5.0_05を C:\Program Files\Javaにインストールしたので、環境変数を C:\Program Files\Java\jdk1.5.0_05\bin; と変更した。
設定ができたかどうかのテスト方法が「コア Java2 Vol.1 基礎編」p45にあった。 コマンドプロンプトで、どのディレクトリからでも、 java -version といれる。下記のように、version情報が出ればよい。 C:\JavaTry\Application1>java -version java version "1.5.0_05" Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_05-b05) Java HotSpot(TM) Client VM (build 1.5.0_05-b05, mixed mode, sharing) [蛇足1] 設定が失敗している場合には、 C:\JavaTry\Application1>cd .. C:\JavaTry>cd .. C:\>cd c:\Program Files C:\Program Files>cd Java\jdk1.5.0_05\bin などと面倒なことをしてようやく、 C:\Program Files\Java\jdk1.5.0_05\bin>java -version といれることになる。 [蛇足2] Java以外の普通のWindows プログラムがコマンドプロンプトから起動するかどうかやってみた。 C:\Documents and Settings\Owner>cd c:\Program Files C:\Program Files>cd JTrim C:\Program Files\JTrim>Jtrim.exe 首尾よく起動した。なお、Jtrim.exeとは、フリーの画像処理ソフト。
3 NoClassDefFoundErrorのエラー 以前、Win2000、jdk1.2やj2se-1.3を使って「独習Java」(ジョセフ・オニール、翔泳社 1999/5 初版)の例題を走らせたことがありった。そのときの例題のいくつかで、まずトライしたが、コマンドプロンプトでコンパイルはできて、Example.classはできるが、「NoClassDefFoundError」となって実行できなくて弱った。以前にはこんな問題はなかった。
4 対策は、set CLASSPATH=. サンマイクロのホームページに、日本語のチュートリアルがあるのを見たが、j2eeチュートリアル であった。Enterprize Editionは、今の私はまだ早い。
javaチュートリアル は英文ながら、なにかありそうだった。
その中の"Your First Cup of Java (for Microsoft Windows)"に、 「NoClassDefFoundErrorのエラーが出る場合は、コンパイルしたExample.classを見つけられないから」 というような記述があり,対策として、 "If you still have problems, you might have to change your CLASSPATH variable. To see if this is necessary, try "clobbering" the classpath with the following command: set CLASSPATH=. とあったが、これをどこに使うかがよく分からず。
Java覚え書き(日立ソフト インターネットビジネス部) の中の「Javaアプリケーションの実行方法あれこれ (2001.3.29)」に下記のように、明確にあって、やっと解決した。やれやれ。
「NoClassDefFoundError」に遭遇したときにまず目を通すべし。 DOSプロンプトなどのコマンドプロンプトから、 C:\xxx> type MyApplication.java public class MyApplication { public static void main(String args[]) { } } C:\xxx> javac MyApplication.java C:\xxx> set CLASSPATH=. ← カレントディレクトリにパスを通す C:\xxx> java MyApplication
ただ、set CLASSPATH=. は、コマンドプロンプトで最初に一度やればよく、毎回やる必要はなくて助かる。Win2000では、set CLASSPATH=.も不要であった。
5 API 仕様 一通りやってからホームページにしたのでは、ひっかかったところも忘れたり、混線したりするので、ひとつ、プログラムが通る度に記述してみる。ややこしいオブジェクト指向的用法も、JavaScriptである程度理解したので、以前よりは楽にできることを期待したが、なかなかそうはいかない。
JavaTM 2 Platform, Standard Edition, 1.4.0 API 仕様 も参考にしつつ、悪戦苦闘した経過をほぼそのまま記述する。(2005/10時点の最新は、Java2 SE5.0 API仕様 )。
システムのあるドライブ外、たとえば、d トライブにjavaのプログラムをおいた場合、コマンドプロンプトでドライブ変更する方法を知らず、cd(change directry)にこだわって失敗した。たんに、d: でよいのだった。
なお、大半は2003/8に作っていたが、2004/9に、「Javaの基礎2」 を作成した。主に、継承関係であるが、試行錯誤の過程をかなり克明に書いた。 オブジェクト指向の概念がある程度つかめたと思う段階で、7章は、再度トライしたものである(1節、2節が2004/9。3節は2003/8のもの)。
以前、教育用言語のLogowriterに興味をもった。Lispなみのリスト処理、再帰用法をもち、タートル・グラフィックスというやさしいグラフィック機能も持つという面白い言語である。変数や関数の型には一切制限がない点もよかった。
「独習Java」から離れて、自前の最初のトライは型破りだが、JAVAで再帰が使えるかどうかをみることから始めた。オブジェクト指向を含め、基本をある程度マスターしたら、アプレットに取り組むが、ロゴのタートル・グラフィックスを使ってやってきたことをアプレット化して高度にしてみる。 最大公約数のアルゴリズムの図化、ハノイの塔なども試みたいので、本格的に再帰が使えるかどうかをまずトライしたものである。
脱線するが、Lispで組まれて、Unix,Linuxで使われているEmaxというエディタでは、Lispが動く。xyzzyという名前になっているが、Windowsでも使えるようになっている。
class gcdtest{ public static void main(String[] args) { int ans= gcd(24,16); System.out.println("GCD of 24 & 16 is " + ans); } static int gcd(int m,int n){ if (m%n == 0) return n; return gcd(n,m%n); } } C:\Documents and Settings\Owner>cd c:\Language\javaMytrial C:\Language\javaMytrial>javac gcdtest.java C:\Language\javaMytrial>set CLASSPATH=. C:\Language\javaMytrial>java gcdtest GCD of 24 & 16 is 8
最初は、再帰のgcdなるメソッドのために、別のクラスが必要と思い、エラーに悩まされたが、クラスには複数のメソッドがおけるということなので、class gcdtestも、mainメソッドの他にあってもいいだろうと考えて解決できた。上記で、
[参考:Logowriter] 再帰 最大公約数 TO 最大公約数 :M :N IF (余り :M :N) = 0 [OP :N] OP 最大公約数 :N (余り :M :N) END
class hanoi{ public static void main(String[] args) { hanoi2(3,"X","Y","Z"); System.out.println("stop"); } static void hanoi2(int n,String X,String Y,String Z){ if (n == 0) return; hanoi2(n-1,X,Z,Y); System.out.println(n + "番目の円盤を " + X + "の柱から " + Z + "の柱へ移す"); hanoi2(n-1,Y,X,Z); } } C:\Language\javaMytrial>javac hanoi.java C:\Language\javaMytrial>java hanoi 1番目の円盤を Xの柱から Zの柱へ移す 2番目の円盤を Xの柱から Yの柱へ移す 1番目の円盤を Zの柱から Yの柱へ移す 3番目の円盤を Xの柱から Zの柱へ移す 1番目の円盤を Yの柱から Xの柱へ移す 2番目の円盤を Yの柱から Zの柱へ移す 1番目の円盤を Xの柱から Zの柱へ移す stop [参考:Logowriter] 再帰 ハノイの塔 TO HNI :N :X :Y :Z IF :N = 0 [STOP] HNI :N - 1 :X :Z :Y MOVE :N :X :Z HNI :N - 1 :Y :X :Z END
さすがに、これは最大公約数ほど簡単ではなかったが、無事できて本格的な再帰用法も使えそうだ。がぜん、やる気がでてきた。
whileによる繰り返しでは、条件を満たして繰り返しのループを抜けるときに、return、breakが使えるまでに少々、苦労した。試行錯誤を繰り返し、一応解決。
class gcdtest{ public static void main(String[] args) { int amari,ans=0,m=512,n=224; while (n>0) { amari=m%n; if (amari==0) { ans=n; break; } m=n; n=amari; } System.out.println("GCD of 512 & 224 is " + ans); } } GCD of 512 & 224 is 32
最大公約数は再帰用法を使ってでやったものだが、再帰を使うために必要だったメソッドは特にいらないということで、メインメソッドの中に、直接、whileによる繰り返しをいれた。このとき、繰り返しを抜け出すのはbreak。
class gcdtest{ public static void main(String[] args) { int ans= gcd(504,392); System.out.println("GCD of 504 & 392 is " + ans); } static int gcd(int m,int n){ int amari; while (n>0) { amari=m%n; if (amari==0) { return n; } m=n; n=amari; } return 0; } } GCD of 504 & 392 is 56
最大公約数、こんどは再帰のときとき同様、メソッドを別にたててみた。このときの抜け出しはreturn。
配列のnew演算子の使い方、配列をメソッドに渡すときや大きさを取り出すlengthプロパティの記述がややこしかった。あらためて、本を精読し、多少の試行錯誤で一応解決。あわせて、乱数発生でMathクラスの静的メソッドなるものを使ってみる。
class gokei{ public static void main(String[] args) { double data[]=new double[5]; //newの次は型名 for(int i=0;i<data.length;i++) { data[i]=Math.random(); //Mathクラスの静的メソッド System.out.println(data[i]); } System.out.println("5ケの乱数の合計" + gokei2(data)); } static double gokei2(double data[]){ //呼び出しは、[]不要 double gokei=0.0; for(int i=0;i<data.length;i++) { //lengthは配列の大きさ gokei += data[i]; } return gokei; } } C:\Language\javaMytrial>java gokei 0.6051313759458448 0.1400009188522261 0.40665218366727673 0.2617307190714415 0.16959920602141543 5ケの乱数の合計1.5831144035582043 あらかじめ、配列に値をいれておくときは、newは不要で、たとえば、 int data[]={10,20,30,40,50}; とするだけでよい。
私がひっかかったところにはコメント行をつけた。new演算子は、配列(Array)クラスのインスタント化と聞いているが、配列を宣言して要素を割り当てるということだから、型名と大きさが必要とわかれば混乱しない。
data.lengthのところも、[]をつけて失敗した。メソッドに配列を渡すときの[]の要、不要をみるため、特に必要はなかったが、gokei2というメソッドをたててトライした。
class fibonacci{ public static void main(String[] args) { int fiboarray[] =new int[20]; //フィボナッチ数列の作成 fiboarray[0]=1; fiboarray[1]=1; for(int i=2;i<fiboarray.length;i++) { fiboarray[i]=fiboarray[i-1]+fiboarray[i-2]; } //フィボナッチ数列の表示 System.out.println("Fibonacci数列"); for(int i=0;i<fiboarray.length;i++) { System.out.print(fiboarray[i] + " "); } } } Fibonacci数列 1 1 2 3 5 8 13 21 34 55 89 144 233 377 610 987 1597 2584 4181 6765
数列の前2つの値を加えたものを新しい要素とするのがフィボナッチ数列で、13世紀、イタリアの数学者が自然を観察していて気付いたものという。そして、ある程度いったところからば隣り合う2数が黄金分割比と関連があったり、面白いものである。
たとえば、19番目と20番目の 4181/6765=0.618034の値は、ほとんど黄金分割比そのものである。
(広中平祐の数学教室 サンケイ出版 1985年 から)。
配列は、最初に大きさを指定しないといけないことが問題であり、結果を簡単には1行に並べて表示できないことも不満。もっとも、フィボナッチ数列の例のように、forによる繰り返しとSystem.out.printではできるが。
Javaクラスライブラリに、VectorクラスとかStackクラスというのがある。ロゴやLispのリスト処理的なこともできそうとトライした。分かっていないせいか、型変換で苦労させられた。他に方法があるだろうと、コメントしておいた。
配列の値の合計をforループで求めるのは簡単だが、あえて再帰でやってみた。「独習Java」をみていて、こういうときはVectorが配列よりも向いているようだとトライしてみた。
import java.util.*; class Vectortry{ public static void main(String[] args) { // ベクトルとその要素を作成する Vector vector = new Vector(); vector.addElement(new Integer(1)); vector.addElement(new Integer(2)); vector.addElement(new Integer(3)); vector.addElement(new Integer(4)); vector.addElement(new Integer(5)); // ベクトルの要素を表示する System.out.println(vector); System.out.println("合計 " + gokei(vector)); } static int gokei(Vector vector){ if (vector.isEmpty()) return 0; // vectorの最初の要素を取り出して、2行がかりで整数に直す。他に方法がある? Integer obj = Integer.valueOf(vector.firstElement().toString()); int n=obj.intValue(); // vectorから最初の要素をとり除く。 vector.remove(0); //vector.removeElementAt(0);でも同じ return n + gokei(vector); } } C:\Language\javaMytrial>java Vectortry [1, 2, 3, 4, 5] 合計 15 [参考:Logowriter] 再帰 合計 to GOKEI :DATA if :DATA = [ ] [output 0] output (first :DATA ) + (GOKEI butfirst :DATA) end print GOKEI [1 2 3 4 5]
コメントをつけた2行は、 int n=Integer.valueOf(vector.firstElement().toString()).intValue(); と、1行にしたり、parseIntを使ってやや簡潔にはできる。 int n=Integer.parseInt(vector.firstElement().toString()); しかし、vectorの中味は 整数としていたのに、なぜ、こんなに複雑なことをしないといけないのか、 不満である。vector.firstElement()の出力はobjectとか、Integerのカプセル化とか、オブジェクト 指向は難解である。 「独習Java」には、カプセル化を「データとそれを処理するコードを関連づけるメカニズム、コードはデータ の周りに施される保護カプセルのようなもの」とあるのだが?
配列の合計とか、その中の最大値を求めるような問題は、上記のようなことでできるが、配列の先頭に値をいれるとか、配列の中のある値より小さいときは、その位置に数値をいれて、配列をそのまま取り出すという問題は、配列もベクトルもスタックもあまりうまくいきそうになかった。いろいろ、試行錯誤したが、配列では大きさがあらかじめ、きまっているから、なにか値を追加するため、配列の大きさが増えるときことには弱そうである。
冒頭に紹介したJavaTM 2 Platform, Standard Edition, 1.4.0 API 仕様 を見ていて、リスト処理のできるArrayListクラスというのがあることに気付いた。その試行錯誤を章をあらためて次に述べる。
//リスト Fput import java.util.*; class Fput { public static void main(String[] args) { //リストとその要素を作成する ArrayList al = new ArrayList(10); //コマンドライン引数をargsで受け取りリストに。 for (int i=0; i < args.length; i++) al.add(i,new Integer(args[i])); //リストの要素を表示する System.out.println(al); //リストの最初に1を追加する al.add(0,new Integer(1)); System.out.println(al); } } ロゴのFPUTというコマンドを実現しようとしたものである。 もとになるリストをコマンドライン引数で用意し、 java Fput 3 5 7 9 とする。これをargsで受け取り、リストに入れる。その先頭に1をいれたリストを出力するものである。 結果は [1, 3, 5, 7, 9] なお、JDK1.2のときは、main(String args[])と、[]の位置は、argsの方にあったが、今は main(String[] args) と、String側に移っている。いつのまに?
リストの任意の位置に値を挿入するものである。20003/8にもトライしており、ArrayListを見いだしたり、我ながら善戦しているとは思ったが、プログラムはちょっとひどいので、Insert その3に移して、2004/9に更新したリストから先に示す。 1、2節は、「Javaの基礎2」 に書いたような試行錯誤の結果、オブジェクト指向の概念がある程度つかめたと思う段階で、2004/9に、再度トライしたものである。
2節は、変更点のみのリストなので、念のため、全リストを「Insertのjavaリスト(txtファイル) におく。20003/8には、やはり、さまざま試行錯誤しており、そのリストもある。
//リスト Insertクラス import java.util.*; public class Insert { int n; ArrayList al; public Insert(int n,ArrayList al){ this.n = n; this.al = al; } public ArrayList insert2(int n,ArrayList al) { int i; for (i=0; i//リスト InsertTry import java.util.*; public class InsertTry { public static void main(String[] args) { ArrayList al = new ArrayList(10); //リストとその要素を作成する for (int i=0; i < args.length; i++) al.add(i,args[i]); //コマンドライン引数をリストに。 int n=6; //リストのnより大きい数の前にnを挿入する。 Insert obj= new Insert(n,al); //Insertクラスのインスタンス System.out.println(obj.insert2(n,al)); //リストの要素を表示する } } [実行結果] コマンドライン引数 3 5 7 9 に、整数 6 をいれるもの java InsertTry 3 5 7 9 [3, 5, 6, 7, 9] コメント 1 コンストラクタ thisキーワードも使って、しゃれてみたが、みごとにエラー this.n = n; も this.al = al; も「シンボルを解決できない」となる。 単に、n = n; al = al; ならOK。 public class Insert の冒頭に、下記の型宣言をいれなかったためであった。 int n; ArrayList al; 2 コンストラクタで、this(n,al) とすると「コンストラクタの呼び出しが再帰的」 となり、もうひとつ、引数の異なるコンストラクタを作ってみたが、結果は同じ。 this(n,al)は、「独習Java」P136のようなケースで使うものらしく、この場合は無 理して、使うこと自体が無理のようだ。 3 コマンドライン引数は文字列、ArrayListのaddメソッドの引数の一つはオブジェクト したがって、 al.add(i,args[i]) でいいのだが、 最初は、al.add(i,new Integer(args[i])) と、Integerクラスのコンストラクタで、 Integerに 変換していたが、次の4、5のように、ArrayListクラスとの関係で不必要。 4 ArrayList al.get(i)はオブジェクトを返す。alにIntegerオブジェクトが入ってい ても、下記で整数化しようとすると、 int nn=Integer.valueOf(al.get(i)).intValue(); エラーになる。5のように、toString()も使う必要がある。 5 文字列、つまりStringオブジェクトであっても、 int nn=Integer.parseInt(al.get(i)); やはり、エラー。 一般的なオブジェクトという正体不明(?)なものは、toString()を使って、String オブジェクトに変換してからでないと、整数化できないものらしい。 int nn=Integer.parseInt(al.get(i).toString()); 6 Insertクラスは、結果をArrayListで戻そうとして、 return(al.add(i,new Integer(n)))と直接やると、 「互換性のない型。検出値: void 期待値: java.util.ArrayList」 となって、エラー。 return al とする。
数値のかわりに、文字でやってみた。数値比較の大なり小なりの記号手はなく、 compareToというStringクラスのメソッドを使ってみた。変更点のみ示し、全リストは、念のため、「Insertのjavaリスト(txtファイル) におく。
//リスト (変更点のみ) public class Insert { ............. public ArrayList insert2(String n,ArrayList al) { ............. for (i=0; i < al.size(); i++){ if (n.compareTo(al.get(i).toString()) <= 0) { al.add(i,n); return al; } } ............. } } public class InsertTry { public static void main(String[] args) { ............. String n=new String("d"); } } [実行結果] コマンドライン引数 a c e f h k lに、文字列 dをいれるもの java InsertTry a c e f h k l [a, c, d, e, f, h, k, l] コメント 1 compareToは、Stringオブジェクトのインスタンスで、引数もStringオブジェクト。 最初は、 n compareTo(...)という書き方をしてしまった。 2 compareToの出力は整数、1, 0,-1。"a".compareTo("b")の場合には、-1 となる。 3 数を文字列として扱った場合には、lexicographically less とか、greater とかで、 たとえば、10は3より小さいとみなされる。
//リスト import java.util.*; class Insert { static int s=0; static ArrayList insert2(int n,ArrayList al) { for (int i=0; i < al.size(); i++){ int nn=Integer.parseInt(al.get(i).toString()); if (nn > n) { al.add(i,new Integer(n)); s=1; break; } } if (s==0) System.out.println(n + "より大きい数はリストにありません。"); return al; } } import java.util.*; class InsertTry { public static void main(String[] args) { ArrayList al = new ArrayList(10); for (int i=0; i < args.length; i++) al.add(i,new Integer(args[i])); int n=6; Insert.insert2(n,al); System.out.println(al); } } コメント(2003/8) 1 java InsertTry 3 5 7 9 として、リスト中の n=6 より大きい値の前に挿入しよう としている。結果は[3, 5, 6, 7, 9]。 n=12 なら、リスト中にはそれより大きい値がないので、そのむねコメントして、も とのリストを出力している。(2004/9の1では、それを最後にいれたリストを出力)。 2 いままでは、ひとつのクラスに、mainと、そこから呼び出されるメソッドをいれてき た。この例題もそれで十分だが、勉強のため、そのメソッドを別のクラスに入れてみた。 クラス名も必要になり、記述が複雑になる。 3 Insert.insert2(n,al) とすると、 static ArrayList insert2(int n,ArrayList al) または、 static Object insert2(int n,ArrayList al) のように、staticをつけないといけない。クラスに関連づけられるのがクラス(静的) メソッドというわけだ。 4 メソッド名だけで呼び出して、ArrayListの中味、alを出力するだけなら、メソッド からreturnを落として、ArrayListもObjectもとって、voidとした方が簡単かもしれな い。 5 staticをとって、インスタンスメソッドとして呼び出すには、 Insert obj= new Insert2(); //インスタンス化 obj.insert2(n,al); と、new 演算子を使う。 6 こういう例の場合、3,4,5 の3通りのどれがふさわしいのだろうか? コメント追加(2004/9) コンストラクタをつけ、判定のためにいれたsやbreakをとって、プログラムを簡潔 にしただけで、骨格は変わっていない。ただ、その判定のためのsがなぜ、staticなの か、また、1節のようにreturnを使って、breakを不要とする方がすっきりする。ちょ っとお粗末ではあった。 結局、3でやったようだが、5の方がやはりいいように思う。1節はそのように変更した。 4の方法ではできないのではないか?
終わりに
Javaの基礎というにはおこがましいが、再帰とかリスト処理とか、かたよった例題を扱いながら、慣れないと難解なオブジェクトの神髄の理解過程(まだその途上)と使い方を試行錯誤した結果をまとめた。
コンパイルエラーの大半は型についてのものだった。特に、Objectを出力するという、ArrayListやVectorのメソッド、(get,remove,setなど)の処理は、まだ釈然としない。 System.out.printlnで出力するのは問題ないが、要素を取り出して処理しようとしたり、returnで結果を戻そうとすると、ひっかかることが多かった。
そもそも、Objectの出力とはなにを意味しているのかというのが疑問である。「独習Java」には、「オブジェクトとは状態と動作の両方を定義する記憶領域、データとそれを操作するコードの組み合わせ、クラスのインスタンス」と言っているのだが?
オブジェクト指向言語の3つの特徴、カプセル化、継承、ポリフォーフィズム。カプセル化については、5章でふれた。後の2つを自分で利用するところまでいって理解したいものだ。ようやく到達したこともすぐ忘れるので、自分に対するメモとして、まずはアップしておこう。理解が進めば更新することにして。(以上、2003/8)
なお、2003/9には、下記を作成した。
数学的例題 (未完)
タートルグラフィックスとフラクタル (アプレット1)
マンデルブロート・カオス (アプレット2)
さらに、前述したが、2004/9に、「Javaの基礎2」 を作成した。主に、継承関係であるが、試行錯誤の過程をかなり克明に書いた。