ここまでのプログラムで何気なく使ってきた「変数」。countとか isLeapYearとか iとかいろいろな変数を使用してきました。変数は、基本型と参照型に分類されるということをちらっと説明しました。基本型は 8種類ありましたが覚えているでしょうか。そして、参照型についてはまだ説明していないので、何なのか分からなくて大丈夫です。
変数のもうひとつの分類方法としては、宣言場所別の分類があります。変数は以下の 4つのどれかに分類されます。
ローカル変数というのは、インデントを 2段以上引いたところで宣言した変数のことです。言い換えれば、メソッドの内部で宣言した変数のことです。例えば、for文のところでよく登場した変数 iや、mainメソッドやその他のメソッドのカッコ「{ }」の中で宣言した変数のことです。
引数とは、ついさきほど登場しました。メソッドの宣言部のカッコの中で宣言されている変数のことです。
staticなフィールドとは、このページで解説するタイプのものです。ざっくり言えば、インデントを 1段引いたところ、つまりクラスの内側だけどメソッドの外側で宣言した変数で、かつ、予約語staticを使用している変数ということになります。
最後のインスタンスフィールドについては、もう少し後の章で説明します。ただ、今のうちに存在とこの名称だけは覚えておいてください。ざっくり言えば、インデントを 1段引いたところ、つまりクラスの内側だけどメソッドの外側で宣言した変数で、かつ、予約語staticを使用していない変数ということになります。
変数の 4つの分類について、以下にサンプルコードを挙げておきます。
G201/G201.java
(11, 15, 22行目に黄線が生じますが問題ありません)
/** * 変数を役割ごとに分類します。 * このプログラムは実行しても何も処理が行われません。 * eclipseで作成した際には、11, 15, 22行目に黄色線が出ますが問題ありません。 */ public class G201 { /** * staticなフィールド。 */ private static int p; /** * インスタンスフィールド。 */ private int q; /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { int a; a = G201.method(10, 20); } /** * 2つの数を合計します。 * @param x 1つ目の数 * @param y 2つ目の数 */ private static int method(int x, int y) { int z = x + y; return z; } }
このプログラムは実行しても何も起こりません。ただ、変数の分類を説明しただけのプログラムです。
さきほどの 4つの分類を当てはめてみると、以下のようになります。
正しく分類できたでしょうか。ここでは、staticなフィールドについて説明していきます。
staticなフィールドの特徴は、基本的にはプログラムの実行中はいつでもどこでも使えるということがあります。
先のプログラム(G201.java)は、ローカル変数 aは mainメソッドの中でしか使えませんし、ローカル変数 zは methodメソッドの中でしか使えません。また、methodメソッドの引数 xと yは、methodメソッドの中でしか使えません。それに対して、staticなフィールドである変数 pは、mainメソッドの中でも methodメソッドの中でも使用することができます(G201.javaでは使用していませんが)。
staticなフィールドの動きを確認するためのプログラムのソースコードを以下に挙げます。
G202/G202.java
/** * staticなフィールドを確認します。 */ public class G202 { /** * カウンタ。 */ private static int counter = 0; /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { G202.counter ++; System.out.println("[A] カウンタの値は " + G202.counter); G202.incrementCounter(); G202.incrementCounter(); G202.counter ++; System.out.println("[C] カウンタの値は " + G202.counter); } /** * カウンタをインクリメントします。 */ private static void incrementCounter() { G202.counter ++; System.out.println("[B] カウンタの値は " + G202.counter); } }
実行結果は以下の通り。
[A] カウンタの値は 1 [B] カウンタの値は 2 [B] カウンタの値は 3 [C] カウンタの値は 4
staticなフィールドである変数 counterは、mainメソッドの中でも incrementCounterメソッドの中でも利用可能で、G202.counter++;
の実行によって、ひとつずつ増加していることが分かります。
なお、G202.counter++
のG202.
の部分は省略可能で、単にcounter++;
と書いても大丈夫です。そして、世の中一般には省略することが普通なのですが、本ウェブサイトでは省略を一切しないで記述していきます。というのも、省略すると、staticなフィールドと(後の章で登場する)インスタンスフィールドとの違いが分かりづらくなるため、という理由があります。
また、8行目にて counterに初期値 0を代入していますが、この代入はプログラムが実行された直後、mainメソッドが実行されるより前のタイミングで行われていることに注意してください。これまでの話では、「プログラムは mainメソッドから始まる」ということでしたが、staticなフィールドの初期化は、mainメソッドの開始に先立って実施されます。なお、変数の初期化とスコープ(変数が有効範囲)の話については、変数の種類ごとに後にまとめて解説します。
staticなフィールドの存在を知ると、あるメソッドが別のメソッドとの間で値を受け渡しする方法には 2つの方法があることが分かります。それらを並べてみたいと思います。
G202.javaは後者の方法ということになります。
一見すると後者のほうがラクだと思います。ところが、プログラムの規模がどんどん大きくなったとき、あまりに後者一辺倒の方法で開発を進めていくとソースコードが破綻していくことがあります。
メソッドとメソッドとのやりとりは、極力、引数と戻り値の関係で実現することが望ましい。それができないような場合には、staticなフィールドを介して値を受け渡しする、と心がけると良いと思います。
Javaには、たくさんのメソッドが用意されている…と staticなメソッドの解説の際に説明しましたが、staticなフィールドもたくさん用意されています。一部の staticなメソッドについてはここまで学んできたことを踏まえて利用することができます(なお、大半の staticなフィールドについては、予約語import
を使った記述を必要としますので、詳細は後の章に記述します)。
Java SE 7(Java 1.7)の Mathクラスの説明書(API)を見てみることにします。次のリンクをクリックしてください→Mathクラス。この中に staticなフィールドである PIの記述を発見できたでしょうか。
さて、さっそく、円周率の値を格納している staticなフィールド PIの値を確認してみます。
G203/G203.java
/** * staticなフィールドを利用します。 */ public class G203 { /** * メインメソッド。 * @param args 引数 */ public static void main(String[] args) { System.out.println("円周率の値は " + Math.PI); } }
実行結果は以下の通り。
円周率の値は 3.141592653589793
なお、この staticなフィールド PIは、値を変更することができません。以下のような記述はビルドエラーになります。
// ビルドエラー Math.PI = 10.0;
ビルドエラーになる理由は、PIの宣言に予約語final
が使用されているためです…さきほどの説明書(API)を見ていただければ finalが記述されていることが確認できるかと思います。詳しいことは後の章で説明しますが、final
予約語が指定されている変数は、値を初期化したが最後、変更することができません。また、final
予約語をつけた変数については、すべて大文字で記述する慣例になっています(そのため、この変数名は piではなく PIとなっています)。
また、これまで利用してきた System.outの outや、MySystem.in の inも実は staticなフィールドなのですが、これらは基本型ではなく参照型の変数となっています。オブジェクト指向の考え方を学んでからでないと混乱するため、解説は後回しにします。一応、Java 1.7の Systemクラスの APIのページをリンクしておきます→Systemクラス。変数 outが staticなフィールドであることが確認できるかと思います。