Javaに関する様々な情報をご紹介します。

Javaに関する様々な情報をご紹介します。
評価

0

インスタンス化

以下のプログラムにおいて
    
a.なぜ14行目の「QC qc = new QC();」が実行されるのでしょうか?
b.なぜ5行目の「qa.qc.qcMtd();」がOKで、4行目の「qa.qb.qbMtd();」がコンパイルエラーとなる理由は何でしょうか?

1   public class Q {    
2       public static void main (String args[]){    
3           QA qa = new QA();    
4   //     qa.qb.qbMtd();    
5           qa.qc.qcMtd();    
6       }
7   }
8    
9   class QA{    
10      QA(){    
11          System.out.println("QA-Constractor");    
12          QB qb = new QB();    
13      }    
14      QC qc = new QC();    
15  }
16    
17  class QB{    
18      QB(){    
19          System.out.println("QB-Constractor");    
20      }    
21      void qbMtd (){ System.out.println("QB-Method");}    
22  }    
23
24  class QC{    
25      QC(){    
26          System.out.println("QC-Constractor");    
27      }    
28      void qcMtd (){
29               System.out.println("QC-Method");
30      }    
31  }    
--------------------    
プログラムの実行結果    
QC-Constractor    
QA-Constractor    
QB-Constractor    
QC-Method
--------------------    

b.(続き) 尚、4行目の「qa.qb.qbMtd();」は、次のように9行目の後に「QB qb」を追加し12行目を「qb = new QB();」と書き換えるとコンパイルエラーとならず、実行も正常に終了します。
......
4        qa.qb.qbMtd();
......
9   class QA{
         QB qb;  
10      QA(){
11          System.out.println("QA-Constractor");    
12          qb = new QB();    
13      }

c.また、「b.続き」において12行目を「QB qb = new QB();」とすると、コンパイルは通るのですが、実行時「NullPointerException」がでてしまいます。仕様の何に抵触しているのでしょうか?


たぶん「継承」、「初期化」あたりの問題と思いますが、よく分かりません。JAVAの仕様を理解したいので回答宜しくお願いします。

6

回答

5622

閲覧

6件の回答

評価

0

コンストラクタのスペル。

インスタンスのスコープを勉強する必要がある。
基礎であり、ものすごく重要なところ。

評価

0

a. 言語仕様
クラスQAのフィールドを宣言し、
同時にインスタンス生成している。
どうしてって言われても、
あなたがそうなるようにコーディングしてるから。

b. スコープの問題
qbは、QAのコンストラクタ(10〜13行)の間でしか
存在しないローカル変数。

c. これもスコープの問題
ローカル変数と、フィールドは、同じ名前をつける事が可能だが、
その場合は、変数名だけ指定するとローカル変数を指定した事になる。
同名のローカル変数がある場合、フィールドの方を使いたかったら、
this.変数名でアクセスする。

評価

0

先のインスタンス化の質問に対するご指摘、ご回答ありがとうございました。この質問は、JAVAはmainメソッドだけから処理を開始し処理の枝葉を広げていくものとばかりとの考えから、インスタンス生成が別途行えるのはなぜかの疑問からでした。JAVA仕様の不理解そのものですが、手持ちのJAVA本にはこの辺のところが見当たらず、質問した次第です。
尚、自分なりに疑問解消へ、インスタンス化について色々テストしてみました。以下のような仕様になっているようです。名称など含め自己流で纏めてありますので、ご指摘お願いします。

A. メインクラスはロードされると自動的に、
(1)最初にクラス変数の処理が記述順に行われ、
(2)次にmainメソッドの処理が開始される。
A+ メインクラスは、自身をインスタンス化する都度、自身に定義のインスタンス変数の処理の記述順実施と(続いて)自身のコンストラクタ処理が行われる。

B. クラスはインスタンス化されると自動的に
(1)    最初にクラス変数の処理が記述順に行われ(最初のインスタンス化の1回だけ)
(2)    次にインスタンス変数の処理が記述順に行われ(インスタンス化の都度)、
(3)    その次に、コンストラクタが実行される(インスタンス化の都度)。

class DA {
    DA (int i) { System.out.println(" ----- DA Constructor " + i); }  //コンストラクタ
    DB dbi = new DB(300);                            // インスタンス変数の処理
    static DB dbs = new DB(400);                    // クラス変数の処理
}
class DB {
    DB (int i) { System.out.println(" ----- DB Constructor " + i); }  //コンストラクタ
}
public class D {
   D (){ System.out.println(" ----- Dmain Constructor"); }    //コンストラクタ
   DA dai = new DA(100);                              // インスタンス変数の処理
   DA daj = new DA(888);                              // インスタンス変数の処理
   static DA das = new DA(200);                    // クラス変数の処理
   static DA dass = new DA(555);                   // クラス変数の処理
   public static void main (String args[]){          // クラスメソッド
       new D();                                   // 自身のインスタンス化
   }
}
/*  実行結果
 ----- DB Constructor 400
 ----- DB Constructor 300
 ----- DA Constructor 200
 ----- DB Constructor 300
 ----- DA Constructor 555
 ----- DB Constructor 300
 ----- DA Constructor 100
 ----- DB Constructor 300
 ----- DA Constructor 888
 ----- Dmain Constructor
*/

評価

0

「メインクラス」という考えはしない方がいい。
それはあくまで呼び出す側の選択で、作る側は単
にmain()という開始エントリをどのクラスに書い
ておくか、だけ。
main()は複数あっても良いし、main()の中で自身
をインスタンス化する必要はない。
それどころか、main()で一つもインスタンス化を
しなくても良い。

評価

30

A.
mainが実行されたからって、
mainが記述されているクラスの
すべてのメンバ変数が初期化される訳ではないよ?

クラスロードされた時に、staticなフィールドが初期化されます。

B.(1)
×最初のインスタンス化の1回だけ
これも、クラスロードされた時に、staticなフィールドが初期化されているから。

public class A {
  A() {
    System.out.println("A: constructor.");
  }
  public static void main(String[] args) {
    System.out.println("main: start.");
    B.staticMethod();
    System.out.println("main: method called.");
    B b1 = new B();
    System.out.println("main: instance 1 created.");
    B b2 = new B();
    System.out.println("main: instance 2 created.");
  }
}

class B {
  static C c1 = new C(1);
  C c2 = new C(2);

  B() {
    System.out.println("B: constructor.");
  }

  static void staticMethod() {
    System.out.println("B: static method.");
  }
}

class C {
  C(int a) {
    System.out.println("C: constructor(" + a + ")");
  }
}

-------- 実行結果
main: start. 
  ↑mainが実行されたからと言って、
    Aのコンストラクタは呼ばれない。
C: constructor(1)
  ↑インスタンスは生成していないけど、
    staticメソッドを実行する為にロードされたから。
B: static method.
main: method called.
  ↓ここからBのインスタンス化
C: constructor(2)
B: constructor.
main: instance 1 created.
C: constructor(2)
B: constructor.
main: instance 2 created.

評価

0

なるほど、B(1)を見落としてた。

クラスへの最初の参照がインスタンス化であれば、タイミングは同じなんだけどね(実際そういう処理が多いだろうし)。

satomi氏のコードではstaticメソッドだけど、staticフィールドへの参照でもいいし、またはClass.forName()でもいい。

ロードとインスタンス化を切り離して考えられれば、main()のあるクラスとないクラスの扱いに違いがないことが理解できるんだろう。
main()というstaticメソッドはインスタンス化とは関係がない。

質問から6ヶ月以上経過しているので、回答を書き込むことはできません。