Back To Main

┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
┃ ◆Javaで珈琲ブレイク vol.022 01/01/03 ◆
┃……………………………………………………………………………………………
┃ [不定期] まぐまぐ ID=0000088576 Melma! ID=m00061296
┃……………………………………………………………………………………………
┃ 今回からご覧になる方は、バックナンバーご活用下さい
┃ http://javacafebreak.tripod.com
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

◆目次◆

■新年のご挨拶
■IDとパスワードの比較 Question 11 (a) (b)
■パスワードを変更するときは?
■forループを使った同じプロセスの処理 Question 11 (c) (d)
■isLoginフラッグを使ったロジック分岐処理 Question 11 (e)
■BankUserInterface.java (2.0101)


皆さん、明けましておめでとうございます。

Mr.Hackです。

■新年のご挨拶
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

早いもので、Javaで珈琲ブレイクを4月に創刊して以来8ヶ月がたちました。最初は、見切り発車的な要素が多々あったように思いますが、自分のJavaの知識の確認、プログラムの面白さ、また、コンピュータをいかに効率よく作動させることができるかというコンピューターサイエンスの紹介を目的に、一歩ずつ進んできました。ご紹介した知識が、皆さんにどれだけ受け入れて頂いているかは、未知数なところがありますが、良識な読者の方々からお礼・励ましのメールをいただいたりして、少しずつ手応えを感じつつあります。Javaで珈琲ブレイクは、珈琲ブレイクするような気楽な気持ちで読んでもらいたいなと思い名付けましたが、内容的には、他のメルマガに比べ結構難しい内容になっているかもしれません。しかしながら、何度も何度も読み返して頂き、いつか、いつも机の上にあるバイブルになればいいなと願っております。来期は、研究室でのアシスタントや学業と今まで以上に多忙な日々になりそうです。そのため、どこまで発行していけるかわかりませんが、愛読して頂いてる読者の方々のためにも、よりいっそうがんばっていこうと思います。今年もどうぞよろしくお願い致します。


それでは、今年もがんばって勉強していきましょう。皆さんにご紹介したEclipseに少しずつなれてきましたか?Eclipseの機能をちょくちょくご紹介していきますが、あくまでJava・コンピューターサイエンスを勉強するツールという位置づけで行きたいと思います。不幸にしてEclipseを起動できない環境の読者の方もJava・コンピューターサイエンスの勉強はできますから、がんばってついてきてくださいね。そして、Eclipse関係の記述は、いつかEclipseを動かせるだけのコンピューターをお持ち頂いた時に、じっくりまた、Javaで珈琲ブレイクを読み直してください。きっと得るところがあると思います。

前回は、BankUserInterface.javaをコピー&ペーストし、それを少しだけ修正してEclipseの機能をみてきました。今回は、実際にBankUserInterfaceクラスをみていきます。

まず、vol.012のBankUserInterface.javaをみてください。このクラスをvol.012のBankAccount.javaと一緒に実行すると、『仮想バンキングシステムへようこそ』のメッセージが表示された後、いきなり、メインメニューに入れました。しかしながら、これでは、プログラムを起動できさえすれば、誰でも、メインメニューを表示し、預け入れ、引き出し、残高参照ができてしまいますね。それを、ある特定のユーザー(権限を与えられた口座のユーザー)だけが、預金情報にアクセスできるようにしようというのが、今回です。vol.012後編のQuestion 11をもう一度みてください。

■IDとパスワードの比較 Question 11 (a) (b)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

■Question 11
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1.バンクアカウントのIDとパスワードを設定しなさい。仕様は下記の通り。

(a) BankAccountInterfaceクラスを実行した時、アカウントIDを入力してもらう
(b) 引き続きパスワードを入力してもらう
(c) パスワードが不正なら、もう一度入力してもらう
(d) もし、三回入力しても不正なら、終了する旨を伝え、プログラム終了
(e) もしパスワードが正しければ、メインメニューを表示し銀行取引を開始する

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

(a)、(b)は、権限があるかどうかというauthenticationをチェックするために、アカウントIDとパスワードをチェックするわけですが、プログラムのどこでチェックすればいいでしょう?プログラムが開始したことを示す、『仮想バンキングへようこそ』というメッセージが表示された後、権限が与えられたユーザーだけしかメインメニューを表示させなければいいので、その二つの間にパスワードチェックロジックを入れればいいですね。

ユーザーIDとパスワードをそれぞれコンソール上から得ます。

System.out.print(" ユーザーIDを入力してください: ");
String accountId = buffer.readLine().trim();
System.out.print(" パスワードを入力してください: ");
String userPassword = buffer.readLine().trim();

でいいですね。BufferedReaderクラスのインスタンスへの参照変数、bufferはすでに作成されているので、これを使います。trim()というメソッドが出てきましたね。これは、どこのクラスのメソッドですか?ドットが二つ以上ある一連の項はどのようにみればよかったのでしたっけ?

最初にドットを挟んだ左二つの項をみて、それが返すクラスのインスタンスの参照と最後のtrim()をみればいいのでしたね。buffer参照変数は、BufferReaderクラスのインスタンスをさしていましたので、API仕様でBuffereReaderクラスを引きます。そうしますと、readLine()はStringクラスのインスタンスへの参照を返すので、次のtrim()はStringクラスのメソッドだとわかります。

┌──────────────────────────────────┐
buffer.readLine() は、String型のインスタンスへの参照を返すので、
String stringLine = buffer.readLine() と同じ
よって、buffer.readLine().trim()は、stringLine.trim()と同じ。
└──────────────────────────────────┘


trim()は余分なwhite sapceをとってくれるので、万が一ユーザーが入力した後にスペースを入れても、そのスペース分をtrimし(取り)ます。trim()メソッドは同じく、trimしたStringを戻り値として返しますので、userPasswordが参照するオブジェクトは、String型です。ここまでは、もういいですね。ここは、超重要ですよ。いまだ、二つ以上のドットの扱い方をわからない人は、もうNGですよ。3度ほどカバーしてますからね。

さて、これで、accountIdとuserPasswordは、ユーザーが入力したString型の値を参照しています。これが、ユーザーから入力する前から(データベース等に)存在する(と仮定した)ID("MrHack")とパスワード("mRha9")が一致しているか、チェックしなければなりません。どうやってチェックしますか?

すでに存在しているaccoundIdとpasswordの文字列と、ユーザーが入力した文字列をそれぞれ比較すればいいですね。実際にデータベースはないですが、accountIdを"MrHack"、passwordを"mRha9"、そして、残高を0として、擬似的にBankAccountのインスタンスを生成して、その参照変数をbankAccountObjectとしたものが、すでにあります。これを利用して、accountIdである"MrHack"の文字列を取り出すのはどうでしょうか?

BankAccountクラスには、getAccountId()というメソッドがありますね。これは、戻り値にString型のaccountIdの値を返します。これでアカウントIDが取り出せたら、あとは、String型のequals()メソッドをつかって、比較できそうですね。例えば、こんな感じになります。

┌──────────────────────────────────┐
// accountIdはユーザーが入力した値
accountId.equals(bankAccountObject.getAccountId());

または、

bankAccountObject.getAccountId().equals(accountId);
└──────────────────────────────────┘

equals()の引数、accountIdはユーザーが入力した文字列です。すでに存在するアカウントIDはgetAccoundId()で取り出します。

String#equals()の戻り値は、boolean型ですので、ユーザー入力文字列と、すでに存在する文字列とが一致していればtrueを返します。

では、パスワードはどう比較しましょう?アカウントと同じく、

┌──────────────────────────────────┐
// userPasswordはユーザーが入力した値
bankAccountObject.getPassword().equals(userPassword);
└──────────────────────────────────┘

としますか?いい線ですね。しかし、あいにく、getPassword()メソッドは、BankAccountクラスには無いのです。思い出してください。なぜ作らなかったのでしたっけ?クライアント側がパスワードを得ることができるというのは、BankUserInterfaceクラス側でパスワードがわかってしまう、ということでしたね。何らかの方法で、あるユーザーのインスタンスを参照できて、getPassword()が存在していれば、そのメソッドを使って得ることができてしまうわけですから。そのために、getPassword()はもうけないで、クライアント側は、あくまでユーザーからの文字列が、インスタンス内に保持されているパスワード文字列が一致しているかどうかを、BankAccountクラス側のequalsToPassword()メソッドを使って、問い合わせるだけにしたのです。

┌──────────────────────────────────┐
bankAccountObject.equalsToPassword(UserPassword);
└──────────────────────────────────┘

こうすれば、クライアントにオブジェクト内に保持しているパスワードを渡さずに、パスワード比較という目的だけを達成できますね。クライアントはパスワードが何かを知る必要はなく、単にユーザーIDが適切かどうかさえわかればいいのです。ここは、ちょっとしたポイントですね。


■パスワードを変更するときは?
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ところで余談ですが、クライアント側がパスワードを知らないとパスワード変更ができないんじゃないの?とお思いの方もいらっしゃるかもしれません。クライアント側がパスワードの中身を知らなくても、もちろん、変更はできます。まず、ユーザーからのパスワードと比較し一致すれば、上記のequalsToPassword()はtrueを返すので、クライアントのBankUserInterfaceクラスでは、

if (bankAccountObject.equalsToPassword(userPassword)) {
bankAccountObject.setPassword(newPassword);
}

とできますね。bankAccountObjectが参照しているオブジェクト内のパスワードは知る必要はなく、一致するか否かだけがわかればよく、一致した場合だけ、setPasswordで上書きしてあげればいいことになります。これは、ユーザーがパスワードを忘れてしまって、リセットするときにも有効な方法ですね。


■forループを使った同じプロセスの処理 Question 11 (c) (d)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ユーザーがそれぞれ入力した、IDとパスワードを、equals()とequalsToPassword()で比較して、それぞれ一致した場合(それぞれのメソッドがtrueを返した場合)、めでたく、正規のユーザーだとわかりますから、プログラムのロジックを、メインメニュー表示へ移します。もし、正規のユーザーでない場合は、ログイン失敗です。ここまでを、Pseudo Code(スード・コード、疑似コード)で書くと以下のようになります。


System.out.print(" ユーザーIDを入力してください: ");
String accountId = buffer.readLine().trim();
System.out.print(" パスワードを入力してください: ");
String userPassword = buffer.readLine().trim();

if (bankAccountObject.getAccountId().equals(accountId) かつ
bankAccountObject.equalsToPassword(UserPassword)) {
//ログイン成功・メインメニュー表示のロジックに移動
}
else {
//ログイン失敗・プログラムを終了する
}

ですね。しかし、ログイン失敗した場合でも、Question 11は、

Question 11
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
(c) パスワードが不正なら、もう一度入力してもらう
(d) もし、三回入力しても不正なら、終了する旨を伝え、プログラム終了
(e) もしパスワードが正しければ、メインメニューを表示し銀行取引を開始する
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

なので、(c)、(d)から、3回ユーザーにIDとパスワードを入力するチャンスを与えていますね。三回、同じようにユーザーに入力を促したいわけですから、上記のPseudo Codeのプロセスを3回繰り返せば良さそうです。for ループを使えば、

// 同じプロセスを三回繰り返す
for (int i = 0; i < 3: i++) {

System.out.print(" ユーザーIDを入力してください: ");
String accountId = buffer.readLine().trim();
System.out.print(" パスワードを入力してください: ");
String userPassword = buffer.readLine().trim();

if (bankAccountObject.getAccountId().equals(accountId) かつ
bankAccountObject.equalsToPassword(UserPassword)) {
//ログイン成功・メインメニュー表示のロジックに移動
break;
}
else {
//ログイン失敗・もう一度繰り返す
}
}

となりますね。ログインに成功した場合はいいですね。breakキーワードを使って、for ループを抜け出します。breakを実行すると、それ以下は実行されず、forループを抜け出します。ここで一つ確認ですが、breakに似たような役割をするcontinueというキーワードがありますが、これは、breakのようにcontinue以下に続く文を実行しませんが、breakとちがって、ループを終了せずにもう一度ループを開始します。

┌──────────────────────────────────┐
breakは、以下に続く文を実行せずに、ループを終了。
continueは、以下に続く文を実行せずに、ループをもう一度実行。
└──────────────────────────────────┘

失敗した場合は、break文もcontinue文もないので、iが3になるまでfor ループが実行されますね。そして、iが3になったとき自動的にfor ループを抜け出します。


■isLoginフラッグを使ったロジック分岐処理 Question 11 (e)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

ここで注意しないといけないのは、このままだと、次のプロセスで、ログインが成功したのか、しなかったのかという手がかりが残っていない、ことです。

┌──────────────────────────────────┐
iの値が手がかりにならない?
└──────────────────────────────────┘

いい線ですね。しかしながら、ここでiは、for ループの中で宣言していますから、スコープ外では使えません(ここで、スコープとは、ブロック、{}を意味すると考えてください)。

┌──────────────────────────────────┐
それでは、iをfor ループの外で宣言したら?
└──────────────────────────────────┘

これなら、いけそうですね。

┌──────────────────────────────────┐
int i = 0;

for (;i < 3; i++) {
//ログイン認証のロジック
}

if (i < 3) {
//メインメニュー表示のロジック
}
else {
//ログイン失敗なので、プログラム終了
}
└──────────────────────────────────┘

iが3まで達しないとき、つまり、3に達する前にbreak文でfor ループを抜けたとき(ログイン成功の時)は、次のif節でiを使ってチェックできます。

しかしながら、iをfor ループの外で使うのは、あまりよろしくありません。といいますのは、iはあくまで、indexとして使うという、暗黙の約束事があるから、indexをiと省略できるのです。Javaでは、変数はmeaningful(意味のある)言葉を使わなければいけませんでしたね。それは、後からみたときに省略語を使うと、理解しづらいからです。またここでは、メインメニューを表示するか否かのロジックにiを使っています。これは、iという変数の目的外の行為ですね。あるいは、上記のfor ループとif文のあいだに、長いロジックがあり、謝ってiを他のfor ループで使ってしまう(iをインクリメント(増加)させてしまう)かもしれません。そうなると、認証後のメインメニューを表示するかどうかのif文で、iを使うのは危険ですね。

以上を考えると、ログインが成功したか否かを次のロジックに通知する手段として、フラグ(flag)を用いるのがいいと思います。フラグとは、オン・オフを操作するスイッチのような役割をするものです。たとえば、boolean型のisLoginという変数をフラグとしてもうけ、

┌──────────────────────────────────┐
boolean isLogin = false;

if (bankAccountObject.getAccountId().equals(accountId) かつ
bankAccountObject.equalsToPassword(UserPassword)) {
//ログイン成功・メインメニュー表示のロジックに移動
isLogin = true;
break;
}
else {
//ログイン失敗・もう一度繰り返す
isLogin = false;
}
└──────────────────────────────────┘

とするのです。こうすれば、以下で、ログインしているかしていないかを判断しやすくなります。メニュー表示するかしないかのif文のロジックは、isLoginを用いて、

┌──────────────────────────────────┐
if (isLogin) {
//メインメニュー表示のロジック
}
else {
//ログイン失敗なので、プログラム終了
}
└──────────────────────────────────┘

となりますね。以上をvol.12であつかったBankUserInterface.javaに付け加えると、下記のversion 2.0101になります。 下記のバージョンを参考にしながら、前回のBankUserInterface.javaを書き換えておきましょう。年が変わったので、メジャーバージョンも一つあがりました。メジャーバージョン+月日という表記は、年が変わると一つバージョンがアップするという短所がありますが、バージョン表記には、とても便利でしたね。皆さんももう実践されているかと思います。

BankUserInterfaceクラスをEclipseかCpadで実行するとIDとパスワードを求められます。適切なID("Mrhack")に対する適切なパスワード("mRha9")を入力しないとログインできないことと、3回間違えると、ログインできないことを確認してください。

次回は、Eclipseの簡単な機能設定(Preferences)と、紙面があれば、実用で使えるユニットテスト(単体テスト)を勉強していきます。皆さんはもうユニットテストという概念は、vol.006で紹介しましたのでご存じかと思いますが、もっと便利にテストできるJUnitというツールを紹介していこうと思います。

それでは、またお会いしましょう。

Mr.Hack


■BankUserInterface.java (2.0101)
━━━━━━━━━━━━ ここから ━━━━━━━━━━━━━━━━━━
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.IOException;

/**
* コンソールベース・ユーザー・インターフェイス。
* Main()がユーザーインターフェイスのロジックのプロセスを担当。
* version 1.731からログインIDとパスワードをチェックし、適切ならメインメニューに進む。
* 簡略化のため、データ構造・データベースは考慮していない。
* ユーザーからの入力がYesかNoかを判断するメソッドisContinued()がある。
*
* @author Mr.Hack
* @version 2.0101
* @since 1.0607
*/
public class BankUserInterface {

/**
* コンソールからの文字列がy、Y、n、Nかをチェックする。y, Yをユーザーが入力したとき
* <code>false</code>を返し、n、Nのとき、<code>true</code>を返し、それ以外の場合、
* メッセージをコンソールに出力し、再度ユーザーに入力を促す。
*
* @since 1.0609
* @return true ユーザーがnかNを入力した時
* false ユーザーがyかYを入力した時
* @exception IOException コンソールからの入力時例外が発生した時
*/
private static boolean isContinued() throws IOException {
BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));
while (true) {
String answer = buffer.readLine();
if (answer.equals("y") || answer.equals("Y")) {
return false;
}
else if (answer.equals("n") || answer.equals("N")){
return true;
}
else {
System.out.print(" 入力が正しくありません。YかNを入力して下さい:");
}
}
}


/**
* コンソールベースのユーザーインターフェイスを実行するメインメソッド。
* 下記の手順でプロセスを実行する。
*
* <PRE>
* 1.ユーザーにユーザーIDを求める。
* 2.ユーザーにパスワードを求める。
* 3.ユーザーIDとパスワードが適切ならば、4.以下を実行。不適切ならば、6を実行。
* 4.メインメニューを表示する。
* 5.ユーザーがメニュー番号を選ぶと、各ロジック(預金・引き出し・残高参照・終了)を実行。
* 終了を選んだとき、6を実行。
* 6.プログラムが終了する。
*
* また、メニュー番号以外をユーザーが選ぶともう一度適切な入力を促す。
* </PRE>
*
* @param args コマンドライン引数。使用せず。
*/
public static void main(String[] args) {
System.out.println(" _________________________________________");
System.out.println("");
System.out.println(" 仮想バンキングへようこそ ");
System.out.println(" _________________________________________");
System.out.println("");

//コンソールからの入力ストリームを確立し、バッファーとして蓄えられるようにする
BufferedReader buffer = new BufferedReader(new InputStreamReader(System.in));

boolean nextTransaction = true; //次の取引をするかどうかのフラッグ
boolean printMenu = true; // mainメニューを表示するかどうかのフラッグ
boolean isLogin = false; //login成功かどうかのフラグ

// BankAccountの初期化
BankAccount bankAccountObject = new BankAccount("MrHack", "mRha9");

// IOExceptionをまとめてキャッチする
try {
// IDに対すパスワードをチェックする
for (int i = 0; i < 3; i++) {
System.out.println("");
System.out.print(" ユーザーIDを入力してください: ");
String accountId = buffer.readLine().trim();
System.out.print(" パスワードを入力してください: ");
String userPassword = buffer.readLine().trim();
if (bankAccountObject.getAccountId().equals(accountId) &&
bankAccountObject.equalsToPassword(userPassword)) {
isLogin = true;
break;
}
else {
System.out.println("");
System.out.println(" ユーザーIDに対するパスワードが一致しません");
isLogin = false;
}
}
//適切なパスワードが入力された場合、ログイン
if (isLogin) {
// nextTransactionがtrueの間(isContinued()でtrue)、
// ループする(ユーザーがEnterキーをタイプした場合はループ)
while (nextTransaction) {
if (printMenu) {
System.out.println(" _________________________________________");
System.out.println("");
System.out.println(" [1] 預金をする");
System.out.println(" [2] 引き出しをする");
System.out.println(" [3] 残高を参照する");
System.out.println(" [4] 取引を終了する");
System.out.println(" _________________________________________");
System.out.println("");
System.out.print(" ご希望のお取引番号をお選び下さい : ");
}

char choice = 0;

try {
choice = buffer.readLine().charAt(0);
}
// String#charAt()がスロー、ユーザーが何も入力しないでEnterキーを押したとき
catch (IndexOutOfBoundsException e) {
System.out.print(" 番号が正しくありません。もう一度入力して下さい:");
printMenu = false;
continue;
}
// BufferedReader#readLine()がスロー
catch (IOException e) {
throw e;
}

switch (choice) {
//預金ロジック
case '1':
int balance = (int) bankAccountObject.getBalance();
System.out.println("");
System.out.println(" あなたの現在の残高は" + balance + "円です");
System.out.println("");
System.out.print(" 預金額を入力して下さい: ");
// ユーザーが適切な数値(数値でマイナスではない値)を入力するまでループ
while (true) {
try {
// double型の値を取得。数値でない場合、NumberFormatExceptionをスロー
double depositAmount = Double.parseDouble(buffer.readLine());
bankAccountObject.deposit(depositAmount);
balance = (int) bankAccountObject.getBalance();
System.out.println("");
System.out.println(" " + (int) depositAmount + "円を預金しました");
System.out.println(" あなたの現在の残高は" + balance + "円です");
System.out.println("");
break; // 例外が発生しなければ、whileループを抜ける
}
// Double#parseDouble()がスロー
catch (NumberFormatException e) {
System.out.print(" 入金額が正しくありません。もう一度入力して下さい : ");
}
// BankAccount#deposit()がスロー
catch (IllegalArgumentException e) {
System.out.println("");
System.out.print(e.getMessage() + "もう一度入力して下さい : ");
}
// BufferedReader#readLine()がスロー、ここは例外のバイパスをしてもよい
catch (IOException e) {
throw e;
}
}
printMenu = true;
break;
//引き出しロジック
case '2':
balance = (int) bankAccountObject.getBalance();
System.out.println("");
System.out.println(" あなたの現在の残高は" + balance + "円です");
System.out.println("");
// 考え方は、預金の時と同じ
while (true) {
try {
double withdrawAmount = Double.parseDouble(buffer.readLine());
bankAccountObject.withdraw(withdrawAmount);
balance = (int) bankAccountObject.getBalance();
System.out.println("");
System.out.println(" " + (int) withdrawAmount + "円を引き出しました");
System.out.println(" あなたの現在の残高は" + balance + "円です");
System.out.println("");
break;
}
catch (NumberFormatException e) {
System.out.print(" 引出額が正しくありません。もう一度入力して下さい : ");
}
catch (IllegalArgumentException e) {
System.out.print(" " + e.getMessage() + "。もう一度入力して下さい : ");
}
catch (IOException e) {
throw e;
}
}
printMenu = true;
break;
//残高参照ロジック
case '3':
balance = (int) bankAccountObject.getBalance();
System.out.println("");
System.out.println(" あなたの現在の残高は" + balance + "円です");
System.out.println("");
printMenu = true;
break;
//終了ロジック
case '4':
System.out.println("");
System.out.print(" お取引を終了しますか?(終了:y/続行:n) : ");
//次の取引を続けるかどうかのフラグを設定
nextTransaction = isContinued();
printMenu = true;
break;
// 1, 2, 3, 4以外のキャラクタ
default :
System.out.print(" 番号が正しくありません。もう一度入力して下さい:");
printMenu = false;
}
}
}
// ログインできなかった場合
else {
System.out.println(" プログラムを終了致します");
}
}
// BufferedReader#readLine()がスロー
catch (IOException e) {
System.out.println(" 不正な処理を行ったため、プログラムを終了します");
return;
}
finally {
// ストリームを閉じる
try {
buffer.close();
}
catch (IOException e) {
System.out.println(" ストリームをクローズ出来ませんでした");
}
}
System.out.println("");
System.out.println(" ご利用ありがとうございました");
}
}
━━━━━━━━━━━━ ここまで ━━━━━━━━━━━━━━━━━━


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
◆Javaで珈琲ブレイク◆
───────────────────────────────────
皆様からの激励・批判のメールをお待ちしております。
【発行者】 Mr.Hack - javacafebreak@hotmail.com
【掲示板】 https://javacafebreak.tripod.com/cafe_entrance.html
※質問は上記の掲示板からどうぞ。
【サイト】 http://javacafebreak.tripod.com
【発行数】 まぐまぐ[1003] Melma[158]
【解除】http://javacafebreak.tripod.com
※解除は上記のサイトからいつでもできます。
────────────────────────────────────
(c)2002 MR.HACK ALL RIGHTS RESERVED
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

© 2002 MR.HACK ALL RIGHTS RESERVED