【初心者用】Javaのメソッドに付ける『static』って何?!

Java

【初心者用】Javaのメソッドに付ける『static』って何?!

 

Javaでプログラムを記述する時、メソッドには必ず「static」を書くと思ってませんか? 取り敢えず、おまじないのようなものと思って深く考えずに書いてませんか? 「static」は重要なキーワードで正しく意味を理解しておく必要があります。今回は「static」について詳しく説明します。また、Javaのプログラムがどのように実行されるのか、簡単に解説します。

 

まず、この記事は以下のような人を対象としています。

対象者・staticの意味を知りたい人

・クラスメソッドとインスタンスメソッドの違いを知りたい人

・Javaのプログラムがどのようにして実行されているのか知りたい人

・スタック領域とヒープ領域の違いについて知りたい人

 

この記事を読むと、次のようなことが理解できるようになります。

この記事を読むとできること・メソッドを定義する時、staticが必要か理解できる

・クラスメソッドとインスタンスメソッドの違いが理解できる

・スタック領域とヒープ領域の動作について理解できる

 

太郎さん
太郎さん

メソッドの書き方の記事を読んだんですが、メソッドには「static」は付けないといけないのですか?

続石講師
続石講師

付けるかどうかは、どのようなメソッドを作るかによって変わってきます。

太郎さん
太郎さん

今までは「おまじない」と思って、特に考えずに書いてきたんですが、ダメなんですね?!

続石講師
続石講師

今回は「static」について説明しましょう!! Javaのプログラムがどのようにしてい実行されるかも、あわせて説明します。

 

メソッドの書き方を詳しく説明している記事は以下を参照してください。

【初心者の悩み解決!!】Java言語でのメソッドの書き方
多くの書籍ではmain()の中に全てのコードを記述していますが実際には複数のメソッドに分けて記述します。しかし、初心者はメソッドに分けて記述しようとすると記述方法が分からず、いきなりプログラムが書けなくなります。今回はメソッドの書き方を詳しく説明します。

 

static修飾子

「static」は「静的な」という意味で、メソッドとフィールド(クラス変数)に付けることができます。static修飾子が付いているメソッドを「クラスメソッド」、フィールドを「クラス変数」と呼び、static修飾子が付いていないメソッドを「インスタンスメソッド」、フィールドを「インスタンス変数」と呼びます。

クラスメソッドとクラス変数を「クラスメンバ」、インスタンスメソッドとインスタンス変数を「インスタンスメンバ」と呼びます。

 

クラスメンバはインスタンスを作成(new)しなくても利用できる」のに対し、「インスタンスメンバはインスタンスを作成(new)しないと利用できない」とよく説明されています。しかし、この説明だけでは少し分かりづらいのでサンプルプログラムで説明します。

 

クラスメソッドとインスタンスメソッド

以下のサンプルはどちらも「プログラムを実行したら、”Hello”と表示する」ものです。”Hello”と表示する処理はメソッド「showMennsage()」で定義しています。最初のサンプル「StaticMethod.java」はメソッドをクラスメソッドとして定義し、2つ目ののサンプル「NonStaticMethod.java」はメソッドをインスタンスメソッドとして定義しています。

「StaticMethod.java」では、メソッド「ShowMessage()」はクラスメソッドとして定義するため、「static」修飾子を付けます。クラスメソッドを実行する場合、8行目のようにいきなりメソッド名を記述します。

一方、「NonStaticMethod.java」では、メソッド「ShowMessage()」をインスタンスメソッドとして定義しているので、「static」修飾子は付けません。ただし、このインスタンスメソッドを実行するためには、インスタンスメソッドが記述されているクラスのインスタンスを生成しなければなりません。

このインスタンスメソッドが記述されているクラスは「NonStaticMethod」クラス、つまりこのクラス自身なので、8行目で自分自身のインスタンスを作成しています。自分のクラス定義の中で自分自身のインスタンスを作成するのは、少し変な感じがするかも知れません。この違和感はJavaのプログラムがどのように実行されているのか理解するとスッキリするはずです。

8行目でインスタンスを作成し、そのインスタンスを変数「myself」で参照できるようにしています。このmySelfで参照される領域内には、NonStaticMethodクラスで定義されているインスタンスメソッドが存在します。

11行目で実際にインスタンスメソッド「ShowMessage()」を実行しています。インスタンスメソッドを実行するには、「インスタンスを参照する変数名.メソッド名()」と記述します。

 

Javaのプログラムが実行される流れ

Eclipseなどでソースファイル(*.java)を作成し保存するとハードディスクなどの2次記憶装置内にファイルとして保存されます。また、ソースファイルをコンパイルすると中間(バイト)コード(*.class)が作成され2次記憶装置に保存されます。

ソースファイルは人間が理解できる形式で記述しており、コンピュータはそのままでは理解することができません。コンピュータが理解できる機械語の形式に変換したものが中間コードになります。

 

ユーザがプログラムを実行すると、保存されている中間コード(*.class)がメインメモリにロードされ「プロセス」が作成されます。プロセスは実行中のプログラムに関する様々な情報が保存されます。プログラムが終了すると、このプロセスがメモリから破棄され、利用していたデータが全て消去されます。

作成されたプロセスにはデータを保存するために3つの領域が作られています。それが「static領域」「スタック領域」「ヒープ領域」です。

static領域

static領域は特別な領域です。この領域に保存されるものはクラスメンバのみです。つまり「static」修飾子が付けられたクラスメソッドやクラス変数が保存されいます。

保存されたクラスメンバはプログラムが終了するまで消去されません。また、そのまま利用することができます。

StaticMethodクラスではshowMessage()メソッドがクラスメソッドとして定義されていたので、このメソッドの処理内容はstatic領域に保存されます。また、実行するにはStaticMethod.javaの8行目のようにメソッド名だけを記述します。

スタック領域

スタック領域は実行したメソッド名が順番に保存されます。また、宣言したローカル変数などものここに保存されます。

コンピュータの世界では、データを保存する方法として「スタック」と「キュー」の2種類があります。スタックは「FILO(First In,Last Out) 先入れ後出し」または「Last In,First Out(LIFO) 後入れ先出し」と呼ばれ、データを下から積み上げていき、上から取り出します。

メソッドを実行した時のスタック領域の内部は以下のようになります。

このプログラムを実行すると、エントリポイントのmain()メソッドが実行されます。メソッドを実行すると実行しているメソッド名がスタック領域に登録されます(①)。main()メソッド内ではクラスメソッド「methodA()」が実行されています。methodA()に処理が移り、メソッドが実行されるとスタック領域内のmain()メソッドの上にmethodA()が登録されます(②)。methodA()内では別のクラスメソッド「methodB()」が実行されています。したがって、処理がmethodB()に移り、メソッドが実行されます。この時、スタック領域内のmethodA()メソッドの上にmethodB()が登録されます(③)。methodB()の処理が全て終了すると、スタック領域から一番上に積み上げられているmethodB()の情報が消えます(④)。methodB()の処理が終了すると処理はmethodA()に戻ってきて、残りの処理を実行します。最後までmethodA()の処理が実行されると、スタック領域の一番上に積み上げられているmethodA()の情報が消えます(⑤)。この図にはありませんが、main()メソッドが終了するとスタック領域にあるmain()メソッドの情報も消え、プログラムが全て終了します。

このように「どのメソッドからどのメソッドを実行したのか」という情報がスタック領域に積み上げられて管理されています。

また、ローカル変数の情報もスタック領域に保存されます。

ヒープ領域

ヒープ領域にはnew演算子で生成されたインスタンスの情報が保存されます。

インスタンスメンバはこのヒープ領域に保存されます。

スタック領域内に保存された情報は処理が終了したらすぐに消去されるのですが、ヒープ領域内に保存された情報は処理が終了しても残り続けます。参照されなくなったら、ガーベージコレクションによって自動的に消去されます。

 

例えば、インスタンスメソッドはプログラム実行中に何度でも呼び出して実行することができます。これはインスタンスメソッドがヒープ領域内に保存されていて1回メソッドを実行しても、消えずに残っているので何度でも実行させることができるのです。

ヒープ領域の状況については「NonStaticMethod」クラスを元に説明します。

 

NonStaticMethodプログラムを実行すると、プログラムの内容がメモリにロードされ、プロセスが作成されます。プログラム内ではmain()メソッドがクラスメソッドとして定義されているので、main()メソッドはstatic領域に登録されます。

main()メソッド内の処理が開始されると、まず「NonStaticMethod mySelf = new NonStaticMeshot();」が実行されます。new演算子を使ってNonStaticMethodインスタンスを生成し、その領域を変数myselfで参照できるようにしています

この行を実行すると、まずスタック領域に変数「myself」が作成されます。次に、ヒープ領域内にNonStaticMethodクラスのインスタンスメンバが生成されます。インスタンスを作成するために2次記憶装置に保存されている「NonStaticMethod.class」を読み込みます。ヒープ領域内に登録されるメソッドはインスタンスメソッド「showMessage()」のみとなります。クラスメンバである「main()メソッド」は既にstatic領域に登録しているので、再度登録は行いません。

new演算子を使ってインスタンスを作成すると、毎回該当するクラスファイルをヒープ領域内に読み込みます。new演算子を使って作成したインスタンスはヒープ領域内では別々の場所に作成されるので、別のインスタンスとして認識されます。

 

最後に、ヒープ領域内に作成されたインスタンスを変数「myself」で参照できるようにmyself内に参照先(メモリ番地)が保存されます。

ここで、覚えてもらいたいことは、「クラスメソッドとインスタンスメソッドは保存される場所(領域)が異なる」ことです。

 

エントリポイント

どのプログラムにも、実行すると最初に処理されるメソッドが決まっています。この最初に実行されるメソッドのことを「エントリポイント」と呼びますJavaのプログラムでは、エントリポイントは「main()メソッド」と決まっています。

エントリポイントのmain()メソッドはプログラムが終了するまで残っていなければなりません。スタック領域やヒープ領域のデータはいずれ消えてしまうため、これらの領域にmain()メソッドを保存することはできません。したがって、main()メソッドをstatic領域に保存するためにstatic修飾子をつけてクラスメンバとする必要があります。

 

クラスメンバとインスタンメンバの参照制限

クラスメンバとインスタンメンバには次のような制限(注意事項)があります。

  1. インスタンスメンバは、インスタンスメンバに直接アクセスできる
  2. インスタンスメンバは、クラスメンバに直接アクセスできる
  3. クラスメンバは、クラスメンバに直接アクセスできる
  4. クラスメンバは、インスタンスメンバに直接アクセスできない

 

インスタンスメンバは、インスタンスメンバに直接アクセスできる

インスタンスはヒープ領域内に作成され、インスタンスメンバの情報が登録されています。情報として存在しているので、直接アクセスすることができます。

 

インスタンスメンバは、クラスメンバに直接アクセスできる

クラスメンバは、static領域に自動的に保存されています。インスタンスメンバと異なりnew演算子を使ってインスタンスを作成する必要がありません。

インスタンスメンバは、new演算子を使ってヒープ領域内に情報が登録されています。

static領域に保存されたクラスメンバは、プログラムが終了するまで残り続けているので、既にヒープ領域内に存在するインスタンスメンバはクラスメンバに直接アクセスすることができます。

 

クラスメンバは、クラスメンバに直接アクセスできる

クラスメンバは、static領域に自動的に保存されています。static領域に保存されたクラスメンバはプログラムが終了するまで残り続けているので、直接アクセスすることができます。

 

クラスメンバは、インスタンスメンバに直接アクセスできない

クラスメンバはstatic領域に保存され、インスタンスメンバはヒープ領域に保存されます。インスタンスメンバをヒープ領域に保存するためには、new演算子を使ってインスタンスを作成する必要があります。クラスメンバはプログラムを実行するといつでも利用することができますが、インスタンスメンバは意図的にnew演算子を使ってインスタンスを作成しなければ利用することができません。

したがって、クラスメンバは、インスタンスが生成されているかどうか不明なインスタンスメンバに直接アクセスすることはできません。アクセスするためには、まずnew演算子を使ってインスタンスを作成しなければなりません。

クラスメンバがインスタンスメンバを直接アクセスするようなコードを記述すると、コンパイルエラーとなります。

 

サンプルプログラム

以下のサンプルプログラムはクラスの中にクラスメンバとインスタンメンバが存在し、上記の4つのパターンの処理をおこなう処理が記述されています。

 

25行目はクラスメンバがインスタンスメンバにアクセスしようとしているので、コンパイルエラーとなっています。表示されるコンパイルエラーのメッセージは以下の通りです。

 

また、上記のサンプルプログラムが実行された時のメモリのイメージは次のようになります。

この図を見ると、クラスメソッド「staticMethod()」からインスタンス変数「instanceValue」にアクセスできるように思えるかもしれませんが、説明したようにインスタンス変数が100%存在している保証はありません。この図では、ヒープ領域に既にインスタンスがnewで作成されている状態を表してます。

 

インスタンスが生成されていない場合のメモリイメージは以下の通りです。

このように、インスタンスメンバが必ず存在している保証がないため、クラスメンバがインスタンスメンバを直接参照することは禁止されています。なお、インスタンスがヒープ領域に作成されている場合はコンパイルエラーとなりません。

 

まとめ

今回は、メソッドを定義する時に記述する「static」について説明しました。インスタンスを生成せずに実行したいメソッドには「static修飾子」を付けて定義します。staticが付けられたメソッドはstatic領域に自動的に保存され、プログラムが終了するまで消えずに残っています。static修飾子を付けるかどうかは、インスタンスを生成せずにプログラムが終了するまで残しておきたいか、ということで判断します。

また、Javaのプログラムが実行される際のメモリのイメージについても説明しました。JavaはC言語と異なり、直接メモリを操作する必要がないのでメモリに関して意識する必要がありません。しかし、今回説明したようにプログラムが実行される時にメモリ内がどのようになっているのかをイメージすることは大事です。

 

・プログラムを実行すると、static領域、スタック領域、ヒープ領域が作られる

・static修飾子が付いたクラスメンバはstatic領域に保存され、プログラムが終了するまで残っている

・インスタンスメンバはヒープ領域に保存され、使用しなくなったら消去される

・クラスメンバはインスタンスメンバを直接アクセスすることができない

 

 

続石講師
続石講師

static修飾子の説明からプロセスの話まで入り込んでしまいました。

説明は理解できましたか?

太郎さん
太郎さん

色々と難しい話がでてきたので、すぐに理解するのは難しいかも知れません。でも、メモリのイメージを知ることができて良かったです!!

続石講師
続石講師

クラスの継承などもメモリのイメージで考えると良いですよ!!

 

タイトルとURLをコピーしました