コンストラクターに関する一般情報
Конструктор
はメソッドに似た構造で、その目的はクラスのインスタンスを作成することです。デザイナーの特徴:
- コンストラクターの名前はクラスの名前と一致する必要があります (慣例により、最初の文字は大文字で、通常は名詞です)。
- どのクラスにもコンストラクターは存在します。コンストラクターを作成しなくても、Java コンパイラーはデフォルトのコンストラクターを作成します。このコンストラクターは空で、スーパークラスのコンストラクターを呼び出すこと以外は何も行いません。
- コンストラクターはメソッドに似ていますが、メソッドではなく、クラスのメンバーともみなされません。したがって、サブクラスで継承またはオーバーライドすることはできません。
- コンストラクターは継承されません。
- 1 つのクラス内に複数のコンストラクターが存在する場合があります。この場合、コンストラクターはオーバーロードされていると言われます。
- クラスでコンストラクターが定義されていない場合、コンパイラーはパラメーターなしのコンストラクターをコードに自動的に追加します。
- コンストラクターには戻り値の型がありません。型であることさえできません
void
。型が返された場合void
、クラス名との一致にもかかわらず、それはもはやコンストラクターではなくメソッドです。
- 演算子はコンストラクター内で使用できます
return
が、戻り値のない空のみです。
- コンストラクターではアクセス修飾子の使用が可能です。修飾子 、 のいずれかを設定することも、
public
修飾protected
子private
なしで設定することもできます。
- コンストラクターには、修飾子
abstract
、final
、native
、static
またはsynchronized
;を含めることはできません。
- キーワードは、
this
同じクラス内の別のコンストラクターを参照します。使用する場合、その呼び出しはコンストラクターの最初の行である必要があります。
- キーワードは
super
親クラスのコンストラクターを呼び出します。使用する場合、それへの参照はコンストラクターの最初の行でなければなりません。
super
コンストラクターが (引数の有無にかかわらず) 祖先クラスのコンストラクターを呼び出さない場合、コンパイラーは引数なしで祖先クラスのコンストラクターを呼び出すコードを自動的に追加します。
デフォルトのコンストラクター
どのクラスにもコンストラクターは存在します。コンストラクターを作成しなくても、Java コンパイラーによってデフォルトのコンストラクターが作成されます。このコンストラクターは空であり、スーパークラス コンストラクターを呼び出すこと以外は何も行いません。それらの。こう書くと:
public class Example {}
この場合、これは次のように書くことと同じです。
public class Example
{
Example()
{
super;
}
}
この場合、祖先クラスは明示的に指定されておらず、デフォルトではすべての Java クラスがそのクラスを継承する
Object
ため、クラス コンストラクターが呼び出されます
Object
。クラスでパラメーター付きのコンストラクターが定義されているが、パラメーターなしでオーバーロードされたコンストラクターがない場合、パラメーターなしでコンストラクターを呼び出すとエラーになります。ただし、Java バージョン 1.5 以降では、可変長の引数を持つコンストラクターを使用できます。また、可変長の引数を持つコンストラクターがある場合、デフォルトのコンストラクターを呼び出してもエラーにはなりません。可変長の引数が空になる可能性があるため、これは行われません。たとえば、次の例はコンパイルできませんが、可変長の引数を指定してコンストラクターのコメントを解除すると、コンパイルと実行が正常に行われ、コード行が実行されます
DefaultDemo dd = new DefaultDemo()
。コンストラクターが呼び出されます
DefaultDemo(int ... v)
。当然のことながら、この場合は JSDK 1.5 を使用する必要があります。ファイル
DefaultDemo.java
class DefaultDemo
{
DefaultDemo(String s)
{
System.out.print("DefaultDemo(String)");
}
public static void main(String args[])
{
DefaultDemo dd = new DefaultDemo();
}
}
コンストラクターのコメントを外したプログラム出力の結果:
DefaultDemo(int ...)
ただし、クラスがコンストラクターをまったく定義していない一般的なケースでは、デフォルトのコンストラクターの置換が自動的に行われるため、デフォルトのコンストラクターを (パラメーターなしで) 呼び出す必要があります。
オブジェクトの作成とコンストラクター
オブジェクトを作成するときは、次のアクションが順番に実行されます。
- オブジェクトクラスは、プログラム内ですでに使用されているクラスの中から検索されます。そこに存在しない場合は、プログラムで利用可能なすべてのカタログおよびライブラリ内で検索されます。ディレクトリまたはライブラリでクラスが検出されると、クラスの静的フィールドが作成され、初期化されます。それらの。各クラスについて、静的フィールドは 1 回だけ初期化されます。
- オブジェクトにメモリが割り当てられます。
- クラスのフィールドは初期化中です。
- クラスのコンストラクターが実行されます。
- 作成され初期化されたオブジェクトへのリンクが形成されます。この参照は、オブジェクトを作成する式の値です。オブジェクトは、
newInstance()
クラスメソッドを呼び出して作成することもできますjava.lang.Class
。この場合、パラメーター リストのないコンストラクターが使用されます。
コンストラクターのオーバーロード
同じクラスのコンストラクターは、同じ名前と異なるシグネチャを持つことができます。このプロパティは、組み合わせまたはオーバーロードと呼ばれます。クラスに複数のコンストラクターがある場合、コンストラクターのオーバーロードが存在します。
パラメータ化されたコンストラクタ
コンストラクターの署名は、パラメーターの数と型、およびコンストラクター パラメーターのリスト内のそれらの型の順序です。戻り値の型は考慮されません。コンストラクターはパラメーターを返しません。このステートメントは、ある意味、Java がオーバーロードされたコンストラクターまたはメソッドをどのように区別するかを説明しています。Java は、オーバーロードされたメソッドを戻り値の型ではなく、入力パラメータの数、型、および型の順序によって区別します。コンストラクターは type を返すことさえできません
void
。それ以外の場合は、たとえクラス名に似ていても、通常のメソッドになります。次の例はこれを示しています。ファイル
VoidDemo.java
class VoidDemo
{
VoidDemo()
{
System.out.println("Constructor");
}
void VoidDemo()
{
System.out.println("Method");
}
public static void main(String s[])
{
VoidDemo m = new VoidDemo();
}
}
その結果、プログラムは次のように出力します。
Constructor
これは、コンストラクターが戻りパラメーターのないメソッドであることを再度証明します。ただし、コンストラクターには 3 つの修飾子
public
、 、
private
または のいずれかを指定できます
protected
。例は次のようになります。
VoidDemo2.java
class VoidDemo2
{
public VoidDemo2()
{
System.out.println("Constructor");
}
private void VoidDemo2()
{
System.out.println("Method");
}
public static void main(String s[])
{
VoidDemo2 m = new VoidDemo2();
}
}
コンストラクター内に演算子を記述することができます
return
が、戻り値のない空の演算子のみを記述できます。ファイル
ReturnDemo.java
class ReturnDemo
{
public ReturnDemo()
{
System.out.println("Constructor");
return;
}
public static void main(String s[])
{
ReturnDemo r = new ReturnDemo();
}
}
可変長引数でパラメータ化されたコンストラクター
Java SDK 1.5 では、コンストラクターとメソッドの可変長引数という待望のツールが導入されました。以前は、不定数のドキュメントが 2 つの不便な方法で処理されていました。1 つ目は、引数の最大数が少数に制限され、事前にわかっていることを保証するように設計されています。この場合、メソッドに渡される引数のリストのバージョンごとに 1 つずつ、メソッドのオーバーロードされたバージョンを作成することが可能でした。2 番目のメソッドは、事前に未知のものと多数の引数を対象に設計されています。この場合、引数は配列に配置され、この配列がメソッドに渡されました。可変長の引数は、変数の初期化を伴う後続の操作に関与することがほとんどです。予期されるコンストラクターまたはメソッド引数の一部が欠落している場合は、デフォルト値で置き換えると便利です。可変長引数は配列であり、配列として扱われます。たとえば、
Checking
可変数の引数を持つクラスのコンストラクターは次のようになります。
class Checking
{
public Checking(int ... n)
{
}
}
文字の組み合わせ ... は、可変数の引数が使用されること、およびこれらの引数が参照値が変数 n に含まれる配列に格納されることをコンパイラーに指示します。コンストラクターは、引数をまったく使用しない場合も含め、異なる数の引数を指定して呼び出すことができます。引数は自動的に配列に配置され、n を介して渡されます。引数がない場合、配列の長さは 0 です。パラメータのリストには、可変長の引数とともに必須パラメータを含めることもできます。この場合、可変数の引数を含むパラメータは、パラメータのリストの最後になければなりません。例えば:
class Checking
{
public Checking(String s, int ... n)
{
}
}
非常に明らかな制限は、可変長パラメータの数に関するものです。パラメータ リストには可変長パラメータが 1 つだけ存在する必要があります。2 つの可変長パラメーターが与えられた場合、コンパイラーは、一方のパラメーターがどこで終わり、もう一方のパラメーターが始まるかを判断することは不可能です。例えば:
class Checking
{
public Checking(String s, int ... n, double ... d)
{
}
}
ファイル
Checking.java
たとえば、車のナンバープレートを認識し、各車が日中に訪れたエリアのマス目の番号を記憶できる装置があります。エリアマップに従って、記録された車両の総質量から、その日中に指定された 2 つの広場、たとえば 22 と 15 を訪れた車両を選択する必要があります。車が 1 日中に多くの広場を訪れることも、場合によっては 1 つしか訪れることもできないのはごく自然なことです。明らかに、訪問できる広場の数は車の物理的な速度によって制限されます。クラス コンストラクターが引数として、必須パラメーターとしての車の番号と、エリア内の訪問済みの正方形の数 (その数は可変です) を取る小さなプログラムを作成してみましょう。建設者は、2 つのマス目に車が出現したかどうかを確認し、出現した場合は、その番号を画面に表示します。
コンストラクターにパラメーターを渡す
プログラミング言語には主に 2 種類のパラメータがあります。
- 基本型 (プリミティブ);
- オブジェクトへの参照。
「値による呼び出し」という用語は、コンストラクターが呼び出し側モジュールによって渡された値を受け取ることを意味します。対照的に、参照による呼び出しは、コンストラクターが呼び出し元から変数のアドレスを受け取ることを意味します。Java は値による呼び出しのみを使用します。パラメータ値およびパラメータリンク値ごと。Java はオブジェクトの参照による呼び出しを使用しません (ただし、多くのプログラマや一部の本の著者はこれを主張しています)。オブジェクトを Java に渡すとき、パラメータは
参照ではなく、
オブジェクト参照の値によって渡されます。どちらの場合も、コンストラクターはすべてのパラメーターの値のコピーを受け取ります。コンストラクターは入力パラメーターを処理できません。
- コンストラクターはメイン (プリミティブ) 型の入力パラメーターの値を変更できません。
- コンストラクターは入力パラメーターの参照を変更できません。
- コンストラクターは、入力パラメーター参照を新しいオブジェクトに再割り当てできません。
コンストラクターは入力パラメーターを使用して次のことを行うことができます。
- 入力パラメータとして渡されたオブジェクトの状態を変更します。
次の例は、Java では、コンストラクターへの入力パラメーターがオブジェクト参照値によって渡されることを示しています。この例は、コンストラクターが入力パラメーターの参照を変更できないが、実際には入力パラメーターのコピーの参照を変更することも反映しています。ファイル
Empoyee.java
class Employee
{
Employee(String x, String y)
{
String temp = x;
x = y;
y = temp;
}
public static void main(String args[])
{
String name1 = new String("Alice");
String name2 = new String("Mary");
Employee a = new Employee(name1, name2);
System.out.println("name1="+name1);
System.out.println("name2="+name2);
}
}
プログラムの出力は次のとおりです。
name1=Alice
name2=Mary
Java が参照による呼び出しを使用してオブジェクトをパラメーターとして渡す場合、この例ではコンストラクターが
name1
と を入れ替えます
name2
。
name1
コンストラクターは、および変数に格納されているオブジェクト参照を実際には交換しません
name2
。これは、コンストラクターのパラメーターがこれらの参照のコピーで初期化されていることを示唆しています。次に、コンストラクターはコピーを交換します。コンストラクターが作業を完了すると、x 変数と y 変数は破棄され、元の変数は前のオブジェクトを参照し
name1
続けます。
name2
コンストラクターに渡されるパラメーターを変更します。
コンストラクターは、基本型の渡されたパラメーターを変更できません。ただし、コンストラクターはパラメーターとして渡されたオブジェクトの状態を変更できます。たとえば、次のプログラムについて考えてみましょう。
Salary1.java
class Salary1
{
Salary1(int x)
{
x = x * 3;
System.out.println("x="+x);
}
public static void main(String args[])
{
int value = 1000;
Salary1 s1 = new Salary1(value);
System.out.println("value="+value);
}
}
プログラムの出力は次のとおりです。
x=3000
value=1000
明らかに、このメソッドはメインの型パラメータを変更しません。したがって、コンストラクターを呼び出した後も、変数の値は
value
に等しいままになります
1000
。基本的に次の 3 つのことが起こります。
- 変数はパラメータ値(つまり、数値)
x
のコピーで初期化されます。value
1000
- 変数の値は
x
3 倍になり、 と等しくなります3000
。ただし、変数の値はvalue
に等しいままです1000
。
- コンストラクターが終了し、変数は
x
使用されなくなります。
次の例では、オブジェクト参照の値がパラメータとしてメソッドに渡されるため、従業員の給与が正常に 3 倍になります。ファイル
Salary2.java
class Salary2
{
int value = 1000;
Salary2()
{
}
Salary2(Salary2 x)
{
x.value = x.value * 3;
}
public static void main(String args[])
{
Salary2 s1 = new Salary2();
Salary2 s2 = new Salary2(s1);
System.out.println("s1.value=" +s1.value);
System.out.println("s2.value="+s2.value);
}
}
プログラムの出力は次のとおりです。
s1.value=3000
s2.value=1000
オブジェクト参照の値がパラメータとして使用されます。行を実行するとき
Salary2 s2 = new Salary2(s1)
; コンストラクターには
Salary2(Salary x)
変数 object への参照の値が渡され
s1
、コンストラクター内で作成された
s1.value
コピーも
(Salary x)
変数 object を指しているため、コンストラクターは実質的に の給与を 3 倍にします
s1
。
プリミティブによってパラメータ化されたコンストラクター。
オーバーロードされたコンストラクターのパラメーターで絞り込み可能なプリミティブ (たとえば
int <- double
、 ) が使用されている場合、そのようなパラメーターでオーバーロードされたメソッドがないにもかかわらず、絞り込まれた値でメソッドを呼び出すことができます。例: ファイル
Primitive.java
class Primitive
{
Primitive(double d)
{
d = d + 10;
System.out.println("d="+d);
}
public static void main(String args[])
{
int i = 20;
Primitive s1 = new Primitive(i);
}
}
プログラムの出力は次のとおりです。
d=30.0
Primitive
クラスには型パラメーターを持つコンストラクターがない にもかかわらず
int
、入力パラメーターを持つコンストラクターは機能します
double
。コンストラクターを呼び出す前に、変数はtype からtype に
i
展開されます。逆のオプションでは、変数の型が で、コンストラクターがパラメーターのみを持つ場合、この状況ではコンパイル エラーが発生します。
int
double
i
double
int
コンストラクター呼び出しと演算子new
コンストラクターは常に演算子によって呼び出されます
new
。コンストラクターが演算子を使用して呼び出される場合
new
、コンストラクターは常に新しいオブジェクトへの参照を生成します。逆シリアル化されるオブジェクトを置き換える場合を除き、コンストラクターに新しいオブジェクトへの参照の代わりに既存のオブジェクトへの参照を強制的に形成させることは不可能です。また、new 演算子を使用すると、新しいオブジェクトへの参照の代わりに、既存のオブジェクトへの参照を形成することはできません。例: ファイル
Salary3.java
class Salary3
{
int value = 1000;
Salary3()
{
}
Salary3(Salary3 x)
{
x.value = x.value * 3;
}
public static void main(String args[])
{
Salary3 s1 = new Salary3();
System.out.println("First object creation: "+s1.value);
Salary3 s2 = new Salary3(s1);
System.out.println("Second object creation: "+s2.value);
System.out.println("What's happend with first object?:"+s1.value);
Salary3 s3 = new Salary3(s1);
System.out.println("Third object creation: "+s3.value);
System.out.println("What's happend with first object?:"+s1.value);
}
}
プログラムの出力は次のとおりです。
First object creation: 1000
Second object creation: 1000
What's happend with first object?: 3000
Third object creation: 1000
What's happend with first object?: 9000
まず、行
Salary3 s1 = new Salary3()
;を使用します。新しいオブジェクトが作成されます。次に、行
Salary3 s2 = new Salary3(s1)
;を使用する場合 または文字列
Salary3 s3 = new Salary3(s1)
; 既存のオブジェクトへのリンクを作成すると、同じ値が保存され
s1.value s2.value
ます。実際には行内にあります。変数の新しいオブジェクトが作成され、その参照値をコンストラクター パラメーターでオブジェクトに渡すことによって、変数のオブジェクトの状態が変更されます。これは出力結果で確認できます。そして、行を実行するとき; 変数の新しいオブジェクトが作成され、変数のオブジェクトの状態が再び変更されます。
s3.value
1000
Salary3 s2 = new Salary3(s1)
s2
s1
Salary3 s3 = new Salary3(s1)
s3
s1
コンストラクターと初期化ブロック、コンストラクターを呼び出すときのアクションのシーケンス
「オブジェクトとコンストラクターの作成」セクションには、オブジェクトの作成時に実行される一般的なアクションがリストされています。その中には、クラス フィールドの初期化とクラス コンストラクターの実行のプロセスがあり、これらにも内部順序があります。
- すべてのデータ フィールドはデフォルト値 (0、false、または null) に初期化されます。
- すべてのフィールド初期化子と初期化ブロックは、クラス宣言にリストされている順序で実行されます。
- コンストラクターの最初の行で別のコンストラクターが呼び出された場合、呼び出されたコンストラクターが実行されます。
- コンストラクターの本体が実行されます。
Java ではクラス内のフィールドを初期化する方法が 3 つあるため、コンストラクターは初期化に関連します。
- 宣言で値を割り当てます。
- 初期化ブロックで値を割り当てます。
- コンストラクターに値を設定します。
当然のことながら、初期化コードを理解しやすいように整理する必要があります。次のクラスが例として示されています。
class Initialization
{
int i;
short z = 10;
static int x;
static float y;
static
{
x = 2000;
y = 3.141;
}
Initialization()
{
System.out.println("i="+i);
System.out.println("z="+z);
z = 20;
System.out.println("z="+z);
}
}
上の例では、変数は次の順序で初期化されます。静的変数は最初にデフォルト値
x
で初期化されます
y
。次に、静的初期化ブロックが実行されます。次に、変数が
i
デフォルト値に初期化され、変数が初期化されます
z
。次に、デザイナーが作業に取り掛かります。クラス コンストラクターの呼び出しは、フィールドが宣言される順序に依存しないでください。これによりエラーが発生する可能性があります。
コンストラクターと継承
コンストラクターは継承されません。例えば:
public class Example
{
Example()
{
}
public void sayHi()
{
system.out.println("Hi");
}
}
public class SubClass extends Example
{
}
このクラスは、親クラスで定義された
SubClass
メソッドを自動的に継承します。
sayHi()
同時に、
Example()
親クラスのコンストラクターはその子孫に継承されません
SubClass
。
this
コンストラクター内のキーワード
this
コンストラクターは、同じクラス内の別のコンストラクターを参照するために使用されますが、パラメーターのリストは異なります。コンストラクターでキーワードを使用する場合は
this
、最初の行にキーワードを指定する必要があります。この規則を無視すると、コンパイラ エラーが発生します。例: ファイル
ThisDemo.java
public class ThisDemo
{
String name;
ThisDemo(String s)
{
name = s;
System.out.println(name);
}
ThisDemo()
{
this("John");
}
public static void main(String args[])
{
ThisDemo td1 = new ThisDemo("Mary");
ThisDemo td2 = new ThisDemo();
}
}
プログラムの出力は次のとおりです。
Mary
John
この例には 2 つのコンストラクターがあります。最初のものは文字列引数を受け取ります。2 番目のコンストラクターは引数を受け取らず、単にデフォルト名「John」を使用して最初のコンストラクターを呼び出します。したがって、コンストラクターを使用してフィールド値を明示的にデフォルトで初期化できます。これはプログラムでしばしば必要になります。
super
コンストラクター内のキーワード
コンストラクターは、スーパークラス コンストラクターを呼び出すために使用されます
super
。コンストラクターが を使用する場合
super
、この呼び出しは最初の行になければなりません。そうしないと、コンパイラーはエラーをスローします。以下は例です: ファイル
SuperClassDemo.java
public class SuperClassDemo
{
SuperClassDemo()
{
}
}
class Child extends SuperClassDemo
{
Child()
{
super();
}
}
この単純な例では、コンストラクターには、 class に加えて classのインスタンスを作成する
Child()
呼び出しが含まれています。これはサブクラス コンストラクターで実行される最初のステートメントである必要があるため、この順序は常に同じであり、 かどうかには依存しません。これが使用されない場合、基本クラスから始まる各スーパークラスのデフォルト (パラメーターなし) コンストラクターが最初に実行されます。次のプログラムは、コンストラクターがいつ実行されるかを示します。ファイル
super()
SuperClassDemo
Child
super
super()
Call.java
class A
{
A()
{
System.out.println("Inside A constructor.");
}
}
class B extends A
{
B()
{
System.out.println("Inside B constructor.");
}
}
class C extends B
{
C()
{
System.out.println("Inside C constructor.");
}
}
class Call
{
public static void main(String args[])
{
C c = new C();
}
}
このプログラムからの出力:
Inside A constructor.
Inside B constructor.
Inside C constructor.
コンストラクターはクラスの従属順に呼び出されます。これにはある程度の意味があります。スーパークラスはサブクラスの知識を持たないため、実行する必要がある初期化は個別に行われます。可能であれば、サブクラスによって実行される初期化よりも前に実行する必要があります。だからこそ、それを最初に行う必要があります。
カスタマイズ可能なコンストラクター
実行時の型識別メカニズムは、ポリモーフィズムを実装する Java 言語の強力な中心原理の 1 つです。ただし、このようなメカニズムでは、互換性のない型キャストから開発者を保護できない場合があります。最も一般的なケースは、オブジェクトのグループの操作です。オブジェクトのさまざまなタイプは事前に不明であり、実行時に決定されます。型の非互換性に関連するエラーは実行時の段階でのみ発生するため、エラーを見つけて排除することが困難になります。Java 2 5.0 でのカスタム型の導入により、これらのエラーの一部が実行時からコンパイル時に移動され、欠落しているタイプ セーフの一部が提供されます。型から具象型に移行するときに、明示的な型キャストを行う必要はありません
Object
。型カスタマイズ ツールはオブジェクトでのみ機能し、クラス継承ツリーの外側にあるプリミティブ データ型には適用されないことに留意する必要があります。カスタム タイプを使用すると、すべてのキャストが自動的にバックグラウンドで実行されます。これにより、型の不一致を防止し、コードをより頻繁に再利用できるようになります。カスタム型はコンストラクターで使用できます。クラスがカスタム型でない場合でも、コンストラクターをカスタムにすることができます。例えば:
class GenConstructor
{
private double val;
<T extends Number> GenConstructor(T arg)
{
val = arg.doubleValue();
}
void printValue()
{
System.out.println("val: "+val);
}
}
class GenConstructorDemo
{
public static void main(String args[])
{
GenConstructor gc1 = new GenConstructor(100);
GenConstructor gc2 = new GenConstructor(123.5F);
gc1.printValue();
gc2.printValue();
}
}
コンストラクターは、
GenConstructor
class からの派生クラスである必要があるカスタム型パラメーターを指定するため
Number
、どのコンストラクターからも呼び出すことができます。
GO TO FULL VERSION