広告

■□■□■□■□■□■□■□■□■□■□■□■□■□■□■
                      2010年03月17日

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

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


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


━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
(2009年11月開講コース)018号
 当記事はvol.018のリバイバル(revival)版です。
 vol.018の内容を最新のEclipse、Javaに基づいて書き直しています。
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━


========================================================
◆ 01.ファイルへの入出力とメニュー・バー
========================================================

それでは、前回のソースコードの変更箇所を解説していきます。

とりあえず、HumanInfoWindowのソースコード全体を再度書き出し
ておきましょう。

-------------------------------------------------------
package jp.co.flsi.lecture.ui;

import java.awt.Button;
import java.awt.Choice;
import java.awt.FileDialog;
import java.awt.Frame;
import java.awt.Label;
import java.awt.Menu;
import java.awt.MenuBar;
import java.awt.MenuItem;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

import jp.co.flsi.lecture.human.HumanInfo;
import jp.co.flsi.lecture.util.CalendarConverter;

public class HumanInfoWindow extends Frame {
   private HumanEventListener eventListener = new HumanEventListener();
   private HumanActionListener actionListener = new HumanActionListener();
   private Label labelDateOfBirth = new Label();
   private Choice nengoChoice = new Choice();
   private Label labelYear = new Label();
   private Label labelMonth = new Label();
   private Label labelDay = new Label();
   private Label labelName = new Label();
   private Label labelResponse = new Label();
   private Button instanceButton = new Button();
   private Label labelAge = new Label();
   private TextField textFieldYear = new TextField();
   private TextField textFieldMonth = new TextField();
   private TextField textFieldDay = new TextField();
   private TextField textFieldName = new TextField();
   private HumanInfo humanInfo = null;
   private CalendarConverter calendarConverter = null;
   private String filePathName;  // 060910
   private BufferedWriter out;  // 060910
  
   class HumanEventListener implements WindowListener {
      public void windowActivated(WindowEvent e) {};
      public void windowClosed(WindowEvent e) {
         System.exit(0);
      };
      public void windowClosing(WindowEvent e) {
         Frame frame = (Frame)e.getSource();
         frame.setVisible(false);
         frame.dispose();
      };
      public void windowDeactivated(WindowEvent e) {};
      public void windowDeiconified(WindowEvent e) {};
      public void windowIconified(WindowEvent e) {};
      public void windowOpened(WindowEvent e) {};
   };
  
   class HumanActionListener implements ActionListener {
      public void actionPerformed(ActionEvent e) {
         Button button = (Button)e.getSource();
         HumanInfoWindow window = (HumanInfoWindow)button.getParent();
         int nengoIndex = nengoChoice.getSelectedIndex();
         int year = Integer.parseInt(window.getTextFieldYear().getText());
         int month = Integer.parseInt(window.getTextFieldMonth().getText());
         int day = Integer.parseInt(window.getTextFieldDay().getText());
         HumanInfo human = window.getHumanInfo();
         CalendarConverter calendarConverter = window.getCalendarConverter();
         if (nengoIndex < 4) {
            calendarConverter.setJaNameOfEra(nengoIndex);
            calendarConverter.setJaYear(year);
            calendarConverter.convertJaCal2GreCal();
            year = calendarConverter.getGreYear();
         }
         human.setDateOfBirth(year, month, day);
         human.setName(window.getTextFieldName().getText());
         window.getLabelResponse().setText(human.getAge() + "歳");
      };
   };
  
   class HumanMenuActionListener implements ActionListener {
      private MenuItem menuItem;
      private HumanInfoWindow window;
      public HumanMenuActionListener(HumanInfoWindow window) {
         this.window = window;
      };
      public void actionPerformed(ActionEvent e) {
         menuItem = (MenuItem)e.getSource();
         if ("終了".equals(menuItem.getLabel())) {
            window.setVisible(false);
            window.dispose();
         }
         else if ("ファイルを開く".equals(menuItem.getLabel())) { // 060910
            FileDialog fDialog = new FileDialog(this.window, "開く", FileDialog.LOAD); // 060910
            fDialog.setVisible(true); // 060910
            filePathName = fDialog.getDirectory() + fDialog.getFile(); // 060910
            try { // 060910
               BufferedReader in = new BufferedReader(new FileReader(filePathName)); // 060910
               window.getTextFieldName().setText(in.readLine()); // 060910
               window.nengoChoice.select(Integer.parseInt(in.readLine())); // 060910
               window.getTextFieldYear().setText(in.readLine()); // 060910
               window.getTextFieldMonth().setText(in.readLine()); // 060910
               window.getTextFieldDay().setText(in.readLine()); // 060910
               window.labelResponse.setText(in.readLine()); // 060910
               in.close(); // 060910
            } catch (IOException e1) { // 060910
               System.out.println(e1); // 060910
            } // 060910

         } // 060910
         else if ("ファイルを閉じる".equals(menuItem.getLabel())) { // 060910
            try { // 060910
               out.close(); // 060910
            } catch (IOException e1) { // 060910
               System.out.println(e1); // 060910
            } // 060910
         } // 060910
         else if ("名前をつけて保存".equals(menuItem.getLabel())) { // 060910
            FileDialog fDialog = new FileDialog(this.window, "名前をつけて保存", FileDialog.SAVE); // 060910
            fDialog.setVisible(true); // 060910
            filePathName = fDialog.getDirectory() + fDialog.getFile(); // 060910
            try { // 060910
               out = new BufferedWriter(new FileWriter(filePathName, false)); // 060910
               out.write(window.getTextFieldName().getText()); out.newLine(); // 060910
               out.write(Integer.toString(window.nengoChoice.getSelectedIndex())); out.newLine(); // 060910
               out.write(window.getTextFieldYear().getText()); out.newLine(); // 060910
               out.write(window.getTextFieldMonth().getText()); out.newLine(); // 060910
               out.write(window.getTextFieldDay().getText()); out.newLine(); // 060910
               out.write(window.labelResponse.getText()); // 060910
               out.close(); // 060910
            } catch (IOException e1) { // 060910
               System.out.println(e1); // 060910
            } // 060910
         } // 060910
         else if ("上書き保存".equals(menuItem.getLabel())) { // 060910
            try { // 060910
               out = new BufferedWriter(new FileWriter(filePathName, false)); // 060910
               out.write(window.getTextFieldName().getText()); out.newLine(); // 060910
               out.write(Integer.toString(window.nengoChoice.getSelectedIndex())); out.newLine(); // 060910
               out.write(window.getTextFieldYear().getText()); out.newLine(); // 060910
               out.write(window.getTextFieldMonth().getText()); out.newLine(); // 060910
               out.write(window.getTextFieldDay().getText()); out.newLine(); // 060910
               out.write(window.labelResponse.getText()); // 060910
               out.close(); // 060910
            } catch (IOException e1) { // 060910
               System.out.println(e1); // 060910
            } // 060910
         } // 060910
      };
   };
  
   public HumanInfoWindow()  {
      super();
      setTitle("人事情報");
      setSize(400, 180);
      setLayout(null);
      Menu fileMenu = new Menu("ファイル");
      Menu editMenu = new Menu("編集");
      MenuItem openMItem = new MenuItem("ファイルを開く");
      MenuItem closeMItem = new MenuItem("ファイルを閉じる");
      MenuItem nmsaveMItem = new MenuItem("名前をつけて保存");
      MenuItem saveMItem = new MenuItem("上書き保存");
      MenuItem exitMItem = new MenuItem("終了");
      fileMenu.add(openMItem);
      fileMenu.add(closeMItem);
      fileMenu.add(nmsaveMItem);
      fileMenu.add(saveMItem);
      fileMenu.add(exitMItem);
      MenuItem clearMItem = new MenuItem("データ・クリア");
      editMenu.add(clearMItem);
      MenuBar menuBar = new MenuBar();
      menuBar.add(fileMenu);
      menuBar.add(editMenu);
      setMenuBar(menuBar);
      labelDateOfBirth.setBounds(10, 60, 80, 20);
      nengoChoice.setBounds(120, 60, 80, 20);
      nengoChoice.add("明治");
      nengoChoice.add("大正");
      nengoChoice.add("昭和");
      nengoChoice.add("平成");
      nengoChoice.add("西暦");
      getTextFieldYear().setBounds(210, 60, 40, 20);
      labelYear.setBounds(255, 60, 20, 20);
      getTextFieldMonth().setBounds(290, 60, 20, 20);
      labelMonth.setBounds(315, 60, 20, 20);
      getTextFieldDay().setBounds(350, 60, 20, 20);
      labelDay.setBounds(375, 60, 20, 20);
      labelName.setBounds(10, 90, 60, 20);
      getTextFieldName().setBounds(70, 90, 320, 20);
      instanceButton.setBounds(120,120, 160, 20);
      labelAge.setBounds(10, 150, 100, 20);
      getLabelResponse().setBounds(110, 150, 100, 20);
      add(labelDateOfBirth);
      add(nengoChoice);
      add(getTextFieldYear());
      add(labelYear);
      add(getTextFieldMonth());
      add(labelMonth);
      add(getTextFieldDay());
      add(labelDay);
      add(labelName);
      add(getTextFieldName());
      add(instanceButton);
      add(labelAge);
      add(getLabelResponse());
      labelDateOfBirth.setText("生年月日:");
      labelYear.setText("年");
      labelMonth.setText("月");
      labelDay.setText("日");
      labelName.setText("氏名:");
      instanceButton.setLabel("年齢計算");
      labelAge.setText("現在の年齢:");
      instanceButton.addActionListener(actionListener);
      HumanMenuActionListener menuActionListener = new HumanMenuActionListener(this);
      openMItem.addActionListener(menuActionListener); // 060910
      closeMItem.addActionListener(menuActionListener); // 060910
      nmsaveMItem.addActionListener(menuActionListener); // 060910
      saveMItem.addActionListener(menuActionListener); // 060910
      exitMItem.addActionListener(menuActionListener);
      addWindowListener(eventListener);
      setVisible(true);
   }

   public TextField getTextFieldYear() {
      return textFieldYear;
   }

   public TextField getTextFieldMonth() {
      return textFieldMonth;
   }

   public TextField getTextFieldDay() {
      return textFieldDay;
   }

   public TextField getTextFieldName() {
      return textFieldName;
   }

   public Label getLabelResponse() {
      return labelResponse;
   }
  
   public HumanInfo getHumanInfo( ) {
      if (humanInfo == null) humanInfo = new HumanInfo();
      return humanInfo;
   }
  
   public CalendarConverter getCalendarConverter( ) {
      if (calendarConverter == null) calendarConverter = new CalendarConverter();
      return calendarConverter;
   }
  
   public static void main(String[] args) {
      HumanInfoWindow frame = new HumanInfoWindow();
   }

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

43行目
-------------------------------------------------------
private String filePathName;  // 060910
-------------------------------------------------------
は、ファイルのパス(ファイルの所在場所とファイル名を表現できる
ように、ディレクトリー名とファイル名をつなぎ合わせたもの)を入
れるための、String型の変数filePathNameを宣言しています。
この変数は、ファイルからの読み込み時には入力ファイルの名前を、
ファイルへの書き込み時には出力ファイルの名前を入れておくものです。

この変数がどこで使われているのか確認したいときは、Eclipseで下記の
ように操作します。

┌補足─────────────────────────┐
Eclipseで変数やメソッドの出現箇所を調べたいときは、次のように
操作します。

たとえば、HumanInfoWindowの中で、filePathNameという変数がどこ
で使われている(出現している)か調べることにしましょう。

(1)HumanInfoWindowのソースコードの中で、どこかのfilePathName
(たとえば上記の43行目のfilePathName)をクリックします。すると
filePathNameの文字列のバックグラウンド(背景)が着色されますね。
(Eclipseのツール・バーの中で、「出現箇所のマークのトグル」スイ
ッチ(黄色いマーカー・ペンで線を引いているようなアイコンのボタン)
がONになっていること(ボタンが押されてへっこんでいる状態)が前提に
なります。出現箇所を調べる操作がうまくいかないときは、このトグル・
スイッチを何度かクリックし直してみてください。)

(2)右端の(縦長の)スクロール・バーのさらに右側にいくつか黄緑色や
水色の縞模様のようなマークが付いていることを確認してください。
このマークは、この位置にfilePathNameという変数名が出現していること
を意味します。

(3)そのマークの位置までスクロール・バーのスライダーをすべらして
スライダーの真ん中を黄緑色や水色の縞模様のマークに合わせます。
そうすると、その出現箇所の位置までスクロールしますので、
filePathNameの出現箇所が見つかります。
(filePathNameのバックグラウンドが黄緑色や水色なのですぐにわか
ります。ちなみに、書き込み出現箇所(変数への代入が行われている
所、または変数の宣言)が黄緑色、その他の出現箇所は水色になって
います。)

こうして出現箇所を追っかけていって、filePathNameがどのように
使われているのかを調べます。
└───────────────────────────┘

まずは、出現箇所を自分で調べて、どのように使われているのか考えて
みてください。
あとで解説します。

次に、44行目
-------------------------------------------------------
private BufferedWriter out;  // 060910
-------------------------------------------------------
は、前回お話したBufferedWriterのインスタンスを代入するための変数
outを宣言しています。
これも、まずは出現箇所を自分で調べて、どのように使われているのか
考えてください。
これも、あとで解説します。

次に218行目〜221行目
-------------------------------------------------------
openMItem.addActionListener(menuActionListener); // 060910
closeMItem.addActionListener(menuActionListener); // 060910
nmsaveMItem.addActionListener(menuActionListener); // 060910
saveMItem.addActionListener(menuActionListener); // 060910
-------------------------------------------------------
は、それぞれ「ファイルを開く」、「ファイルを閉じる」、「名前をつ
けて保存」、「上書き保存」のMenuItem(メニュー項目)にイベント・
リスナーを登録(addActionListener()メソッド)していますね。
前回説明した
-------------------------------------------------------
exitMItem.addActionListener(menuActionListener);
-------------------------------------------------------
と同様なので、解説不要だと思います。変数menuActionListenerで表され
ているイベント・リスナーのクラスはHumanMenuActionListenerでした。

変数がどのように定義(宣言)されていたのかわからなくなったときは、
下記のような操作によって、簡単に調べられます。

┌補足─────────────────────────┐
Eclipseで変数やメソッドやクラスの定義(宣言)を調べたいときは、
次のように操作します。

たとえば、HumanInfoWindowの中の221行目のmenuActionListenerという
変数がどこでどのように定義されているのか調べることにしましょう。

(1)HumanInfoWindowのソースコードの中で、221行目のmenuActionListener
をクリックします。
するとその文字列のバックグラウンド(背景)が水色になりますね。

(2)続いてF3キーを押します。すると、自動的にその変数の定義(宣言)文
に飛んで、定義箇所の色が反転します。
(別のファイルで定義されているときは、別のファイルが自動的に開かれ、
その中の定義箇所に飛んでくれます。)

ちなみに、F3の代わりにF2キーを押すと、その変数やメソッドやクラスに
対する簡単な解説文が現れます。(例えば224行目のsetVisibleをクリック
してからF2キーを押してみて下さい。)
特にメソッドがどういう働きをしていて、どんな引数や戻り値を持っている
かを調べるときに役立ちます。(ただし、英文です。)
└───────────────────────────┘

では、そのイベント・リスナーHumanMenuActionListenerの中の変更箇所を
見ていきましょう。

96行目〜113行目
-------------------------------------------------------
else if ("ファイルを開く".equals(menuItem.getLabel())) { // 060910
   FileDialog fDialog = new FileDialog(this.window, "開く", FileDialog.LOAD); // 060910
   fDialog.setVisible(true); // 060910
   filePathName = fDialog.getDirectory() + fDialog.getFile(); // 060910
   try { // 060910
      BufferedReader in = new BufferedReader(new FileReader(filePathName)); // 060910
      window.getTextFieldName().setText(in.readLine()); // 060910
      window.nengoChoice.select(Integer.parseInt(in.readLine())); // 060910
      window.getTextFieldYear().setText(in.readLine()); // 060910
      window.getTextFieldMonth().setText(in.readLine()); // 060910
      window.getTextFieldDay().setText(in.readLine()); // 060910
      window.labelResponse.setText(in.readLine()); // 060910
      in.close(); // 060910
   } catch (IOException e1) { // 060910
      System.out.println(e1); // 060910
   } // 060910

} // 060910
-------------------------------------------------------
は、「ファイル」メニューの「ファイルを開く」メニュー項目を選択した
ときに実行される内容を記したものです。

96行目のif文はvol.016で説明したif ("終了".equals(menuItem.getLabel()))
と同じパターンですから、説明しなくてもおわかりですね。

97行目
-------------------------------------------------------
FileDialog fDialog = new FileDialog(this.window, "開く", FileDialog.LOAD); // 060910
-------------------------------------------------------
は、ファイル・ダイアログのインスタンスを生成しています。FileDialogの
コンストラクターの第一引数には、このファイル・ダイアログの親のウィン
ドウを指定する必要があります。

ここでは、this.windowが指定されていますが、このthisはこの
HumanMenuActionListenerのインスタンス自身を表し、このthis.windowのwindowは
HumanMenuActionListenerのインスタンスの属性であり、86行目で宣言されていて、
87行目のコンストラクターで引数として値が取り込まれるようになっていますね。

これは、217行目でHumanMenuActionListenerのインスタンスを生成しているときに
HumanInfoWindowのthisが渡されているものです・・・ということは以前お話しま
したね。

第二引数は、ファイル・ダイアログのタイトル・バーに表示する文字列を指定します。
つまり、タイトルを指定するわけです。ここでは「開く」という文字列をタイトルに
指定しています。

最後の引数は、ファイル・ダイアログのモードを指定します。このモードは
FileDialog.LOAD(ファイルを取り込むモード)かFileDialog.SAVE(ファイルを
保存する)かのどちらかになります。ここでは、FileDialog.LOADを指定していま
す。つまり、これからファイルを読み込むつもりであることがわかります。

98行目
-------------------------------------------------------
fDialog.setVisible(true); // 060910
-------------------------------------------------------
はもうおわかりですね。ファイル・ダイアログを表示しています。

ファイル・ダイアログが表示されると、(通常の操作としては)アプリケー
ションの使用者はファイルの存在場所(ディレクトリーすなわちフォルダー)
とファイル名を指定し、「開く」ボタンをクリックします。

そうすると、次の文が実行されます。

99行目
-------------------------------------------------------
filePathName = fDialog.getDirectory() + fDialog.getFile(); // 060910
-------------------------------------------------------
上記のgetDirectory()というメソッドとgetFile()というメソッドは、それぞれ
ファイル・ダイアログが使用者の入力から受け取ったディレクトリー名(フォル
ダー名)とファイル名を取り出すためのメソッドです。

これらディレクトリー名とファイル名が+演算子によってつなぎ合わされて、
ファイルのパスになって、filePathName変数に代入されています。

101行目
-------------------------------------------------------
BufferedReader in = new BufferedReader(new FileReader(filePathName)); // 060910
-------------------------------------------------------
は前回説明したFileReaderのインスタンスの生成とBufferedReaderのインス
タンスの生成をいっしょに行ったものです。

前回は、
-------------------------------------------------------
FileReader aFileReader = new FileReader("c:\abc\efg\file1.txt");
BufferedReader in = new BufferedReader(aFileReader);
-------------------------------------------------------
という2つの文で説明しました。
この場合、他の場所でaFileReaderという変数を使う場面がなければ、
いちいちaFileReaderという変数を宣言する必要は無く、
-------------------------------------------------------
BufferedReader in = new BufferedReader(new FileReader("c:\abc\efg\file1.txt"));
-------------------------------------------------------
という形で1つの文にまとめることができるのです。

上の文ではファイルのパスが"c:\abc\efg\file1.txt"という文字列で指定され
ていますが、101行目ではファイルのパスがさらに変数filePathNameで指定され
ています。
こうしておくと、変数filePathNameによって指定されたパスのファイルがオー
プンされ、変数inを通して読み取ることができます。

102行目
-------------------------------------------------------
window.getTextFieldName().setText(in.readLine()); // 060910
-------------------------------------------------------
では、このin変数を通してファイルから1行のデータ読み込みを行っています。
(BufferedReaderの)readLine()というメソッドがファイルから1行のデータを
読み込むメソッドです。

実は、FileReaderクラスにもデータを読み込むためのメソッドはあるのですが、
1行の読み込みという形にはなっていないので、少し使い方が面倒です。
それでBufferedReaderクラスのreadLine()メソッドを使えるようにするために、
101行目の文でFileReaderのインスタンスにBufferedReaderをかぶせたのです。

なお、102行目のwindow.getTextFieldName()はHumanInfoWindowに貼り付けてある、
氏名を入力するためのテキスト・フィールドのオブジェクト(インスタンス)を
取り出す操作になりますね。
そして、そのオブジェクトのsetText()メソッドは引数に指定した文字列をテキス
ト・フィールドにセットしてくれますから、in.readLine()を引数に指定したと
いうことはファイルから読み取った1行目のデータをこのテキスト・フィールドに
セットすることになります。

つまり、ファイルの1行目には、あらかじめ氏名のデータが入っているものと
しているわけです。

103行目
-------------------------------------------------------
window.nengoChoice.select(Integer.parseInt(in.readLine())); // 060910
-------------------------------------------------------
は、in.readLine()によってファイルから1行のデータを読み取ったものを
Integer.parseInt()によってint型に変換しています。readLine()メソッドは
戻り値としてString型を返すため、int型にしたい場合はIntegerのparseInt()メソッド
によってint型に変換する必要があります。そしてwindow.nengoChoice.select()は、
このHumanInfoWindowに貼り付けてある年号を選択するためのChoiceのインスタンスに
対してselect()メソッドを呼び出しているのですが、このselect()メソッドは引数に
指定したインデックス(番号)の項目を選択状態にします。

このChoiceオブジェクトには上から順番に「明治」「大正」「昭和」「平成」「西暦」
という項目を組み込んであり、上から順番にインデックスがそれぞれ0,1,2,3,4となる
ことは以前お話した通りです。それで、たとえばselect()メソッドの引数に2を指定
すると「昭和」の項目が選択された状態にしてくれます。

この引数(インデックス)はint型なので、103行目ではファイルから読み込んだ
文字列(実際のデータは数字の文字)をint型に変換しているのです。

そして、readLine()メソッドは繰り返し実行するたびに1行ずつ次の行を読んで
いきますから、103行目のreadLine()ではファイルの2行目を読み込むことになり
ます。
つまり、ファイルの2行目には、あらかじめ年号を表すインデックスの数字が入っ
ているものとしているわけです。

104行目〜106行目
-------------------------------------------------------
window.getTextFieldYear().setText(in.readLine()); // 060910
window.getTextFieldMonth().setText(in.readLine()); // 060910
window.getTextFieldDay().setText(in.readLine()); // 060910
-------------------------------------------------------
は103行目と同様に、それぞれファイルから1行ずつデータを読み取りながら、
(生年月日の)年や月や日を入力するテキスト・フィールドにそれらのデータ
をセットしています。
つまり、ファイルの3行目、4行目、5行目にはあらかじめ、それぞれ(生年月日の)
年、月、日のデータが入っているものとしているわけです。

107行目
-------------------------------------------------------
window.labelResponse.setText(in.readLine()); // 060910
-------------------------------------------------------
も同様にしてファイルから6行目のデータを読み取って、labelResponseが表す
ラベルにセットしていますが、labelResponseは年齢を表示するラベルでしたね。

つまり、ファイルの6行目にはあらかじめ、年齢のデータが入っているものとして
いるわけです。

107行目
-------------------------------------------------------
in.close(); // 060910
-------------------------------------------------------
のclose()は、ファイルをクローズする(閉じる)ためのメソッドです。

ところで、101行目〜107行目の外側を
-------------------------------------------------------
try {


}
-------------------------------------------------------
というブロックが囲んでいます(100行目と109行目)が、これは何だと思いますか。

これは、例外的な(異常な)事象が発生したときにそれを検出するためのものです。

ここでは、たとえばreadLine()メソッドやclose()メソッドを実行したときに
(何らかの理由、たとえばハードディスクの故障などで)ファイルが読めない
とか閉じられないとかいう、予期しない事象が発生する可能性があります。
そういう場合にはJavaはIOException(IOはInput/Outputの略、つまり入出力の
意味。またExceptionは例外の意味)という名前のクラスのオブジェクトを生成し
ます(正式にはException(例外)をthrowする(投げる)という言い方をする)。

なお、このような例外的な事象はIOException以外にもたくさんありますが、
Javaでは、例外的な事象はExceptionというクラスのサブクラスで表現すること
になっており(あるいはExceptionクラスのサブクラスとして作成することに
なっており)、それぞれクラス名の末尾にExceptionをつけることになってい
ます。

こういった例外的な事象が発生したときに、それに対処するためには、まず
例外的な事象を検出する必要があります。これがtry{  }ブロックの役割です。

そして、それに続いて
-------------------------------------------------------
catch (IOException e1) {
   // ここでe1に対して何らかの処理をする;
}
-------------------------------------------------------
というようにcatchのブロックをつけます。

catchブロックでは、引数に指定されたExceptionの型(上記ではIOException)が
検出されたときに、そのブロック内の文が実行されます。
いわば、引数に指定された型のExceptionのオブジェクトがcatchされ(捕らえられ)、
それに対して何らかの処理が行われるわけです。

上記の場合はe1という引数の変数にIOException型のオブジェクトが渡されますので、
ブロックの中でe1に対して何らかの処理を行うことができます。
あるいは、IOExceptionが発生したときに行うべき処理をこのブロック内に書いて
おきます。

また、catchブロックは
-------------------------------------------------------
catch (FileNotFoundException e1) {  // <1>
   // ここでe1に対して何らかの処理をする;
}
catch (IOException e2) {  // <2>
   // ここでe2に対して何らかの処理をする;
}
catch (ArithmeticException e3) {  // <3>
   // ここでe3に対して何らかの処理をする;
}
-------------------------------------------------------
というように複数続けることができます。
それぞれ、<1>のブロックはFileNotFoundExceptionが発生したときに実行され、
<2>のブロックはIOExceptionが発生したときに実行され、<3>のブロックは
ArithmeticExceptionが発生したときに実行されることになります。
(なお、FileNotFoundExceptionは実はIOExceptionのサブクラスなので、
IOExceptionの一種です。したがって、<2>のブロックはもっと正確にいうと
FileNotFoundException以外のIOExceptionが発生したときに実行される、
ということになります。)

これはtryのブロックで検出したExeptionの種類に応じて処理を振り分けられ
るということを意味します。

なお、どこかのメソッド呼び出しが何らかのExceptionを発生する可能性があ
る場合はそれに対して必ず処理をする必要があります(上記のようにtry{}catch{}
でExceptionを捕らえて処理するほかに、外にExceptionを投げ出すこともできるが
これについては後述)。
もし処理をしていないとEclipseがそれを見つけてエラーを表示します。
Eclipseはさらにそのエラーに自動的に対処する機能も持っているのですが、
その使い方については後述します。

なお、Exceptionとしては、アプリケーションの使用者が入力すべきデータを
間違えた(たとえば月日の数字を入力すべきテキスト・フィールドに、25月と
か61日とかいった、ありえない数字を入力してしまったとかの)場合など、
人為的なエラーの場合も取り扱うべきですが、現在の段階でこういった人為的
なエラーに対する対処法を説明するのは面倒なので、これについても後述させ
ていただきます。

それでは、109行目〜111行目
-------------------------------------------------------
   } catch (IOException e1) { // 060910
      System.out.println(e1); // 060910
   } // 060910
-------------------------------------------------------
を見てみましょう。
このcatchブロックは、101行目〜108行目の間でIOExceptionが発生した場合に
そのIOExceptionのオブジェクトが持つ文字列を標準出力に書き出すようにして
います。

このように、System.out.println()の引数にString型でないオブジェクトを指定
すると、そのオブジェクトのtoString()メソッドが呼び出されてオブジェクトか
らString型のデータが取り出されることになっています。そして、ここではそれ
はIOExceptionの情報を記述する文字列になっています。

次の114行目〜120行目
-------------------------------------------------------
else if ("ファイルを閉じる".equals(menuItem.getLabel())) { // 060910
   try { // 060910
      out.close(); // 060910
   } catch (IOException e1) { // 060910
      System.out.println(e1); // 060910
   } // 060910
} // 060910
-------------------------------------------------------
については宿題(下の演習問題)にまわします。

続いて121行目〜137行目
-------------------------------------------------------
else if ("名前をつけて保存".equals(menuItem.getLabel())) { // 060910
   FileDialog fDialog = new FileDialog(this.window, "名前をつけて保存", FileDialog.SAVE); // 060910
   fDialog.setVisible(true); // 060910
   filePathName = fDialog.getDirectory() + fDialog.getFile(); // 060910
   try { // 060910
      out = new BufferedWriter(new FileWriter(filePathName, false)); // 060910
      out.write(window.getTextFieldName().getText()); out.newLine(); // 060910
      out.write(Integer.toString(window.nengoChoice.getSelectedIndex())); out.newLine(); // 060910
      out.write(window.getTextFieldYear().getText()); out.newLine(); // 060910
      out.write(window.getTextFieldMonth().getText()); out.newLine(); // 060910
      out.write(window.getTextFieldDay().getText()); out.newLine(); // 060910
      out.write(window.labelResponse.getText()); // 060910
      out.close(); // 060910
   } catch (IOException e1) { // 060910
      System.out.println(e1); // 060910
   } // 060910
} // 060910
-------------------------------------------------------
を見てみましょう。
これは、「ファイル」メニューの中の「名前をつけて保存」メニュー項目が選択
されたときの処理を記述していますね。
ファイルに書き出す処理になりますから、122行目のFileDialogのインスタンス
生成では、コンストラクターの第三引数にFileDialog.SAVEを指定しています。

126行目は、FileWriterクラスとBufferedWriterクラスを使っているという点を
除けば101行目と同様のパターンなので説明不要でしょう。
なお、FileWriterクラスにもファイルに書き出すためのメソッドはあるのですが
そのメソッドの使い方が少し面倒臭いため、BufferedWriterのメソッドを使用す
るようにしています。

127行目
-------------------------------------------------------
out.write(window.getTextFieldName().getText()); out.newLine(); // 060910
-------------------------------------------------------
は氏名のテキスト・フィールドに入っているデータをBufferedWriterのwrite()メ
ソッドでファイルに書き出しています。その後のout.newLine()はファイルに改行
のコードを書き込みます。
このようにJavaのソースコードでは、1行に複数の文を書くこともできます。

ここでは、1行分のデータを書き出して最後に改行のコードを書くということを
一まとめに見やすく表現するために1行にまとめて書いてあります。

以下、128行目〜132行目も、先ほどのファイルからの読み取りとちょうど逆の
作業をしているだけですから、容易に理解できますね。

残りの部分も解説不要でしょう。

また、書き出しているファイルの構造が
-------------------------------------------------------
1行目は氏名
2行目は年号を表す番号(インデックス)
3行目は(生年月日の)年
4行目は(生年月日の)月
5行目は(生年月日の)日
6行目は年齢
-------------------------------------------------------
になっていて、ファイルの読み取り時の構造と一致していることがわかりますね。

プログラムを作るときには、このようにファイルの構造をきちんと取り決めておく
必要があります。

続いて138行目〜151行目
-------------------------------------------------------
else if ("上書き保存".equals(menuItem.getLabel())) { // 060910
   try { // 060910
      out = new BufferedWriter(new FileWriter(filePathName, false)); // 060910
      out.write(window.getTextFieldName().getText()); out.newLine(); // 060910
      out.write(Integer.toString(window.nengoChoice.getSelectedIndex())); out.newLine(); // 060910
      out.write(window.getTextFieldYear().getText()); out.newLine(); // 060910
      out.write(window.getTextFieldMonth().getText()); out.newLine(); // 060910
      out.write(window.getTextFieldDay().getText()); out.newLine(); // 060910
      out.write(window.labelResponse.getText()); // 060910
      out.close(); // 060910
   } catch (IOException e1) { // 060910
      System.out.println(e1); // 060910
   } // 060910
} // 060910
-------------------------------------------------------
は、「ファイル」メニューの中の「上書き保存」メニュー項目が選択されたときの
処理を記述していますね。

これは上書き保存なのでファイル・ダイアログを開いてファイル名の読み込みなど
を行う必要はないですが、そのことを除けば「名前をつけて保存」のときと同じで
すね。したがって、これも解説を略させていただきます。

ところで、人事情報の管理のためのアプリケーションとなると、たとえば特定の部署
に所属する社員をリストしたり、何らかの条件を持つ社員を検索してリストするなど、
複数のデータを一挙に処理したり検索したり、あるいは特定の人の関連データを一挙
に削除したりといった操作も必要になりますね。
こういった複雑な操作を普通のファイルを使って処理するのは面倒ですが、データ
ベース(Relational Database Management System(リレーショナル・データベース
管理システム))と呼ばれるソフトウエアを使うと簡単に効率よく処理できます。

次回からは、このデータベースを使うお話に入ります。



========================================================
◆ 02.文法解説 [プリミティブ型の型変換]
========================================================

boolean型を除くと、お互いに異なるプリミティブ型どうしで値を代入し合うことが
できます。
この時、サイズの小さい型から大きい型に値を渡す場合は、自動的に(暗黙のうちに)
型変換が行われますが、サイズの大きい型から小さい(あるいは同じサイズの)型に値
を渡す場合は、明示的な型変換(キャスト)が必要になります。この場合、値が変わっ
てしまう可能性がありますので、注意が必要です。

(例)
short s = -100;
int   i;
long  l;
char  c = 60000;

i = s;     // shortからintへ暗黙の型変換
l = i;     // intからlongへ暗黙の型変換
s = (short)l;     // longからshortにキャスト
s = l;     // これはエラー
s = (short)c;     // charからshortにキャスト
s = c;     // これはエラー
i = c;     // charからintへ暗黙の型変換


では、今回はここまでにします。



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

上記の114行目〜120行目
-------------------------------------------------------
else if ("ファイルを閉じる".equals(menuItem.getLabel())) { // 060910
   try { // 060910
      out.close(); // 060910
   } catch (IOException e1) { // 060910
      System.out.println(e1); // 060910
   } // 060910
} // 060910
-------------------------------------------------------
は、実は無意味な処理を行っています。
なぜ無意味なのか答えてください。

また、代わりの処理として、「ファイル」メニューの「ファイルを閉じる」メニ
ュー項目を選択したらHumanInfoWindowのテキスト・フィールドの中のデータが
すべて空にされるようにプログラムを書き換えてください。



======================================================
◆ 04.前回の演習問題の答
======================================================

今回の本文中に解答が含まれています。



┏━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
★ホームページ:
      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) 2010 Future Lifestyle Inc. 不許無断複製