やあ!今日は、オブジェクトに関する非常に重要なトピックについて考えます。誇張することなく、このトピックは実生活で毎日使用されると言っても過言ではありません。Java コンストラクターについて話しています。この用語を聞くのは初めてかもしれませんが、実際にはコンストラクターをすでに使用したことがあります。あなたはそれに気づいていなかっただけです:) 私たちは後でこれを自分自身に納得させます。
レッスンの冒頭で、気づかないうちにコンストラクターを既に使用していると述べたことを覚えていますか? 私たちが言ったことは本気でした。実際、Java のすべてのクラスには、いわゆるデフォルト コンストラクターがあります。引数は必要ありませんが、任意のクラスのオブジェクトを作成するたびに呼び出されます。
(CarFactory.java:15) at CarFactory.main(CarFactory.java:23) プロセスは終了コード 1 で終了しました
ブーム!プログラムは、ある種の理解できないエラーで終了します。原因を推測してみてもらえますか?問題は、コンストラクターに組み込んだロジックにあります。より具体的には、次の行です。
コンストラクターとは一体何ですか?なぜ必要なのでしょうか?
2 つの例を考えてみましょう。
public class Car {
String model;
int maxSpeed;
public static void main(String[] args) {
Car bugatti = new Car();
bugatti.model = "Bugatti Veyron";
bugatti.maxSpeed = 378;
}
}
車を作成し、そのモデルと最高速度を設定しました。しかし、実際のプロジェクトでは、 Carオブジェクトに 2 つのフィールドがないことは明らかです。たとえば、フィールドが 16 個ある場合があります。
public class Car {
String model;// model
int maxSpeed;// maximum speed
int wheels;// wheel width
double engineVolume;// engine volume
String color;// color
int productionYear;// production year
String ownerFirstName;// first name of owner
String ownerLastName;// last name of owner
long price;// price
boolean isNew;// flag indicating whether car is new
int seatsInTheCar;// number of seats in the car
String cabinMaterial;// interior material
boolean insurance;// flag indicating whether car is insured
String manufacturerCountry;// manufacturer country
int trunkVolume;// size of the trunk
int accelerationTo100km;// how long it takes to accelerate to 100 km/h (in seconds)
public static void main(String[] args) {
Car bugatti = new Car();
bugatti.color = "blue";
bugatti.accelerationTo100km = 3;
bugatti.engineVolume = 6.3;
bugatti.manufacturerCountry = "Italy";
bugatti.ownerFirstName = "Amigo";
bugatti.productionYear = 2016;
bugatti.insurance = true;
bugatti.price = 2000000;
bugatti.isNew = false;
bugatti.seatsInTheCar = 2;
bugatti.maxSpeed = 378;
bugatti.model = "Bugatti Veyron";
}
}
新しいCarオブジェクトを作成しました。問題が 1 つあります。フィールドは 16 個ありますが、初期化したのは 12 個だけです。コードを見て、忘れたフィールドを見つけてください。そう簡単ではありませんね?この状況では、プログラマは簡単に間違いを犯し、一部のフィールドの初期化に失敗する可能性があります。その結果、プログラムは誤った動作をします。
public class Car {
String model;// model
int maxSpeed;// maximum speed
int wheels;// wheel width
double engineVolume;// engine volume
String color;// color
int productionYear;// production year
String ownerFirstName;// first name of owner
String ownerLastName;// last name of owner
long price;// price
boolean isNew;// flag indicating whether car is new
int seatsInTheCar;// number of seats in the car
String cabinMaterial;// interior material
boolean insurance;// flag indicating whether car is insured
String manufacturerCountry;// manufacturer country
int trunkVolume;// size of the trunk
int accelerationTo100km;// how long it takes to accelerate to 100 km/h (in seconds)
public static void main(String[] args) {
Car bugatti = new Car();
bugatti.color = "blue";
bugatti.accelerationTo100km = 3;
bugatti.engineVolume = 6.3;
bugatti.manufacturerCountry = "Italy";
bugatti.ownerFirstName = "Amigo";
bugatti.productionYear = 2016;
bugatti.insurance = true;
bugatti.price = 2000000;
bugatti.isNew = false;
bugatti.seatsInTheCar = 2;
bugatti.maxSpeed = 378;
bugatti.model = "Bugatti Veyron";
System.out.println("Model: Bugatti Veyron. Engine volume: " + bugatti.engineVolume + ". Trunk volume: " + bugatti.trunkVolume + ". Cabin material: " + bugatti.cabinMaterial +
". Wheel width: " + bugatti.wheels + ". Purchased in 2018 by Mr. " + bugatti.ownerLastName);
}
}
コンソール出力: モデル: Bugatti Veyron。エンジン容積:6.3。トランク容積: 0。キャビン材質: ヌル。ホイール幅: 0. null 氏が 2018 年に購入車の購入に 200 万ドルを費やした購入者は、明らかに「 null 氏」 と呼ばれることを好まないでしょう。しかし真剣に、肝心なのは、私たちのプログラムがオブジェクトを間違って作成したということです。車輪幅が 0 (つまり、車輪がまったくない) の車、トランクが欠落しており、未知の素材で作られたキャビン、そして何よりも所有者が未定義です。 。プログラムの実行中にこのような間違いがどのように「起こる」かは想像するしかありません。このような状況を何とか回避する必要があります。新しい車を作成するときは、プログラムを制限する必要があります。オブジェクトでは、モデルや最高速度などのフィールドを常に指定する必要があります。それ以外の場合は、オブジェクトの作成を阻止したいと考えます。コンストラクターはこのタスクを簡単に処理します。彼らには理由があってその名前が付けられました。コンストラクターは、各新しいオブジェクトが一致する必要がある一種のクラス「スケルトン」を作成します。便宜上、2 つのフィールドを持つ単純なバージョンのCarクラスに戻りましょう。要件を考慮すると、Carクラスのコンストラクターは次のようになります。
public Car(String model, int maxSpeed) {
this.model = model;
this.maxSpeed = maxSpeed;
}
// And creating an object now looks like this:
public static void main(String[] args) {
Car bugatti = new Car("Bugatti Veyron", 378);
}
コンストラクターがどのように宣言されるかに注意してください。これは通常のメソッドに似ていますが、戻り値の型がありません。また、コンストラクタでは大文字で始まるクラス名( Car )を指定します。さらに、コンストラクターは、新しいキーワードthisとともに使用されます。this というキーワードは、特定のオブジェクトを示すためのものです。コンストラクター内のコード
public Car(String model, int maxSpeed) {
this.model = model;
this.maxSpeed = maxSpeed;
}
これは、ほぼそのまま解釈できます: 「この車 (現在作成しているもの) のモデルは、コンストラクターに渡されるモデル引数です。この車 (作成しているもの) のmaxSpeedは、コンストラクターに渡されるmaxSpeed引数です。」コンストラクタ。" そしてそれがまさに起こります:
public class Car {
String model;
int maxSpeed;
public Car(String model, int maxSpeed) {
this.model = model;
this.maxSpeed = maxSpeed;
}
public static void main(String[] args) {
Car bugatti = new Car("Bugatti Veyron", 378);
System.out.println(bugatti.model);
System.out.println(bugatti.maxSpeed);
}
}
コンソール出力: Bugatti Veyron 378 コンストラクターは必要な値を正しく割り当てました。コンストラクターが通常のメソッドと非常に似ていることに気づいたかもしれません。そうです。コンストラクターは実際にはメソッドですが、特定の機能があります:) メソッドと同様に、引数をコンストラクターに渡します。メソッドの呼び出しと同様に、コンストラクターの呼び出しも、指定しない限り機能しません。
public class Car {
String model;
int maxSpeed;
public Car(String model, int maxSpeed) {
this.model = model;
this.maxSpeed = maxSpeed;
}
public static void main(String[] args) {
Car bugatti = new Car(); // Error!
}
}
コンストラクターが私たちが達成しようとしていたことを達成していることがわかります。速度やモデルがなければ車を作成することはできません。コンストラクターとメソッドの類似点はこれで終わりではありません。メソッドと同様に、コンストラクターもオーバーロードできます。あなたの家に2匹のペットの猫がいると想像してください。あなたはそのうちの一匹を子猫のときに迎えました。しかし、2番目のものはすでに成長していたときに通りから拾ったもので、正確に樹齢はわかりません。この場合、プログラムで 2 種類の猫を作成できるようにします。名前と年齢がある猫 (最初の猫の場合) と、名前のみを持つ猫 (2 番目の猫の場合) です。このために、コンストラクターをオーバーロードします。
public class Cat {
String name;
int age;
// For the first cat
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
// For the second cat
public Cat(String name) {
this.name = name;
}
public static void main(String[] args) {
Cat smudge = new Cat("Smudge", 5);
Cat streetCatNamedBob = new Cat("Bob");
}
}
「name」および「age」パラメータを持つ元のコンストラクタに加えて、name パラメータのみを持つコンストラクタをもう 1 つ追加しました。前のレッスンでメソッドをオーバーロードしたのとまったく同じ方法です。これで、両方の種類の猫を作成できるようになりました :)
public class Cat {
public static void main(String[] args) {
Cat smudge = new Cat(); // The default constructor is invoked here
}
}
一見すると、目に見えないものです。オブジェクトを作成したので、どうなるでしょうか? ここでコンストラクターはどこで何かを行っているのでしょうか? これを確認するには、 Catクラスの空のコンストラクターを明示的に作成してみましょう。その中にいくつかのフレーズを表示します。フレーズが表示される場合、コンストラクターが呼び出されています。
public class Cat {
public Cat() {
System.out.println("A cat has been created!");
}
public static void main(String[] args) {
Cat smudge = new Cat(); // The default constructor is invoked here
}
}
コンソール出力: 猫が作成されました。 確認がありました!デフォルトのコンストラクターは常にクラス内に目に見えない形で存在します。しかし、それについてもう 1 つ知っておく必要があります。引数を使用してコンストラクターを作成すると、デフォルトのコンストラクターはクラスから削除されます。実際、これの証拠はすでに上で見ました。それは次のコードにありました。
public class Cat {
String name;
int age;
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Cat smudge = new Cat(); //Error!
}
}
Catコンストラクターをstringパラメーターとintパラメーターで宣言したため、名前と年齢なしでCat を作成することはできませんでした。これにより、デフォルトのコンストラクターがクラスから即座に消えてしまいました。したがって、引数のないコンストラクターを含め、クラス内に複数のコンストラクターが必要な場合は、それを個別に宣言する必要があることに注意してください。たとえば、動物病院向けのプログラムを作成しているとします。私たちのクリニックは善行を行い、名前も年齢も不明のホームレスの子猫を助けたいと考えています。コードは次のようになります。
public class Cat {
String name;
int age;
// For cats with owners
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
// For street cats
public Cat() {
}
public static void main(String[] args) {
Cat smudge = new Cat("Smudge", 5);
Cat streetCat = new Cat();
}
}
明示的なデフォルト コンストラクターを作成したので、両方のタイプの猫を作成できます :) 他のメソッドと同様、コンストラクターに渡される引数の順序は非常に重要です。コンストラクター内の name引数とage引数を交換してみましょう。
public class Cat {
String name;
int age;
public Cat(int age, String name) {
this.name = name;
this.age = age;
}
public static void main(String[] args) {
Cat smudge = new Cat("Smudge", 10); // Error!
}
}
エラー!コンストラクターは、Catオブジェクトの作成時に、数値と文字列をこの順序で渡す必要があることを明確に規定しています。したがって、私たちのコードは機能しません。独自のクラスを宣言するときは、必ずこれを覚えておいてください。
public Cat(String name, int age) {
this.name = name;
this.age = age;
}
public Cat(int age, String name) {
this.age = age;
this.name = name;
}
これら 2 つはまったく異なるコンストラクターです。「なぜコンストラクターが必要なのでしょうか?」という質問に対する答えを一文で表現するとしたら、「オブジェクトが常に有効な状態であることを保証するため」と言えるでしょう。コンストラクターを使用すると、すべての変数が正しく初期化されます。プログラムには、速度 0 の車やその他の「無効な」オブジェクトは含まれません。その主な利点はプログラマーにとってです。(オブジェクトの作成後に) フィールドを手動で初期化すると、何かを見落としてバグが発生する危険性が非常に高くなります。しかし、コンストラクターではこれは起こりません。必要な引数をすべて渡せなかった場合、または間違ったタイプの引数を渡した場合、コンパイラーはただちにエラーを登録します。また、プログラムを入れるべきではないと別途に言わなければなりません。■ コンストラクター内のロジック。これがメソッドの目的です。メソッドでは、必要な機能をすべて定義する必要があります。コンストラクターにロジックを追加することがなぜ悪い考えであるかを見てみましょう。
public class CarFactory {
String name;
int age;
int carsCount;
public CarFactory(String name, int age, int carsCount) {
this.name = name;
this.age = age;
this.carsCount = carsCount;
System.out.println("Our car factory is called " + this.name);
System.out.println("It was founded " + this.age + " years ago" );
System.out.println("Since that time, it has produced " + this.carsCount + " cars");
System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}
public static void main(String[] args) {
CarFactory ford = new CarFactory("Ford", 115 , 50000000);
}
}
自動車工場を記述するCarFactoryクラス があります。コンストラクター内で、すべてのフィールドを初期化し、いくつかのロジックを組み込みます。つまり、ファクトリに関するいくつかの情報を表示します。これに関しては何も悪いことはないようです。プログラムは正常に動作します。コンソール出力: 私たちの自動車工場はフォードと呼ばれています 115 年前に設立されて以来、5000 万台の車を生産しています 平均して年間 434782 台の車を生産しています しかし、私たちは実際に時間遅れの鉱山を敷設しました 。そして、この種のコードは非常に簡単にエラーを引き起こす可能性があります。ここでフォードの話ではなく、設立して 1 年も経たずに 1000 台の車を生産した「アミーゴ モーターズ」という新しい工場について話しているとします。
public class CarFactory {
String name;
int age;
int carsCount;
public CarFactory(String name, int age, int carsCount) {
this.name = name;
this.age = age;
this.carsCount = carsCount;
System.out.println("Our car factor is called " + this.name);
System.out.println("It was founded " + this.age + " years ago" );
System.out.println("Since that time, it has produced " + this.carsCount + " cars");
System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
}
public static void main(String[] args) {
CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
}
}
コンソール出力: 私たちの自動車工場は Amigo Motors と呼ばれています。 スレッド "main" java.lang.ArithmeticException: / by zero 0 年前に設立されて以来、 CarFactory で 1000 台の自動車を生産してきました。
System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
ここでは計算を実行し、生産された自動車の数を工場の築年数で割っています。そして、私たちの工場は新しい(つまり、築0年)ため、0で割りますが、これは数学ではできません。その結果、プログラムはエラー終了します。
GO TO FULL VERSION