Developer

【はじめてのJava】インスタンス化 ~復習問題~
2022.10.31
Lv1

【はじめてのJava】インスタンス化 ~復習問題~

はじめてのJava

このシリーズでは、初めてJavaやプログラミングを勉強する方向けに、Javaによるプログラミングの基礎を説明していきます。
目標レベルは、Javaの資格試験の一つである「Oracle Certified Java Programmer, Silver」(通称Java Silver)に合格できる程度の知識の習得です。
はじめてJavaやプログラムに触れる方にもできるだけわかりやすい解説を心がけていきます。


インスタンス化 ~復習問題~

設計図であるクラスをもとに実体となるインスタンスを作成する「インスタンス化」について学習を進めてきました。今回は「インスタンス化」の内容を復習し、JavaSilverにおいてよく狙われるポイントを確認していきます。
特に、インスタンス化においては、static変数・インスタンス変数・ローカル変数のスコープや特徴の違いを正確に理解しておく必要があります。

目次


インスタンス化のおさらい

インスタンス化とは、クラスを基にして新しくインスタンスを生成することを言います。
インスタンス化のメリットとしては、1つのクラスから複数のインスタンスを作成することが可能な点が挙げられます。
(例えば、車の色を変数に持つCarクラスを作成すると、変数を自由に変更し異なる色の車のインスタンスを作成できます。)
クラスやインスタンス化の考えを利用してプログラムを作成しておくことで、ソースコードの変更や修正の手間を最低限にできます。
ソースコードては、3種類の変数[static変数・インスタンス変数・ローカル変数]が頻繁に用いられますが、それぞれ書き方やスコープ(変数の利用可能な範囲)が異なるため十分理解しておきましょう。
簡単に各変数の特徴をまとめると下記の通りです。

static変数
クラス内で共用される変数
有効範囲は、変数を宣言したクラスを用いることが可能な範囲。(staticメソッド内でも利用できる・またインスタンス化の必要もない)

インスタンス変数
インスタンス毎に保持している変数。
インスタンスと紐づき、インスタンス化することで利用できる。(staticメソッド内ではそのまま利用することはできない)

ローカル変数
メソッド内部やメソッド引数、for文内で宣言された変数
有効範囲は宣言されたメソッドや構文内のみ。

インスタンス化と変数に関する各記事は、下記を参照してください。

(1)インスタンス化                           記事はこちら
(2)変数
[1]static変数                              記事はこちら
[2]インスタンス変数           記事はこちら
[3]ローカル変数                    記事はこちら


問題

それでは、さっそく問題を解いてみましょう!

public class Cat {
	String name = "タマ"; 
	static String call = "にゃあ";

	void Getcall() { 
		System.out.println("名前は" + name + "と言います。" + call + "と鳴く!");
	}
}

このプログラムを利用した以下のクラスを、コンパイル・実行したときの結果として、正しいものを選びなさい。

public class Main {
	public static void main(String[] args) {

		Cat a = new Cat();

		a.name = "ポチ";
		Cat.name = "レオ";
		
		a.call = "わん";
		Cat.call = "がおー";

		a.Getcall();
	}
}

A. 「名前はタマと言います。にゃあと鳴く!」表示される。
B. 「名前はポチと言います。わんと鳴く!」表示される。
C.    「名前はレオと言います。がおーと鳴く!」表示される。
D. 「名前はタマと言います。がおーと鳴く!」表示される。
E.      実行時に例外がスローされる。
F.      コンパイルエラーが発生する。


解答

解答 F

Mainの結果

>javac Main.java
>java Main
Exception in thread “main” java.lang.Error: Unresolved compilation problem:
非 static フィールド Cat.name を static 参照できません
at Cat.Main.main(Main.java:8)

解説

今回の問題を解く上で注意すべきポイントは、2点です!

[1]static変数とインスタンス変数のスコープの違い
[2]static変数とインスタンス変数の参照方法の違い

これら2点に注目し、設問を見ていきましょう!

MainクラスはCatクラスをインスタンス化し、変数の代入やメソッドの実行を行っています。
4行目ではCatクラスのインスタンス化を行っており、6・7行目では変数nameを参照し値の代入、9・10行目では変数callを参照し値を代入、そして12行目ではメソッドGetcallを実行しようとしています。

この時、重要なのが各変数・メソッドがstatic・インスタンス・ローカルのいずれによって定義されているかです。
Catクラスに遡って確認すると、変数name・メソッドGetcallがインスタンス、変数callがstaticで宣言されていることが分かります。(変数name・変数callにはそれぞれ「タマ」「にゃあ」が初期値として代入されています。)

ここで、変数nameがインスタンス変数であることで生じる問題が1点あります。それは、Mainクラス7行目での変数nameの参照方法です。
6行目は4行目で作成したインスタンスを基に「インスタンス名.変数名」の形・7行目ではインスタンス化せずに「クラス名.変数名」の形で、変数を参照及び値の代入を行おうとしています。
インスタンス変数は、インスタンス化を行わずに参照することはできません。ですので、7行目でエラーが発生します。

したがって、解答は、F.     コンパイルエラーが発生する。になります!

[補足]
なおstatic変数はインスタンスを作成せずとも参照することが可能です!
よって9行目のインスタンス化せずに「クラス名.変数名」の形、10行目のインスタンスを基に「インスタンス名.変数名」の形のいずれによっても、変数の参照と値の代入は可能です。

仮にエラーの原因である、7行目での変数nameの参照行を削除するとプログラムは正常に動作します。

public class Cat {
	String name = "タマ"; 
	static String call = "にゃあ";

	void Getcall() { 
		System.out.println("名前は" + name + "と言います。" + call + "と鳴く!");
	}
}
public class Main {
	public static void main(String[] args) {

		Cat a = new Cat();

		a.name = "ポチ";
		//Cat.name = "レオ"; この行を削除する!

		a.call = "わん";
		Cat.call = "がおー";

		a.Getcall();
	}
}

この時、変数name・変数callには再代入された最も新しい値に上書きされます。
したがって変数nameはポチ・変数callはがおーになります。
よってメソッドGetcallの実行結果は、「名前はポチと言います。がおーと鳴く!」表示される。 となります!

Mainの結果

>javac Main.java
>java Main
名前はポチと言います。がおーと鳴く!

次回の内容

今回はインスタンス化の復習問題を扱いました。

次回は、インスタンス化の際に実行される特殊なメソッドコンストラクタに関して学びます。

次回記事はこちら


はじめてのJavaシリーズの記事一覧はこちら
シリーズ全記事一覧
環境構築編
プログラムの作り方編
データ型と変数編
演算子編


java 11 の練習問題一覧はこちら
はじめてのJavaシリーズの練習問題一覧はこちら