CodeGym /Java Blog /무작위의 /자바 생성자
John Squirrels
레벨 41
San Francisco

자바 생성자

무작위의 그룹에 게시되었습니다
안녕! 오늘 우리는 객체와 관련된 매우 중요한 주제를 고려할 것입니다. 과장 없이 실생활에서 이 주제를 매일 사용하게 될 것이라고 말할 수 있습니다! 우리는 Java 생성자에 대해 이야기하고 있습니다. 이 용어를 처음 듣는 것일 수 있지만 실제로는 이미 생성자를 사용했습니다. 당신은 그것을 깨닫지 못했습니다 :) 우리는 나중에 이것을 확신합니다.

세계에서 생성자는 무엇이며 왜 필요한가요?

두 가지 예를 살펴보겠습니다.

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 객체를 만들었습니다. 한 가지 문제가 있습니다. 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. 캐빈 재질: null. 바퀴 너비: 0. 2018년에 Mr. null이 구입했습니다 . 차를 사기 위해 200만 달러를 포기한 귀하의 구매자는 분명히 " Mr. null "이라고 불리는 것을 좋아하지 않을 것입니다! 그러나 심각하게 핵심은 우리 프로그램이 개체를 잘못 생성했다는 것입니다. 휠 너비가 0인 자동차(즉, 바퀴가 전혀 없음), 트렁크가 누락됨, 알 수 없는 재료로 만들어진 캐빈, 그리고 무엇보다도 정의되지 않은 소유자 . 프로그램이 실행 중일 때 그러한 실수가 어떻게 "사라질" 수 있는지 상상할 수 있을 뿐입니다! 우리는 어떻게든 그런 상황을 피해야 합니다. 프로그램을 제한해야 합니다: 새 자동차를 만들 때개체, 모델 및 최대 속도와 같은 필드가 항상 지정되기를 원합니다. 그렇지 않으면 개체 생성을 방지하려고 합니다. 생성자는 이 작업을 쉽게 처리합니다. 그들은 이유 때문에 이름을 얻었습니다. 생성자는 각각의 새 개체가 일치해야 하는 일종의 "골격" 클래스를 만듭니다. 편의를 위해 두 개의 필드가 있는 간단한 버전의 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 는 건설자." 그리고 그것이 바로 일어나는 일입니다.

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!
   }
  
}
생성자가 우리가 달성하려고 했던 것을 달성하는 것을 볼 수 있습니다. 이제 속도나 모델 없이는 자동차를 만들 수 없습니다! 생성자와 메서드의 유사성은 여기서 끝나지 않습니다. 메서드와 마찬가지로 생성자도 오버로드될 수 있습니다. 집에 고양이 두 마리가 있다고 상상해 보세요. 당신은 그들 중 하나를 새끼 고양이로 얻었습니다. 하지만 두 번째는 이미 다 자란 상태에서 길거리에서 가져온 것인데 정확히 몇 살인지 알 수 없습니다. 이 경우 우리는 프로그램이 이름과 나이가 있는 고양이(첫 번째 고양이)와 이름만 있는 고양이(두 번째 고양이)의 두 종류의 고양이를 만들 수 있기를 원합니다. 이를 위해 생성자를 오버로드합니다.

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" 매개변수가 있는 원래 생성자 외에 이름 매개변수만 있는 생성자를 하나 더 추가했습니다. 이전 수업에서 메서드를 오버로드한 것과 정확히 같은 방식입니다. 이제 두 종류의 고양이를 모두 만들 수 있습니다 :)
생성자가 필요한 이유는 무엇입니까?  - 2
수업 시작 부분에서 당신이 깨닫지 못한 채 이미 생성자를 사용했다고 말했던 것을 기억하십니까? 우리는 우리가 말한 것을 의미했습니다. 사실 Java의 모든 클래스에는 기본 생성자라는 것이 있습니다. 인수를 사용하지 않지만 모든 클래스의 개체를 만들 때마다 호출됩니다.

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
   }
}
콘솔 출력: 고양이가 생성되었습니다! 확인이 있습니다! 기본 생성자는 항상 클래스에 보이지 않게 존재합니다. 그러나 그것에 대해 한 가지 더 알아야 합니다. 인수를 사용하여 생성자를 생성하면 기본 생성자가 클래스에서 제거됩니다. 사실, 우리는 위에서 이것에 대한 증거를 이미 보았습니다. 이 코드에 있었습니다.

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!
   }
}
문자열int 매개변수로 Cat 생성자를 선언했기 때문에 이름과 나이 없이 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;
}
이들은 완전히 다른 두 생성자입니다! "왜 생성자가 필요한가요?"라는 질문에 대한 답을 한 문장으로 표현한다면 "객체가 항상 유효한 상태를 갖도록 하기 위해"라고 말할 수 있습니다. 생성자를 사용하면 모든 변수가 올바르게 초기화됩니다. 프로그램에는 속도가 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 클래스가 있습니다 . 생성자 내부에서 모든 필드를 초기화하고 일부 논리를 포함합니다. 공장에 대한 일부 정보를 표시합니다. 이 정도면 나쁠 건 없는 것 같습니다. 프로그램이 잘 작동합니다. 콘솔 출력: 우리 자동차 공장은 Ford라고 합니다. 설립된 지 115년이 된 이래 지금까지 5000만 대의 자동차를 생산했습니다. 평균적으로 연간 434782대의 자동차를 생산합니다. 하지만 실제로 시간 지연 광산을 깔았습니다. 그리고 이런 종류의 코드는 매우 쉽게 오류로 이어질 수 있습니다. 이제 우리는 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 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);
   }
}
콘솔 출력: 우리 자동차 공장은 "main" 스레드에서 Amigo Motors 예외라고 합니다. java.lang.ArithmeticException: / by zero 설립된 지 0년이 지난 지금까지 CarFactory에서 1000대의 자동차를 생산했습니다 . (CarFactory.java:15) at CarFactory.main(CarFactory.java:23) 프로세스가 종료 코드 1로 종료됨 팔! 프로그램은 어떤 종류의 이해할 수 없는 오류와 함께 끝납니다. 원인을 추측할 수 있습니까? 문제는 우리가 생성자에 넣은 논리에 있습니다. 보다 구체적으로 다음 줄은 다음과 같습니다.

System.out.println("On average, it produces " + (this.carsCount/this.age) + " cars per year");
여기에서는 계산을 수행하고 생산된 자동차 수를 공장 연식으로 나눕니다. 그리고 우리 공장은 새 공장이기 때문에(즉, 0년이 되었기 때문에) 수학에서는 할 수 없는 0으로 나눕니다. 결과적으로 프로그램은 오류와 함께 종료됩니다.

우리는 무엇을 했어야 했습니까?

모든 논리를 별도의 메서드에 넣습니다. 그것을 printFactoryInfo() 라고 부르자 . CarFactory 객체를 인수로 전달할 수 있습니다 . 거기에 모든 논리를 배치하고 동시에 잠재적인 오류(예: 0년 관련 오류)를 처리할 수 있습니다. 각자에게. 유효한 개체 상태를 설정하려면 생성자가 필요합니다. 비즈니스 로직을 위한 메소드가 있습니다. 하나를 다른 것과 섞지 마십시오. 배운 내용을 보강하려면 Java 과정에서 비디오 강의를 시청하는 것이 좋습니다.
코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION