前書き
Javaに触れていると変数やメソッドについて以下のように呼称されているのをよく見かけます。
呼び方、多スギ!
初学者への嫌がらせのようにも思えますが、それぞれにきちんと意味や理由があるため、イラスト付きで視覚的に理解を深めていきましょう!
変数
この記事に変数とメソッドについてまとめる予定でしたが、長くなってしまったため、ここでは変数についてのみまとめています。
フィールド変数
単に「フィールド」と言ったりもします。
メンバフィールドと同義です。
呼び方が多すぎるのでさっそく被っていますね。
▼イラストの青いエリアに書かれるものがフィールド変数となります。
イラストを見て、フィールド変数とは何か、自分なりの言葉で説明してみましょう!
フィールド変数とは
- クラス内で使用する変数
- クラス内において、記載した箇所から下が全てスコープとなる
明示的に初期化していなくても、コンパイル時に自動でnullや0で初期化されるという特徴があります!
インスタンス変数
記載エリアはフィールド変数と同じです。
インスタンス変数とは、クラスのインスタンスが生成された(newした)ときに、そのインスタンスが保有する変数のことです。
このとき、保持される変数はインスタンス毎に異なります。
文字だけでは分かりづらいと思いますので、サンプルコードとイラストを見てみましょう。
▼メインメソッドからDinnerクラスのインスタンスを2つ生成するサンプルコード
1 public class App {
2 public static void main(String[] args) throws Exception {
3 Dinner firstBite = new Dinner("一口目"); //1つ目のインスタンス生成
4 Dinner secondBite = new Dinner("二口目"); //2つ目のインスタンス生成
5
6 System.out.println(firstBite.appetizer);
7 System.out.println(secondBite.appetizer);
8 }
9 }
1 public class Dinner {
2 public String appetizer;
3
4 public Dinner(String bite){
5 this.appetizer = bite;
6 }
7 }
一口目
二口目
上のサンプルコードの流れは以下のようになります。
- Appクラスファイル読み込み
- メインメソッドを実行
- メインメソッド3行目、1つ目のDinner型インスタンス生成
└インスタンス名:firstBite内にインスタンス変数:appetizerを保持する - メインメソッド4行目、2つ目のDinner型インスタンス生成
└インスタンス名:secondBite内にインスタンス変数:appetizerを保持する
※firstBiteのappetizerとsecondBiteのappetizerは変数名は同じですが、別のインスタンス上にある別の変数です。そのため、それぞれ個別の値を保持します。 - メインメソッド6行目、firstBiteのインスタンス変数appetizerが参照されて「一口目」と出力される
- メインメソッド7行目、secondBiteのインスタンス変数appetizerが参照されて「二口目」と出力される
▼実行時のイメージは以下のようになります。
イラスト内、オレンジエリアのappetizerがインスタンス変数となります。
インスタンス変数とは何か、自分なりの言葉でまとめてみましょう!
インスタンス変数とは
- 生成されたインスタンス内に存在する変数
- インスタンス毎に異なる値を保持する
- インスタンス内にある変数なので「インスタンス変数」と呼称
- インスタンス内に値が保持されているので、(そのクラス)のメソッド間で変数を共用できる
- 他のクラスから参照されるときは、インスタンスを作成・経由して変数名で呼び出す
フィールド変数同様、明示的に初期化していなくても、コンパイル時に自動でnullや0で初期化されます!
ローカル変数
メソッド内に記載される全ての変数がこれに合致します。
各変数の寿命は、宣言したブロック{}内に納まります。
▼イラストの赤・濃い黄色・薄い黄色エリアに書かれるものは全てローカル変数です。
ローカル変数とは
- メソッド内で宣言した変数のこと
- スコープ(有効範囲)は宣言したラインから次の }まで
- 宣言したブロック内でしか使うことができない変数なのでローカル変数と呼称
ローカル変数の場合は、変数宣言時に明示的に初期化しなければコンパイルエラーとなります!
※ただし、配列の場合は明示的に初期化しなくても0やnullで初期化されます。
クラス変数
staticで修飾された変数名のことです。
先程のサンプルコードにstatic変数を追加し、コードの流れと、実行時のイメージについて確認していきましょう。
▼一口食べる毎にカロリーが加算されるサンプルコード
1 public class App {
2 public static void main(String[] args) throws Exception {
3 Dinner firstBite = new Dinner("一口目");
4 Dinner.calorie += 1.5; //カロリー追加
5 Dinner secondBite = new Dinner("二口目");
6 Dinner.calorie += 2.2; //カロリー追加
7
8 System.out.println(firstBite.appetizer);
9 System.out.println(secondBite.appetizer);
10 System.out.println("calorie:" + Dinner.calorie); //カロリーを出力
11 }
12 }
1 public class Dinner {
2 public String appetizer;
3 static double calorie; //クラス変数
4
5 public Dinner(String bite){
6 this.appetizer = bite;
7 }
8 }
一口目
二口目
calorie:3.7
追記した箇所にはコメント(//)を付けています。
注目していただきたいのはメインメソッドの4,6行目と10行目です。
calorieへは、インスタンス(dinner)を経由しないで、クラス名(Dinner)から直接アクセスしています。
インスタンス変数と同様、dinner.calorieでアクセスすることも可能ですが、static上に存在するものへアクセスする時、この方法は推奨されていません。
また、staticなものへはstaticなものからしかアクセスできません。
staticなcalorieへアクセスできるのは、メインメソッドもstaticなメソッドであるからです!(メインメソッドの2行目に注目)
クラス変数(staticで修飾した変数)は、イラストの青・赤・黃(濃淡両方)、全てのエリアに記載することができます。
クラス変数について自分の言葉でまとめてみましょう!
クラス変数とは
- staticで修飾された変数のことで、そのデータは通常の領域とは別のstatic領域に保持される。
- インスタンスとは別の領域に値が保持されるので、インスタンス間で共用が可能。
- staticなものからstaticな値・メソッドへはインスタンスを生成しなくてもアクセスできる。
- static領域は1つしかないので、同名のクラス変数を同じクラス内で作成することはできない。(別のクラスであれば可能)
また、Javaではクラス変数と同名のインスタンス変数を作成することができません。
1 public class Dinner {
2 public String appetizer;
3 static double calorie;
4 public int calorie;
5
6 public Dinner(String bite){
7 this.appetizer = bite;
8 }
9 }
▲このコードでは、3行目と4行目で変数名が重複しているため、コンパイルエラーとなります。
データ型がdoubleとintで異なっていますが、エラーとなりますし、データ型をdoubleで揃えてもエラーのままです。
グローバル変数
他言語の場合、グローバル変数とはclass定義の外側に書かれた、どこからでもアクセスすることができる変数を指します。
しかし、Javaには明確にグローバル変数と呼ばれるものがありません。
ではなぜ、グローバル変数という言葉を見聞きするのかというと、変数名の前に「public static」を付けることで同じ働き(=どこからでもアクセス可能)を実現することができるからです。
(それならこれがグローバル変数では?と思いますが、今回はとりあえず横に置いておきます)
グローバル変数とは、クラス変数の強化版と覚えておきましょう。
※クラス変数との違いは後述を参照ください。
また、@shiracamus(しらかみゅ)さんからコメントにて、サンプルコード付きでご教示いただいております! 是非、こちらもご覧ください!
グローバル変数もクラス変数と同様、どこにでも記載できます。(イラストの青・赤・黃(濃淡含む)エリア)
では、サンプルコードでグローバル変数について確認していきましょう!
▼一口毎にカロリーが加算されて、ご飯の総量が減るサンプルコード
public class App {
public static void main(String[] args) throws Exception {
Dinner firstBite = new Dinner("一口目");
Dinner.calorie += 1.5;
Meal.foodAmount -= 0.2; //MealクラスのfoodAmountへ直接アクセス
Dinner secondBite = new Dinner("二口目");
Dinner.calorie += 2.2;
Meal.foodAmount -= 0.4; //MealクラスのfoodAmountへ直接アクセス
System.out.println(firstBite.appetizer);
System.out.println(secondBite.appetizer);
System.out.println("calorie:" + Dinner.calorie);
System.out.println("foodAmount:" + Meal.foodAmount); //MealクラスのfoodAmountへ直接アクセス
}
}
public class Dinner {
public String appetizer;
static double calorie;
public Dinner(String bite){
this.appetizer = bite;
}
}
public class Meal {
public static double foodAmount = 10.0; //どこからでもアクセス可能
}
一口目
二口目
calorie:3.7
foodAmount:9.4
Dinnerクラスのフィールド変数(appetizer)にアクセスするには、インスタンスを生成して、生成したインスタンスを経由する必要がありましたが、public staticを付けたfoodAmountへは、クラス名を使って直接アクセスすることができます。(メインメソッドもstaticメソッドですからね!)
グローバル変数とは
- public staticで修飾された、どこからでもアクセスできる変数
- 広すぎるスコープはバグを生む原因となるため、乱用はしない
クラス変数とグローバル変数の違い
再度の念押しとなりますが、javaにはグローバル変数がありません。
そのため、ここではグローバル変数改め、どこからでもアクセスできる変数と記述します。
どこからでもアクセスできるということは、当然ながらアクセス修飾子はpublicとなります。
一方でクラス変数は「private」「記述なし」「protected」「public」を使って、アクセス範囲を制限することができます。クラス間で共用されない「private static String name」なんてクラス変数としては意味がありませんが、いちおう文法上は間違っていないため、コンパイルと実行は可能です。
つまり、クラス変数とグローバル変数の違いは、それが指す変数のアクセス修飾子、あるいはニュアンスによるものとなるのだと思います。断言できないのは、そもそもJavaにグローバル変数が明示するものがないためです。悪魔の証明じみていますね。
後書き
本記事の内容については、調べながらまとめていったので大きく誤っている部分はないかと思いますが、細かいニュアンスが異なっている可能性があります。
自身の理解が及び次第、適宜修正していきますので、是非この記事と併せて他の解説もご確認いただけますと幸いです!
また、本記事の投稿にあたり、下記の記事を参考にさせていただきました。
- 【Java入門】変数の種類、ローカル変数・フィールド変数・グローバル変数
- インスタンス変数、クラス変数の違い
- 【Java】変数を初期化しなくてもいい時・しなきゃいけない時(+初期化の方法いろいろ)
- 【Java入門】変数のスコープ(有効範囲、ローカル変数、インスタンス変数、static変数)
イラストやサンプルコードについて、同内容を記載するのは冗長かなと思ったりもしたのですが、そこだけ見ても分かるようにしておきたかったので、重複している箇所が多々あります。
見づらいかと思いますが、お目溢しくださいませ!