- ベストアンサー
『sun教科書 javaプログラマ』で勉強している者です
javaの資格を取るべく独学中の者なのですが、回答を読んでも何故そうなるのか理解できない問題に出くわして困っています。ご教授いただければ助かりますm(__)m [問題3-15] 次のコードがあります。 public class CodeWalkTwo { int x = 3; static int y = 2; public static void main(String[] args){ int x = 10; int y = 10; CodeWalkTwo boardWalk = new CodeWalkTwo(); boardWalk.printIt(); boardWalk.printIt(y); } {x = x+1;} static{y += y;} void printIt(){ System.out.print(++x); } void printIt(int y){ System.out.print(" " + ++y); } } 実行結果は次のどれですか。 A. 5 11 B. 11 11 C. 5 5 D. 17行目でコンパイルエラーが発生する E. 5行目と6行目でコンパイルエラーが発生する 正解はA。その理由は2・3行目で宣言されるインスタンス変数およびstatic変数と、5・6行目で宣言されるローカル変数とは異なるスコープだからなのだそうですが、解説が短すぎてよく分かりません>< なぜ、片方はインスタンス変数でもう一方はローカル変数を実行しているのでしょう?
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
実際の現場では、こういったようなコード(問題3-15)はあんまし好ましくないんですけどね。そもそも、一文字変数というのはループの中だけとか局所的、限定的に使われるべきであって、それこそ周りの開発者さんに忌み嫌われると思います。(私自身、そういう時代がありましたよ。今だからこそ、こういったような偉そうな口が叩けるんですけども。) まあ、ちょっとだけこの本の著者さんのフォローをするとすれば、実際にはこれと同じくらい、もしくは人によってはそれ以上とも言えるぐらい複雑な問題が出たりするんですよ。中には、全てのコードが表示されずに、「何行目から何行目まではこのようになっています。」みたいな感じで、かいつまんで出題されてたりしますからね。もちろん、それでも本当に理解できている人であれば、1コードずつ追っていくことができるわけでして。 >なぜ、片方はインスタンス変数でもう一方はローカル変数を実行しているのでしょう? 片方のインスタンス変数を実行しているprintIt()内では「this」が省略されていて、もう一方のローカル変数を実行しているprintIt(int y)内では自メソッドの引数が使われているからです。 実際には、以下のように変更してみるとよく分かることと思います。 // 18行目を以下のように改変。(3+1)+1=5で、結果は同じ。 System.out.print(++this.x); // 22行目を以下のように改変。(2+2)+1=5で、ローカル変数「int y = 10;」は使用されず。 System.out.print(" " + ++this.y); 厳密には、mainメソッド内で以下のようなコードも使用可能です。(コメント内は、いずれもインスタンス生成直後の所に追加した場合。) boardWalk.printIt(x); // 「int x = 10;」が使われる。 boardWalk.printIt(boardWalk.x); // 「int x = 3;」に、インスタンス初期化子で1を加算しインクリメントを1。 boardWalk.printIt(boardWalk.y); // 「static int y = 2;」に、静的初期化子で2を加算しインクリメントを1。 boardWalk.printIt(CodeWalkTwo.y); // 「boardWalk.y」が引数の場合と同じ。 // boardWalk.printIt(CodeWalkTwo.x); // 「int x = 3;」の先頭にstaticがないので、コンパイルエラー。 最後に、this.printIt()やthis.printIt(y)については、mainメソッド内で直接使用することはできません。(ヒントは、インスタンスメソッドです。)
その他の回答 (3)
- choconamacream
- ベストアンサー率44% (152/338)
>引数なしのメソッドの場合、その中で使われる変数はコンパイラによりthisキーワードが供給されてインスタンス変数あるいはstatic変数を呼び出す。そしてパラメータ変数が指定されていれば、そのメソッドが定義されているのと同じブロック内(スコープ内)のローカル変数にアクセスして実行する。 >ということでしょうか? その通りです(但し、今回のように「変数が同じ名前」の時のみ)。 今使われている本の最後のページまで読破し終えてから、もう一度このスレを読み直してみて下さい。その時には、また違った視点から理解が深まっていることと思います。(逆に言えば、なぜ私がこのような回答が出来るのかというと、一通りSJC-Pの勉強をし終えているからです。単にそれだけですね。)
お礼
SJC-P試験の経験でいらしたんですか!どおりで痒いところにまで手が届く丁寧な回答だったわけですね。 >使われている本の最後のページまで読破し終えてから、もう一度このスレを読み直してみて下さい。 そうしてみます。現在あと2章を残すのみのなったのですが、前の部分を忘却していないか心配です^_^;。 とびとびの返信でしたが最後までの回答ありがとうございました。お世話になりましたm(__)m
- choconamacream
- ベストアンサー率44% (152/338)
>ところで、このthisはコンパイラが自動的に提供しているキーワードだと思うのですが、どういうときに提供されたりされなかったりするのでしょう?void printIt(int y)内では提供されていないようなので。 thisキーワードは、基本的に省略可能となっています(入力してもしなくてもどっちでも同じ)。ただし、メソッド内などある特定のブロック{}内で同名の引数などと区別したい場合には、thisを使用します。これが#1さんがおっしゃっていた、 『インスタンス変数、static変数とローカル変数、パラメータ変数が同じ名前の場合、 ローカル変数、パラメータ変数が使われるという決まりがあります。』 の意味です。void printIt(int y)内でも一番外側で定義されているフィールドを強制的に使用したければ、thisを使ってパラメータ変数が使われないようにしないといけないということです。 >それと”もう一方のローカル変数を実行しているprintIt(int y)内では自メソッドの引数が使われているからです”の部分をもうすこし詳しく教えてもらえないでしょうか。static変数もローカル変数もint yであることに変わりないと思うのですが…。 同じ「int y」でも、それぞれのyのスコープが違います。基本的に、ある変数のスコープというのは、その変数が定義されているブロック({}で囲まれている部分)内でのみ有効です。 [問題3-15]のコードではなぜかコンストラクタがないため、以下のようなコードを作ってみました。 CodeWalkTwo(int y){ System.out.println("y=" + y + ",this.y =" + this.y); this.y=y; // 1.フィールドの初期化。 System.out.println("y=" + y + ",this.y =" + this.y); y=this.y; // 2.コンストラクタの引数値を無効化。(フィールドで上書き。) System.out.println("y=" + y + ",this.y =" + this.y); } mainメソッド内でのインスタンス生成時で、以下のように変更してから実行すると、次のような結果となります。 CodeWalkTwo boardWalk = new CodeWalkTwo(y); // コンストラクタの引数を指定。 [実行結果1→2] y=10,this.y =4 y=10,this.y =10 y=10,this.y =10 5 11 ちなみに、「this.y=y;」と「y=this.y;」の実行順序を逆にすると以下のような結果となります。 [実行結果2→1] y=10,this.y =4 y=4,this.y =4 y=4,this.y =4 5 11
補足
たびたびの親切な回答ありがとうございますm(__)m。理解できているのか自信がないので確認させてください。 引数なしのメソッドの場合、その中で使われる変数はコンパイラによりthisキーワードが供給されてインスタンス変数あるいはstatic変数を呼び出す。そしてパラメータ変数が指定されていれば、そのメソッドが定義されているのと同じブロック内(スコープ内)のローカル変数にアクセスして実行する。 ということでしょうか?
- neko_noko
- ベストアンサー率45% (146/319)
非常にややこしいので、変数x、yの名前をちょっと変えると、 class CodeWalkTwo { int instanceX = 3; //インスタンス変数 static int staticY = 2; //static変数 public static void main(String[] args){ int localX = 10; //ローカル変数 int localY = 10; //ローカル変数 CodeWalkTwo boardWalk = new CodeWalkTwo(); boardWalk.printIt(); boardWalk.printIt(localY); //(1)ローカル変数が使われる } {instanceX = instanceX+1;} //static修飾子 static{staticY += staticY;} //static修飾子(実はstaticYはここでしか使われていない) void printIt(){ System.out.print(++instanceX); //インスタンス変数 } void printIt(int parameterY){ //パラメータ変数 System.out.print(" " + ++parameterY); //(2)パラメータ変数が使われる } } ポイントはコメントで示した(1)、(2)ですが、 インスタンス変数、static変数とローカル変数、パラメータ変数が同じ名前の場合、 ローカル変数、パラメータ変数が使われるという決まりがあります。 あとは、順にどの値が入ってるかを追いかけてみれば見えてくると思います。
補足
変数名が全部一緒なんて、ほんと分かり難いですよね。たぶん著者はわざとしたんだと思いますけど。 >22行目を以下のように改変。(2+2)+1=5で、ローカル変数「int y = 10;」は使用されず。 System.out.print(" " + ++this.y); 本文のコードを上記のとおりに書き換えると実行結果が「5 5」になりました。なるほどvoid printIt()内では「this」が省略されていて、インスタンス変数が参照されるのですね! ところで、このthisはコンパイラが自動的に提供しているキーワードだと思うのですが、どういうときに提供されたりされなかったりするのでしょう?void printIt(int y)内では提供されていないようなので。 それと”もう一方のローカル変数を実行しているprintIt(int y)内では自メソッドの引数が使われているからです”の部分をもうすこし詳しく教えてもらえないでしょうか。static変数もローカル変数もint yであることに変わりないと思うのですが…。 他言語での経験もなくjavaで初めてプログラミングを学ぼうと志している身です。回答の理解がわるい点はご了承ください(>_<)