JavaRush /Java Blog /Random-KO /Java의 생성자

Java의 생성자

Random-KO 그룹에 게시되었습니다
안녕하세요! 오늘 우리는 우리의 물건과 관련된 매우 중요한 주제를 살펴볼 것입니다. 여기서는 이 지식을 매일 실제 업무에 활용하게 될 것이라고 과장 없이 말할 수 있습니다! 생성자에 대해 이야기하겠습니다. 이 용어를 처음 들어보셨을 수도 있지만 사실 생성자를 사용해 보셨을 수도 있지만 스스로는 눈치 채지 못했을 겁니다. :) 이에 대해서는 나중에 살펴보겠습니다.

Java의 생성자는 무엇이며 왜 필요한가요?

두 가지 예를 살펴보겠습니다.
public class Car {

   String model;
   int maxSpeed;

   public static void main(String[] args) {

       Car bugatti = new Car();
       bugatti.model = "Bugatti Veyron";
       bugatti.maxSpeed = 407;

   }
}
우리는 자동차를 만들고 모델과 최대 속도를 설정했습니다. 그러나 실제 프로젝트에서는 Car 개체에 분명히 2개 이상의 필드가 있습니다. 예를 들어, 16개의 필드가 있습니다!
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration 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.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

   }

}
우리는 새로운 Car 객체를 생성했습니다 . 한 가지 문제: 16개의 필드가 있지만 12개만 초기화했습니다 ! 지금 코드를 사용하여 잊어버린 항목을 찾아보세요! 그렇게 쉽지는 않죠? 이러한 상황에서 프로그래머는 쉽게 실수를 하고 일부 필드의 초기화를 건너뛸 수 있습니다. 결과적으로 프로그램 동작에 오류가 발생합니다.
public class Car {

   String model;//model
   int maxSpeed;//max speed
   int wheels;// disk width
   double engineVolume;//engine capacity
   String color;//color
   int yearOfIssue;//year of issue
   String ownerFirstName;//Owner's name
   String ownerLastName;//owner's last name
   long price;//price
   boolean isNew;//new or not
   int placesInTheSalon;//number of seats in the cabin
   String salonMaterial;// interior material
   boolean insurance;//is it insured
   String manufacturerCountry;//manufacturer country
   int trunkVolume;// trunk volume
   int accelerationTo100km;//acceleration 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.yearOfIssue = 2016;
       bugatti.insurance = true;
       bugatti.price = 2000000;
       bugatti.isNew = false;
       bugatti.placesInTheSalon = 2;
       bugatti.maxSpeed = 407;
       bugatti.model = "Bugatti Veyron";

       System.out.println("Model Bugatti Veyron. Engine size - " + bugatti.engineVolume + ", trunk - " + bugatti.trunkVolume + ", salon is made of" + bugatti.salonMaterial +
       ", disc width - " + bugatti.wheels + ". Was acquired in 2018 by Mr. " + bugatti.ownerLastName);

   }

}
콘솔 출력:
부가티 베이론 모델. 엔진 배기량 - 6.3, 트렁크 - 0, 널 소재 인테리어, 림 폭 - 0. 널 씨가 2018년에 구입했습니다.
자동차 구입에 200만 달러를 지불한 귀하의 구매자는 분명히 "Mr. Null"이라고 불리는 것을 좋아하지 않을 것입니다! 그러나 결국 우리 프로그램은 림 너비가 0인 자동차(즉, 림이 전혀 없음), 누락된 트렁크, 알 수 없는 재료로 만들어진 내부, 심지어 알 수 없는 사람의 소유물 등 잘못 생성된 개체로 끝났습니다. . 프로그램이 실행되는 동안 어떻게 그러한 오류가 발생할 수 있는지 상상할 수 있습니다! 우리는 어떻게든 그러한 상황을 피해야 합니다. 프로그램에는 제한 사항이 필요합니다. 예를 들어 새 차량 개체를 만들 때 모델과 최대 속도를 항상 지정해야 합니다. 그렇지 않으면 객체 생성을 허용하지 마세요. 생성자 함수는 이 작업에 쉽게 대처합니다. 그들은 이유 때문에 이름을 얻었습니다. 생성자는 클래스의 각 새 객체에 대응해야 하는 일종의 클래스 "골격"을 생성합니다. 편의상 두 개의 필드가 있는 Car 클래스 의 더 간단한 버전으로 돌아가겠습니다 . 요구 사항을 고려하면 Car 클래스 의 생성자는 다음과 같습니다.
public Car(String model, int maxSpeed) {
   this.model = model;
   this.maxSpeed = maxSpeed;
}
이제 객체를 생성하는 모습은 다음과 같습니다.
public static void main(String[] args) {
   Car bugatti = new Car("Bugatti Veyron", 407);
}
주의하세요생성자가 생성되는 방법. 일반 메소드와 유사하지만 반환 유형이 없습니다. 이 경우 클래스 이름은 생성자에도 대문자로 표시됩니다. 우리의 경우 - 자동차 . 또한 생성자는 new-to-you 키워드 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", 407);
       System.out.println(bugatti.model);
       System.out.println(bugatti.maxSpeed);
   }

}
콘솔 출력:
부가티 베이론 407
생성자가 필요한 값을 성공적으로 할당했습니다. 생성자가 일반 메소드와 매우 유사하다는 것을 눈치챘을 것입니다! 이것이 바로 그 방법입니다. 생성자는 메서드이며 약간만 구체적입니다. :) 메서드에서와 마찬가지로 매개변수를 생성자에 전달했습니다. 메소드 호출과 마찬가지로 생성자를 지정하지 않으면 생성자를 호출할 수 없습니다.
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!
   }

}
알다시피, 디자이너는 우리가 달성하려고 했던 것을 해냈습니다. 이제 속도나 모델 없이는 자동차를 만들 수 없습니다! 생성자와 메서드의 유사점은 여기서 끝나지 않습니다. 메서드와 마찬가지로 생성자도 오버로드될 수 있습니다. 집에 고양이 두 마리가 있다고 상상해 보세요. 당신은 그 중 한 마리를 새끼 고양이로 데려왔고, 두 번째 한 마리는 어른이 되어 길에서 집으로 데려왔는데, 그 아이가 정확히 몇 살인지 알 수 없습니다. 이는 우리 프로그램이 두 가지 유형의 고양이(첫 번째 고양이의 경우 이름과 나이, 두 번째 고양이의 경우 이름만)를 생성할 수 있어야 함을 의미합니다. 이를 위해 생성자를 오버로드합니다.
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 barsik = new Cat("Barsik", 5);
       Cat streetCatNamedBob = new Cat("Bob");
   }

}
"name" 및 "age" 매개변수가 있는 원래 생성자에 이름만 있는 또 다른 생성자를 추가했습니다. 이전 강의에서와 같은 방식으로 메서드를 오버로드했습니다. 이제 우리는 두 가지 버전의 고양이를 성공적으로 만들 수 있습니다. :) 생성자는 왜 필요한가요?  - 2강의 시작 부분에서 이미 생성자를 사용했지만 눈치 채지 못했다고 말한 것을 기억하십니까? 이것은 사실이다. 사실 Java의 모든 클래스에는 소위 기본 생성자가 있습니다. 인수는 없지만 클래스의 객체가 생성될 때마다 실행됩니다.
public class Cat {

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
언뜻 보면 이것은 눈에 띄지 않습니다. 뭐, 우리가 오브제를 만들어서 만들어냈는데, 디자이너의 작업은 어디에 있나요? 이를 확인하기 위해 Cat 클래스에 대한 빈 생성자를 직접 작성 하고 그 안에 몇 가지 문구를 콘솔에 인쇄해 보겠습니다. 표시되면 생성자가 작동한 것입니다.
public class Cat {

   public Cat() {
       System.out.println("Created a cat!");
   }

   public static void main(String[] args) {

       Cat barsik = new Cat(); //this is where the default constructor worked
   }
}
콘솔 출력:
그들은 고양이를 만들었습니다!
확인 사항은 다음과 같습니다! 기본 생성자는 항상 클래스에 보이지 않게 존재합니다. 하지만 당신은 그것의 한 가지 특징을 더 알아야 합니다. 인수를 사용하여 일부 생성자를 생성하면 기본 생성자가 클래스에서 사라집니다. 사실 이에 대한 증거는 위에서 이미 살펴보았습니다. 이 코드에서는 다음과 같습니다.
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 barsik = new Cat(); //error!
   }
}
Cat 에 대한 생성자를 정의했기 때문에 이름과 나이 없이 고양이를 만들 수 없습니다 : 문자열 + 숫자. 기본 생성자는 이 직후 클래스에서 사라졌습니다. 따라서 빈 생성자를 포함하여 클래스에 여러 생성자가 필요한 경우 별도로 생성해야 한다는 점을 기억하십시오. 예를 들어, 우리는 동물병원을 위한 프로그램을 만들고 있습니다. 우리 클리닉은 이름이나 나이도 모르는 집없는 고양이를 돕고 자합니다. 그러면 코드는 다음과 같아야 합니다.
public class Cat {

   String name;
   int age;

   //for domestic cats
   public Cat(String name, int age) {
       this.name = name;
       this.age = age;
   }

   //for street cats
   public Cat() {
   }

   public static void main(String[] args) {

       Cat barsik = new Cat("Barsik", 5);
       Cat streetCat = new Cat();
   }
}
이제 기본 생성자를 명시적으로 작성했으므로 두 유형의 고양이를 모두 만들 수 있습니다. 생성자(모든 메서드와 마찬가지로)의 경우 인수 순서가 매우 중요합니다. 생성자에서 이름과 나이 인수를 바꿔보겠습니다.
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 barsik = new Cat("Barsik", 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;
}
전혀 다른 두 디자이너입니다! "왜 생성자가 필요한가?"라는 질문에 대한 답을 한 문장으로 표현하면 객체가 항상 올바른 상태에 있도록 이렇게 말할 수 있습니다. 생성자를 사용하면 모든 변수가 올바르게 초기화되며 프로그램에는 속도가 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("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}

   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Ford", 115 , 50000000);
   }
}
자동차 생산 공장을 설명하는 CarFactory 클래스가 있습니다 . 생성자 내에서 모든 필드를 초기화하고 여기에 로직을 배치합니다. 즉, 팩토리에 대한 일부 정보를 콘솔에 표시합니다. 이것에는 아무런 문제가 없는 것 같습니다. 프로그램은 완벽하게 작동했습니다. 콘솔 출력:
우리 자동차 공장은 포드(Ford)라고 불리며, 설립된 지 115년이 되었으며, 이 기간 동안 5천만 대의 자동차를 생산했으며, 연간 평균 434,782대의 자동차를 생산합니다.
그러나 사실 우리는 시한폭탄을 설치했습니다. 그리고 그러한 코드는 매우 쉽게 오류로 이어질 수 있습니다. 이제 우리가 Ford에 대해 이야기하는 것이 아니라 설립된 지 1년이 채 되지 않았으며 1000대의 자동차를 생산한 새로운 공장 "Amigo Motors"에 대해 이야기하고 있다고 가정해 보겠습니다.
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("She was founded" + this.age + " years ago" );
   System.out.println("During this time it was produced" + this.carsCount +  "cars");
   System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
}


   public static void main(String[] args) {

       CarFactory ford = new CarFactory("Amigo Motors", 0 , 1000);
   }
}
콘솔 출력:
우리 자동차 공장은 "main" 스레드의 Amigo Motors Exception이라고 합니다. java.lang.ArithmeticException: / by zero 0년 전에 설립되었습니다. 이 기간 동안 CarFactory.<init>(CarFactory.java:15)에서 1000대의 자동차를 생산했습니다. CarFactory.main(CarFactory.java:23) 종료 코드 1로 프로세스가 완료되었습니다.</init>
우리가 도착했어요! 프로그램이 이상한 오류로 종료되었습니다. 그 이유가 무엇인지 추측해 보시겠어요? 그 이유는 생성자에 배치한 논리 때문입니다. 특히 이 줄에서는 다음과 같습니다.
System.out.println("On average she produces" + (this.carsCount/this.age) + "cars per year");
여기에서는 계산을 수행하고 생산된 자동차 수를 공장 연수로 나눕니다. 그리고 우리 공장은 새 공장이므로(즉, 0년 된 공장이므로) 결과는 수학에서 금지되는 0으로 나누는 결과가 됩니다. 결과적으로 프로그램이 오류와 함께 종료됩니다. 우리는 무엇을 했어야 했나요? 모든 로직을 별도의 메서드로 이동하고 이를 호출합니다(예: printFactoryInfo() ) . CarFactory 객체를 매개변수로 전달할 수 있습니다 . 또한 거기에 모든 논리를 배치하는 동시에 0년 동안 발생할 수 있는 오류를 처리할 수도 있습니다. 각자 자신에게. 객체의 상태를 올바르게 설정하려면 생성자가 필요합니다. 비즈니스 로직의 경우 메소드가 있습니다. 하나를 다른 것과 섞어서는 안됩니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION