最終更新日



Java入門1(Application基本)

文科系の学生に、前期、ホームページ作成をしてもらった後、Javaのプログラムに挑戦してもらうことにした。Excelなどは市販の本もたくさんあるのだから、基礎ができていれば、それを参考に試行錯誤すれば十分、もっとも基本的なことをやっておくのがいいだろうと思うから。Javaについては、すでに、基礎、基礎2,基礎3、フラクタル図形などのページを作っているが、やや、個人的興味からはいっているので、学生に配布予定の資料を基にして、その前段の部分を作ることにした。(5章以降はJava入門2 へ)


[このページ]
 1 Javaはオブジェクト指向言語  2 操作、基本データ型、Integerクラス  3 if文と繰り返し処理

4 配列と繰り返し  E [参考]エラーメッセージ例   S [参考] コマンドプロンプトの操作など

[Java入門2]
 5 2進、16進数、API、クラスとイスタンス  6 文字列、Stringクラス

7 オブジェクト、クラス、インスタンス  8 Java アブリケーションプログラム 補足

Java、学生向けのトップページ例(学生向けにまとめたページ(暫定版) と、そこからもリンクしている
 Javaのプログラミング1ーApplication がある。


[2006/11/29 追加・修正]
 4-3 素数 修正。 1も素数としていたが、削除。最初から偶数を除いてチェックするように変更。
 4-4-2 ソート追加、「もっと詳しい途中経緯」。
 4-5 ソート追加、配列の最大値を求め、それを配列右側と置き換える。
 4-6 ソート追加、「大きい順にソート」 と 「4-5-2 大きい順にソート その2」。

1 Javaはオブジェクト指向言語

1 概要と狙い

Javaは、3通りあるが、ここでは、J2SE - Java 2 Platform, Standard Edition - デスクトップ・アプリケーション向け を取り上げる。 J2SEにも、 アブリケーションソフト を作成するもの、インターネット上で動くアプレット とがあり、まずは、アブリケーション版から入り、短いプログラムで、操作とプログラムの基本になれてもらう。その後、アプレット版でグラフィックスを中心にやっていきたい。そして、メニューやボタンなどのGUI(Graphic User Interface)もつけて、ユーザとの対話も図れる所までやってみたい。半年もなくて、無理は承知だが、今後の学習のきっかけになればよしとする。作品の一部は、ホームページにとりこんでもらう予定(上記、Java、学生向けのトップページ例を参考にして)。

最近の言語は、みな、オブジェクト指向 ということで、オブジェクト、クラス、インスタンス、コンストラクタ、メソッドとか、いろいろな用語が出てくるが、例題の中で説明していくことにして、まずは、基本データ型の変数、配列と繰り返し、if などプログラムの基本を重点にしながら進める。もっとも、Javaは典型的なオブジェクト指向言語とあって、クラス、インスタンス、メソッドといったことも最初から使う必要があるが、それも繰り返しやっていって、なれていただこう。

2 準備(大学ではいつでもできる状態、自宅でやるために)

2-1 Javaのシステム
 Googleにて、Javaで検索して、サンマイクロシステムズの日本語版ホームページ(Javaテクノロジ)から、Java2SE5.0(Java 2 SDK, Standard Edition 5.0 をダウンロード、インストール。(2005/10時点での最新版)。
 (しばらく前までは、Java2SE1.4(Java 2 SDK, Standard Edition 1.4.2_06 であり、1.5の時点で、大幅に変わったということで、一気に5.0となったようである)。

2-2 環境変数の設定
 コントロール・パネル-システム-詳細設定-環境変数で、ユーザとシステムの環境変数のPathに、c:\jdk1.5.0_05\bin を加える( Javaの基礎 の図を参照)。再起動後に有効となる。(内部的にはシステム名はまだjdk1.5のままのようである。)

3 Javaのプログラム作成とコンパイル、実行

3-1 プログラム(ソース)の作成
xyzzyや秀丸などのエディタソフト でおこない、拡張子はjavaとして保存。プログラム名とファイル名は同じにする。たとえば、PrintDemo.java。

3-2 コンパイルと実行
コンパイルと実行は、コマンドプロンプト で行う。
スタートープログラムーアクセサリからコマンドプロンプトをショートカットアイコンにしておく。
コマンドプロンプトのcd というコマンドをって、PrintDemo.javaというソースファイルのあるディレクトリ(フォルダ)に移動して、Javaのシステムにあるjavac.exeでコンパイルして機械語(バイトコード)にする。

javac PrintDemo.java 拡張子classのファイルができる。これはどのOSでも使えるように設計されている。ただし、OSごとにバイトコードを解釈、実行する実行ソフトが必要になる。これがJVM(Java Virtual Machine)。
この中にあるjava.exeを使って、java PrintDemo として実行。結果もコマンドプロンプトに出る。
なお、2-2の環境設定は、javacやjavaの起動を簡単に記述できるようにするためである。

4 参考

4-1 API
 クラスライブラリ概要(ネット上のJava2 SE5.0 API仕様 日本語版
(以前のものに、JavaTM 2 Platform, Standard Edition, v 1.4.2 API仕様
 とか、さらに少し古い版のものには、日本語版もある。JavaTM Platform, Standard Edition, v 1.3API 仕様

4-2 本
「独習Java」(ジョセフ・オニール、翔泳社 1999/5 初版)
「Javaチュートリアル 第3版」 M.カンピオーネ他 (ピアソン・エデュケーション 2001/11)
「コアJava2 Vol1.1基礎編」改定版(C.Horstmann/G.Cornell共著 ASCII 2001/12)

4-3 ネット上のもの
オブジェクト指向
オブジェクト指向 WITH JAVA
初心者のためのJava講座
Java講座
Java
プログラミング言語II (理系授業の教材のよう)
4.データ型 (上記の一部)
JavaFAQ: Java FAQ それ自身に関するよくある質問と回答
Java VM Selector for IE v0.91
The Source for Developers Search Results
Java Plug-in Software HTML Converter Download
GUI コンポーネントの作成 (1)
Spinner

2 操作、基本データ型、Integerクラス

最も基本的なアブリケーションプログラムの作成(エディタ)とコンパイル、実行(コマンドプロンプト)の方法を述べる。数値(整数、実数)の基本データ型や足し算、掛け算、余りなどの演算といった基本から、String(文字列)とIntegerクラスと多少、オブジェクトにもふれていく。逐一の理屈はあと回しにして、例題を数こなして、こう書くもの、こう操作していくものという定石になれていくことが先決。

2-1 画面印字

ソースプログラム作成と保存
 ・Zドライブに、JavaTryというフォルダ、その中に、Application1というフォルダを作っておこう。
 ・下記をxyzzyで打って、PrintDemo.java というファイル名で、 z:\JavaTry\Application1 に保存する。
  下記のクラス名とファイル名は一致させる必要がある。
 ・最初の4行、//つきのものは、Javaに対するコメントであるが、コンパイルや実行のとき、
  コマンドプロンプトで使うもの。こうしておくと、コピーー貼り付けで簡単になる。
//cd z:\JavaTry\Application1 (注 通常はcまたはdドライブなので、zはcまたはd)
//set CLASSPATH=.
//javac PrintDemo.java
//java PrintDemo

class PrintDemo  {
  public static void main(String[] args) { 
    System.out.println("First Java Application");
  }
}
コマンドプロンプトを起動、コンパイルと実行
  ・cd z:\JavaTry\Application1をxyzzyからコピーする(Ctrl+Insert)。
  コマンドプロンプト上で、右クリックして貼り付けとする(他の方法はない)。
   (cd は change directory の意味で、javaのプログラムのあるディレクトリに移動。
  ・javac PrintDemo.javaを同様にして、コマンドプロンプト上にはりつけてEnterキー
    (コンパイルして、PrintDemo.class を作成するため)。
  ・タイプミス、文法ミスがあれば、エラーが出るだろう。修正、上書き保存して再度コンパイル。
   このときは、コマンドプロンプト上で上向きの方向キーまたは、F3のキーだけでよい。
 両者とも、コマンド複写の働きをする。詳しくは、下記、[参考]に。 
  ・コンパイルが無事できれば、Application1のフォルダに、PrintDemo.classができていることを確かめよ。 
  ・java PrintDemo で実行(コンバイルずみのclassファイルをJVMに解釈させる)。
[実行結果] 画面に、First Java Application と 出るだけである。
[参考]
 cdコマンドやjavacで出るエラーメッセージのみかたについては、E [参考]エラーメッセージ例 を参照のこと。
 コマンドプロンプト画面での簡便操作法などは、S [参考] コマンドプロンプトの操作など を参照ください。

2-2 円周率と円の面積

  下記を打つのだが、2-1 をそっくり名前をつけて保存(ファイル名はPai.java )として
 修正していくのがよいだろう。

class Pai {
  public static void main(String[] args) { 
    System.out.println("PAI = " + Math.PI);
    //半径rの円の面積
    double r = 100.0;
    double area= Math.PI * r * r;
    double enshu= 2.0 * Math.PI * r;
    System.out.println("半径100の円の面積 = " + area);
    System.out.println("半径100の円周 = " + enshu);
  }
}
[実行結果]
PAI = 3.141592653589793
半径100の円の面積 = 31415.926535897932
半径100の円周 = 628.3185307179587
[コメント] 
 ・mainのある行は複雑であるが、あとで説明する。前の例題でも同じ記述だった。
  しばらくはこう書く約束ということでいこう。
 ・ = は代入であり、数学でいう等号ではない(if文の中で使う等号には==を使う)。
  左辺はメモリのある領域につけられた名前 で、そこに右辺の値ないしは処理結果を代入する。
 ・型名に必要な領域が確保される(この場合の型名はdoubleで64ビットの領域がとられる。3-3[参考] を参照)。
  ・*は掛け算(どのような演算記号があるか、2-2[参考] を参照)。
  ・+は数値同士なら足し算であるが、文字列の結合にも使われる。
  print文の中では、本来数値であったものも暗黙のうちに文字列に変えられている。
  ・Math.PIは、数学的なMathクラス に用意されているクラス変数。

2-3 文字型から整数への変換

[問題]文字列として与えた"50"を整数に変換して、100を足す。
class StringDemo {
  public static void main(String[] args) {
    String s = "50";
    System.out.println("s = " + s);
    int n = Integer.parseInt(s);
    int n100 = n + 100;
    System.out.println("s + 100 = " + n100);
  }
}
 [実行結果]
  s = 50   s + 100 = 150
[コメント] 
 int s = 50; なら、簡単な足し算だが、練習のため、わざわざ、50という数値に""をつけて、String型に
 している。それに100を足したいが、数値にしないとできないので、IntegerクラスのparseInt(s)メソッド 
 を利用している(1-4.1のAPI、Integerクラスのメソッド参照)。次の例題への伏線である。

2-4 コマンドライン引数

[問題]コマンドライン引数で与えた数に100を足す。
class ArgDemo {
  public static void main(String[] args) { 
    System.out.println("Arg = " + args[0]);
    int n = Integer.parseInt(args[0]);
    int n100 = n + 100;
    System.out.println("Arg + 100 = " + n100);
  }
}
 [実行結果]
   java ArgDemo 50
   Arg = 50   Arg + 100 = 150
[コメント] 
  ・実行時に'java ArgDemo 50'という形で与える、この例では50をコマンドライン引数     という。
  ・4-3で後述するように、これを受け取るのが String[] args であり、引数が1ケの場 合はargs[0]である。
  ・コマンドライン引数は、数値で与えたつもりでも、コンピュータ内部で文字列に変えられているので、 
       int n = Integer.parseInt(args[0]); で数値に変換する(前の例題に同じ)。

3 if文と繰り返し処理

コンピュータの最大の特徴は、何万回、何億回もの繰り返し処理をあきもせず、高速で処理してくれるところにあるだろう。同じことをしてもしかたがないから、変数ないし配列で値をかえながら....。場合によっては、条件が満たされたから、処理を打ち切場合もあるから、if文やswitch文による条件分岐も必要になる。

3-1 奇数か偶数か?(コマンドライン引数、整数変換、余り、if)

[問題]コマンドライン引数で与えた数が奇数か偶数か?を判定する。2で割ったあまり(%)が0なら偶数。
  (「独習Java」にあった例題)
class Even {
  public static void main(String[] args) {
    String s= args[0];
    int n=Integer.valueOf(s).intValue();
    if ((n % 2) == 0) {
          System.out.println(n + " = odd");
    } else {
          System.out.println(n + " = even");
    }
  }
}
 [実行結果]
    java Even 16   
       16 = odd
    java Even 11
       11 = even
[コメント] 
 ・条件による分岐、if(...){..... }; ないし、
                   if(...){.....} else(...){.....};という記述法に注意。
  ・{.....}と括弧でくるまれる部分をブロックという。
  ・if文の中の % は余りを求めるもの、== に注意。= を代入の意味で使うので、ほんとうに等しいことを
   問うのは、==。
  ・コマンドライン引数(文字列)を数値に直すのを2-4のparseInt(s)ではなく、別法を使った。
   valueOfでIntegerオブジェクトとし、それをintValueで整数にする。
     int n = Integer.valueOf(args[0]).intValue();
  ・整数はintだが、それをオブジェクトにするのがIntegerクラス(API参照)。
  ・オブジェクトにすると、そのメソッドを使って型変換できたり、なにかと便利。
  ・if elseブロックの省略した記述がある。三項演算子という。下記。
[別解]
class Even {
  public static void main(String[] args) {
    int n=Integer.parseInt(args[0]);
    System.out.print(n + " = "); 
    System.out.println((n % 2) == 0 ? "odd" : "even"); 
  }
}
 最後の行が三項演算子 といわれる、if文の省略形。

[参考]
 余り(remainder)を求める%が出てきたところで、Javaに、どのような演算(Operators)があるかを
まとめたプログラムを紹介する。
  サンマイクロ社の「Javaチュートリアル 第3版」に紹介があり、ネット上にある、Operators である。
 なお、そのプログラムの演算記号一覧ににない 巾乗(a^b)には、Math.pow(double a,double b) を使う。

3-2 繰り返し処理(for)

コンピュータの最大の特徴である繰り返し処理。forとかwhile,do、再帰用法と、いろいろあるが、ここではforループを扱う。

1から10までの和を求める例。
class Repeat {
  public static void main(String[] args) {
    int total = 0;
    for(int i = 0; i <= 10; i++) {
         total = total + i;
    }
    System.out.println("1から10までの和 = " + total);
  }
}
 [実行結果]  1 から10までの和 = 55
[コメント] 
  1 for (i=1; i<=n; i++) は、カウンタとして使う整数iの初期値、条件判定、iの足しこみで、
   条件が満たされている間は、ブロック({ ...}をいう)内の処理を繰り返す。
  2 変数には、型宣言が必要である。この例題のtotal,iはともに整数型、型名はint(、32ビット)である。
  2-2のdoubleは倍精度小数点つきであった。
  3 型宣言はメソッドにも必要で、これまでの例題では、必ず、mainというメソッド(他の言語の関数相当)
   があるが、処理内容はprint文で画面表示するだけで、戻り値がないため、voidとしている。
   それがこの場合のmainメソッドの型名である。
   4  forを使った繰り返し処理の動きをはっきりさせるために、printlnないし、改行しないprint文
  を調べたいところにいれて、結果を見ていくという手段がある(実例、下記)。
    実行結果がおかしいときのプログラム・デバッグ(エラーつぶし)にもよく使う。
  print文は結果を表示するだけに使うわけではない。

class Repeat1 {
  public static void main(String[] args) {
    int total = 0;
     //1から10までの和
    for(int i = 1; i <= 10; i++) {
         total = total + i;
         System.out.print("i = " + i);
         System.out.println(" total = " + total);
    }
    System.out.println("total = " + total);
  }
}
 [実行結果]
i = 1 total = 1
i = 2 total = 3
i = 3 total = 6
i = 4 total = 10
i = 5 total = 15
i = 6 total = 21
i = 7 total = 28
i = 8 total = 36
i = 9 total = 45
i = 10 total = 55
  total = 55 //forを抜け出したところ。

3-3 繰り返しとコマンドライン引数

1から任意の数までの和を求めるためには、2−3のプログラムでは、その都度、プログラムを変え、コンパイルもやり直さないといけない。それでは不便なので、任意の数をコマンドライン引数で与えることにする。コンピュータの最大の特徴は、何万回、何億回もの繰り返し処理をあきもせず、高速で実行するところにあり、それが命。

1からコマンドライン引数で与えられる値までの和を求める
class Repeat2 {
  public static void main(String[] args) {
    int total = 0;
     //1から引数args[0]で与えられた値までの和
    System.out.println("args[0] = " + args[0]);
    //String型の引数から、Integerクラスのstaticメソッドで整数化する。
    int n = Integer.parseInt(args[0]);
    for(int i = 0; i <= n; i++) {
         total = total + i;
    }
    System.out.println("1から" + n + "までの和 = " + total);
  }
}
[実行結果]
C:\JavaTry\Application1>java Repeat2 100
args[0] = 100    1から100までの和 = 5050
args[0] = 10000  1から10000までの和 = 50005000
args[0] = 100000 1から100000までの和 = 705082704
 この結果はおかしい。int型(32ビット)の最大値 2147483647 内で納まらなかったのであろう。
int total = 0; を long total = 0; に変更すると問題なし(longは64ビット)。
args[0] = 100000  1から100000までの和 =  5000050000
args[0] = 1000000 1から1000000までの和= 500000500000
[コメント] 
 ・コマンドライン引数は、文字型配列と同じ形をとる。引数が1ケなら、args[0]。
 ・計算処理をする場合には、数値にする必要があり、IntegerクラスのparseInt(s)メソッドを利用する。 
     (すでに2-4で解説ずみのことではあるが、再掲。)
 ・3-2,3-3の例題についての詳しい解説は、「JavaScriptの基本」2章 参照。

[参考]
 int,long型に関連して、基本データ、8つの基本形をまとめたプログラムを紹介する。
  サンマイクロ社の「Javaチュートリアル 第3版」に紹介があり、ネット上に、Variables にあるものである。
 実行結果のみ、コメントをつけて紹介する。
The largest byte value is 127                 // (8ビット符号つき整数)
The largest short value is 32767              //(16ビット符号つき整数)
The largest integer value is 2147483647       //(32ビット符号つき整数)
The largest long value is 9223372036854775807 //(64ビット符号つき整数)
The largest float value is 3.40282e+38        //(32ビット符号つき浮動小数点)
The largest double value is 1.79769e+308      //(64ビット符号つき浮動小数点)
The character S is upper case.                //(16ビットUnicode文字データ)
The value of aBoolean is true                 
 基本データは上記8通り ある。整数(宣言はint)、倍精度浮動小数点(double)、文字データ(char)が特に重要。
 これらには、いずれもラップクラスといわれるクラスがある。
Integer,Double,Characterクラス などであり、
 上記のプログラムでは、Integer.MAX_VALUE などのクラス変数、
Character.isUpperCase(aChar) のようなクラスメソッドを利用している。

4 配列と繰り返し

多くのデータがある場合、個々に変数名をつけていては、それだけでも大変。そこで登場するのが配列である。一つだけ配列名を決めて、あとは0から始まる添え字で個々のデータを区別するものである。こうしておくと、添え字を繰り返し、forのカウンタ変数、iなどで処理するにも都合がよい。

4-1 配列

[問題] 配列で与えられたデータの合計とその中の最大値を求める問題。
class ArrayDemo {
  public static void main(String[] args) {
    int total = 0;
    int[] array = {300,165,12,50,63,36,200,125,111,100}; 
    int max = 0;
    for(int i = 0; i < array.length; i++) {
       System.out.print(array[i] + " ");
       total = total + array[i];
       if (array[i] > max) {
           max=array[i];
       }
    }
    System.out.println("");
    System.out.println("total = " + total);
    System.out.println("max = " + max);
  }
}
[実行結果]
  300 165 12 50 63 36 200 125 111 100
  total = 1162
  max = 300
[コメント] 
1  この例では、配列名はarrayで、array[0]=300,array[1]=165,....,array[9]=100として、データを識別している。
2  配列の場合も型名は必要、整数型の配列ということうはっきりさせるため、int[] で初期化していることに注意。
3  合計を求める方は3度目だから、さほど問題はないだろう。
  最大値の方は、変数 maxを最初はかりに0としておいて、forループの中のif文で配列の各要素とmaxとを比較
 して、大きい方をmaxにしていくだけ。
4  少し配列データを変え、途中経過 を打ち出した。
class ArrayDemo {
  public static void main(String[] args) {
    int total = 0;
    int[] array = {300,165,50,63,400,200,125,500,100}; 
    int max = 0;
    for(int i = 0; i < array.length; i++) {
       total = total + array[i];
       if (array[i] > max) {
           max=array[i];
       }
       //途中経過
       System.out.println("i = "+ i + " array[" + i  + "] = " + array[i] +
                     " total = " + total + " max = " + max);
    }
    System.out.println("total = " + total);
    System.out.println("max = " + max);
  }
}
[実行結果]
i = 0 array[0] = 300 total = 300 max = 300
i = 1 array[1] = 165 total = 465 max = 300
i = 2 array[2] = 50 total = 515 max = 300
i = 3 array[3] = 63 total = 578 max = 300
i = 4 array[4] = 400 total = 978 max = 400
i = 5 array[5] = 200 total = 1178 max = 400
i = 6 array[6] = 125 total = 1303 max = 400
i = 7 array[7] = 500 total = 1803 max = 500
i = 8 array[8] = 100 total = 1903 max = 500
total = 1903
max = 500

4-2 サイコロの目(配列、乱数、型変換、if else)

[問題]乱数を6000回発生させて、サイコロの目に対応させて、それぞれの目の出現回数を見る。
 (サイコロの目の数にみあう大きさの配列をとる)。

[解1]
class Saikoro {
  public static void main(String[] args) {
    int[] array= new int[6];
    int ransu;
    for(int i = 0; i < 6000; i++) {
       ransu = (int)(6000.0 * Math.random());
       if (ransu < 1000) {
           array[0]++;
       } else if (ransu >= 1000 & ransu  < 2000) {
           array[1]++;
       } else if (ransu >= 2000 & ransu  < 3000) {
           array[2]++;
       } else if (ransu >= 3000 & ransu  < 4000) {
           array[3]++;
       } else if (ransu >= 4000 & ransu  < 5000) {
           array[4]++;
       } else if (ransu >= 5000 & ransu  < 6000) {
           array[5]++;
       }
    }
    for(int i = 0; i < 6; i++) {
      System.out.println((i+1)  + "の目 = " + array[i]);
    }
  }
}
[実行結果]
1の目 = 1009  2の目 = 994  3の目 = 1023
4の目 = 991   5の目 = 954  6の目 = 1029    合計 6000
[コメント] 
1 2-5の例題では、配列の初期値がわかっていたので、直接データを与えるという簡略化した記述であったが、
 今回は6ケという大きさしか分からない。
  そこで、new演算子で配列に必要なメモリ領域 をとる(2-5は例外的にnew不必要、文字列の場合も同じ)。
   もっとも、この場合も、int[] array={0,0,0,0,0,0};とすることもできる。
2  Math.random()は0〜1までのdouble型の乱数を発生する。これを6000倍して、(int)という型変換(キャスト)で、
 0から6000までの整数にする。
  1000まではサイコロの1の目としてarray[0]に1を足す、1000から2000までは2の目としてarray[1]に1を
 足す ....と考えて、if (a){a1} else if(b){b1} else if(,,){...} と並べている。
   (もしaが真ならa1を実行、そうでなく bなら b1を実行、...と続く)。
3  ransu = Math.round(6000.0 * Math.random()); のように、Mathクラスのメソッドroundを使って、
 整数化することもできる。
 (Math.random()とともに、API参照、2-2で出てきたクラス変数のMath.PIも見ておくこと)。

[解2]
class Saikoro2 {
  public static void main(String[] args) {
    int[] array= new int[6];
    int ransu;
    for(int i = 0; i < 6000; i++) {
       ransu = (int)Math.round(6000.0 * Math.random());
          for(int k = 0; k < 6; k++) {
            if ((ransu / 1000) == k) 
                array[k]++;
          }
    }
    for(int i = 0; i < 6; i++) {
      System.out.println((i + 1) + "の目 = " + array[i]);
    }
  }
}
[実行結果]
1の目 = 976     2の目 = 993     3の目 = 1016
4の目 = 1007    5の目 = 1042    6の目 = 966   合計 6000
[コメント] 
1  乱数で発生し、整数化した数は0から5999まで。1000で割った商は0から5までなので、その値が
  インデックスとなる配列 に1足せばよい。
2 [解1]よりずっと簡略化されるが、考え方はやや難しいかもしれないが、プログラミングの主流、
  アルゴリズムといわれるものは解2である。
3  乱数は発生させるたびに値が異なるので、目の出方はいつも同じではない。たまに、合計が、
  解1、解2とも5999にしかならないことがある。理由は目下不明。

4-3 100までの素数(配列、for、余り、boolean、break、if)

素数とは、1と自分自身以外では割り切れない数。2,3,5,7,11....などは素数であるが、1は素数にはいれない。
 100までなら、25個(10までなら4個、1000までなら168個、1000までなら1229個)。
[プログラム]
class Sosu {
  public static void main(String[] args) {
    int number=100;
    int[] sosu= new int[number/2];
    sosu[0]=2;
    sosu[1]=3;
    int n=2;   //nは素数の配列 sosu の添え字
    for(int i = 5; i < number; i=i+2) { //偶数は最初から排除
      boolean prime=true;
      for(int j = 1; j < n; j++) { //jは、sosu の添え字、sosu[1]=3からチェック
         if ((i %  sosu[j]) == 0) { 
            prime=false;  //iがいずれかの素数で割り切れれば、素数ではない。
            break;
         }
      } 
      if (prime) {    
         sosu[n] = i;
         n++;   //次の素数を入れるために、sosu の添え字を1増やす。
      } 
    }
    System.out.println(number + " までの素数");
    for(int j = 0; j < n; j++) {
      System.out.print(sosu[j] + " ");
    }
    System.out.println(" ");
   }
}

[参考]
 「独習Java」の練習問題 4.5 の解答(CD-ROM)には、もっと短いプログラムがある。
 ある数が素数であるかどうかを決めるのに、それまでのすべての数で割って、割りきれなければ、
 素数として打ち出すということで、配列は使っていない。
 無駄な処理も入るのと配列の事例として私の例を出す。複雑にはなる。
[実行結果]
100 までの素数
  2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97
[コメント] 
1 素数と決まった数をいれる配列をint[] sosu= new int[number/2]で用意する。numberはここでは100。
 少なくとも、偶数は素数ではないので、配列要素はnumber/2としている。
2  3や5の倍数も除かれるので、それでも実際よりはずっと多いが、配列要素はあらかじめ決めておく必要がある。
     (自在にできるものとして、VectorとかArrayListがある)。
3  if文の中の % は余りを求めるもの、== に注意。= を代入の意味で使うので、ほんとうに等しいことを
   問うのは、==。
     (すでに3-1で解説ずみのことではあるが、再掲。)
4  breakは、繰り返しforを打ち切るもの。それまでにわかっている素数の配列のすべてで余りを求めて
  そのすべてで0でないなら(割り切れない)、その数も素数。
5 Javaの8つの基本データ型のひとつが真偽を示す booleanで、値はtrueとfalseしかない。
 boolean primeがtrueなら素数であるとして、配列に加えていく。配列の添え字nも増やすという考え方である。
6  なお、繰り返しの制御変数 i は、素数候補の数でもある。偶数を除いていくため、奇数5から、
  2ずつ増やしている。
  そのため、素数配列の添え字のための制御変数 jも、sosu[1]=2 は不要となり、sosu[2]=3 から
 チェックを始めている。
7  途中経緯を打ち出してみた。
  if (prime) のブロックで、新たに素数がきまって、n++する直前に、下記をいれる。
  System.out.println("i=" + i + " j=" + j + " n=" + n + " sosu[n]=" +  sosu[n]);  結果は、
    i=5 j=2 n=2 sosu[n]=5
    i=7 j=3 n=3 sosu[n]=7
    i=11 j=4 n=4 sosu[n]=11
    i=13 j=5 n=5 sosu[n]=13
       (途中省略)
    i=79 j=21 n=21 sosu[n]=79
    i=83 j=22 n=22 sosu[n]=83
    i=89 j=23 n=23 sosu[n]=89
    i=97 j=24 n=24 sosu[n]=97

4-4 ソート(並べかえ)(配列、二重の繰り返し)

 サンマイクロ社の「Javaチュートリアル 第3版」に紹介があり、ネット上に、SortDemo にある例である。
  二重の繰り返し(for)、繰り返し変数、iとiを配列の添え字として、極めて巧妙に使った例である。
[プログラム]
public class SortDemo {
    public static void main(String[] args) {
        int[] arrayOfInts = { 32, 87, 3, 589, 12, 1076,
                              2000, 8, 622, 127 };
        for (int i = arrayOfInts.length; --i >= 0; ) {
            for (int j = 0; j < i; j++) {
                if (arrayOfInts[j] > arrayOfInts[j+1]) {
                    int temp = arrayOfInts[j];
                    arrayOfInts[j] = arrayOfInts[j+1];
                    arrayOfInts[j+1] = temp;
                }
            }
        }
        for (int i = 0; i < arrayOfInts.length; i++) {
            System.out.print(arrayOfInts[i] + " ");
        }
        System.out.println();
    }
}
[実行結果]  3 8 12 32 87 127 589 622 1076 2000
[コメント] 
1 配列データを小さい順に並べ替えるのだが、配列データの隣同士の二つの数値を比較して、大きい方を
 添え字 j の大きい方にもっていくことで、最大値を配列の最後尾にもっていく。
2 この場合の添え字 jの最終値はiそのものだが、最初、iは配列の大きさそのものである。
3 次は、それをのぞいた配列(添え字iを--して)にして、同じことを繰り返す。
 二重の繰り返し(for)を極めて巧妙に使った例である。
4 内部の繰り返し(添え字 j) が終わったところに、途中結果打ち出しのprintln文をいれて、
 以上を確かめてみる。
5 注意すべきは、原型のままでは、jについて「シンボルを見つけられない」とのエラーとなることである。
 jのようなブロック内で宣言されたローカル変数、jはそのブロック内でしか有効 でないから。
6 下記に、修正、追加点と実行結果のみ記述。
        int j; //追加
        for (int i = arrayOfInts.length; --i >= 0; ) { //同じ
            for (j = 0; j < i; j++) {  //jのint宣言をとる。
                ........
                }
            }
             System.out.println("i=" + i + " j=" + j +  " arrayOfInts[j] =" + arrayOfInts[j] ); //追加 
        }
   以下、同じ
[実行結果] 
i=9 j=9 arrayOfInts[j] =2000
i=8 j=8 arrayOfInts[j] =1076
i=7 j=7 arrayOfInts[j] =622
i=6 j=6 arrayOfInts[j] =589
i=5 j=5 arrayOfInts[j] =127
i=4 j=4 arrayOfInts[j] =87
i=3 j=3 arrayOfInts[j] =32
i=2 j=2 arrayOfInts[j] =12
i=1 j=1 arrayOfInts[j] =8
i=0 j=0 arrayOfInts[j] =3
3 8 12 32 87 127 589 622 1076 2000

4-4-2 ソート、もっと詳しい途中結果

  前節のコメント、4,5,6に途中結果をだしたが、それは、そのときの配列の右側、最大値のみを示したもの。
 配列全体にわたる変化を含めて、もっと詳しい途中結果を出してみる。
 なお、クラス名もSortDemo2と変更、arrayOfIntsという配列名は煩雑なので、簡単にarrayと変更した。
 追加した部分は右側にコメントした。
[プログラム]
public class SortDemo2 {
    public static void main(String[] args) {
        int[] array = { 32, 87, 3, 589, 12, 1076,2000, 8, 622, 127 };
        int j;                                           //変更
        System.out.print("元の配列 array[i] = " ); //追加
        for (int i=0; i < array.length; i++)             //追加
              System.out.print(array[i] + " ");          //追加
        System.out.println("");                 //改行のため追加 
        System.out.println("途中経緯");                  //追加
        for (int i = array.length; --i >= 0;) {
            System.out.print("i=" + i + " array[j] = "); //追加
            for (j = 0; j < i; j++) {
                if (array[j] > array[j+1]) {
                    int temp = array[j];
                    array[j] = array[j+1];
                    array[j+1] = temp;
                 }
                 System.out.print(array[j] + " " );       //追加
            }
            System.out.println( " array[" + j + "] = " + array[j] ); //追加
       }
        System.out.println("ソート結果");                 //追加
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");             //ソート結果
        }
        System.out.println();
    }
}
[実行結果]  
元の配列 array[i] = 32 87 3 589 12 1076 2000 8 622 127
途中経緯
i=9 array[j] = 32 3 87 12 589 1076 8 622 127  array[9] = 2000
i=8 array[j] = 3 32 12 87 589 8 622 127  array[8] = 1076
i=7 array[j] = 3 12 32 87 8 589 127  array[7] = 622
i=6 array[j] = 3 12 32 8 87 127  array[6] = 589
i=5 array[j] = 3 12 8 32 87  array[5] = 127
i=4 array[j] = 3 8 12 32  array[4] = 87
i=3 array[j] = 3 8 12  array[3] = 32
i=2 array[j] = 3 8  array[2] = 12
i=1 array[j] = 3  array[1] = 8
i=0 array[j] =  array[0] = 3
ソート結果
3 8 12 32 87 127 589 622 1076 20003 8 12 32 87 127 589 622 1076 2000
[コメント] 
1 恥ずかしながら、これをやる過程でエラーを出して、はじめて下記の行の解釈を誤っていたことに気付いた。
      for (int i = array.length; --i >= 0;) {
2 これは、--iとデクリメント記号が前置されているので、直ちに演算が行われる。
 例では、array.lengthが10なので、繰り返しは i=9 から i=0 までとなる。
3 今まで通りのとデクリメント後置法なら、
       for (int i = array.length-1; i >= 0; i--) {  とするところ。
4 すると、for (j = 0; j < i; j++) {  の行は、i=9 のとき、どうなるか?
 j の繰り返しが 0から8 までで、最後の1項、array[9] が欠けるのではないかと考えさせられた。
5  if (array[j] > array[j+1]) { ということをしている。 
  たとえば、j=8 のとき、array[9] との比較をきちんとしていることになる。難解なプログラムである。

4-5 別解、配列の最大値の置き換え

  4-1、配列の最大値を求め、それを配列の右側に移動、最大値を示した配列の添え字も記憶しておき、
 置き換えられた配列の右側と置き換えてみる。考え方は分かりやすいかもしれない。
[プログラム]
public class SortDemo2 {
    public static void main(String[] args) {
        int[] array = { 32, 87, 3, 589, 12, 1076,
                              2000, 8, 622, 127 };
        int j;
        for (int i = array.length; --i >= 0; ) {
            int max=0;    //配列の最大値
            int n=0;      //最大値のときの配列添え字
            for (j = 0; j <= i; j++) {
                if (array[j] > max) {
                    max=array[j];
                    n=j;
                }
                System.out.print(array[j] + " " );  //途中経過、もとの配列
            }
            j=j-1;             //forの中でjが配列要素数より大きくなったため
            int temp = array[j];
            array[j]=max;     //最大値を配列の右側へ
            array[n]=temp;    //配列の右側にあった値を最大値のあった位置へ
            System.out.print( " max = " + max +  " n = " + n);       //途中経過
            System.out.println( " array[" + j + "] = " + array[j] ); //途中経過
        }
        for (int i = 0; i < array.length; i++) {
            System.out.print(array[i] + " ");         //最終結果
        }
        System.out.println();
    }
}
[実行結果]  
32 87 3 589 12 1076 2000 8 622 127  max = 2000 n = 6 array[9] = 2000
32 87 3 589 12 1076 127 8 622  max = 1076 n = 5 array[8] = 1076
32 87 3 589 12 622 127 8  max = 622 n = 5 array[7] = 622
32 87 3 589 12 8 127  max = 589 n = 3 array[6] = 589
32 87 3 127 12 8  max = 127 n = 3 array[5] = 127
32 87 3 8 12  max = 87 n = 1 array[4] = 87
32 12 3 8  max = 32 n = 0 array[3] = 32
8 12 3  max = 12 n = 1 array[2] = 12
8 3  max = 8 n = 0 array[1] = 8
3  max = 3 n = 0 array[0] = 3
3 8 12 32 87 127 589 622 1076 2000

4-6 大きい順にソート

独習Java」4章末尾の「理解度チェック」(P124)にあったもの、4節を補強するため、途中経緯を付加して掲載する。
[問題] 
    複数の引数を受け取り、それを数値に変換して、昇順に表示する。
[プログラム]
class SortNumbers {
  public static void main(String args[]) {
    // 配列の宣言,割り当て,初期化
    int size = args.length;
    double data[] = new double[size];
    int j;
    for(int i = 0; i < size; i++) 
      data[i] = Double.valueOf(args[i]).doubleValue();
    for(int i = 0; i < size - 1; i++) {
      System.out.print("i=" + i + " data[j]=");      //追加
      for(j = i; j < size; j++) {
        if(data[j] > data[i]) {
          double temp = data[j];
          data[j] = data[i];
          data[i] = temp;
        }
        System.out.print(data[i] + " " );       //追加
      }
      System.out.println( " data[" + i + "] = " + data[i] ); //追加
    }
    System.out.println("配列を昇順に表示するソート結果");    //追加 
    for(int i = 0; i < size; i++)
      System.out.print(data[i] + " ");
    System.out.println("");
  }
}
[実行結果]  (途中経緯も含む)
D:\JavaTry\Application1>java SortNumbers 25.0 5.0 100.0 50.0 75.0
i=0 data[j]=25.0 5.0 25.0 50.0 75.0  data[0] = 100.0
i=1 data[j]=5.0 5.0 25.0 50.0  data[1] = 75.0
i=2 data[j]=5.0 5.0 25.0  data[2] = 50.0
i=3 data[j]=5.0 5.0  data[3] = 25.0
配列を昇順に表示するソート結果
100.0 75.0 50.0 25.0 5.0
[コメント] 
1    for(int i = 0; i < size - 1; i++) {
        for(int j = i; j < size; j++) {
  i は i < size ではなく、ひとつ少ない繰り返し。ここではsize=5だから、i=0,1,2,3 だけ。
2  i=0 のときは、j は 0から < sizeまで繰り返し、最大のデータを左端に移動。
3  i=1 のときは、jは1からの繰り返しで、最大のものを除外して処理し、2番目に大きいものを左端に。
4  i=2 のときも同様。
5  i=3 の処理後は、data[4] には、最小のデータのみが残ることになる。

4-5-2 大きい順にソート その2

上記の途中経緯に乱れがあるのが気になる。そこで、4-4の流儀で、配列の後ろから、一つ前と比較
 して、大きい方を前へもっていくように変更した。
 下記のjは、常に配列の右端から始め、iより大きい間、繰り返す。iは、0,1,2,3,4と増えていくので、次々と
左端(大きい数が入っている)を無視して処理していく。
[プログラム]
class SortNumbers2 {
  public static void main(String args[]) {
    int size = args.length;
    double data[] = new double[size];
    int j;
    for(int i = 0; i < size; i++) 
      data[i] = Double.valueOf(args[i]).doubleValue();
    for(int i = 0; i < size; i++) {               //変更
      System.out.print("i=" + i + " data[j]=");         //追加
      for(j = size-1; j > i; j--) {               //変更
        if(data[j] > data[j-1]) {                 //変更
          double temp = data[j-1];                //変更
          data[j-1] = data[j];                    //変更
          data[j] = temp;                         //変更
        }
        System.out.print(data[j] + " " );                 //追加
      }
      System.out.println( " data[" + i + "] = " + data[i] ); //追加
    }
    System.out.println("配列を昇順に表示するソート結果");    //追加 
    for(int i = 0; i < size; i++)
      System.out.print(data[i] + " ");
    System.out.println("");
  }
}
[実行結果] (途中経緯も含む)
D:\JavaTry\Application1>java SortNumbers2 25.0 5.0 100.0 50.0 75.0
i=0 data[j]=50.0 75.0 5.0 25.0  data[0] = 100.0
i=1 data[j]=50.0 5.0 25.0  data[1] = 75.0
i=2 data[j]=5.0 25.0  data[2] = 50.0
i=3 data[j]=5.0  data[3] = 25.0
i=4 data[j]= data[4] = 5.0
配列を昇順に表示するソート結果
100.0 75.0 50.0 25.0 5.0

以上で、Java入門1 本文は終了、5章以降はJava入門2(Application応用)

E [参考] エラーメッセージの一例

  2-2 繰り返しの例題を使い、cdによるディレクトリ(フォルダ)変更やjavacによるコンパイル時の、
  タイプミス、不注意による典型的なJavaのエラーメッセージの一例を示す。
1  //cd z:\Java Try\Application1
2  //javac Repeat.java
3  //set CLASSPATH=.
4  //java Repeat
5 
6  class Repeat {
7    public static void main(String[] args) {
8      int total = 0;
9        //1から10までの和
10      for(int i = 0; i <= 100; i++) {
11         total = total + i;
12      }
13      System.out.println("total = " + total);
14    }
15 }

E-1 cdコマンドのエラー例

1 Z:>cd z:\Java Try\Application1
 指定されたパスが見つかりません。
  JavaTryと空白なしで作ったはず。ディレクトリ(フォルダ)名を間違えるとこうなる。

2 Z:>//cd z:\JavaTry\Application1
ファイル名、ディレクトリ名、またはボリューム ラベルの構文が間違っています。
  //は、Javaに対するコメントで、これをコマンドプロンプトで使うときは//を使ってはいけない。

3 Z:>z:\JavaTry\Application1
 'z:\Java' は、内部コマンドまたは外部コマンド、操作可能なプログラムまたはバッチ ファイルとして
 認識されていません。
  cd z:\JavaTry\Application1としなくてはいけない。cdを忘れている。

E-2 javacによるコンパイルエラー例

4 Z:\JavaTry\Application1>javac Repet.java
 エラー: Repet.java を読み込めません。
  javacの対象ファイル名のスペルミス。cdを実行していないときも、こうなる。

5 Z:\JavaTry\Application1>javac Repeat.jave
javac: Repeat.jave は無効なフラグです。
使い方: javac  
  javacの対象ファイルの拡張子は、java
  以下、使用可能なオプションとして、ずらずら出てくる。

6 z:\JavaTry\Application1>javac Repeat.java
Repeat.java:12: ';' がありません。
    }
 11行目の区切り記号;を忘れると、次の行で注意が来る。

7  Repeat.java:7: ')' がありません。
  public static void main(String[] args {
                                        ^
 丸括弧を閉じるのを忘れた場合、小さな山印に注意。 その近辺にエラーがある。
 
8  Repeat.java:7: シンボルを見つけられません。
シンボル: クラス Spring
場所    : Repeat の クラス
  public static void main(Spring[] args) {
                          ^
 「シンボルを見つけられない」とは、StringのようにJavaできまっている用語を間違えた場合に出る他、
 次の9,10などのケースもある。

9 int とか doubleとか、型を定義した変数のスペルミス
        tota1 = total + i;
         ^
   totalのエルを1(イチ)と間違えた。
     
10  変数に型定義をしなかった場合
   for(i = 0; i <= 100; i++) {
        ^
   i ははじめて使う変数なのに、intと型定義を忘れたため。
    (以下、iを使っているところ、すべてにこのエラーが出る他
      Repeat.java:11: 演算子 + は int,i に適用できません。
         total = total + i; とか、
      Repeat.java:11: 互換性のない型
        検出値  : 
        期待値  : int
        total = total + i; 
      などのような恐ろしげなそっセージまで、6個もエラーが出る)

11  メソッド、クラスを閉じる中カッコを忘れた場合
   Repeat.java:15: '}' がありません。

12 ダブルクォーテーションを忘れた場合
  Repeat.java:13: 文字列リテラルが閉じられていません。
    System.out.println("total =  + total);                   ^
  Repeat.java:14: ')' がありません。
 と、エラーは2個になる。

13 10行目、<= を離した場合
Repeat.java:10: 式の開始が不正です。
    for(int i = 0; i < = 100; i++) {
                       ^
14 空白に全角を使った場合
Repeat.java:13: \12288 は不正な文字です。
  System.out.println("total = " + total);

S [参考] コマンドプロンプトの操作など

S-1 簡便操作法

1 タイプミスなどを修正した後 再度、javacからやり直す必要がある。
 一度、打ったコマンドは上向きの方向キーでコピーされる。
2 実行もした後、プログラムの手直しをした場合も、javacからやり直す必要がある。
 上向きキーを繰り返し使ったり、場合によっては、下向きのキーで、さかのぼって、以前に打った
 コマンドが使える。

S-2 コマンドプロンプトに出る実行結果を残す

   コマンドプロンプトにて、右クリックー範囲の指定 として、下向き及び右向きの
    方向キーで、範囲を指定する。白くなった部分で、右クリック してxyzzyにはりつける。
   その部分は、Javaのプログラムとは関係ないから、コメントにする。
   先頭に/*、最後に*/ とすれば、その間がコメントになる。

S-3 文章やプログラムの複写

 文章やプログラムの複写には下記キー使用 が便利。
  選択            shift+方向キー(長文のときは、先頭でクリック、最後でshift+クリック)
  コピー(クリップボードへの) ctrl+Insertキー
  貼り付け          shift+Insertキー  
    なお、移動は、一般には、shift+Deleteキーだが、xyzzyでは、ctrl+Deleteキー。
     [注意] ctrl+Insertキーとするとき、ctrlキー以外だったら、選択していた青い色が消えない。
           こういうときは、Insertキーのみ有効となり、xyzzyで打ったものがが上書きされて、
           前のが消えていく。再度、Insertキーを押して上書きを解除すること。
    コマンドプロンプトへの貼り付けは、shift+Insertキーが使えないので、右クリックー貼り付けと
 せざるをえない。

S-4 プログラム・ファイルの作成も複写から

   たとえば、2-2の例題は、2-1の例題をもとに作成するのがよい。プログラムは似たようなコマンドの羅列だから。
   2-1の例題をxyzzyで読み込み、名前をつけて保存として、2-2からクラス名と同じ Repeat.javaとする。
  タイトルバー でファイル名が変わったことを確認してから、必要な修正、追加をして、
 上書き保存、コンパイル、実行。
  (htmlファイルで使った方法に同じ)。
ページ独自カウント