[Java How To Programming] [Home on 246net] [Home on Alles net]
Powered by SmartDoc

Java Platform, Standard Edition 6(Java SE 6)時代のHelloWorld

TAKAHASHI, Toru
torutk@02.246.ne.jp

目次

はじめてのHello World

はじめてのプログラムといえば、"Hello, World!"をコンソールに表示するプログラムを書くことが定番となっています。それにならってJavaでもHelloWorldプログラムを作ってみます。ですが、ただ普通にコンソールに表示させるのではおもしろくありません。そこで、Javaの標準GUIライブラリであるSwingを使ってGUIに"Hello, World!"を表示させるとともに、Java Platform, Standard Edition 6(以下、Java SE 6)で新たに加ったスプラッシュ画面表示を使ったHelloWorldプログラムを紹介します。

さらに、表示文字列をロケールに応じて切り替える国際化プログラムとして作成します。今回は、地域化リソース(表示文字列)をプロパティ・ファイルとして持たせる方法を使用しています。少々の応用として、プログラムを実行中にプロパティ・ファイルを変更した場合でも、プログラムを再起動せずにその変更を反映する仕組みを使っています。

そして、JavaロギングAPIを使ってメソッドのトレースログを出力するようにしています。本記事は、Windows OS上での操作を例に記述していますが、本プログラムはJava SE 6の標準機能のみを使用しているので、Java SE 6が動作するWindows以外の様々なOSで同じように実行可能です。

ソースファイル構成

HelloWorldプログラムのソースコード、およびプロパティ・ファイル、画像ファイル、ロギング設定ファイルの一覧は以下のとおりです。

ソースファイル構成
ファイル名 概要
HelloWorld.java プログラム本体
HelloWorld.properties デフォルトの地域化リソースを保持するプロパティファイル
HelloWorld_ja.properties 日本語の地域化リソースを保持するプロパティファイル
DukeTubbingSmall.png スプラッシュ画面用の画像ファイル
hellologging.properties JavaロギングAPIの制御用設定ファイル

HelloWorldプログラムのソースコード

HelloWorldプログラムは1つのクラスで構成されます。以下にソースコード全体を示します。ちょっと長いですが、解説は後ろの章で記述します。ここはさっと全体を眺めてみる感じで読み進めてください。

HelloWorld.java
/*
 * Hello World project, for Java SE 6.
 * Copyright (C) 2007 by Toru TAKAHASHI, All Rights Reserved.
 */
package jp.gr.java_conf.torutk.hello;

import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

/**
 * Java Standard Edition 6時代のHello worldプログラム。
 * <p>
 * 以下の機能を持つ
 * <ul>
 * <li>GUI(ダイアログ)に挨拶文を表示する
 * <li>挨拶文は地域化リソースのプロパティ・ファイル方式を使用して定義する
 * <li>ロギングAPIを使用してメソッドのトレースログを出力する
 * </ul>
 * 表示したダイアログの[終了する]ボタンを押すとプログラムが終了する。
 * [継続する]ボタンを押すと、いったんダイアログを閉じて、3秒後にまた
 * ダイアログを表示する。
 * <p>
 * 対象Javaプラットフォーム:Java Platform, Standard Edition 6<br>
 * 
 * @author <a href="mailto:torutk@02.246.ne.jp">Toru TAKAHASHI</a>
 * @version $Id: HelloWorld.java 78 2007-05-20 14:40:41Z toru $
 */
public class HelloWorld {
    /**
     * コンストラクタ。
     */
    public HelloWorld() {
        LOGGER.entering(HelloWorld.class.getName(), "HelloWorld");
        LOGGER.exiting(HelloWorld.class.getName(), "HelloWorld");
    }

    /**
     * GUIのダイアログを表示し挨拶文を表示する。
     * Swingのメッセージダイアログを表示させ、そこにmessageプロパティに
     * 指定された文字列を出す。
     * @return trueは終了するボタンが押された<br>
     *         falseなら継続ボタンが押されたかダイアログがクローズされた
     */
    public boolean printDialog() {
        LOGGER.entering(HelloWorld.class.getName(), "printDialog");
        int selected = JOptionPane.showOptionDialog(
            null, getGreeting(), getTitle(), JOptionPane.YES_NO_OPTION,
            JOptionPane.QUESTION_MESSAGE, null, 
            new Object[] {"終了する", "継続する"}, null
        );
        LOGGER.finest("showOptionDialog returns: " + selected); 
        boolean isExit = (selected == 0) ? true : false;
        LOGGER.exiting(HelloWorld.class.getName(), "printDialog", isExit);
        return isExit;
    }

    /**
     * リソースバンドルから挨拶文を取り出すメソッド。
     * messageプロパティに指定された文字列を取り出し返却する
     * @return 挨拶を表わすnullではない文字列
     */
    private final String getGreeting() {
        LOGGER.entering(HelloWorld.class.getName(), "getGreeting");
        String greeting = getResource().getString("message");
        LOGGER.exiting(HelloWorld.class.getName(), "getGreeting", greeting);
        return greeting;
    }

    /**
     * タイトル文字列を取り出すメソッド。
     * @return nullではないタイトル文字列
     */
    private final String getTitle() {
        LOGGER.entering(HelloWorld.class.getName(), "getTitle");
        String title = getResource().getString("title");
        LOGGER.exiting(HelloWorld.class.getName(), "getTitle", title);
        return title;
    }

    /**
     * HelloWorldクラスに対応するリソースバンドルを取得する。
     * プロパティ・ファイルの更新に対応するため、リソースバンドルの制御で
     * キャッシュ期間を30秒に指定。
     * @return HelloWorld用のリソースバンドル
     */
    private final ResourceBundle getResource() {
        LOGGER.entering(HelloWorld.class.getName(), "getResource");
        ResourceBundle resource = ResourceBundle.getBundle(
            HelloWorld.class.getName(),
            new ResourceBundle.Control() {
                public long getTimeToLive(String aBaseName, Locale aLocale) {
                    return 30 * 1000;  // 30秒
                }
            }
        );
        LOGGER.exiting(HelloWorld.class.getName(), "getResource", resource);
        return resource;
    }

    /**
     * VM 起動時のエントリポイントとなるメソッド。
     * 国際化対応しており、リソースバンドルを使用する。リソースバンドルは、
     * キャッシュ期間を指定してファイル内容の更新に対応する。
     */
    public static final void main(final String[] args) throws InterruptedException {
        LOGGER.entering(HelloWorld.class.getName(), "main", args);
        HelloWorld hello = new HelloWorld();
        boolean isExit = false;
        while (!isExit) {
            isExit = hello.printDialog();
            Thread.sleep(3 * 1000);
        }
        LOGGER.exiting(HelloWorld.class.getName(), "main");
    }

    private static final Logger LOGGER = Logger.getLogger(HelloWorld.class.getName());

}

リソースバンドル・プロパティ・ファイル

表示する文字列は、リソースバンドル・プロパティ・ファイルに記述します。このファイルはテキストファイルなので、エディタ等で簡単に編集することができます。また、プログラム実行中に変更しても画面に反映できるようにしています。

HelloWorld.properties
title = HelloWorld Application
message = Welcome to Java Platform Standard Edition 6

日本語ロケールで実行しているときは、日本語でメッセージを表示したいので、日本語ロケール用プロパティ・ファイルも用意しておきます。

プロパティ・ファイルはエンコーディングとしてISO8859-1だけが有効です。したがって日本語等の非ASCII文字を記述する場合、Unicodeエスケープ形式で記述しなくてはなりません。Unicodeエスケープ形式とは、例えば\u3078のような符号化された形式です。これでは日本語の編集が困難です。

そこで、Shift_JIS等の文字コードで記述したファイルをJDKに付属のコマンドnative2asciiを使ってユニコード・エスケープ形式に変換しています。

HelloWorld_ja.properties
title = HelloWorld Application
message = JDK \
    6\u3078\u3088\u3046\u3053\u305d\u3044\u3089\u3063\u3057\u3083\u3044\u307e\u305b \

ロギング設定ファイル

JavaロギングAPIでログを出力するコードを記述した場合、その出力先、出力有無や出力形式を設定ファイルで定義することができます。

hellologging.properties
handlers = java.util.logging.ConsoleHandler
jp.gr.java_conf.torutk.hello.HelloWorld.level = ALL
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

HelloWorldの解説

ソースコードの書式

HelloWorld.javaは以下の構成になっています。

  1. ファイル先頭コメントを記述します。
  2. パッケージ文が次に来ます。
  3. 続いて、インポート文を記述します。
  4. クラスの文書化コメントを記述します。
  5. クラス宣言が来ます。
  6. フィールドの宣言が来ます。
  7. メソッドの定義が来ます。

ファイル先頭コメント

著作権表記やプロジェクト名等を記述します。

/*
 * Hello World project, for Java SE 6.
 * Copyright (C) 2007 by Toru TAKAHASHI, All Rights Reserved.
 */

パッケージ文

必ずパッケージ文を使用して、パッケージを特定するようにします。

Javaでは言語仕様において一意なパッケージ名を付ける手順としてプログラム著作者(組織)のインターネット・ドメイン名に基づき命名する方法が記載されています。

今回のHelloWorldプログラムは、配布して他のプログラムと組み合わせて使うことを想定していません。その場合、必ずしも一意なパッケージ名である必要はないでしょう。しかし、今後プログラミングするにあたって一意なパッケージ名の付け方を知っておくのが望ましいと言えます。

package jp.gr.java_conf.torutk.hello;
ドメインを持たない個人で使える一意なパッケージ名

個人で作成するプログラムで、個人ではインターネット・ドメインを有していない場合に一意なパッケージ名を使用できるサービスが無償で提供されています。

インポート文

単独の型インポート宣言を使って利用しているクラスをimport文で記述します。

import java.util.Locale;
import java.util.ResourceBundle;
import java.util.logging.Logger;
import javax.swing.JOptionPane;

単独の型インポート宣言を使用する理由はこちらを参照。

クラスの文書化コメント

クラスの概要・責務について記述します。Javadocドキュメントを抽出するときに使用されます。JavadocでHTMLドキュメントを生成するときは、改行させたり箇条書するタグ(<br>や<ul>等)を使用するとよいでしょう。

/**
 * Java Standard Edition 6時代のHello worldプログラム。
 * <p>
 * 以下の機能を持つ
 * <ul>
 * <li>GUI(ダイアログ)に挨拶文を表示する
 * <li>挨拶文は地域化リソースのプロパティ・ファイル方式を使用して定義する
 * <li>ロギングAPIを使用してメソッドのトレースログを出力する
 * </ul>
 * 表示したダイアログの[終了する]ボタンを押すとプログラムが終了する。
 * [継続する]ボタンを押すと、いったんダイアログを閉じて、3秒後にまた
 * ダイアログを表示する。
 * <p>
 * 対象Javaプラットフォーム:Java Platform, Standard Edition 6<br>
 * 
 * @author <a href="mailto:torutk@02.246.ne.jp">Toru TAKAHASHI</a>
 * @version $Id: HelloWorldJavaSE6.sdoc 81 2007-05-20 15:15:20Z toru $
 */

Javadocの標準タグである@authorで作者を示し、@versionでプログラムのバージョン番号を示しています。バージョン番号は、今回はバージョン管理システム(subversion)のIdキーワード展開で自動展開される文字列を指定しています。

クラス宣言

クラスの宣言を記述します。

public class HelloWorld {

設計に応じて、クラス修飾子を指定します。パッケージ外部から利用されるクラスであれば、publicを付けます。別なクラスのスーパークラスとして存在し、自身のインスタンスを持たないなら、abstractを付けます。このクラスのサブクラスの存在を許さないのであれば、finalを付けます。クラス内では浮動小数点演算をFP-Strictモードで実行させる時は、strictfpを付けます。

メソッドの定義

メソッドは、可視性の高いものから順に並べて記述します。ただし、mainメソッドだけは最後に書いています。

本HelloWorldクラスでは以下の順に記述しています。

アクセッサメソッドは、可能な限りprivateにし、かつfinal宣言を付けています。final化することによってサブクラスで書き換えられないようになります。また、コンストラクタの中からアクセッサメソッドを呼ぶ場合にも安全となります。

フィールドの宣言

フィールドは可能な限りprivateにします。また、可能な限りfinalにします。

ソースファイル上のメソッドとフィールドの記述順序について、一般的にはメソッドの定義より前にフィールドの定義を記述するスタイルを見かけます。しかし、クラスの特に利用者にとって重要なのは可視性の高いメソッド/フィールドです。また、メソッドとフィールドではメソッドの方が重要です。

そこで、可視性の高いものを先に、メソッドとフィールドならメソッドを先に記述するスタイルを使用します。

ソースコードの文字コード

ソースファイルをコンパイルする時に、ソースファイルの文字コードはプラットフォームのデフォルトエンコーディングであると仮定されます。例えば、Windows上ではWindows-31Jと仮定されます。

プラットフォームのデフォルト文字コード以外で記述されたソースファイルをコンパイルする場合、-encodingオプションでソースファイルの文字コードを指定します。

文字コードを指定してコンパイル
D:\work> javac -encoding EUCJIS \
    jp/gr/java_conf/torutk/hello/HelloWorld.java

指定できる文字コードについては、Java Encoding & Aliases TableのURLを参照するとよいでしょう。

ソースコードで使用しているAPI

Java SE 6時代のプログラミングでは、GUI、ロギングが標準となってきます。そこで、Hello Worldプログラミングもこれを取り入れることとします。また、表示文字列は、国際化対応となるようリソースバンドルを用いています。

ダイアログを使ったメッセージ表示

以下のダイアログを表示します。

SwingではJOptionPaneクラスを利用し、簡単にダイアログを表示することができます。ダイアログはボタンの文字列を指定したいため、少し柔軟な指定のできるshowOptionDialogメソッドを使用しています。メソッドの引数は以下のとおりです。

        int selected = JOptionPane.showOptionDialog(
            null, getGreeting(), getTitle(), JOptionPane.YES_NO_OPTION,
            JOptionPane.QUESTION_MESSAGE, null, 
            new Object[] {"終了する", "継続する"}, null
        );
  1. 第1引数はダイアログを表示する親コンポーネントを指定します。今回他にGUIは使用しないので、nullを指定しています。この場合画面中央にダイアログが表示されます。
  2. 第2引数はダイアログ本文中に表示する文字列を指定します。
  3. 第3引数はダイアログのタイトルバーに表示する文字列を指定します。
  4. 第4引数はダイアログの下部に表示されるボタンの種類を指定します。今回は2つの選択を意味するYES_NO_OPTIONを指定します。
  5. 第5引数はメッセージの書式を指定します。今回は選択をユーザーに促すためQUESTION_MESSAGEを指定します。
  6. 第6引数はアイコンを指定します。デフォルトのアイコンでよいのでここではnullを指定します。
  7. 第7引数はボタンの文字列を指定します。複数のボタンがあるときは、配列で指定します。
  8. 第8引数はデフォルトでフォーカスを設定するボタンを指定します。今回はフォーカスを設定しないのでnullを指定します。

戻り値は、押されたボタンに対応した数値が返却されます。数値は、第7引数で指定したボタン文字列の配列のインデックス番号に対応します。[終了する]なら0、[継続する]なら1となります。また、どのボタンも押さずに、ダイアログのタイトルバーにある[X]ボタンを押してダイアログを閉じた場合はJOptionPane.CLOSED_OPTION定数となります。

JOptionPaneは、たった一行で各種ダイアログを表示させることができるので、ちょっとしたプログラムを作る際に重宝します。

ロギング

ロギングは、プログラムの実行の過程を外部に出力させる時に使用します。ロギングを組み込んでおくことによって、整然とデバッグが行なえるようになり、開発効率がアップします。ロギングもプロとしてソフトウェアを作成する際には必須のテクニックと言えるでしょう。

ロギングを行うには、各クラス単位でLoggerインスタンスを1つ取得しておきます。Loggerを取得するときに指定する文字列は、ロギングを制御するときに使います。

Loggerインスタンスの取得
// ロギング用のロガー
private static final Logger LOGGER = Logger.getLogger(HelloWorld.class.getName());

このインスタンスに対してログ出力メソッドを呼び出し必要なメッセージを外部に出力します。Loggerインスタンスは階層構造を取っており、階層は文字列上でピリオド('.')で区切られます。その階層の任意の層に対してロギングの制御を行います。階層の構成はアプリケーション次第ですが、通常はパッケージ・クラス名と同じにしておくのが便利です。今回のHelloWorldプログラムでは、パッケージ・クラス名を完全限定名で表わすと"jp.gr.java_conf.torutk.hello.HelloWorld"です。これをそのまま文字列でベタに指定してもよいのですが、スペルミスがあってもエラーになりません。そこで、コンパイル時にチェックがかかるクラス・リテラルで評価されるClassオブジェクトのgetNameメソッドで指定しています。

各メソッドのトレースをログするために、メソッドの入口と出口にロギングを入れています。このトレース用に用意されているのが、Loggerクラスのenteringメソッドとexitingメソッドです。

それぞれ、第1引数にクラス名、第2引数にメソッド名を指定します。第3引数はオプションで、引数や戻り値があればそれを指定します。

以下にHelloWorldクラスでの一例を示します。第1引数のクラス名には、クラス・リテラルを使用しています。第2引数のメソッド名だけは文字列でベタ書きしています。

ロギングの例(トレース)
    public HelloWorld() {
        LOGGER.entering(HelloWorld.class.getName(), "HelloWorld");
        LOGGER.exiting(HelloWorld.class.getName(), "HelloWorld");
    }

ロギングのデフォルト設定では、INFOレベル以上のログが出力されます。メソッドのトレースで使用したenteringメソッドおよびexitingメソッドは、FINERレベルのため、デフォルトではログとして出力されません。そこで、独自のログ設定ファイル(hellologging.properties)を記述し、JavaVM起動時にシステム・プロパティとしてこのファイルを指定します。

ログ設定:ハンドラーの指定
handlers = java.util.logging.ConsoleHandler
java.util.logging.ConsoleHandler.level = ALL
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter

まず、ハンドラー(ログ出力方法を扱う)としてコンソールに出力するConsoleHandlerを指定しています。ファイルとコンソールの双方といった複数のハンドラーの指定も可能です。次に、ConsoleHanderに出力するログのレベルは全て(ALL)を指定しています。3番目に、ConsoleHandlerが出力するログの書式をSimpleFormatterと指定しています。

ログ設定:ロガーの設定
jp.gr.java_conf.torutk.hello.HelloWorld.level = ALL

アプリケーション中からロギングAPIに出力したログは、ロガーとハンドラーの2つのオブジェクトでフィルターされます。ここではロガーの中でフィルターするログレベルを設定します。上記は、ログ階層のjp.gr.java_conf.torutk.hello.HelloWorldに対してlevelの設定をALLに指定しています。

以下に、ログ設定ファイルを指定してJavaVMを起動し、HelloWorldプログラムを実行した例を示します。

実行時にログ設定ファイルを指定する
D:\work> java -Djava.util.loggin.config.file=hellologging.properties \
    jp.gr.java_conf.torutk.hello.HelloWorld
2007/05/06 22:56:16 jp.gr.java_conf.torutk.hello.HelloWorld main
詳細レベル (中): ENTRY
2007/05/06 22:56:16 jp.gr.java_conf.torutk.hello.HelloWorld HelloWorld
詳細レベル (中): ENTRY
  :
D:\work>

1件のログ出力が基本的には2行でコンソールに出力されています。

1行目は、ログを出力した日時、クラス名、メソッド名です。

2行目は、ログのレベルとログ出力文字列です。

上記の例での1件目のログの出力内容は以下のとおりです。

1件目のログ出力内容
ログ出力項目 ログ出力内容
日時 2007年5月6日 22時56分16秒
出力クラス jp.gr.java_conf.torutk.hello.HelloWorld
出力メソッド main
出力レベル finer (詳細レベル-中)
ログ内容 ENTRY (メソッドへ入った)

国際化(リソースバンドル)

国際化は、表示文字列などのロケール依存部分(地域化リソース)をプログラム本体から分離し、リソースとして別にまとめて扱います。Javaには、地域化リソースを扱うAPIとしてリソースバンドル(java.util.ResourceBundle)が用意されています。

リソースバンドルには、次の2つの実装方法があります。

  1. クラスとして定義

    配列を用いてキーと値の組を定義する方法

  2. プロパティファイルとして定義

    プロパティファイルにキーと値の組を定義する

リソースバンドルをプロパティ・ファイルとして定義

今回のHelloWorldプログラムでは、2.の方法で実装しています。まず、リソースバンドルAPIを使ってリソースを検索しているプログラム本体を見てみましょう。

リソースバンドルを取得する
    private final ResourceBundle getResource() {
        LOGGER.entering(HelloWorld.class.getName(), "getResource");
        ResourceBundle resource = ResourceBundle.getBundle(
            HelloWorld.class.getName(),
            new ResourceBundle.Control() {
                public long getTimeToLive(String aBaseName, Locale aLocale) {
                    return 30 * 1000;  // 30秒
                }
            }
        );
        LOGGER.exiting(HelloWorld.class.getName(), "getResource", resource);
        return resource;
    }

ResourceBundleクラスのstaticメソッドgetBundleを使っています。

第1引数には、リソースとして、デフォルトの地域化リソースを保持するプロパティ・ファイルを指定する基となるクラス名を完全限定名で指定しています。

プロパティ・ファイルは、ここで指定したクラスと同じクラスパス上に存在し、クラス名にアンダースコア('_')でロケール文字列を付加し、さらに拡張子.propertiesを付加したファイル名となります。

getBundleメソッドにロケールを明示的に指定していない場合、プログラムを実行している環境のロケールが適用されます。日本語ロケール(ja_JP)で実行した場合、以下の順序でプロパティ・ファイルが検索されます。

  1. HelloWorld_ja_JP.properties
  2. HelloWorld_ja.properties
  3. HelloWorld.properties

今回は、日本語ロケール用に上記2.を用意しており、また非日本語ロケール用に上記3.を用意しています。

第2引数のResourceBundle.Controlは、プロパティ・ファイルの内容をメモリに保持する期間を制限するために指定しています。デフォルトでは最初にプロパティ・ファイルを読み込むと、以降メモリに内容を保持し続けます。そのため、プロパティ・ファイルを変更しても再起動しない限り変更した内容が反映されません。メモリに保持する期間を制限することにより、プログラムを実行中にプロパティ・ファイルを修正すると、修正した内容をプログラムに反映することができます。

ResourceBundle.ControlクラスのgetTimeToLiveメソッドをオーバーライドしてメモリに保持する期限を有限に設定します。

リソースバンドルから文字列を取得

次に、取得したリソースバンドルから、表示文字列を取り出すコードを見てみます。

リソースバンドルから文字列を取得する
    private final String getTitle() {
        LOGGER.entering(HelloWorld.class.getName(), "getTitle");
        String title = getResource().getString("title");
        LOGGER.exiting(HelloWorld.class.getName(), "getTitle", title);
        return title;
    }

Javaプログラムを実行する時に、ロケールを切り替えたい場合は、システムプロパティuser.languageを使って任意のロケールを指定することができます。

D:\work> java -Duser.language=en jp.gr.java_conf.torutk.hello.HelloWorld

HelloWorldのコンパイルと実行

プログラムの実行形態

Java SEでは、プログラムの実行方法に以下の形体があります。

  1. クラスファイルを指定して実行
  2. アプレットとしてWebブラウザ上から実行
  3. Java Web Startとして実行

1.は実行するマシンのファイルシステム上にあらかじめ実行に必要なファイルを置いておく方法です。2.と3.は、Webサーバ上にあるファイルを実行時にダウンロードし実行する方法です。(キャッシュ等の仕組みで必ずしも毎回ダウンロードが必要ではありませんが)

HelloWorldプログラムは、1.の形態で実行するものです。

Javaの実行形式はクラスファイル

Javaは、ソースコードで定義したクラスがそれぞれクラスファイルにコンパイルされます。クラスファイルは、実行時にJavaVM上にロードされ実行されます。

このクラスファイルのフォーマットは、実際に実行する計算機のCPUやOSの種類とは無関係にJavaの仕様として定義されています。ですから、計算機上にJavaVMさえあれば、Javaの実行プログラム(すなわちクラスファイル)は実行可能です。

ソースファイル一式の配置

ソースファイルの配置に関して必ずしもこれでなくてはいけないという規定はありませんが、一般的に以下のように分類してサブディレクトリを作成します。

ソースファイル、設定ファイル、プロパティファイル、画像ファイルをそれぞれ格納するためのディレクトリを作業ディレクトリの直下に以下のように作成します。

srcディレクトリ
Javaのソースファイルを、パッケージ名に対応したサブディレクトリ構成で配置します。
confディレクトリ
設定ファイルなどプログラムが実行時に読み込むファイルを配置します。
imageディレクトリ
画像ファイルを特出して配置します。
classesディレクトリ
コンパイルされたクラスファイルを配置します。ソースファイル構成の段階では空です。

今回のHelloWorldプログラムのソースファイル一式を、上記のディレクトリ構成に従って作業ディレクトリを、D:\workとしたときに配置した様子を以下に示します。

ソースファイル一式のディレクトリ構成
D:\work
  |
  +-- classes
  |
  +-- conf
  |     |
  |     +-- HelloWorld.properties
  |     +-- HelloWorld_ja.properties.SJIS
  |     +-- hellologging.properties
  |
  +-- image
  |     |
  |     +-- DukeTubbingSmall.png
  |
  +-- src
        |
        +-- jp
             +-- gr
                  +-- java_conf
                        +-- torutk
                              +-- hello
                                    |
                                    +-- HelloWorld.java

コンパイル

ソースファイルのコンパイル

作業ディレクトリにおいて以下のようにコンパイルを実施します。

コンパイル
D:\work> javac -d classes src/jp/gr/java_conf/torutk/hello/HelloWorld.java

D:\work> 

javacは、JavaSEの開発キットJDKに含まれるJavaコンパイラ・コマンドです。今回は、1つのソースファイルをコンパイルするだけで、Java標準以外のライブラリも使用しません。ですから、コンパイル時に指定するのは、クラスファイルを出力する先のディレクトリとコンパイルしたいソースファイルとなります。

コンパイルした結果、ソースファイル一式のディレクトリ構成に対してclassesディレクトリの下にクラスファイルが追加され、以下のようになります。

コンパイル後のディレクトリ構成
D:\work
  |
  +-- classes
  |     |
  |     +-- jp
  |          +-- gr
  |               +-- java_conf
  |                     +-- torutk
  |                           +-- hello
  |                                 |
  |                                 +-- HelloWorld.class
  |                                 +-- HelloWorld$1.class
  +-- conf
  |     |
  |     +-- HelloWorld.properties
  |     +-- HelloWorld_ja.properties.SJIS
  |     +-- hellologging.properties
  |
  +-- image
  |     |
  |     +-- DukeTubbingSmall.png
  |
  +-- src
        |
        +-- jp
             +-- gr
                  +-- java_conf
                        +-- torutk
                              +-- hello
                                    |
                                    +-- HelloWorld.java

HelloWorld$1.classと、HelloWorldに$1が付いたクラスは、HelloWorldクラス内で無名クラスとして定義されたものをコンパイルした際に生成されるクラスファイルです。

リソースバンドル・プロパティの変換とコピー

HelloWorld_ja.properties.SJISに、日本語でShift_JIS形式で記述したプロパティ・ファイルを、native2asciiコマンドによりUnicodeエスケープした形式にする必要があります。変換後のUnicodeエスケープされたプロパティ・ファイルは、HelloWorldクラスと同じディレクトリに出力します。リソースバンドルは、プロパティ・ファイルをクラスパス上で検索するので、一番単純な方法はHelloWorldクラスと同じディレクトリに置いてしまう方法です。

native2ascii変換
D:\work> native2ascii conf/HelloWorld_ja.properties.sjis \
    classes/jp/gr/java_conf/torutk/hello/HelloWorld_ja.properties
D:\work>

デフォルト(ロケール指定該当しない場合)のリソースバンドル・プロパティ・ファイルをコンパイルで生成されたHelloWorldクラスファイルと同じディレクトリへコピーします。

デフォルト・プロパティ・ファイルのコピー
D:\work> copy conf/HelloWorld.properties \
    classes/jp/gr/java_conf/torutk/hello/
D:\work>

上記の作業の結果、最終的には作業ディレクトリは以下のようになります。

最終的なディレクトリ・ファイル構成
D:\work
  |
  +-- classes
  |     |
  |     +-- jp
  |          +-- gr
  |               +-- java_conf
  |                     +-- torutk
  |                           +-- hello
  |                                 |
  |                                 +-- HelloWorld.class
  |                                 +-- HelloWorld$1.class
  |                                 +-- HelloWorld.properties
  |                                 +-- HelloWorld_ja.properties
  +-- conf
  |     |
  |     +-- HelloWorld.properties
  |     +-- HelloWorld_ja.properties.SJIS
  |     +-- hellologging.properties
  |
  +-- image
  |     |
  |     +-- DukeTubbingSmall.png
  |
  +-- src
        |
        +-- jp
             +-- gr
                  +-- java_conf
                        +-- torutk
                              +-- hello
                                    |
                                    +-- HelloWorld.java

HelloWorldの実行

以下のコマンドで実行します。

HelloWorldの実行
D:\work> java -cp classes -splash:image/DukeTubbingSmall.png \
    -Djava.util.logging.config.file=conf/hellologgign.properties \
    jp.gr.java_conf.torutk.hello.HelloWorld
2007/05/07 0:09:35 jp.gr.java_conf.torutk.hello.HelloWorld main
詳細レベル (中): ENTRY
2007/05/07 0:09:35 jp.gr.java_conf.torutk.hello.HelloWorld HelloWorld
詳細レベル (中): ENTRY
2007/05/07 0:09:35 jp.gr.java_conf.torutk.hello.HelloWorld HelloWorld
詳細レベル (中): RETURN
2007/05/07 0:09:35 jp.gr.java_conf.torutk.hello.HelloWorld printDialog
詳細レベル (中): ENTRY
    :

スプラッシュ画面

Java SE 6からは、JavaVM起動時にスプラッシュ画面を表示する機能が追加されました。コマンドラインで画像ファイルを指定すれば、まずその画像ファイルが表示されます。

スプラッシュ画面は、SwingまたはAWTのウィンドウを最初に表示した際に自動で閉じます。しかし、HelloWorldプログラムのようにウィンドウを表示しない(ダイアログはウィンドウとして扱われません)場合、スプラッシュ画面はプログラム上で明示的に閉じない限り、プログラム終了まで表示されたままとなります。

応用として、本記事で紹介したHelloWorldプログラムに、起動後スプラッシュ画面を閉じる処理を追加するのは読者への課題ということにします。

ダイアログ

起動後、ダイアログが表示されます。

[終了する]ボタンを押すと、HelloWorldプログラムは終了します。

[継続する]ボタンを押すと、ダイアログはいったんクローズしてから3秒後に再表示されます。再表示のときに毎回リソースバンドルから表示するメッセージの文字列を取得しています。そこで、プロパティ・ファイルを変更後一定時間(最大30秒)したのちにダイアログを再表示させると、プロパティ・ファイルの変更がダイアログの表示に反映されます。

さいごに

本記事では、Java SE 6の上で文字列を表示する簡単な「Hello World」プログラムを題材に、JavaならではのGUIでの表示、国際化対応、それからプロフェッショナルなプログラマーとしてはぜひ押えておきたいロギング機能を使った少し高度なHelloWorldプログラムを作成しました。

本格的なJavaプログラミングの出発点として本記事が役に立つことができれば著者として幸いです。

参考文献

  1. 書籍「Java国際化プログラミング」、Andrew Deitsch, David Czarnecki著、風間一洋訳、オライリー・ジャパン、2002年刊