Home: Up

Javaプログラムの作り方


1.JAVA言語

JavaはCやC++、BASICなどと同じように言語の名前です.
Javaプログラムを実行する為にはそのための環境が必要になります.

ここではJDK(Java Development Kit)を用いてコンパイル/実行を行う事を想定しています.
(JDKのダウンロード)

JDKは現在バージョン1.2を開発中で、現行の最上位バージョンは1.1.5です.
Java言語が生まれてまだ月日が経っていないせいもあって、Java言語の仕様は現在もかなりのペースで進化しつづけています.
それは即ち、現在のJavaの仕様では実現できない事や、不完全な部分も多分に含んでいるという事です.
現状では開発者はJavaの不完全さと戦いながらプログラミングをしている状態ですが、それでもこれだけのスピードで普及しているのは「Javaを使えば・・」と言った期待を持たせられる言語であるからです.

Javaを勉強して得か損かも明確ではありませんが、興味を引かれる言語である事だけは確かです.


2.JAVAプログラムのコンパイル/実行

Java言語で作ったプログラムがどのように実行されるかを以下に記します.

  1. プログラム(テキスト文書)を作成します. ファイル名は****.javaという名前にします.
  2. Javaプログラムをコンパイルします.
    テキストで書かれたプログラムが「バイトコード」と言う名の中間言語に翻訳されます.
    コンパイルの仕方はJDK(Java Development Kit)をインストールしていれば
    javac ****.java[リターン]
    と入力します.
  3. クラス名.classファイルが生成されます.
    .javaファイル内に複数のクラスが宣言されていればその数だけ.classファイルができます.
  4. 出来上がったクラスファイルを実行します.
    クラスファイル(クラス名.class)はバイトコードです.
    バイトコードはそのままで実行できるわけではありません.
    実行するにはJDKの場合、以下のように入力します.
    java クラス名[リターン]
※ファイル名及びクラス名などJavaの上ではすべて大文字/小文字が区別されます.
※上記の中で「入力します」と書かれている部分はすべて「コマンドライン」上で行います.以降も同様です.

ここで疑問になるのは、なぜコンパイルしてできたファイルを実行するのにJavaコマンドを用いるのか?と言う事です.
コンパイルしてできた「バイトコード」がそのままで実行できないのはなぜでしょう.

バイトコードとは、仮想CPUにおける機械語です.

そしてjavaコマンドは仮想マシン(JVM:Java Virtual Machine)の起動コマンドです.
javaコマンドによって起動された仮想マシン上でバイトコードが実行されるわけです.

では、なぜ仮想マシンなどと言うものが存在するのでしょう.
実はjavaコマンドはいろいろなプラットフォーム用に開発/配布されています.
(Windows/Solaris/Macなどなど)
そして各機種用のjavaコマンドを実行する事で全ての機種で同じ仕様の仮想マシンが起動されるわけです.
そうなればしめたもので、その仮想マシンで動作するコード(=バイトコード)はどのプラットフォーム上でも動作可能になるというわけです.

これが、Javaの「プラットフォーム非依存性」です.

尚、以降の記事における例はWin32プラットフォームを元に記述していますが、ほとんどの記事は他のプラットフォームでもそのまま適用できると思います.
また、本記事はJDK1.1.5(1.1以上)をベースにしています.


3.JAVAでのコンソールベースアプリケーション

3−1.いわゆる"HelloWorld"系プログラム

まずは以下のプログラムを入力して2. の手順に従ってコンパイル/実行してみましょう.
あ、もちろんコメントは省略してかまいません.

/**
 * お勉強用テストアプリケーション3_1.
 * @author 木下 信
 * @version 1.0
 */
class Test3_1 {
    /**
     * 実行開始時に呼び出されるメソッド.
     * <p>
     * Systemクラスの(public staticな)outフィールドの
     * println()メソッドを呼び出します.<br>
     * Systemクラスはインスタンス化されないクラスで、
     * フィールド/メソッド共にすべてstaticとして宣言されています.<br>
     * つまり、クラス名.メソッド名()で誰でも使用できるものです.<br>
     * (メソッドのライブラリみたいなものです)
     * @param args コマンドラインパラメタ文字列群
     */
    public static void main(String[] args) {
        System.out.println("Test3_1のmainメソッドが実行されました");
    }
}

実行できたでしょうか?
ところで、このプログラムのファイル名は何でもかまわないのですが(拡張子は.java)、 実際にはクラス名と同一のファイル名を付ける事が推奨されています.
(本当は常にクラス名とファイル名を一致させるべきなんですがこの辺については後程)
※クラス名とは識別子classの後ろの文字列の事です.

ちなみにこのプログラムはTest3_1というクラスになっていますが、オブジェクト指向でもなんでもありません.
(プログラマ側は何のオブジェクトも作りません)
Cのプログラムと比べてもちょっとmain関数/println関数に余計なものがついてる程度です.

main()メソッド(JavaではCでいうところの「関数」をメソッドと呼びます:現実には似て非なるものですが)は Cでのmain()関数と同じでプログラムのエントリポイントとなります.
java Test3_1[リターン]
とやるとそのクラスのmain()メソッドが呼ばれるわけです.
main()の手前についてる奴等についてはここでは考えないで「main()はこのように書くもの」と思いましょう.

そして、println()メソッドは実行したらわかるのですが、Cのprintf()と同じようにコンソールに対して文字列を出力します.
但し、「コンソール(いわゆるMS-DOS画面)に対して」と言う部分は実際には手前の「System.out」の部分が相当しています.
これについてもここでは考えないで、このように書けば文字列は表示できるんだなと思ってください.

ただ、この段階で知っておいてほしい事として、「javaのプログラム(実装)はすべてclassの中に書く」と言う事です.
上の例でもいわゆるmain関数をclass宣言文で囲っています.
classはC言語でいうところの一つのソースファイルのようなものと考えればわかりやすいと思います.
classの中にはCのソースファイルと同じく複数の変数(フィールド/メンバ)と複数の関数(オペレーション/メソッド) を定義する事ができ、その参照可能範囲(スコープ)は基本的にそのクラス内のみであるという点で類似しています.
(クラス外から使用する為にpublic等の識別を使います)
とりあえず今の段階では、「とにかくclassと言う宣言があって初めてプログラムを動かせる」と思っていていいと思います.


3−2.いわゆる"HelloWorld"系プログラムその2

今度もおもむろに以下のプログラムを入力して同様にコンパイル/実行してみましょう.

/**
 * お勉強用テストアプリケーション3_2.
 * @author 木下 信
 * @version 1.0
 */
class Test3_2 {
    /**
     * 「標準出力」(コンソール)に表示を行います.
     */
    void print() {
        System.out.println("Test3_2のオブジェクトのメソッドが呼ばれました");
    }
    /**
     * 実行開始時に呼び出されるメソッド.
     * @param args コマンドラインパラメタ文字列群
     */
    public static void main(String[] args) {
        System.out.println("Test3_2のmainメソッドが実行されました");
        //オブジェクトの生成
        Test3_2             test    = new Test3_2();
        //オブジェクトのprint()メソッドを呼び出します.
        test.print();
    }
}

結果は3−1.と同様です.では何が変わったのでしょうか?
実は今度のは一つのオブジェクトを作り出しています.
オブジェクトを作成しているのはmain()メソッド内の「new Test3_2()」というところです.
で、「作られるオブジェクトって何」っていうとTest3_2クラスのオブジェクトなわけです.
Test3_1もクラスなのに、実行してもオブジェクトが作られないみたいな事を言って、Test3_2は 作られるの?という疑問が出ると思います.
この理由はmain()メソッドの左の「static」が鍵になります.

static宣言されたメソッドは「クラスメソッド」と呼ばれます.
(static宣言された変数は「クラス変数/クラスメンバ/クラスフィールド」などと呼ばれます)
で、こいつらはクラスにつき一つしか存在しないよってな宣言になります.
逆に、static宣言されてないメソッド/フィールドについては「オブジェクトごとに別個に存在するよ」 って意味であり、逆に言えば「オブジェクトが無いと使用できませんよ」という意味なのです.

ここでクラスとオブジェクトの関係についてちょびっと.
クラスとはCにおけるソースファイルのようなものと言いましたが、別の観点で言えばクラスはCで 言うところの「typedef struct」宣言のようなものと言えます.
つまり、クラスは型(抽象データ型)の宣言なわけです.
で、オブジェクトって何って言うと、その型を実体化したものと言う事になります.
(Cの感覚で言えばその型の変数を作る事→実領域を取る事となります) ※実体化させることをインスタンス化すると呼びます
クラス型の変数を作った時にその変数に代入できるのはそのクラス型のオブジェクトだってな感じです.

で、main()メソッドがなぜstaticとしなければならないのかはもうわかりますね.
プログラムの実行を開始しようとした時には何のオブジェクトも存在しません(内部的には色々ありますが).
従ってオブジェクトが無くても実行可能なstaticメソッドであるmain()から実行が開始されるわけです.

後、クラスをインスタンス化するのがnew クラス名()で、 その次の行でオブジェクトのメソッドを呼び出すんですが、 この辺の説明は後回しにしたいと思います.


ところでコメントの書き方が妙だと思っておられる方もいる事でしょう.
これについては以下の記述を見てください.

/**
 * ←このようなコメントの書き方をしている部分は
 * javadocというコマンドラインツールでリファレンスマニュアルを
 * 自動生成する時に抜き出される部分です.
 * 抜き出された部分はHTMLになるのでHTMLタグを書く事ができます.
 * (ちなみに3−1.のサンプルにある<p>と言うのはHTMLでの「段落開始」です)
 * この書き方に慣れていればインターフェイス仕様書の作成という作業は
 * あってないようなものとなります.
 * @〜〜 みたいな部分は後回しでもいいので、
 * とりあえずクラスとpublicメソッドの前にはこの形の
 * コメントを入れておくべきです
 */

3−3.いわゆる"HelloWorld"系プログラムその3

次はこれです.今度はクラスを二つ作っていますがファイル名はTest3_3Main.javaとでもしておきましょう.

/**
 * お勉強用テストアプリケーション3_3.
 * <p>
 * このクラスのインスタンスは生成されません.<br>
 * インスタンス化されるクラスと起動用処理を分けると、
 * オブジェクトの構成がわかりやすくなります.<br>
 * @author 木下 信
 * @version 1.0
 */
class Test3_3Main {
    /**
     * 実行開始時に呼び出されるメソッド.
     * <p>
     * このようにメインを置くクラスにはmainしか置かないと
     * いう手段もあるかもしれません.
     * @param args コマンドラインパラメタ文字列群
     */
    public static void main(String[] args) {
        System.out.println("Test3_3Mainのmainメソッドが実行されました");
        //オブジェクトの生成
        Test3_3              test    = new Test3_3();
        //オブジェクトのprint()メソッドを呼び出します.
        test.print();
    }
}

/**
 * お勉強用テストクラス3_3.
 * @author 木下 信
 * @version 1.0
 */
class Test3_3 {
    /**
     * コンストラクタはこのクラスが生成される時に呼び出されます.
     * <p>
     * この場合は何も初期処理を行いません.
     */
    Test3_3() {
    }
    /**
     * 「標準出力」(コンソール)に表示を行います.
     */
    void print() {
        System.out.println("Test3_3のオブジェクトのメソッドが呼ばれました");
    }
}

このプログラムをコンパイルするとTest3_3Main.classとTest3_3.classの二つのクラスファイルが生成されます.
main()メソッドがあるのはTest3_3Main.classの方ですので実行は
java Test3_3Main[リターン]
とします.

でも結果はやっぱりおんなじ様なもんですね.
じゃあ今度は何が変わったのでしょうか?
もう何が変わったかも想像できるかとは思いますが、コメントにもあるとおり、このプログラムではTest3_3Main クラスのインスタンス(オブジェクト)は生成されません.
つまり、随時的に実行されるだけの部分を別クラスにしています.
そして、インスタンス化されるほうのTest3_3クラスですが、 これには2つのメソッドが定義されています.
この2つのメソッドの内、void print()というやつはTest3_2と同じメソッドです.

メソッドってやつの「関数」と異なるところは、メソッドには必ず所属するもの(オブジェクト)があるという事です.
この場合print()はクラスTest3_3のインスタンスオブジェクトに所属しており、インスタンスメソッドと呼ばれます.
だから、呼ぶ時もtest.print()という風に書かなければなりません.
しかし、それ以外の部分は「関数」と同じなのでprint()が呼ばれた時にどうなるかは説明不要でしょう.

じゃあ、もう一個のTest3_3()というメソッドは何でしょう.
さくさく行く為に言ってしまうとこれはコンストラクタというものです.
コンストラクタとは、クラスからオブジェクトを生成(インスタンス化)する際に 呼び出されるメソッドで、主にオブジェクトの初期設定を行います.
コンストラクタはクラス名と同じ名前を持ち、 また、戻り値を持たない(厳密には自分自身が復帰値ですが)為、戻り値の記述を行いません.
(print()メソッドの方はvoidを戻り値としていますね)

ところで、Test3_2においてもオブジェクトを生成したといっていたのにコンストラクタの話は出てきませんでした.
これはなぜかということを入念に説明しようとすると継承の概念の説明 をしなければならず、長くなるので、興味のある方だけ見てみてください.
ここでは、とりあえずTest3_2のようにコンストラクタを省略するのは邪道ですと思っておきましょう.


3−4.JavaのCoreAPIを使ってみる

今までのプログラムでもJavaのAPIであるjava.lang.Systemクラスを使っていたのですが 次は別のものも使ってみましょう.
以下のプログラムを作ってみました.

import java.util.*;
/**
 * お勉強用テストアプリケーション3_4.
 * <p>
 * 現在の日付を表示します.
 * @author 木下 信
 * @version 1.0
 */
class Test3_4 {
    /**
     * 実行開始時に呼び出されるメソッド.
     * @param args コマンドラインパラメタ文字列群
     */
    public static void main(String[] args){
        //Dateオブジェクトを生成します(現在時刻で初期化されます).
        Date                date = new Date();
        //カレンダーオブジェクトを生成します.
        Calendar            calendar =
                         Calendar.getInstance(TimeZone.getTimeZone("JST"));

        //カレンダーオブジェクトに現在の日付を設定します.
        calendar.setTime(date);

        //print()はprintln()の改行しない版です

        System.out.print(   calendar.get(Calendar.YEAR)+"年");
        System.out.print(   (calendar.get(Calendar.MONTH)+1)+"月");
        System.out.print(   calendar.get(Calendar.DATE)+"日の");
        System.out.print(   calendar.get(Calendar.HOUR)+"時");
        System.out.println( calendar.get(Calendar.MINUTE)+"分です");
    }
}

このプログラムは随時的に実行されます(main()メソッドしかないので当然ですね).
先頭にあるimportはとりあえず他のクラスを使う時のおまじないと思っておいてください.
そして、main()メソッドでは2つのクラスのインスタンスを作成します.
Dateクラスについては以前説明したのと同様にnew クラス名()と書かれていますね.
次のCalenderクラスにはちょっと訳の分からない事が書いてあります.

これは、Calenderクラスの「クラスメソッド」(staticメソッド)であるgetInstance()メソッドの呼び出し文です.
getInstance()メソッドはCalenderクラスのインスタンスを返します.
要はこのメソッドの中でnew クラス名()としてインスタンスを生成してそれを返すのですが、 なぜそんな事をするのでしょう.この説明を始めると難解な概念が出てきてしまうので ここでは説明しません.興味のある方は「デザインパターン」のFactory Methodというやつについて 勉強してみてください.
で、ここではCalenderクラスのインスタンスはnewで作れない事になっているのでこうしてるんだという事にして、 次にそれに渡しているTimeZone.getTimeZone()メソッドです.
これも、Calender.getInstance()と同様にTimeZoneクラスのクラスメソッドによってインスタンスを返しています.
ここの意味は「日本時間のカレンダーオブジェクトを作れ」という風に理解しておけばいいでしょう.

※javaは国際化された言語なので日付や通貨などに国の指定がいるのです.

で、次に出来上がったカレンダーオブジェクト(Calenderクラスのインスタンス)に対してsetTime()メソッド で現在時刻(Dateオブジェクト)を渡しています.
これによってカレンダーオブジェクトは日本時間での現在時刻を管理している状態になりました.

後はカレンダーオブジェクトから年/月/日/時/分を取り出して標準出力に書き出しているだけです.
おっと説明不足でしたが、Calender.YEARとかはCalenderクラスのクラスフィールド(staticフィールド) といいましてint型の定数値です.(calender.YEARと書いても同じです)

このプログラム自体は理解できると思いますが、こういうプログラムを自分で作れといわれると厄介ですね.
厄介だけど、「こういうプログラムを作りたい」って思った時には、 まずそれを簡単に実現できるクラス(API)が無いかを 探さなければならないのはプログラミングの常ですので、 javaのコアAPIにどんな物があるかは各自調べてみてください.
(もちろんこのページからのリンクにもたくさんの説明が載ってます)

import文について

import java.util.*;って何なんでしょう.これはパッケージから説明しないといけません.

javaではpackageという概念が用いられていまして、クラスの名前の衝突を避ける手段となっています.
(パッケージについて)
で上記のCalenderクラス、Dateクラスというのは本当の名前はjava.util.Calender,java.util.Dateという名前なのです.
これらの名前をクラスのフルネームと呼びます.
ソース上のCalenderとなっているところをjava.util.Calender、Dateとなっているところをjava.util.Dateに置き換えると 先頭のimport文は不要なのです.でもっていちいちjava.util.というのを書くのがめんどくさいし ソースも読みづらくなってしまうもんだから、「このソースの中でjava.utilパッケージ内のクラスを使うけど 毎回書くのがめんどくさいからクラス名だけで許してよ」ってコンパイラに教えてあげるのがimport文です.

※Cのinclude文みたいに思うかもしれませんが意味はぜんぜん違うのでした.


JavaおよびHotJavaは米国Sun Microsystems社の商標または登録商標です.