staticフィールドの必要性 (入力最小値)

次のように実行するプログラムを検討します(赤が入力)。入力商品コードの最小値を求めています。

D:Java>java Rec2InputMinimum
1件目入力です
  商品コード>>D05
  数量>>50
2件目入力です
  商品コード>>B10
  数量>>90
3件目入力です
  商品コード>>B05
  数量>>75
4件目入力です
  商品コード>>C01
  数量>>45
5件目入力です
  商品コード>>Z99
  数量>>0
入力で、最小(辞書並び)の商品コードのレコードは次の通りです。
   3番目レコード
        商品コード:B05
        数量:75
D:Java>

最後に入力しているZ99は、入力データの最後を意味するデータです。 入力件数は、入力を始める時点で決まっておらず、入力の繰り返しの終了は、 商品コードでZ99を入力した時としています。
これを、前のページで作成したクラスを使って作成した例を示します。
このように入力範囲が限定されている場合、 その入力範囲外になるデータとなる大きな値を、 最小値用変数の初期値に設定するアルゴリズムはよく使われる手法です。 ここでの設定値はZ99です。
以下に上記の流れ図に従ったプログラムコードを示します。

public class Rec2InputMinimum
{
	public static java.util.Scanner stdin = new java.util.Scanner(System.in);

	public static void main(String[] arg)
	{
		Record2 recMin = new Record2("Z99", 0); //最小レコード憶用
		Record2 rec;
		int n = 0;	// 入力データ個数用
		int nMin = 0;
		do
		{
			n++;
			System.out.println(n + "件目入力です");
			rec = new Record2(stdin);
			if (rec.sho.compareTo(recMin.sho) < 0)
			{
				recMin = rec;
				nMin = n;
			}

		} while (rec.sho.compareTo("Z99") != 0);
		System.out.println("入力で、最小(辞書並び)の商品コードのレコードは次の通りです。");
		recMin.display(nMin);
	}
}

rec.shoの商品コードを比較する場合は、文字列なので < では、判定できません。
文字列の大小を判定(辞書並び)するには、compareToメソッドを使うことになります。
rec.sho.compareTo(recMin.sho)と使っていますが、これはrec.shoからrecMin.shoを 引いたような値を戻り値にします。
さて上記のコードの"Z99"のように、定数を剥き出しで複数の箇所に 使うことは好ましくありません。重複して使う意味のある定数は、識別子に割り当てて使うべきです。
 (C言語では、このような場合に#defineを使いました。)
Java言語では、定数を割り当てる識別にstaticフィールドが使われます。 問題は、どのクラスのstaticフィールドにするかです。 定数が、そのアプリケーション専用の情報か、その他にも使われる予定の情報か によって変ります。 つまり、何を意味する情報かによって どのクラスのstaticフィールドにするかが決まります。
この例であれば、商品コードを管理するRecord2クラスに、 この商品コードの定数となるべきstaticフィールドを用意するのがよいでしょう。
以下は、HIGH_VALUE の名前でstaticフィールドを用意した例です。

import java.util.Scanner;

public class Record2
{
	public static String HIGH_VALUE = "Z99";
	// 存在しないデータで、商品コードはこの文字列より小さいコードとする

	public String sho;	// 商品コード
	public short suu;	// 数量

	//n番目表示としてオペレータ用で画面表示する
	public void display(int n)
	{
		System.out.printf("%4d番目レコード\n", n);
		System.out.printf("\t商品コード:%s\n", sho);
		System.out.printf("\t数量:%d\n", suu);
	}

	//商品コードと数量を引数で設定するメソッド
	public void init(String shoCode, int n)
	{
		sho = shoCode;
		suu = (short)n;
	}

	//引数ありコンストラクタ
	public Record2(String sho, int n)
	{
		init(sho, n);
	}

	//引数なしコンストラクタ
	public Record2()
	{
	}

	//商品コードと数量をキー入力
	public void init(Scanner stdin)
	{
		System.out.print("  商品コード>>");
		sho = stdin.nextLine();
		System.out.print("  数量>>");
		String s = stdin.nextLine();
		suu = (short)Integer.parseInt(s);
	}

	//キー入力データで初期化するコンストラクタ
	public Record2(Scanner stdin)
	{
		init(stdin);
	}
}

このクラス図は次のようになります。このように、staticフィールドにはアンダーラインをつけることになっています。

このページの先頭で提示したプログラムを、このstaticフィールドを利用したプログラムに 変更すると次のようになるでしょう。

public class Rec2InputMinimum
{
	public static java.util.Scanner stdin = new java.util.Scanner(System.in);

	public static void main(String[] arg)
	{
		//Record2.HIGH_VALUE = "Y99";//必要なら入力範囲の上限を変更することもできます。
		Record2 recMin = new Record2(Record2.HIGH_VALUE, 0); //最小レコード憶用
		Record2 rec;
		int n = 0;	// 入力データ個数用
		int nMin = 0;
		do
		{
			n++;
			System.out.println(n + "件目入力です");
			rec = new Record2(stdin);
			if (rec.sho.compareTo(recMin.sho) < 0)
			{
				recMin = rec;//最小値を更新
				nMin = n;
			}

		} while (rec.sho.compareTo(Record2.HIGH_VALUE) != 0);
		System.out.println("入力で、最小(辞書並び)の商品コードのレコードは次の通りです。");
		recMin.display(nMin);
	}
}

この staticが付くフィールドは、クラス変数と呼ばれます。(参考)
クラス変数の記憶域は、オブジェクトの記憶域と違い、new で生成する前から存在して、 new で増える記憶域ではありません。 つまり、new で何回生成しても、staticが付くフィールドの記憶域は1つで増えません。 そしてpublicであればグローバル変数のような使い方ができます。
この例におけるRecord2.HIGH_VALUEクラス変数で、 必要なら先頭で設定し直すことにより、上限や終端データを変更する使い方ができます。

対して、オブジェクトの記憶域である shoやsuuは new でコンストラクタ実行ごとに増えて行きます。これらは、 インスタンス変数と呼ばれます。