これまで説明した通り、変数は宣言すれば使えます。 しかし、いつまでも使い続けられるわけではありません。 例えば、メソッド定義
static void printStars (int m) { int i; for (i = 0; i < m; i++) { System.out.print("*"); } }
での変数 i
は、メソッド呼び出しの際に用意されます。
しかし、この呼び出しが終了しますと、変数 i
は使えなくなります。
このような変数は局所変数(local variable)とよばれます。
データを局所変数に格納しても、他のメソッドからはそのデータは利用できません。 複数のメソッドの間でデータを共有するには、大域変数(global variable)というものを用います。 大域変数とは、プログラムのどこからでも使える変数です。 あるメソッドで大域変数にデータを格納し、他のメソッドでそれを取り出すことができます。
実は、Javaには大域変数は用意されていません。 これは、クラス変数(class variable)というもので代用します。 クラス変数とは、クラスに割り当てられる変数と考えてください。 クラス変数と対照的なのがインスタンス変数(すなわちフィールド)です。 クラスのインスタンスを生成しますと、インスタンスごとにインスタンス変数が用意されます。 一方、クラス変数は、インスタンスをいくら生成しても、クラスにひとつだけ用意されます。 次の図は、クラス変数とインスタンス変数のイメージです。
次のプログラムは、出勤時刻に関して遅刻の回数を数えるものです。
AttendingTime.java の3行目で、クラス変数 lateCount
を用意します。
クラス変数は、フィールドと同じように指定します。
違いは、キーワード static
を書くところです。
/* 1*/ class AttendingTime { /* 2*/ int hour, minute; /* 3*/ static int lateCount = 0; /* 4*/ AttendingTime (int h, int m) { /* 5*/ this.hour = h; /* 6*/ this.minute = m; /* 7*/ } /* 8*/ void checkLate () { /* 9*/ if (540 < 60 * this.hour + this.minute) { /*10*/ lateCount++; /*11*/ } /*12*/ } /*13*/ int getLateCount () { /*14*/ return lateCount; /*15*/ } /*16*/ }
/* 1*/ class AttendingTimeTest { /* 2*/ public static void main (String[] args) { /* 3*/ AttendingTime dec01 = new AttendingTime(8, 55); /* 4*/ dec01.checkLate(); /* 5*/ AttendingTime dec02 = new AttendingTime(9, 10); /* 6*/ dec02.checkLate(); /* 7*/ AttendingTime dec03 = new AttendingTime(9, 5); /* 8*/ dec03.checkLate(); /* 9*/ System.out.println(dec03.getLateCount()); /*10*/ } /*11*/ }
b00a001@Ampere:~/java% java AttendingTimeTest 2 b00a001@Ampere:~/java%
大域変数を用いて定数(constant)を表すことがよくあります。 定数とは、常に変わらない値のことです。 プログラムに定数を直接書きますと、それが何を表しているのか分かりにくいものです。 そこで、大域変数に定数を代入しておき、定数の代わりに変数名を書きます。 定数に分かりやすい名前をつけ、プログラムを読みやすくするのです。
定数を表す変数へは、新たな代入が行われないようにしなくてはいけません。
変数を宣言するときに、キーワード final
を書きますと、値の変更が禁止されます。
これによって、どの変数が定数を表すのかが明確になります。
上記のプログラムでは、AttendingTime.java の9行目の条件
540 < 60 * this.hour + this.minute
の 540
が何を表しているのかが分かりにくいでした。
これは始業時刻9:00の絶対時刻(分)です。
定数を
final static int OPENINGHOUR = 9, OPENINGMINUTE = 0;
と定め、条件を
60 * OPENINGHOUR + OPENINGMINUTE < 60 * this.hour + this.minute
と書きますと、出勤時刻が始業時刻以後ならば…と読めるわけです。
/* 1*/ class AttendingTime { /* 2*/ int hour, minute; /* 3*/ static int lateCount = 0; /* 4*/ final static int OPENINGHOUR = 9, OPENINGMINUTE = 0; /* 5*/ AttendingTime (int h, int m) { /* 6*/ this.hour = h; /* 7*/ this.minute = m; /* 8*/ } /* 9*/ void checkLate () { /*10*/ if (60 * OPENINGHOUR + OPENINGMINUTE /*11*/ < 60 * this.hour + this.minute) { /*12*/ lateCount++; /*13*/ } /*14*/ } /*15*/ int getLateCount () { /*16*/ return lateCount; /*17*/ } /*18*/ }
ここで、クラスメソッドについて思い出してください。
クラスメソッドを定義するときは、キーワード static
を用いました。
また、呼び出しのときはインスタンスを伴いませんでした。
クラス変数とクラスメソッドは、これまで同じクラスのものしか使いませんでした。
他のクラス class のクラス変数 variable は、class.
variable と書くと使えます。
同様に、他のクラス class のクラスメソッド method(
...)
は、class.
method(
...)
と書くと使えます。
上記のプログラムでは、遅刻の回数を表示するのに、getLateCount
メソッドを用いて
System.out.println(dec03.getLateCount());
と書きました。 このメソッドは、定義
int getLateCount () { return lateCount; }
を見ると分かりますが、単にクラス変数 lateCount
の値を返すだけで、インスタンス dec03
のデータは使っていません。
そこで、クラスメソッドとして
static int getLateCount () { return lateCount; }
と定義し直すことにします。 このメソッドは、これまでのクラスメソッドのように
System.out.println(getLateCount());
と書いても呼び出せません。
メソッドが定義されているクラス(AttendingTime
)と呼び出すクラス(AttendingTimeTest
)が異なるからです。
System.out.println(AttendingTime.getLateCount());
と書くと呼び出せます。
/* 1*/ class AttendingTime { /* 2*/ int hour, minute; /* 3*/ static int lateCount = 0; /* 4*/ final static int OPENINGHOUR = 9, OPENINGMINUTE = 0; /* 5*/ AttendingTime (int h, int m) { /* 6*/ this.hour = h; /* 7*/ this.minute = m; /* 8*/ } /* 9*/ void checkLate () { /*10*/ if (60 * OPENINGHOUR + OPENINGMINUTE /*11*/ < 60 * this.hour + this.minute) { /*12*/ lateCount++; /*13*/ } /*14*/ } /*15*/ static int getLateCount () { /*16*/ return lateCount; /*17*/ } /*18*/ }
/* 1*/ class AttendingTimeTest { /* 2*/ public static void main (String[] args) { /* 3*/ AttendingTime dec01 = new AttendingTime(8, 55); /* 4*/ dec01.checkLate(); /* 5*/ AttendingTime dec02 = new AttendingTime(9, 10); /* 6*/ dec02.checkLate(); /* 7*/ AttendingTime dec03 = new AttendingTime(9, 5); /* 8*/ dec03.checkLate(); /* 9*/ System.out.println(AttendingTime.getLateCount()); /*10*/ } /*11*/ }
なお、遅刻の回数を求めるのに、メソッド getLateCount
を用いず、クラス変数 lateCount
の値を直接取り出す方法もあります。
この場合でも、クラスが異なる理由から、
System.out.println(lateCount);
では値は取り出せません。
System.out.println(AttendingTime.lateCount);
と書くと取り出せます。
ここで、インスタンスメソッドとクラスメソッドの違いをまとめます。
static
を用います。this
参照は使えず、インスタンス変数(フィールド)にアクセスできません。インスタンスメソッドとクラスメソッドのどちらを使うべきかは一概には言えません。
今、1999年5月号創刊の月刊誌があるとします。 ある人が1999年8月号からその雑誌を買い続けましたが、2000年12月号を最後に買うのをやめてしまいました。 結局17冊買ったことになります。 創刊号は通巻1号で、最後に買ったのは通巻20号です。 もし創刊号から買っていれば、20冊になっていたはずです。
この話を次のようにプログラムにしました。
このプログラムが動くように Magazine
クラスを定義してください。
(ヒント: count
は、引数が Magazine
クラス2つで返り値が int
型であるクラスメソッドです。)
class MagazineTest { public static void main (String[] args) { Magazine firstIssue = new Magazine(Magazine.FOUNDYEAR, Magazine.FOUNDMONTH); Magazine startBuy = new Magazine(1999, 8); Magazine endBuy = new Magazine(2000, 12); System.out.println(Magazine.count(startBuy, endBuy)); System.out.println(firstIssue.totalNumber()); System.out.println(endBuy.totalNumber()); System.out.println(Magazine.count(firstIssue, endBuy)); } }
b00a001@Ampere:~/java% java MagazineTest 17 1 20 20 b00a001@Ampere:~/java%
今日の演習11にしたがってJavaプログラムを作成し、konishi@twcu.ac.jpあてにメールでそのプログラムを提出してください。 メールには、学生番号、氏名、科目名、授業の日付けを明記してください。