一番かんたんなJava入門

これからJavaを始めようという人の為の超入門サイトです。丁寧、簡単にこだわった解説なので初心者にぴったりです

【Java】 staticって何?

time 2013/07/05

sponsored link

staticじゃないメンバ(インスタンスメンバ)

 今回はstaticの意味を解説したいと思います。

 まだstaticの意味がよく分かっていない人も、書いたことは何回もあるんじゃないでしょうか?mainメソッドを書く時に出てきますよね。mainメソッドはstaticなメソッドなんです。

 staticなメソッドとかstaticな変数とかstaticなクラスなんかもあるんですが、このちょっと難解なstaticを理解するには、逆にstaticじゃない変数って何なのか?staticじゃないメソッドって何なのか?を理解すると分かりやすいです。

 なのでまずは非staticなメンバとはどういうものかを理解する為に以下のクラスを見てください。

Human.java

public class Human{
	String name;                         // メンバ1
        int birthday;                        // メンバ2
        int manpukudo;                       // メンバ3

        Human(String name, int birthday, int manpukudo){  // コンストラクタ1
                this.name = name;
                this.birthday = birthday;
                this.manpukudo = manpukudo;
        }
        Human(String name, int birthday){               // コンストラクタ2
                this(name, birthday, 50);
        }
        Human(String name){
                this(name, 0, 50);                     // コンストラクタ3
        }
        Human(){                                          // コンストラクタ4
                this("不明", 0, 50);
        }

        void eat(){                     // メンバ4
                this.manpukudo += 60;
        }
}

 続けて読んで頂いている人は知ってると思いますが、これは人間オブジェクトを作る為のHumanクラスです。

 このクラスにはご覧のように4つのメンバと4つのコンストラクタが存在します。

 4つのメンバというのは、メンバ変数のnamebirthdaymanpukudo
 それにメンバメソッドであるeat( )です。

 これらのメンバは全てstaticではありません。どこにもstaticとは書いていないですよね?staticな変数やメソッドにはその宣言部分にstaticと書く必要があり、何も書いていなかったら非staticだということです。

 なのでこのクラスのメンバは全て非staticなメンバです。非staticなメンバというのは、インスタンス固有のメンバだということです。

 「インスタンス固有の」という表現は全く難しく考える必要はありません。「名前」はもちろんそのインスタンスが持つ名前だし、「生年月日」も「満腹度」も「食べるという動作」も全て、それぞれのインスタンスに関連付いているというのが感覚的に分かるかと思います。この感覚がよく分からないという人はオブジェクトって何?から読み直すことをオススメします。

 このように非staticな変数・メソッドというのはインスタンスに関連したものなので、インスタンス変数(インスタンスフィールド)インスタンスメソッドという風に表現します。両方合わせてインスタンスメンバとも言います。

staticな変数(クラス変数)

 非staticなメンバがインスタンスに関連付いたものであるなら、逆にstaticなメンバとはどのようなものなのか?例としてこのクラスに一つstaticなメンバを加えたいと思います。

Human.java

public class Human{
        static int count_Human = 0;
	String name;
        int birthday;
        int manpukudo;

        //以下省略
}

 staticなcount_Humanというint型変数を加えてみました。上記のように変数の宣言の前にstaticを付ければstaticな変数になります。

 count_Humanはその名の通りHumanのカウントを表す変数です。つまり何人のHumanオブジェクトが作られたかをカウントする変数です。初めは0人なので0で初期化しています。Humanオブジェクトの数をカウントできるように、コンストラクタにちょこっと細工をします。

Human.java

public class Human{
        static int count_Human = 0;
	String name;                                      
        int birthday;                                     
        int manpukudo;                                 

        Human(String name, int birthday, int manpukudo){  // コンストラクタ1
                this.name = name;
                this.birthday = birthday;
                this.manpukudo = manpukudo;
                count_Human++;
        }
        Human(String name, int birthday){               // コンストラクタ2
                this(name, birthday, 50);
        }
        Human(String name){
                this(name, 0, 50);                     // コンストラクタ3
        }
        Human(){                                          // コンストラクタ4
                this("不明", 0, 50);
        }

        //以下省略
}

 一つ目のコンストラクタに「count_Human++」を足しました。これでコンストラクタが呼ばれたら、つまりHumanインスタンスが作られる度に、count_Humanの値が1増えます。
 1つ目以外のコンストラクタが呼ばれた時は増えないのか?と心配していただいた方、大丈夫です。2つ目、3つ目、4つ目のコンストラクタが呼ばれたらthis( )で1つ目のコンストラクタが呼ばれるようになっていますので、newする時に引数がいくつであろうと結局1つ目のコンストラクタが呼ばれてカウントは1増えます。(参考:〔コンストラクタとは〕 〔this( )の意味〕

 この「count_Human」つまり「Humanの人数」はインスタンス固有の値ではないというのは感覚的に分かりますよね?「名前」や「満腹度」はノブオ固有のものですが、「Humanの人数」はノブオ固有のものとは言えないですね。ノブオの人数ではないのですから。

 このcount_Humanはインスタンスに紐付いたものではなく、一体何に紐付いた値と言えるか??

 Humanクラス自体に紐付いた値と言えるのではないでしょうか?つまりstaticな変数count_HumanはHumanクラス固有の値と言えます。

 例えばHumanクラスとは別にDogクラスがあったとしてDogクラスにもそのインスタンスの数をカウントするstaticな変数count_Dogがあったとすると、それはDogクラスの固有の値と言えます。Humanクラス内のstaticなメンバは、(そのインスタンスではなく)Humanクラス自体に紐付いているメンバであり、Dogクラス内のstaticなメンバは、(そのインスタンスではなく)Dogクラス自体に紐付いているわけです。

 このようにstaticなメンバというのは、クラス固有のメンバという意味でとらえることができます。

 なので、staticな変数、staticなメソッドのことをクラス変数クラスメソッドと表現します。両方合わせてクラスメンバ、あるいは静的メンバとも言います。静的というのはstaticのことです。

 インスタンスに紐付いた値(名前や生年月日など)と、クラス自体に紐付いた値(作られたインスタンスの数など)の違いをしっかり理解する必要があります。

クラス変数(staticな変数)にアクセスする

 オブジェクトって何? 〔メンバ変数とは〕で説明した通り、インスタンス変数(非static変数)にアクセスするには、

インスタンスを表す変数名.メンバ

の形でした。具体的には、

Human human1 = new Human();
human1.name = "ノブオ";
System.out.print(human1.name);

 ↑このようにHuman型の変数human1を宣言して、そこにnewで作ったインスタンス(の参照)を代入することで、変数human1がHumanインスタンスを表す変数になり、「human1.name」の形で、「human1の名前」にアクセスできるので、そこにノブオを代入したり、その値を出力したりできるわけです。

 しかし、クラス変数(static変数)にアクセスする方法はちょっと違います。

 もし、同じように「human1.count_Human」とやってしまうと「human1の人数」という意味になってしまいます。意味がおかしいのが感覚的に分かりますよね?Humanオブジェクトをhuman1、human2、human3、・・・と作っていっても、「human1の人数」は1人です。

 count_Humanは「human1の人数」ではなく、あくまで「Humanの人数」です。大文字になっていることに注意してほしいのですが、つまりcount_HumanとはHumanクラス(オブジェクト)の数です。

 インスタンスに属するパラメータではなく、クラス自体に属するパラメータというのはそういうことです。繰り返しになりますが、そういうクラスに属するパラメータをクラス変数、static変数、静的変数(全部、同義)と言うわけです。

 ではクラス変数(static変数)にアクセスするにはどうするか?

クラス名.メンバ

 この形で、そのクラスのstaticなメンバにアクセスできます。クラスに属するパラメータなので、「クラス名.」の形になるのは意味的に理解できますね。
 では具体的にやってみます。

Human human1 = new Human();
System.out.print(Human.count_Human);

 これで実行すると「1」と出力されます。Humanインスタンスが一つ作られたことによってcount_Humanの値は1になっているからです。

 ↓こんな風にもできます。

HumanTest.java

public class HumanTest{
	public static void main(String[] args){
                System.out.println(Human.count_Human);  //①
		
                Human human1 = new Human();
                System.out.println(Human.count_Human);  //②
                
                Human human2 = new Human();
                System.out.println(Human.count_Human);  //③
	}
}

 ①の時点ではまだHumanオブジェクトは作られていないので「count_Human」の値は「0」、②の時点では「1」、③の時点では「2」になるので、このクラスを実行すると、

static1

という風に出力されます。

 ところで、①に注目してもらいたいのですが、クラス変数(static変数)に関してはそのクラスのインスタンスが存在しなくても、「Human.count_Human」というように、その値にアクセスすることができます。逆にインスタンス変数の場合は、もちろんインスタンスが存在しなければそのパラメータ自体存在しないので、インスタンスを作ってからでないとアクセスできません。

 当然ですね、「human1.name」という風にインスタンス変数にアクセスできるのは、human1というインスタンスが存在するからであって、human1インスタンスが無ければアクセスのしようがありません。もちろんインスタンス変数だけでなくインスタンスメソッドについても同様です。

全てのインスタンスに共通するパラメータ

 もう一つ、微妙にニュアンスが違うstaticの使い方として、全てのインスタンスに共通するパラメータとして使う方法があります。

 例えば人間の腕は2本、足は2本です。これはインスタンス固有のパラメータというよりも全てのインスタンスに共通するパラメータです。

 そのようなパラメータを定義したい場合は以下のようにすることができます。

Human.java

public class Human{
    static int count_Human = 0;
    static int count_arms = 2;
    static int count_legs = 2;
    String name;                                      
    int birthday;                                     
    int manpukudo;

    //省略

}

 こうしておけば、以下のような使い方ができます。

HumanTest.java

class HumanTest{
    public static void main(String[] args){
        Human human1 = new Human("Nobuo");
        System.out.println(human1.name + "の腕は" + human1.count_arms + "本です");
    }
}

 

HumanTest.javaの実行結果

Nobuoの腕は2本です

 ご覧のように、ここではhuman1.count_armsという風にインスタンス変数のようにアクセスしています。

 先程の例のcount_Humanに関しては、各インスタンスとは直接関係のない値ですが、この場合は各インスタンスに共通するパラメータとして使っているのが分かると思います。

 count_Humanの値は、インスタンスとは関係がないものなのでHuman.count_Humanという風にクラス名を使ってアクセスしていました。

 一方、count_armsの値は、human1.count_armsという風にインスタンスが持っている値としてアクセスしています。

 意味的にはこのアクセスの仕方で正しいと言えますが、実はstaticな変数へアクセスする方法はどちらでも構いません。human1.count_Humanとしても、Human.count_armsとしても文法上は全く問題ありません。

 要はstatic変数は、アクセスする方法は「クラス名.」でも「インスタンス名.」でもどちらでも問題ないものの、インスタンスごとに固有の値を持つことはできない変数だと言えます。

 逆に言えば、全てのインスタンスに(必ず)共通な値を持てる、あるいは、クラスにとって固有の値を持てるということです。

まとめ & 注意

 staticのニュアンスがなんとなく理解できましたでしょうか?

 一言でいうと、クラスに属するのがstatic、インスタンスに属するのが非staticだということです。

 そして一つ注意。

 クラスメソッド(staticなメソッド)から、インスタンス変数やインスタンスメソッド(非staticなメンバ)にアクセスすることはできません。

 「this」を使って、そのインスタンス変数にアクセスすることが出来るのはメンバメソッドって何?で説明しましたが、それはインスタンスメソッド内(あるいはコンストラクタ)だからこそ可能なんです。

 「このインスタンス」を意味する「this」は、インスタンスメソッド内で使うからこそ、「このインスタンス」がどのインスタンスを指しているか分かるのであって、インスタンスとは無関係に呼ばれるstaticメソッド内から「this.name」とやっても、誰の名前やねん!ってことになるのでコンパイルエラーになります。

 インスタンスの有り無しに関係なく使えるクラスメソッドからインスタンス変数にアクセスしようとしても、一体どのインスタンスのパラメータにアクセスしようとしているのか判断のしようがありませんからね。

 例えば、

Main.java

public class Main{
        int a;
	public static void main(String[] args){
                a = 10;
	}
}

 こんなことはできません。コンパイルエラーになります。「a」は非staticなので、このMainクラスのインスタンス変数として宣言されています。それをstaticメソッドであるmainメソッドから触ることはできません。

 これもアウトです。↓

Main.java

public class Main{
        int a;
	public static void main(String[] args){
                this.a = 10;
	}
}

 上述した通り、staticメソッド内で「this」を使うことはできません。インスタンスを作ってもいないのに「このインスタンス」も何もありません。

 mainメソッドがあるクラスは特別扱いしてしまいがちですが、mainメソッドを持つクラスでもnewすることでインスタンスを作ることもできます。なので、以下のように書けば一応コンパイルは通ります。

Main.java

public class Main{
        int a;
	public static void main(String[] args){
                Main main = new Main();
                main.a = 10;
	}
}

 こんなことを実際することがあるのか知りませんが。。もちろんインスタンスを作ろうが、staticメソッド内で「this.」は使えません。

 逆にstaticな変数なら、staticなメソッドからでもアクセスできます。staticな変数というのはインスタンスに関係なく存在できるものなので、同じくインスタンスとは関係ないstaticなメソッドから触るのは全然OKです。↓

Main.java

public class Main{
        static int a;
	public static void main(String[] args){
                a = 10;
	}
}

 これでも大丈夫です。↓

Main.java

public class Main{
	public static void main(String[] args){
                int a;
                a = 10;
	}
}

 これなら、「a」はMainクラスのメンバとしてではなく、mainメソッド内のローカル変数として宣言されていることになるので、もちろんそのメソッド内では自由に触れます。

 ローカル変数というのはこのようにメソッド内や、あるいはfor文やif文のブロック内で宣言された変数のことを言います。ローカル変数のスコープ(存在範囲)はその宣言されたブロック内だけです。

 逆に言えば、クラスブロック直下に宣言されているのがメンバ変数です。混乱しないように整理して覚えておきましょう。

 ちなみにローカル変数にstaticを付けることはできません。staticか非staticかというのは、そのクラスのメンバに限り設定できるということです。

 次回は、アクセス修飾子とゲッターについての説明です。

sponsored link

Androidアプリを作ろう

コメント

  • すごくわかりやすかったです!
    プログラム経験は一応あるものの、staticが何なのかイメージが掴めず困っていたところにこのページに出会い、理解することができました。
    プログラムに対する苦手意識が消えました!

    by 匿名 €2015/09/27 16:07

  • 前半のときのように一例を出して実行などして画面をたくさんみせてほしかったです。最後のほうで理論的な話が多くなってしまって前半に比べてわかりづらかったです。続編期待しています。

    by とおりすがり €2015/11/22 20:57

  • ご意見ありがとうございます。

    by Nobuo@管理人 €2015/11/23 22:17

down

コメントする



一番かんたんなJava入門

Androidアプリの作り方

忘備録

私の作ったAndroidアプリ

おすすめ入門書

おすすめプログラミングスクール

無料で試せる今がチャンス!

管理人

Nobuo_CREATE

Nobuo_CREATE

WordPressテーマPrinciple、マテリアルを作ったり、Androidアプリを作ったり、Java入門サイトを作ったり、本を書いたりしています。どうぞよろしく。 [詳細]



sponsored link

オススメ書籍

[オススメpoint]

 この本は全く何も分からない初心者の方にお勧めです。プログラミングをするには覚えなければならない事が無茶苦茶いっぱいありますが、この本は教えてくれる順番、その構成が素晴らしいです。RPGのゲームを作るというストーリーにのっとってちょっとずつ難しいことを教えてもらえます。
 無機質で膨大なデータが載っているような本は読む気にならないという方は、こういうストーリー仕立ての本でチャレンジしてみてはいかがでしょうか?(注:RPGを作る為の本ではありません。)

[オススメpoint]

 ある程度、Javaを読み書きできるようになったら、オブジェクト指向について学ぶべきです。本書は、抽象的で分かったような分からんようなオブジェクト指向について、非常に分かりやすい例を出して説明してくれています。オブジェクト指向とは何なのか?という本質を掴むのにこれほど適した本はないと思います。オブジェクト志向の理念を理解できれば、より効率のいいコードをより楽に書けるようになるはずです。Java上級者を目指すなら必読の一冊!

只今、急拡大中

JavaからのRuby on Rails入門

JavaからのRuby on Rails入門

COBOLからのJAVA習得