JPIN#6 part2
staticってなんなん?
javaでコードを書いている上でよく出てくる単語にstaticというものがある
JPIN#6part1でもMainクラスに登場した
public static void main(String[] args) の部分である
ここで出てきた語句にはそれぞれ意味がある
まずpublicは他のクラスからの介入ができる、公開されたメソッドであるという意味である
voidは戻り値を受け取らないという意味である
mainはオブジェクトを実行するときはまずここから読んでくださいねってJVMに知らせているmainメソッドという意味である
String[] argsは配列argsはプログラムを起動するときに与えたコマンドライン文字列が文字列の配列として受け取るという意味である
ではstaticとは何か・・・
staticとはずばり領域への自動コピーということである
今までメソッドを使用するときは以下のように使用していた
A a = new A(); a.m;
これはヒープ領域にAクラスのコピーを作成してそのアドレスをaに記憶させる
そしてそのaよりAクラスを見てmメソッドを実行するという流れである
しかしもしこのmメソッドがstaticで宣言されていたらこれをする必要はなく
A.m;
で実行することができる
これはstaticを加えることでコンピュータはコードをメモリに読み込む際に自動的にstaticと書かれた部分をstatic領域と呼ばれる部分にコピーをしている
なので新たにヒープ領域にコピーせずに使用することができる、インスタンスを作る必要がない!
ちなみにstaticじゃないものをダイナミックという
またここで出てくるstaticは正しくはstatic修飾子という
staticのメモリの動き
まずは以下のコードを見ていこう
まずはAクラス
public class A { static int num; int value; static void hello() { System.out.println("hello"); } void test() { System.out.println("test"); } }
続いてこれを動かすMainクラス
public class Main { public static void main(String[] args) { A.num =10; System.out.println(A.num); A.hello(); A a = new A(); a.value = 10; a.test(); } }
まずこの両クラスのstaticと記載されたものを見てみよう
Aクラスではint型のnumとhelloメソッド、Mainクラスではmainメソッドである
これは最初にstatic領域にコピーされる
ではスタック領域には初めにmainメソッドから実行される
mainメソッドではまず最初に『A.num =10;』とあるので
Aクラスのnumに10を代入する
この時にnumはstatic領域にコピーされているのですぐに代入することができる
そしてnumを出力している
続いて『A.hello();』を実行しているのでhelloが出力される
続いて、
A a = new A(); a.value = 10; a.test();
とあるのでAクラスのコピーをヒープ領域にコピーしてその参照アドレスをaに記憶させる
そしてaに書いてあるvalueに10を代入する
次にaに書いてあるtestメソッドを実行する
するとtestと出力される
このようにしてstatic領域は使われているのである
気付いた人もいるかもしれないがstatic領域にコピーされると変数の書き込みが記憶されてしまうのである
よって一度描いてしまったらずっとそのままになってしまう
staticがない分についてはあくまでコピーを毎回している形なのでコピーごとに変数を代入して様々な出力をすることが可能である
staticのメリット
続いてstatic領域を使うメリットを紹介したい
以下のコードを見ながら確かめよう
まずはSingletonクラス
public class Singleton { private static Singleton instance; private String value; public String getValue() { return value; } public void setValue(String value) { this.value = value; } private Singleton() {} public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; } }
続いてこれを動かすMainクラス
public class Main { public static void main(String[] args) { Singleton a = Singleton.getInstance(); Singleton b = Singleton.getInstance(); a.setValue("hello"); System.out.println(b.getValue()); } }
まず最初にstaticを確認しよう
SingletonクラスではSingleton型instanceとSingleton型メソッドのgetInstance、Mainクラスではmainメソッドがそれに当てはまる
これらはまずstatic領域にコピーされる
ではスタック領域にmainメソッドが最初に来るのでそこから追っていこう
まず『Singleton a = Singleton.getInstance();』とあるのでSingletonクラスのgetInstanceメソッドをSingleton型のaに記憶させる
続いて『Singleton b = Singleton.getInstance();』とあるのでSingletonクラスのgetInstanceメソッドをSingleton型のbに記憶させる
次に『a.setValue("hello");』とあるので変数aに書いてあるsetValueメソッドに”hello"を渡して実行する
ここでaに書いてあるgetInstanceを見てみると、以下のようになっている
public static Singleton getInstance() { if(instance == null) { instance = new Singleton(); } return instance; }
今、instanceはnullなので新たにヒープ領域に作られたSingletonクラスのコピーのアドレスを記憶して返す
よって『a.setValue("hello");』は『instance.setValue("hello")』となる
よってヒープ領域にあるSingletonクラスのvalueに”hello"が代入される
次に『System.out.println(b.getValue());』とある
ここで同じくbにはgetInstanceメソッドが書かれており、現在はinstanceはnullではない(Singletonクラス①である)のでそのままinstanceを返す
よって『b.getValue()』は『instance.getValue()』となる
getValueの中身は以下になっている
public String getValue() { return value; }
今、valueには”hello"が入っているのでgetValueは"hello"になる
よって"hello"が出力されて終了する
ちなみに『private Singleton() {}』というコードが書いているがこれを入れることによってこのSingletonクラスはインスタンス化できないようにしている
さてstaticを使うメリットはこのようなSingletonクラスを作れることある
上記のシングルトン(パターン)はインスタンスを一つしか作らないことを「保証する」ための仕組みとなっている
ただ単にインスタンスを一つにしたいというだけならstaticフィールドにすればできるが、それを知らずに誰かがnewしてしまったらインスタンスが複数になってしまう
あるいは、staticフィールドにインスタンスを作りたいけど、初期化の順番をコントロールしたい、というケースもあるかもしれない
マルチスレッドでの動作を考慮しないといけないかもしれない
そういう時にこのようなクラスが役に立つ
細かい制御をしたいときはインスタンスを使う方がいい
クラスに同じ条件を与えてクラスの利用を制限したいときはstaticを利用すればいい
ということである