目次 | 索引 |
---|---|
これまで、Javaのプログラムをいろいろ作成してきました。 その中には、他の.classファイルがないと実行できないものもありました。 実は、Javaのプログラムとは、クラス定義の集まりです。 そして、クラス定義は一般的に次のような形になります。
class classname { class variable declarations field declarations constructor definitions class method definitions instance method definitions }
ここで、クラス変数とコンストラクタについてはまだ触れていません。 以下で説明します。
クラスのインスタンスを扱う場合、これまでは、生成してからフィールドに値を格納しました。 例えば次のように書きました。
Time x = new Time(); x.hour = 13; x.minute = 15;
変数や配列では、初期化を使ってプログラムを短くしました。 インスタンスでも、 コンストラクタ ( constructor )というものを用いますと、初期化に相当することができます。 上記の例ですと、例えば次のように書けます。
Time x = new Time(13, 15);
コンストラクタはメソッドに似ています。 どちらも、あらかじめ定義しておき、呼び出しによってその機能が働きます。 コンストラクタとメソッドは以下の点が異なります。
void
や
int
などと書いて、返り値の型を指定しました。
コンストラクタ定義では、返り値の型は指定しません。
new
を用います。
次のプログラムでは、コンストラクタを2つ定義しています。
OpeningTime.javaの3行目から6行目までは、第1引数を
hour
フィールドに格納し、第2引数を
minute
フィールドに格納するコンストラクタです。
7行目から10行目までは、
hour
フィールドに9を格納し、
minute
フィールドに0を格納するコンストラクタです。
特に断らなければ、
OpeningTime
クラスのインスタンスは9時を表すということです。
/* 1*/ class OpeningTime { /* 2*/ int hour, minute; /* 3*/ OpeningTime (int h, int m) { /* 4*/ this.hour = h; /* 5*/ this.minute = m; /* 6*/ } /* 7*/ OpeningTime () { /* 8*/ this.hour = 9; /* 9*/ this.minute = 0; /*10*/ } /*11*/ void print () { /*12*/ System.out.print(this.hour); /*13*/ if (this.minute < 10) { /*14*/ System.out.println(":0" + this.minute); /*15*/ } else { /*16*/ System.out.println(":" + this.minute); /*17*/ } /*18*/ } /*19*/ }
/* 1*/ class OpeningTimeTest { /* 2*/ public static void main (String[] args) { /* 3*/ OpeningTime x = new OpeningTime(9, 30); /* 4*/ OpeningTime y = new OpeningTime(); /* 5*/ x.print(); /* 6*/ y.print(); /* 7*/ } /* 8*/ }
b00a001@Ampere:~/java% java OpeningTimeTest 9:30 9:00 b00a001@Ampere:~/java%
コンストラクタをひとつも定義しない場合は、デフォルトのコンストラクタ
classname
()
が用意されます。
実は、これはインスタンスの生成のときにこれまで用いてきたものです。
クラスを設計するとき、普通はコンストラクタを用意します。
これまで説明した通り、変数は宣言すれば使えます。 しかし、いつまでも使い続けられるわけではありません。 例えば、メソッド定義
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 sato = new AttendingTime(8, 55); /* 4*/ sato.checkLate(); /* 5*/ AttendingTime suzuki = new AttendingTime(9, 10); /* 6*/ suzuki.checkLate(); /* 7*/ AttendingTime yamada = new AttendingTime(9, 5); /* 8*/ yamada.checkLate(); /* 9*/ System.out.println(yamada.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*/ }
クラス変数とクラスメソッドは、これまで同じクラスのものしか使いませんでした。 Javaでは、他のクラスのクラス変数やクラスメソッドを用いることもできます。 関係の深いクラス変数やクラスメソッドをひとまとめにしてクラスを定義し、他のクラスからそれらを利用するわけです。 ソフトウェアの設計法では、関係の深い機能同士をひとまとめにしたプログラム単位を モジュール ( module )とよびます。 Javaのクラスは、モジュールとしての役割も果たすのです。
他のクラス
class
のクラス変数
variable
は、
class
.
variable
と書くと使えます。
同様に、他のクラス
class
のクラスメソッド
method
(
...
)
は、
class
.
method
(
...
)
と書くと使えます。
上記のプログラムでは、遅刻の人数を表示するのに、
getLateCount
メソッドを用いて
System.out.println(yamada.getLateCount());
と書きました。
遅刻の人数を求めるのに、インスタンス
yamada
にメッセージを送るのは不自然です。
このメソッドは、定義
int getLateCount () { return lateCount; }
を見ると分かりますが、単にクラス変数
lateCount
の値を返すだけで、インスタンス
yamada
のデータは使っていません。
そこで、クラスメソッドとして
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 sato = new AttendingTime(8, 55); /* 4*/ sato.checkLate(); /* 5*/ AttendingTime suzuki = new AttendingTime(9, 10); /* 6*/ suzuki.checkLate(); /* 7*/ AttendingTime yamada = new AttendingTime(9, 5); /* 8*/ yamada.checkLate(); /* 9*/ System.out.println(AttendingTime.getLateCount()); /*10*/ } /*11*/ }
なお、遅刻の人数を求めるのに、メソッド
getLateCount
を用いず、クラス変数
lateCount
の値を直接取り出す方法もあります。
この場合でも、クラスが異なる理由から、
System.out.println(lateCount);
では値は取り出せません。
System.out.println(AttendingTime.lateCount);
と書くと取り出せます。
ここで、インスタンスメソッドとクラスメソッドの違いをまとめます。
static
を用います。
this
参照は使えず、インスタンス変数(フィールド)にアクセスできません。
インスタンスメソッドとクラスメソッドのどちらを使うべきかは一概には言えません。
比例代表制の選挙で、得票数から議席数を計算するJavaアプリケーションを作成してください。 比例代表制とは、各政党の得票数に応じて全議席を比例配分する選挙制度です。 この配分方式として、ドント方式というものを用いることにします。 例に基づいて、このドント方式を説明します。
政党A | 政党B | 政党C | |
---|---|---|---|
得票数 | 900 | 420 | 240 |
議席数 | 3 | 1 | 0 |
1 | (1) 900/1=900 | (3) 420/1=420 | [5] 240/1=240 |
2 | (2) 900/2=450 | [7] 420/2=210 | -- 240/2=120 |
3 | (4) 900/3=300 | -- 420/3=140 | -- 240/3=80 |
4 | [6] 900/4=225 | -- 420/4=105 | -- 240/4=60 |
ドント方式では、まず、得票数を1で割った商、2で割った商、3で割った商、…を、政党ごとに求めます。 そして、これらの商に対して大きい順に番号をつけます。 この番号順に、その政党の議席数を一つ増やします。 番号が全議席数に達したら終了します。 このときの議席数が、最終的な議席数になります。
この例では、政党Aが1番目、2番目、および4番目の商によって、議席数3となります。 政党Bは、1で割った商が3番目になって、1議席です。 政党Cは、1で割った商が4番目以内に入りませんので、議席を獲得できません。
今、4つの政党が10の議席を争う選挙が行なわれ、それぞれ5100票、2200票、900票、および500票を得たとします。 ドント方式に基づいて、政党ごとの議席数を計算してください。
b00a001@Ampere:~/java% java PartyTest 5100 votes, 6 seats. 2200 votes, 3 seats. 900 votes, 1 seats. 500 votes, 0 seats. b00a001@Ampere:~/java%
プログラムは穴埋めとします。 コメントを参考にして、???の部分を正しいものにしてください。 ポイントは、すべての商を一気に計算するのではなく、必要な部分だけを計算するところです。 その部分とは、政党ごとの商
得票数 / (現在の議席数 + 1)
です。 この商が最大となる政党を選び、その政党の議席数を一つ増やすという手続きを、全議席数の数だけ繰り返せばよいのです。
class Party { ??? // 政党の得票数と議席数を格納するフィールド (例 ??? // えば vote と seat) の定義。 ??? // 全議席数 10 を定数として TOTALSEATS に格納。 Party (int vote) { ??? // コンストラクタの定義。その政党の得票数は ??? // 引数の vote、議席数は 0。 } void add () { ??? // その政党の議席数を一つ増やす。 } void print () { ??? // その政党の得票数と議席数を画面に出力する。 } static Party maxParty (Party[] p) { ??? // 商「得票数 / (現在の議席数 + 1)」が最大 ??? // である政党を返す。引数 p は政党の配列。 ??? // はじめに p[0] を候補としてその添字と商 ??? // (例えば index と quotient) を覚えておき、 ??? // p[1], p[2], ... の商と比較して、quotient ??? // より大きければ index と quotient を更新 ??? // すればよい。 } }
class PartyTest { public static void main (String[] args) { int i; Party[] p = {new Party(5100), new Party(2200), new Party(900), new Party(500)}; for (i = 0; i < Party.TOTALSEATS; i++) { Party.maxParty(p).add(); } for (i = 0; i < p.length; i++) { p[i].print(); System.out.println(); } } }
今日の演習10に従ってJavaプログラムを作成し、そのプログラムをkonishi@twcu.ac.jpあてにメールで提出してください。 メールには、学生番号、氏名、科目名、授業日(11/22)を明記してください。