ITエンジニアのプレイングマネージャー化応援サイト

9.インスタンスの活用

この記事では、当社 の新人エンジニア研修の参考にJava8を解説します。

前回はクラスメソッドについて解説しました。

今回はインスタンスの活用について解説します。

いままでは、話をできるだけ単純にするためにインスタンスの話題はできるだけ避けてきました。

しかし、ここからはいよいよオブジェクト指向の本丸、クラスからインスタンスを作成するということについて学んでいきます。

クラスは設計図、インスタンスは実物でしたね。

 

1.オブジェクト指向とは

Objectとは、モノのことです。

この記事の1回目でお話ししたように、オブジェクト指向とは他の工業製品と同じように

部品を組み合わせることでプログラム全体を作り上げるという考え方でした。

例えば、皆さんがお使いのパソコンも多くの部品から構成されています。

私たちは、それぞれの部品の内部構造を知らなくても、それらを組み合わせて使うことができます。

例えば、パソコンとプリンタをつないで印刷することができます。

また、それぞれの部品単位でアップデートすることもできます。

例えば、古くなったハードディスクを新しいものに交換する、などといったことができます。

この考え方をプログラムに応用したのがオブジェクト指向です。

 

つまり、オブジェクトはプログラムの部品です。

そして、オブジェクトの設計図がクラスです。

クラスには、オブジェクトに共通する属性(情報や機能)を定義します。

この設計図をもとに作成した実際の部品がインスタンスです。

人によって言葉の使い方に多少のずれがありますが、この研修では、

オブジェクト(抽象概念) = クラス(設計図) + インスタンス(具体的なもの)

という言葉で使い分けています。

単にオブジェクトといった場合には、それがクラスのことを指すこともあれば、インスタンスのことを指すこともあります。

ですからこの原稿ではオブジェクトという言葉の使用は極力避けて、クラスとインスタンスという言葉を使うようにしています。

 

例えば、今現在あなたが見ているこの画面も、ウインドウというインスタンスの上にメニューというインスタンスが乗っていて、

クリックの情報を受け取るインスタンスがあるという風になっているというと少しはイメージの助けになりますでしょうか?

 

これは比喩的な表現ですが、パソコンクラスというものを作ったとして、

あなたや隣の人の机に乗っている具体的なパソコンがインスタンスです。

新入社員クラスがあったとして、あなたやあなたの隣の人がそのインスタンスです。

身近な例を思いつくままに挙げてみてください。

 

2.フィールドを持ったクラスの宣言

例えば、新人エンジニアのクラスを作成してみましょう。

話を単純化するために社員番号と名前だけを持っているとします。

メインメソッドがないので実行できませんが、これで立派にクラスを宣言できました。

NewEngineer1クラスは二つの情報を持っています。

これらの情報を総称してフィールドと呼びます。

では、さっそく一人のエンジニアを誕生させてみましょう。

NewEngineer1クラスを使うExample01というクラスを作成します。

以下のサンプルプログラムを見てください。

実行する前に上から順番に解説していきます。

 NewEngineer se1;

ここでは、NewEngineer型の変数se1を宣言しています。

いままでも「String str」といった形を目にしてきましたね。

se1 = new NewEngineer();

ここで、変数se1に代入して初期化しています。

何を代入したのかというとNewEngineerクラスのインスタンスです。

イメージとしては一人のエンジニアを作り出したわけです。

この時、newという演算子を使っています。

new演算子は配列のところでも出てきましたね。

実は、new演算子はクラスの全てのインスタンス・メンバーをメモリーにコピーするための演算子です。

また、NewEngineer()というのはコンストラクタというものです。

コンストラクタは文字列のところでも出てきました。

コンストラクタはクラス名と同じ名前のメソッドのようなものです。

コンストラクタの作り方は後で学びますので、ひとまずコンストラクタを利用するには

new クラス名(実引数);

と書くのだということをおさえましょう。

 

なお、宣言と初期化を1行で書くこともできます。

NewEngineer se1= new NewEngineer();

 

実行してみます。

<結果>

0
null

実は、フィールドはローカル変数と違い、初期化する必要はないのですが、初期値が以下のように決まっているのです。

真偽値 false
整数型 0
浮動小数点型 0.0
文字型 \u0000
参照型 null

では、あたらめてフィールドに値を代入してみます。

<結果>

1
yamada

 

3.メソッドを持ったクラスの宣言

先の新人エンジニアクラスにメソッドを加えてNewEngineer2としてみましょう。

先ほどの例ではIDと名前をそれぞれprintlnメソッドを使って表示させていましたが、一度にIDと名前を表示するshowメソッドを加えることにします。

簡易的にこのクラスを実行するためには、このクラスにメインメソッドを追加してやりました。

<結果>

私のIDは2、名前はtabuchiです。

メソッドの

インスタンスメソッドを呼び出すときは

変数名.メソッド名(実引数)

でしたね。

ちなみに、このように自分で作成したクラスにメインメソッドを書き足して簡易的に実行すると単体テストが容易になります。

後で削除しなければなりませんが。

 

4.インスタンスの生成

 

上記サンプルプログラムに以下のように書き足して、登場人物を2人にしてみましょう。

※同じフォルダ内にNewEngineerというクラスが2つ存在することができないため、ここでは、先のNewEngineerクラスをそのまま使っています。

<結果>

My id is 1
My name is imai

 

では、以下のようなプログラムを実行したら何が表示されるでしょうか?

<結果>

NewEngineer@15db9742

私の環境では、上記のように表示されました。

これは、配列のところで見た参照ですね。

ここで、参照について見ていきましょう。

 

5.参照型と基本型

実は、Javaで使用できる変数の型には基本型(プリミティブ型)と参照型があります。

基本型は、boolean、int、 doubleなど値そのものが入っています。

一方、参照型は、その名の通りインスタンスへの参照が入っています。

決してインスタンスそのものが入っているわけではありませんので注意しましょう。

NewEngineer yamazaki = new NewEngineer();

と書いたときに、変数yamazakiにはNewEngineerクラスのインスタンスへの参照が代入されているのです。

 

したがって、一つのインスタンスを2つの参照が指しているという状態を作ることも可能です。

以下のサンプルプログラムを見てください。

<結果>

yamazaki
yamazaki

<図解 スタック領域とヒープ領域>

 

6.参照型の配列

参照型の配列を作成することもできます。

以下のサンプルプログラムを見てください。

<結果>

1:tabuchi
2:shinohara
3:kokubun

 

7.NullPointerException

このとき、起こしがちなミスとして、配列は作ったものの、インスタンスを作り忘れるというものがあります。

例えば、以下のサンプルプログラムを見てください。

<結果>

Exception in thread “main” java.lang.NullPointerException
at Example35_5.main(Example35_5.java:7)

7行目で「NullPointerException」が発生しました。

Pointer(参照)がNull(空)であるというException(例外)です。

se[0]には何も入っていないのに、その参照を使おうとしたことが原因です。

基本型では起こりえませんが、参照型の場合は、参照に何も入っていないnullという状態があるのです。

おそらく、これから皆さんが学習を進めていくうえで、一番多く遭遇するのがこのNullPointerExceptionです。

しかも、このNullPointerExceptionは非検査例外といってプログラマの責任で必ず対処しておかなければならない例外なのです。
例外のところで詳述します。

よって、IDEもチェックしてくれません。

nullは、特別な値で参照型の変数に代入することができます。

<結果>

null

nullは文字列の”null”とは別物ですし、””(空文字)とも数値の0とも違うものです。

<結果>

str1はnullです
str2はnullではありません
str3はnullではありません

ちなみに、Javaにはガーベージコレクション(Garbage collection:ゴミ集め)という機構があり、どこからも参照されなくなったインスタンスはメモリから消去されます。

このガーベージコレクションの仕組みがないと、プログラマが意図的にガーベージコレクションをしてやらないといけないためメモリリークというエラーの原因になります。

今では当たり前ですが、Javaが登場した際にはガーベージコレクションの仕組みを備えていたことも画期的なことでした。

 

8.インスタンス変数の初期値

また、インスタンス変数は、インスタンスが生成されたときに自動的にnullで初期化されるということも覚えておいてください。

<結果>

0 : 0.0 : false : null : null

また、インスタンス変数が基本型であった場合はそれそれ、数値は0、真偽値はfalseで初期化されることも併せて押さえてください。

になみに、ローカル変数の初期値はどうでしたでしょうか?

そうですね。ローカル変数は初期化してからしか使えない(初期値は不定)ということを改めて思い出しておいてください。

 

9.参照渡しと値渡し

インスタンスの参照をメソッドの引数として渡すことができます。

引数にインスタンスを渡すことでたくさんのデータを一つの引数で渡すことができるようになります。

例えば、以下のサンプルプログラムを見てください。

<結果>

1 : yamazaki
2 : imai

この引数の渡し方を参照渡しといいます。

参照渡しの場合のインスタンスは同一のものを指していますから、呼び出し元のインスタンスと呼び出し先のメソッドのインスタンスは同じです。

つまり、メソッド側でインスタンスに変更を加えた場合は、元のインスタンスにも変更が及びます。

その説明をしているのが以下のサンプルプログラムです。

<結果>

1 : yamazaki_san
yamazaki_san

これは、基本型を引数に渡した時とは違う結果です。

基本型を引数に渡した場合は、値はコピーされて呼び出し元の値と呼び出し先のメソッドの値は別物(コピー)になります。

以下のサンプルプログラムで確認してください。

<結果>

11
10

この引数の渡し方を値渡しといいます。

Javaだけではなく、多くのプログラミング言語で共通の概念です。

 

10.メソッドの戻り値に参照を使う

参照をメソッドの戻り値にすると複数のデータを一度に戻すことができます。

メソッドの戻り値は一つのみでしたが、この方法を使うことで実質は複数の値を同時に返すことが可能になります。

以下のサンプルプログラムを見てください。

<結果>

これらの例では簡単すぎて、メソッドで参照を扱うことのメリットはあまり感じられなかったかもしれません。

しかし、これから研修が進むにつれ、その恩恵を受けることができるようになります。

 

11.コンストラクタ

クラスにはフィールドとメソッドがありました。

実はもう一つ、クラスにはコンストラクタというものがあります。

英語で”constructor”とは「建設者」という意味があります。

つまり、コンストラクタとはインスタンスを作り出すモノのことです。

インスタンスが生成されるときに自動的に実行される特別なメソッドのようなものだと思ってください。
※正確には、コンストラクタがヒープメモリーの必要容量を用意します。

コンストラクタの構文は以下の通りです。

クラス名(引数列) {

  命令文

}

つまり、コンストラクタはクラスと同じ名前になります。

引数を渡せますので、インスタンスを初期化するのに使うことができます。

ただし、戻り値は定義できません。なぜなら、インスタンスそのものが戻り値だからです。

以下のサンプルプログラムを見てください。

<結果>

3 : tabuchi

このとき、

this.id = id;

のようにthis参照を使っていることに注目してください。

“this”=「この」ということで記述されている自分自身のインスタンスを指してます。

このthisは、以下のように仮引数の名前を変えることで付けずに済ますことも可能ですが、分かりやすくなるのでつける方が良いでしょう。

NewEngineer2(int a, String b) {

 id = a;

 name = b;

}

this.を省略すると、ローカル変数や引数にも同じ名前の変数があった場合、そちらが優先されてしまうという問題もあります。

 

なお、今までコンストラクタを作成することなくインスタンスを作成してきました。

それは、Javaによってデフォルト・コンストラクタ(default constructor)というものが補われていたからでした。

デフォルト・コンストラクタは、上記の例では以下のような形をしています。

NewEngineer2() {
}

つまり、引数も命令文もない形です。

インスタンスをヒープ領域に作成するだけのコンストラクタです。

また、this(引数)と記述すると自分自身のコンストラクタを呼び出すことができます。
※ただし、この記述ができるのはコンストラクタの最初の1行でなくてはいけないというルールがあります。

以下のサンプルプログラムを見てください。

<結果>

0 : 未定

このとき、以前、メソッド学んだオーバーロード(多重定義)が使われていることに注目してください。

オーバーロードとは、同じクラスの中でメソッド名と戻り値の型が同じで、

引数の型や数、並び順が違うメソッドを複数定義することをいいました。

コンストラクタもメソッド同様、呼び出し時に指定される引数によって実行されるコンストラクタが区別されるのです。

また、コンストラクタから自分のメソッドを呼び出すこともできます。

以下のサンプルプログラムを見てください。

<結果>

My id is 4.
My name is imai.

これまで見てきたとおり、Javaにはインスタンスごとに存在する変数・メソッドと、クラスで唯一存在する変数・メソッドがありました。

では、これらはどのように使い分けるのが良いのでしょうか?

いまからその点について学んでいきましょう。

 

12.インスタンス変数とクラス変数の使い分け

インスタンス変数は、個々の実体(インスタンス)ごとに固有の情報を保持する目的で使用します。

クラス変数は、クラス全体で共通の1つの情報を保持する目的で使用します。

クラス変数の代表例をJavaのAPIから探せば、クラスMathのPIがあります。

public static final double PI

その名の通り、円周率を表すフィールドです。

publicはこのフィールドがクラス外に公開されていて他のどのクラスからも利用できることを示しています。

publicというのは英語で「公共の」という意味がありますね。

staticというキーワードがクラス変数であることの宣言になります。

staticというのは英語で「静的な」という意味があります。動的にインスタンスを作らなくても良いということを表しています。

finalというのは定数ということです。変数のように変えない、変えてはいけないということを宣言しています。

finalというのは英語で「最後」という意味ですから、これ以上変化しない、最後の値であるということを表現しているわけです。

円周率の値がプログラムによって違っていては(普通は)困りますので、すべてのクラスから共通で使える公開情報にしているのです。

さらに、「3.141592653589793」というリテラルをプログラマが円周率のつもりで使っても、プログラムを読む人には伝わりません。

“PI”と書かれているからこそ「円周率なのだ」というメッセージを発信することができるのです。

 

なお、定数は大文字を使うという命名規則がありました。

命名規則は強制ではありませんが、Javaプログラマーが守っているルールですから皆さんも従うようにしてください。

 

値を表示させてみましょう。

<結果>

3.141592653589793

 

皆さんが作成するクラスでも、クラス共通の情報としたいもの、個別の情報としたいものがあると思います。

例えば、車を例にとって考えてみましょう。

車と行っても抽象的な車ではなく、あなたの家にあるような具体的な車です。

例えば、ここではプリウスとしましょう。

クラス変数の例:タイヤの数、ターボの有無、ガソリンタンクの大きさ、など

インスタンス変数の例:ナンバープレートの情報、現在のガソリン量、現在のスピード、など

このような例が思い浮かぶと思います。

 

以下にクラス変数を使ったサンプルプログラムを見てみましょう。

例えば、今まで作成してきたNewEngineerクラスにおいてクラス変数にすべきものを挙げてみましょう。

例として、新人エンジニアの人数はどのインスタンスが保持すべきでしょうか?

人数の情報は個々のインスタンスが持つべき情報ではありませんね。

クラス変数とすべきです。

以下のサンプルプログラムを見てください。

<結果>

My id is 4.
My name is imai.
There are 1 people.

My id is 3.
My name is shinohara.
There are 2 people.

My id is 2.
My name is tabuchi.
There are 3 people.

このように個々のインスタンスに持たせることのできない変数はクラス変数にします。

しかし、クラス変数を使いすぎると様々な箇所で変更可能な変数ができてしまいます。

変数にはスコープ(範囲)という概念があり、できるだけスコープは狭くすべきです。

スコープを狭くすることでプログラマーが意図しない変数の使用を避けるようにすべきです。

 

13.インスタンスメソッドとクラスメソッドの使い分け

次に、クラスメソッドはどんな時に使ったらよいでしょうか?

ここでもJava8のAPIにその範を求めましょう。

Mathクラスのrandomメソッドを見てみましょう。

このクラスの説明を読むと以下のようになっています。

0.0以上で1.0より小さい、正の符号の付いたdouble値を返します。戻り値は、この範囲からの一様分布によって擬似乱数的に選択されます。

ゲームなどで有効そうなメソッドですね。

以下のサンプルプログラムを見てください。

結果は様々な乱数が表示されます。

この時に例えば、Mathのインスタンスを作ってそのインスタンスのrandomメソッドを呼び出すというのはどうでしょうか?

もちろんそうしてもいいと思います。

これはプログラミングというよりは設計の問題になりますが。

しかし、まどろっこしいですね。

乱数を発生させるためだけにインスタンスを生成して、その都度使い捨てるのは、メモリやCPU時間の無駄です。

そこで、Javaの設計者はクラスから直接呼び出せるクラスメソッドにした訳です。

 

ちなみに、Mathクラスはその名の通り数学のためのクラスです。

数学でよく使う様々な計算ツールが揃っています。

このようなクラスをユーティリティクラスと呼ぶことがあります。

MathクラスをAPIで見てみましょう。

コンストラクタの定義が見当たりませんね。

こんどはJavaのソースコードを見てみましょう。

IDEをお使いのことと思いますので、ソースコードのMathをキーボードのコントロールキーを押したままクリックしてみてください。

Javaのコースコードを見ることができました。

クラスの宣言のすぐ下に以下のようなコンストラクタが見えますか?(私の環境では110行目でした)

private Math() {}

ここでコンストラクタがprivateで宣言されています。

privateはpublicの反対の意味で非公開ということですね。

つまり、このコンストラクタは他のクラスからは呼び出せないようになっているのです。

その意図は、インスタンスを作らせない純粋なユーティリティクラスであるという宣言です。

Mathクラスはインスタンスを作れないクラスなのですね。

 

最後にメソッドの呼び出し方をまとめておきます。

同じクラス内に定義されているメソッド メソッド名(実引数)
インスタンスに定義されているメソッド 変数名.メソッド名(実引数)
スタティックなメソッド クラス名.メソッド名(実引数)※ただし、変数名.メソッド名(実引数)と書いてもコンパイラが直してくれる
スーパークラスのメソッド super.メソッド名(実引数)※未出

 

ここまで理解できたら以下の練習問題を解いてみましょう。

7.オブジェクトの利用

 

今回はインスタンスの活用について見てきました。

次回のテーマは継承(拡張)です。

これも、オブジェクト指向特有のテーマです。

親クラスを拡張して子クラスを作っている巧妙なJavaの仕組みを学んでいきましょう。

 

JavaSE8の解説に戻る

新入社員研修ポータル

IT企業の人財育成に関することなら全てお任せ下さい TEL 0120-559-463 受付時間 10:00 - 17:00 (土・日・祝日除く)

ZOOMを使った遠隔研修メニュー(PDFが開きます)

ZOOMを使った遠隔研修

新人エンジニアのためのJavaタイピングゲーム

新人プログラマのためのプログラミング動画

YouTubeチャンネル

お問い合わせはこちらから

    お名前 (必須)

    メールアドレス (必須)

    題名(件名)

    メッセージ本文

    確認画面は表示されません。上記内容にて送信しますので、よろしければチェックを入れてください。

    新入社員研修ポータル

    PAGETOP
    Copyright © Say Consulting Group, Inc. All Rights Reserved.