次のコードを検討してください。
_class Test {
Test() {
System.out.println("In constructor of Superclass");
}
int adds(int n1, int n2) {
return(n1+n2);
}
void print(int sum) {
System.out.println("the sums are " + sum);
}
}
class Test1 extends Test {
Test1(int n1, int n2) {
System.out.println("In constructor of Subclass");
int sum = this.adds(n1,n2);
this.print(sum);
}
public static void main(String[] args) {
Test1 a=new Test1(13,12);
Test c=new Test1(15,14);
}
}
_
スーパークラスにコンストラクタがある場合、子クラス用に構築するすべてのオブジェクトによって呼び出されます(例:クラス_Test1
_のObject a
はTest1(int n1, int n2)
を呼び出し、およびその親Test()
)。
なぜこれが起こるのですか?
このプログラムの出力は次のとおりです。
スーパークラスのコンストラクターで
サブクラスのコンストラクターで
合計は25です
スーパークラスのコンストラクターで
サブクラスのコンストラクターで
合計は29です
コンストラクターが呼び出されたときに、スーパークラス内のすべてのフィールドが初期化されていることを確認できるためです。
here の3.4.4を参照
はい。スーパークラスは、派生クラスも構築する前に構築する必要があります。そうしないと、派生クラスで使用できるはずの一部のフィールドを初期化できません。
ちょっとした注意:スーパークラスコンストラクターを明示的に呼び出して、いくつかのパラメーターを渡す必要がある場合:
baseClassConstructor(){
super(someParams);
}
スーパーコンストラクターは、派生コンストラクターへの最初のメソッド呼び出しでなければなりません。たとえば、これはコンパイルされません:
baseClassConstructor(){
foo();
super(someParams); // compilation error
}
Javaクラスは、次の順序でインスタンス化されます。
(クラスロード時)0.静的メンバーおよび静的初期化子ブロックの初期化子、宣言順に。
(新しいオブジェクトごと)
これがJavaの仕組みです。子オブジェクトを作成すると、スーパーコンストラクターが(暗黙的に)呼び出されます。
ここで、testをtest1クラスに拡張すると、uはtest1内のtestのすべてのメソッドと変数にアクセスできます。メモリが割り当てられている場合にのみuがクラスメソッドまたは変数にアクセスでき、そのためにデフォルトまたはパラメータ化されたコンストラクタが必要であるため、コンパイラはクラスを拡張しようとしていることに気付くことに注意してくださいuがすべてのメソッドにアクセスできるようにするためのスーパークラスコンストラクター。
簡単に言えば、スーパークラスにパラメーター化されたコンストラクターがある場合、子クラスコンストラクターの最初の行で明示的にsuper(params)を呼び出す必要があります。
「コンストラクターが明示的にスーパークラスコンストラクターを呼び出さない場合、Javaコンパイラーはスーパークラスの引数なしコンストラクターへの呼び出しを自動的に挿入します。スーパークラスに引数なしコンストラクターがない場合、コンパイル時エラーが発生します。オブジェクトにはそのようなコンストラクターがあるため、オブジェクトが唯一のスーパークラスである場合は問題ありません。」
(ソース: https://docs.Oracle.com/javase/tutorial/Java/IandI/super.html )
基本クラスコンストラクターは、派生クラスコンストラクターの前に呼び出されます。これは、派生クラスのコンストラクターが実行されるときに基本クラスが適切に構築されることを保証するため、理にかなっています。これにより、派生クラスの構築中に基本クラスのデータの一部を使用できます。
知っているように、これらのフィールドはオブジェクトの状態を表すため、オブジェクトの作成前にクラスのメンバー変数(フィールド)を初期化する必要があります。これらのフィールドが明示的に初期化されていない場合、コンパイラーは引数なしのデフォルトコンストラクターを呼び出して、暗黙的にデフォルト値を提供します。そのため、サブクラスコンストラクターはスーパークラスの引数なしのデフォルトコンストラクターを呼び出すか、コンパイラーによって暗黙的に呼び出されます。ローカル変数は、コンパイラーによってデフォルト値が提供されません。
サブクラスのオブジェクトを作成する場合、スーパークラスで定義されているすべてのメンバー関数とメンバー変数を考慮する必要があります。一部のスーパークラスコンストラクターで一部のメンバー変数が初期化される場合があります。 したがって、サブクラスオブジェクトを作成すると、対応する継承ツリー内のすべてのコンストラクターが上下に呼び出されます。
具体的には、変数がprotectedとして定義されている場合、サブクラスで常にアクセス可能になりますサブクラスが同じパッケージ内にあるかどうかに関係なく。ここで、サブクラスからスーパークラス関数を呼び出して、この保護された変数の値を出力する場合(スーパークラスのコンストラクターで初期化される場合があります)、正しい初期化値を取得する必要があります。したがって、すべてのスーパークラスコンストラクターが呼び出されます。
内部的にJavaは各コンストラクターでsuper()を呼び出します。したがって、各サブクラスコンストラクターはsuper()を使用してスーパークラスコンストラクターを呼び出すため、上下に実行されます。
注:変数ではなく関数をオーバーライドできます。
別の観点からこれに答えようとします。
Javaは自動的にスーパーコンストラクターを呼び出さなかった。クラスを継承する場合、スーパーコンストラクターを暗黙的に呼び出すか、自分で書き換える必要があります。スーパークラスがどのように機能するかについての内部知識がありますが、これは悪いことです。
背後でスーパーコンストラクターを呼び出すのは少し直感的ではないことに同意します。一方で、彼らがどのようにもっと直感的な方法でこれを行うことができたかはわかりません。
サブクラスのデフォルトコンストラクターには、デフォルトのsuper()呼び出しがあります。
//Default constructor of subClass
subClass() {
super();
}
サブクラスは、そのスーパークラスとそれらのフィールドからフィールドを継承しますhave構築/初期化されます(コンストラクタの通常の目的です:インスタンスが必要に応じて動作するように、クラスメンバーを初期化します。一部の人々が、それらの貧しいコンストラクターでより多くの機能...)
基本クラスプロパティを派生クラスに継承しているため、派生クラスコンストラクターが変数を初期化するために基本クラス変数の一部を必要とする場合があります。そのため、最初に基本クラス変数を初期化し、次に派生クラス変数を初期化する必要があります。 Javaが最初の基本クラスコンストラクターを呼び出し、次に派生クラスコンストラクターを呼び出します。
また、親クラスを初期化せずに子クラスを初期化しても意味がありません。
コンストラクターは、オブジェクトを動作可能にするロジックを実装します。オブジェクトはプライベートフィールドに状態を保持できるため、そのクラスのメソッドのみがそれらにアクセスできます。したがって、コンストラクターを呼び出した後にサブクラスのインスタンスを実際に動作させるには(つまり、基本クラスから継承したものを含むすべての機能はOKです)、基本クラスのコンストラクターを呼び出す必要があります。
これが、システムがこのように機能する理由です。
基本クラスのデフォルトコンストラクターが自動的に呼び出されます。これを変更する場合は、サブクラスのコンストラクターの最初の行にsuper()
を記述することにより、基本クラスのコンストラクターを明示的に呼び出す必要があります。
スーパークラスのコンストラクターが最初に呼び出されるため、プログラム内のすべてのメソッドが最初にヒープに存在し、コンパイル後にスタックに格納されるため、スーパークラスのコンストラクターが最初に呼び出されます。
両親が最初に出る!!そして実世界のように、子は親なしでは存在できません。したがって、子(サブクラス)クラスでthrmを使用するには、親(スーパークラス)を最初に初期化することが重要です。