広告

■□■□■□■□■□■□■□■□■□■□■□■□■□■□■
                      2011年12月21日

   Java総合講座 - 初心者から達人へのパスポート
                 2009年11月開講コース 052号

                                セルゲイ・ランダウ
 バックナンバー: http://www.flsi.co.jp/Java_text/
■□■□■□■□■□■□■□■□■□■□■□■□■□■□■


-------------------------------------------------------
・現在、このメールマガジンは以下の2部構成になっています。
[1] 当初からのコース:vol.xxx(xxxは番号)が振られています。
   これは現在、中級レベルになっています。
[2] 2009年11月開講コース:xxx号(xxxは番号)が振られています。
   これは現在、初心者向けのレベルになっています。
・このメールマガジンは、画面を最大化して見てください。
小さな画面で見ていると、不適切な位置で行が切れてしまう
など、問題を起すことがあります。
・このメールマガジンに掲載されているソース・コード及び
文章は特に断らない限り、すべて筆者が著作権を所有してい
ます。また、これらのソース・コードは学習用のためだけに
提供しているものです。
-------------------------------------------------------


========================================================
◆ 01.データベースを使用するアプリケーションの開発(続き)
========================================================

前回までで、データベースに対して、データの「新規登録」、
「検索」、「更新」、「削除」を行うアプリケーションの基本的
なパターンができあがりました。

この基本的なプログラミングのパターンを身に付ければ、いくらでも
応用が効きます。たとえば、資産管理システムとか図書(蔵書)管理
システムとか、自分で応用して必要なアプリケーションを作ってみて
ください。



今回は、これまで作ってきた人事情報管理システム(の雛形)に
セキュリティー(安全性)のための機能を少し付け足すことにし
ます。

人事情報管理システムが誰でも使えるようでは困ります(人事情報
を誰でもが変更したり検索したりできてはまずい)ので、パスワー
ドで保護することにしましょう。

ただし、今回はパスワードを入力する画面の作り方に専念し、パス
ワードはプログラムの中に組み込む形で済ませる簡単なもの(セキュ
リティー強度はかなり低い)とします。

本格的なセキュリティーの話は、もっとずっと後にします。



以前、HumanResourceJFrameを作成したときにCardLayoutに設定した
ことを覚えているでしょうか。(忘れてしまった人は、032号を
確認してください。)
CardLayoutは紙芝居のように画面を差し替えることができるLayoutManager
でしたね。
アメリカ人は紙芝居の代わりにcard(カード=日本語ではトランプ)
をめくるようなイメージで理解していたのでCardLayoutという名前が
付けられました。

JTabbedPaneも紙芝居のような振る舞いをする点ではCardLayoutに似て
いますが、タブがついていてタブをクリックすることでページを選択
できるという点で異なります。

今回は、このCardLayoutを利用し、初期画面ではパスワードを要求
するようにして、正しいパスワードを入力しないことには
HumanResourceEntryPaneのページ(CardLayoutではページと呼ばず、
カードと呼ぶ)が開かないようにプログラミングしてみましょう。


ではまず、パスワードを入力する画面を作成します。

Eclipseを起動して

(1) パッケージ・エクスプローラーの中のJStudy1(プロジェクト)の配下
のsrcの配下のjp.co.flsi.lecture.ui(パッケージ)を右クリックします。

(2) 「新規」→「その他」を選択します。

(3) 「WindowBuilder」配下の「Swing Designer」配下の「JPanel」を選択し、
「次へ」ボタンをクリックします。

(4) 「ソース・フォルダー」に「JStudy1/src」、「パッケージ」に
「jp.co.flsi.lecture.ui」が入力されていることを確認し、「名前」の欄に
LoginPane
と入力しましょう。

(5) 「スーパークラス」が「javax.swing.JPanel」になっていることを確認し、
「完了」ボタンをクリックします。

LoginPane.javaの編集画面になりますね。

いつものように、(下のほうに3つ並んでいるタブのうちの「Design」タブを
クリックすることによって)Designビューを開き、「LoginPane.java」タブを
ダブル・クリックすることによってエディター画面を最大化しましょう。

(6) Palette(パレット)のLayouts配下のGridBagLayoutをクリックし、
画面の灰色の部分(JPanel)をクリックすることによって、このJPanel
のレイアウトをGridBagLayoutに変更します。


続いて、このJPanel(画面の灰色の部分)の上に以下のGUI部品を貼り付け
てください。

(1) JLabelを「column 0, row 0」が表示される位置(以後、これを「00の位置
に貼り付け」というようにcolumnの数字とrowの数字を並べて簡略に表記する
ことにする)に貼り付け、変数名(Variable)はjLabelUserにします。
textプロパティーの値を「ユーザー名:」にします。

(2) JTextFieldを10の位置に貼り付け、変数名(Variable)はjTextFieldUser
にします。

(3) JLabelを01の位置に貼り付け、変数名(Variable)はjLabelPasswordにし
ます。
textプロパティーの値を「パスワード:」にします。

(4) JPasswordFieldを11の位置に貼り付け、変数名(Variable)はjPasswordField
にします。

(5) JPanelを02の位置に貼り付け、変数名(Variable)はjPanelLoginButtonに
します。 これはJButtonを貼り付けるための土台にします。

(6) JButtonをその土台(jPanelLoginButton)に貼り付け、変数名(Variable)
はjButtonLoginにします。
textプロパティーの値を「ログイン」にします。

(7) その土台(jPanelLoginButton)をクリックしてハンドルを表示し、右端の
ハンドルを目いっぱい右にドラッグして(2セル分)広げます。


(筆者は画面をスタイリッシュにする意思は持っていませんので、画面が格好悪い
と思う人は自分で調整して下さい。)


なお、JPasswordFieldというのはJTextFieldのサブクラスで、パスワー
ドを入力するためのテキスト・フィールドです。このGUI部品に文字列
を入力すると、入力したのとは違う文字(デフォルトでは*)が表示さ
れますので、パスワードを入力しても他の人が盗み読むことができま
せん。
この表示される文字をエコー文字(echo character)と呼びますが、
エコー文字には*以外の文字を設定することもできます。



では、次に「ログイン」ボタンをクリックしたときの振る舞いをプロ
グラミングしましょう。

「ログイン」ボタンをクリックしたら、jPasswordFieldに入力された
パスワードの値を調べて、正しい値であればHumanResourceEntryPaneの
カードを開くようにするための小細工をします。

では、「ログイン」ボタンを右クリックし、「Add event handler」→
「アクション」→「actionPerformed」を選択してください。

例によって、ソース・コードの中に

--------------------------------------------------------
jButtonLogin.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
   }
});
--------------------------------------------------------

という行が挿入されますね。
これを次のように書き換えましょう。

--------------------------------------------------------
jButtonLogin.addActionListener(new ActionListener() {
   public void actionPerformed(ActionEvent e) {
         String pass = new String(jPasswordField.getPassword());
         if ("aaaaa".equals(pass)) {
            ((CardLayout) getParent().getLayout()).next(getParent());
         }
   }
});
--------------------------------------------------------

ここで、getPassword()というのはJPasswordFieldのメソッドで、
JTextFieldのgetText()メソッドと同じ働きをしますが、ただし
戻り値はString型ではなくchar型の配列(char[])になります。
このchar型の配列をString型のオブジェクトに変換するために、
上記のソース・コードでは、

--------------------------------------------------------
String pass = new String(getJPasswordField().getPassword());
--------------------------------------------------------

というようにStringのコンストラクターを呼び出して、char型の配列
からString型のインスタンスを生成しています。

その次の行の

--------------------------------------------------------
if ("aaaaa".equals(pass)) {
--------------------------------------------------------

では、パスワードの値がaaaaaであるかどうかをチェックしています。
ここでは、aaaaaなんていうわかりやすいパスワードにしましたが、
もちろん実際に使うパスワードはこんなわかりやすいものにしては
いけません。

┌補足─────────────────────────┐
ここでは、アプリケーションを簡素化するためにパスワードを
固定値にしましたが、本来ならこのようにプログラムの中にパス
ワードを固定値で組み込むのは危険です。ハッカーに簡単に読み
取られてしまいます。
本来なら、パスワードは暗号化して別のファイルに保存するのが
普通です。そして、パスワードは本人が自由に設定/変更できる
ようになっているのが普通です。
また、上記のソース・コードではユーザー名はダミー(見せかけ)
であり、チェックしていませんが、本来なら、ユーザー名とパス
ワードの組み合わせを暗号化してファイルに保存しておき、入力
されたユーザー名とパスワードの組み合わせがそのファイルに登録
されているかどうかをチェックするのが普通です。

ここらへんの話は、もっと後で、インターネット関連のセキュリ
ティー技術をお話しするときに、いっしょに説明いたします。
└───────────────────────────┘

その下の

--------------------------------------------------------
((CardLayout) getParent().getLayout()).next(getParent());
--------------------------------------------------------

の中のgetParent()というのはGUI部品(Component)の親(parent)を
取り出すメソッドです。(親というのは、そのComponentを貼り付け
ているコンテナーを意味します。)
この行では、このJPanelの親(このLoginPaneを貼り付けるウインドウ)
のレイアウトがCardLayoutであることを前提として、そのCardLayout上の
次のカード(次のカードはHumanResourceEntryPaneになるようにあとで
ソース・コードを編集する)を開いています。

また、getLayout()というのはコンテナーのLayoutManagerを取り出す
メソッドで、このLoginPaneはHumanResourceJFrame(に貼り付けられ
ているJPanelの上)に貼り付ける予定なので、(HumanResourceJFrame
(に貼り付けられているJPanel)のLayoutManagerはCardLayoutに
設定してあるので)CardLayoutにキャストしています。
(getLayout()メソッド自体はLayoutManager型の戻り値を返します
ので、それをキャストによってCardLayout型に変換しています。)

そして変換されたCardLayoutオブジェクトに対してnext()メソッド
を呼び出しています。next()メソッドはCardLayout上の次のカード
を開くメソッドです。(上でCardLayoutにキャスト(変換)したのは、
このnext()メソッドを使いたいためです。)

なお、next()メソッドの引数には、このLayoutManagerの持ち主で
ある、親のコンテナーを指定することになっていますので、ここで
はgetParent()メソッドの呼び出しによって親を指定しています。



では次に、HumanResourceJFrameのソース・コードをエディターで
開いて下さい。

まず、ソース・コードの先頭のフィールドの定義が並んでいる所に

--------------------------------------------------------
private LoginPane loginPane = null;
--------------------------------------------------------

というフィールドの定義を追加しましょう。

続いて、このソース・コードの中の

--------------------------------------------------------
private HumanResourceEntryPane getHumanResourceEntryPane() {
   if (humanResourceEntryPane == null) {
      humanResourceEntryPane = new HumanResourceEntryPane();
      humanResourceEntryPane.setPreferredSize(new Dimension(0, 0));
      humanResourceEntryPane.setName("humanResourceEntryPane");
   }
   return humanResourceEntryPane;
}
--------------------------------------------------------

というコードの下あたりに(場所が決まっているわけではないが、
なるべくわかりやすい場所に)下記のようなメソッドを追加しま
しょう。

--------------------------------------------------------
private LoginPane getLoginPane() {
   if (loginPane == null) {
      loginPane = new LoginPane();
      loginPane.setPreferredSize(new Dimension(0, 0));
      loginPane.setName("loginPane");
   }
   return loginPane;
}
--------------------------------------------------------

では、次にソース・コードの中のgetJContentPane()メソッドの中を
見てください。
その中に

--------------------------------------------------------
jContentPane.add(getHumanResourceEntryPane(), getHumanResourceEntryPane().getName());
--------------------------------------------------------

という行がありますね。
この行の前に

--------------------------------------------------------
jContentPane.add(getLoginPane(), getLoginPane().getName());
--------------------------------------------------------

という行を追加して下さい。このadd()メソッドの呼び出しは
jContentPane(HumanResourceJFrameのお腹に張り付いているJPanel)
にLoginPaneを貼り付けることを意味します。


すると、これらのコードは下記のような順番に並ぶことになります。

--------------------------------------------------------
jContentPane.add(getLoginPane(), getLoginPane().getName());
jContentPane.add(getHumanResourceEntryPane(), getHumanResourceEntryPane().getName());
--------------------------------------------------------

これらの行の順番が大事なので注意して下さい。
こうすると、HumanResourceJFrameを起動したときに、最初に
LoginPaneのページ(カード)を開くようになります。

CardLayoutの場合は、コンテナーのadd()メソッドを実行した順番に
カードが並ぶので、上記の順番にすると、LoginPaneのカードが
先頭になるのです。



では、HumanResourceJFrameを起動してテストしてみましょう。
正しいパスワードを入力しないことにはHumanResourceEntryPaneの
画面が開かないことが確認できますね。



ところで、さきほどはCardLayoutのnext()メソッドというものを使い
ましたが、next()メソッドがあるんだったら、previous()メソッドと
か、類似した他のメソッドもあるんじゃないかと、勘のいい人は推測
するでしょうね。
はい。その通りです。previous()メソッドもあるし、他の類似したメ
ソッドもありますよ。

でも、ここでは説明しませんのでご自分で調べてみてください。

ここでは、調べ方だけを説明しておきましょう。
(Eclipseのコンテンツ・アシストを使っても調べられますが、ここ
ではAPIリファレンスを使うオーソドックスなやり方を説明します。)

以前(007号で)APIリファレンスのWebページを紹介しましたが、今回はその
見方を簡単に説明しておきましょう。

まず、APIリファレンス(reference(アメリカ人の発音ではレフランス
という感じだが、日本人は通常リファレンスと発音している)とは参考書
のこと)というのはAPIの参考書という意味で、正式な名称ではありません。
正式な名称はAPI Specification(日本語では「API仕様」と訳されている)
です。
(私は「API仕様書」と訳すべきだと思うが個人的な意見はここでは
差し控え、以後「API仕様」と呼ぶことにする。)


では、007号で紹介した(J2SE 6の)API仕様のWebページを開いてみま
しょう。
次のWebページを開いてください。

http://java.sun.com/javase/ja/6/docs/ja/api/index.html


(あるいは、JDK 6のドキュメントのホームページ

http://java.sun.com/javase/ja/6/docs/ja/

から上のWebページにたどり着くこともできます。
この「JDK 6ドキュメント」のページには他にもいろいろとドキュメント
がリストされていますので、時間のある人は読んでみてください。)


このWebページは、3つのフレーム(区画)に分かれていますね。

左上のフレームにはパッケージのリストがあり、左下のフレームには
クラスのリストがあります。そして右側のフレームには最初は概要の
ページが開かれています。


たとえば、上記のCardLayoutについて調べたいときには、左下のフレーム
のクラスのリスト(アルファベット順に並んでいる)の中からCardLayout
を見つけて、それをクリックします。

そうすると、右側のフレーム(さっき概要のページが開かれていたところ)
にCardLayoutの説明のページが現れます。

上から順番に見ていきましょう。

まず最初に

--------------------------------------------------------
java.awt
クラス CardLayout
--------------------------------------------------------

と書いてあるのはCardLayoutがjava.awtパッケージに含まれていること、
クラスであることを示しています。(インターフェースの場合は、クラス
の代わりにインターフェースと書かれています。)

その下に

--------------------------------------------------------
java.lang.Object
     java.awt.CardLayout
--------------------------------------------------------

と書いてあるのはCardLayoutがObject(java.langパッケージにはいっ
ているクラス)のサブクラスであることを示しています。つまりクラス
の階層構造を示しています。

その下に

--------------------------------------------------------
すべての実装されたインタフェース:
LayoutManager, LayoutManager2, Serializable
--------------------------------------------------------

と書いてあるのは、CardLayoutがLayoutManagerとLayoutManager2と
Serializableという3つのインターフェースを実装していることを
示しています。
ただし、実はLayoutManager2はLayoutManagerを拡張(extends)し
たものなので、LayoutManager2を実装しているだけでLayoutManager
も実装したことになります。

その下の

--------------------------------------------------------
public class CardLayout
extends Object
implements LayoutManager2, Serializable
--------------------------------------------------------

は、このCardLayoutのクラス定義の部分を抽出したものです。

さらにその下にはCardLayoutの解説が続きます。

その下には表形式で「コンストラクタの概要」や「メソッドの概要」
が続きます。フィールドがあるクラスならば「フィールドの概要」
も並びますがCardLayoutにはありません。

スーパークラスから継承したものがあれば(CardLayoutの場合は
「クラス java.lang.Object から継承されたメソッド」がある)
それらもリストされます。(リストの中で下線が引かれているとこ
ろをクリックするとその部分の説明に飛びます。)

「メソッドの概要」の下には、さらに「コンストラクタの詳細」や
「メソッドの詳細」などが続きます。

あとは、どんなメソッドがあるのかご自分で調べてみてください。


なお、左下のフレームの中で斜体字になっているものがありますが、
斜体字はインターフェースを示しています。
また、Dateなどのように同じクラス名が複数並んでいることがあり
ますが、これはパッケージが異なるものです。
パッケージを指定してからクラスを探したいときは、左上のフレーム
の中からパッケージを指定(クリック)すると、左下のリストはその
パッケージ内のものだけになります。


では、今回はここまで。


(続く)



========================================================
◆ 02.文法解説 [continue文]
========================================================

[continue文]

ループの繰り返し処理の途中ですぐに次の繰り返し処理に入りたいときは

continue;

を使います。

ループが入れ子になって階層構造になっているときは、直近のループ
の中での次の繰り返し処理に入ることになります。
たとえば、

--------------------------------------------------------
int i = 0;
do {
   i = (int) (Math.random() * 10);
   System.out.println("(ここはdoループの中)");
   System.out.println(i + "で始めた数を増加させながら偶数を見つけます。");
   for (int n = 0; n < 10; n++) {
      System.out.println("(ここはforループの中)");
      if ((n + i) % 2 == 1) continue;
      System.out.println((n + i) + "は偶数です。");
   }
   System.out.println("(forループ終わり)");
} while (i != 5);
System.out.println("(doループ終わり)");
--------------------------------------------------------

とすると、このcontinue文が実行されると、その下の行の

System.out.println((n + i) + "は偶数です。");

は実行されず、いきなり次の繰り返しに進みます。
すなわち、n++が実行され、n < 10 がtrueであることが判定された上で

System.out.println("(ここはforループの中)");

の行の実行へと進んでいきます。

複数のループが入れ子になっているとき、すなわち複数のループに
よって階層構造になっているときに、どの階層のループの次の繰り
返しに進みたいのかを明示したい場合は、

continue  ラベル名;

という形式で指定します。

このラベル名を指定するときには、ちょっと注意が必要です。

前回のbreakはループの外に抜け出ることを意味しますから、
抜け出る階層はどのブロックの階層でもいいのですが、continue
はループを続行することを意味しますから、ループを抜け出すこと
はできません。したがって、一般のブロックを指定することはでき
ません。
continueでラベル名を指定するときにはループにつけたラベル名で
なければなりません。

ラベルというのは、文または複文(複合文)に名前をつけるもので、
たとえば、単文であれば

--------------------------------------------------------
ラベル名: 文;
--------------------------------------------------------

という形式でラベル名をつけます。
あるいは、複文であれば、ブロックの場合は

--------------------------------------------------------
ラベル名: {文; 文; 文; 文;}
--------------------------------------------------------

あるいは、ブロックを含むforループの場合は

--------------------------------------------------------
ラベル名: for (...;...;...) {文; 文; 文; 文;}
--------------------------------------------------------

あるいは、ブロックを含むwhileループの場合は

--------------------------------------------------------
ラベル名: while(...) {文; 文; 文; 文;}
--------------------------------------------------------

あるいは、ブロックを含むdo-whileループの場合は

--------------------------------------------------------
ラベル名: do {文; 文; 文; 文;} while(....);
--------------------------------------------------------

というような形式でラベル名をつけます。

そして、break文に指定できるラベル名は抜け出たい階層を示す
ものでなければならないため、ブロックまたは、ブロックを含む
ループにつけられたラベル名でなければなりません。単文につけ
られたラベル名は指定できません。

一方、continue文に指定できるラベル名はループにつけられた
ラベル名でなければなりません。一般のブロックや単文につけ
られたラベル名は指定できません。
つまり、continue文に指定できるラベル名は

--------------------------------------------------------
findEven: for (int i = 0; i < 10; i++) {
   ...
   if ((i%2) == 1) continue findEven;
   ...
}
--------------------------------------------------------

のようにつけます。上記のcontinue文ではfindEvenというラベル名
がつけられたforループ内の次の繰り返しに進むことになります。


そうすると、たとえば、

--------------------------------------------------------
int i = 0;
outerLoop: do {
   i = (int) (Math.random() * 10);
   System.out.println("(ここはdoループの中)");
   System.out.println(i + "で始めた数を増加させながら偶数を見つけます。");
   for (int n = 0; n < 10; n++) {
      System.out.println("(ここはforループの中)");
      if ((n + i) % 2 == 1) continue outerLoop;
      System.out.println((n + i) + "は偶数です。");
   }
   System.out.println("(forループ終わり)");
} while (i != 5);
System.out.println("(doループ終わり)");
--------------------------------------------------------

というソース・コードの場合は、
continue outerLoop;
が実行されると、outerLoopのラベル名がつけられたdoループ内の
次の繰り返しに進みますから、

i = (int) (Math.random() * 10);

の行から順番に実行されていくことになります。


(続く)



================================================
◆ 03.演習問題
================================================

上記のcontinue文の振る舞いを確認するために、下記のような
プログラムを作って実行してみてください。
そして、コンソールに出力される結果を確認し、なぜそのよう
な出力がされるのかを理解してください。
理解できない場合は、これまでの復習をしてください。

--------------------------------------------------------
public class ContinueTest {

   public static void main(String[] args) {
      System.out.println("[Example 1]");
      int i = 0;
      do {
         i = (int) (Math.random() * 10);
         System.out.println("(ここはdoループの中)");
         System.out.println(i + "で始めた数を増加させながら偶数を見つけます。");
         for (int n = 0; n < 10; n++) {
            System.out.println("(ここはforループの中)");
            if ((n + i) % 2 == 1) continue;
            System.out.println((n + i) + "は偶数です。");
         }
         System.out.println("(forループ終わり)");
      } while (i != 5);
      System.out.println("(doループ終わり)");

      System.out.println("\n[Example 2]");
      outerLoop: do {
         i = (int) (Math.random() * 10);
         System.out.println("(ここはdoループの中)");
         System.out.println(i + "で始めた数を増加させながら偶数を見つけます。");
         for (int n = 0; n < 10; n++) {
            System.out.println("(ここはforループの中)");
            if ((n + i) % 2 == 1) continue outerLoop;
            System.out.println((n + i) + "は偶数です。");
         }
         System.out.println("(forループ終わり)");
      } while (i != 5);
      System.out.println("(doループ終わり)");
   }

}
--------------------------------------------------------


(次回に続く。)



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      http://www.flsi.co.jp/Java_text/
★このメールマガジンは
     「まぐまぐ(http://www.mag2.com)」
 を利用して発行しています。
★バックナンバーは
      http://www.flsi.co.jp/Java_text/
 にあります。
★このメールマガジンの登録/解除は下記Webページでできます。
      http://www.mag2.com/m/0000193915.html
★このメールマガジンへの質問は下記Webページにて受け付けて
 います。わからない所がありましたら、どしどしと質問をお寄
 せください。
      http://www.flsi.co.jp/Java_text/
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━┛

Copyright (C) 2011 Future Lifestyle Inc. 不許無断複製