Javaの落とし穴

1. static変数へのアクセス
2.







































static変数へのアクセス

Javaのキーワード、staticは、常に、落とし穴が潜んでいるような気がします。
今日、Javaの学習中に、えっ、と思うことがありました。
以前にも、static宣言されている class変数は(例えば static PrintStream out;)、初期化しなくても、そのまま使えるというのは、あまり大きな声で言うべきことではないのですが、全然知りませんでした。
そして、今日、新たに、static変数の落とし穴を、発見してしまいました。
それは何かというと、ここは、テスト形式で、皆さんにも考えていただきましょう。

いくつになりますか?
こんなコードがあるとします。
という問題なのですが、まだ、インスタンス/オブジェクト を、作成したことがない方には、申し訳ありませんが、難しすぎます。

それで、このコードをある程度読める方だけに対しての問題になってしまいます。
では、このコードで、コンソール画面に出力される数字は、いくつでしょうか?

答えは、こうなります

では、どうしてこうなるかを、説明する前に、私を含め、間違って、「間違った答」 と、答えてしまった人は、どのように考えたのかを、見ていきましょう。
そして、その後、その考え方のどこが間違っているのかを、指摘したいと思います。

では、もう一度、問題のコードを、見てみましょう。

01:class の宣言、StaticActionクラスを、作ります。
02:インスタンス変数、changeable を、宣言し、12 を、代入します。
03:クラス変数、stat を、宣言し、34 を、代入します。
06:では、TryStaticActionクラス を使って、StaticActionクラス の 変数 の、
   アクションについて、調べてみましょう。
07:ここから、main()メソッド です。
08:変数の宣言で、int型、a,b,c,d としました。
10:ここで、StaticActionクラス型変数、sa1 の宣言と、
   インスタンス の 作成・代入 を、行っています。
11:インスタンス/オブジェクト sa1 の、インスタンス変数 changeable を、
   56 に、設定しました。
12:インスタンス/オブジェクト sa1 の、クラス変数/static変数 stat を、
   78 に、設定しました。(このやり方は、作法が悪い!)
14:ここで、StaticActionクラス型変数、sa2 の宣言と、
   インスタンス の 作成・代入 を、行っています。
15:インスタンス/オブジェクト sa2 の、インスタンス変数 changeable を、
   90 に、設定しました。
16:インスタンス/オブジェクト sa2 の、クラス変数/static変数 stat を、
   101 に、設定しました。(このやり方は、作法が悪い!)
18:先に宣言した、int型変数 a に、sa1.changeable (の値) を、代入します。
19:先に宣言した、int型変数 b に、sa1.stat (の値) を、代入します。
21:先に宣言した、int型変数 c に、sa2.changeable (の値) を、代入します。
22:先に宣言した、int型変数 d に、sa2.stat (の値) を、代入します。
24:int型変数、result に、c+b(cの値 と bの値 を、普通に足し算)を、代入します。
25:result の、値 を、コンソールに、プリントします。

ということですが、もし、あなたが、間違えたとしたら、
  c + b
の、ところで間違えたのでしょう。
では、それについて検討してみます。

まず、c の、値は?
21行目 と、15行目を、見ればわかる通り、90 ですね。
まあ、ここは、問題ないと思われます。

では、b の、値は?
ここは、19行目 と、12行目を、見て、78 だと思われます
ですが、ここが、間違っているのです!

24行目の、c + b が、実行される時点では、b の値は、78 ではない!のでした。
では、いくつなのでしょう???

答えは、101 になっているのです!
なぜなら、変数stat は、static変数、すなわち、クラス変数なのです!
つまり、StaticActionクラス および、その すべてのインスタンス に関して、共有される、変数 なのです!
ですから、24行目の、c + b が、実行される時点で、すでに、値 は、16行目の、
  sa2.stat = 101;
のコードによって、クラス変数 static は、101 に書き換えられているのですね。
ですから、当然、
クラス変数 を、参照している 変数b の値も、101 になります。
つまり、
  c + b は、90 + 101
となります。

では、ここで、クラス変数 は、初期化しなくても、使えることを見るために、こんなコードを、書いてみました。

ここで、注目すべきは、12行目の、
  StaticVariable.sVar = 100;
です。
前回のやり方ですと、この部分をこのように書いていました。
  StaticVariable sv1 = new StaticVariable();
  sv1.sVar = 100;
そして、前回は、こんなコードがあったので、余計に複雑になっていました。
  StaticVariable sv2 = new StaticVariable();
  sv2.sVar = 150;
何が、違うのかというと、
クラス変数/static変数 に、アクセスする/設定する 時は、
  クラス名.static変数名
というコードで、設定すべきなのです。
こうすれば、
  クラス名は、二つとなく、ひとつしかない
のですから、インスタンス変数から設定したときのように、どれが、本当の値なのか?迷うことはありませんね。

分かってしまえば、それほど難しい話ではないのですが、
  クラス変数、また、クラスメソッド も、、
  クラスを、初期化する前から使用でき、
  すべてのインスタンス で、共有される
ということが、分かっていないと、とても、難しい問題に思えてしまいます。

どちらかと言えば、基本的な話なので、まあ、私の実力がばれてしまった感じなのですが、また、0 とは言わなくても、1から、覚えていこうと、再確認した次第です。
これで、このトピックは、終わりにします。
本体の、Java入門の方が、まだ進んでいないので、何の話か分からなかった方には申し訳ありませんが、私もこのレベルなので、そんなには早く書けないことも、ご了承していただければと思います。

 項目へ戻る