백엔드 수업 #06 클래스 - 07 생성자

sookyoung.k·2022년 11월 16일
1

일단 지금까지는... 가장 이해하는데 애를 먹고 있는 부분. 화면에 뻐큐날리면서 하는 중 ㅠ 이 개념은 책을 통해서 차근차근 설명을 이해해나가는 것이 더 좋을 것 같다. 사실 강사님께서 아무리 명 강연을 펼치셔도 내가 기본적인 개념이 정립되어있지 않으면 이해하기 어려운 것 같다. 자꾸 모르는 단어, 까먹은 개념이 튀어나오면 뇌가 멈춰버림. 아슬아슬 쌓아가는 개념...


생성자 = 클래스로부터 객체를 생성할 때 호출되어 객체의 초기화를 담당하는 것

  1. 클래스 객체를 생성하기도 하고

  2. 멤버변수(필드)를 초기화하기도 하고

  3. 무조건 한 개 이상 갖는다는 특성을 가지고 있다.

객체 초기화란?
필드를 초기화하거나, 메소드를 호출해서 객체를 사용할 준비를 하는 것

생성자를 실행시키지 않고서는 클래스로부터 객체를 만들 수 없다.

  • new연산자에 의해서 생성자가 실행됨 → 힙(heap) 영역에 객체가 생성됨 → 객체의 주소 리턴
  • 리턴된 주소는 클래스 타입 변수에 저장되어 객체에 접근할 때 이용된다.
  • 만일 생성자가 실행되지 않고 에러가 난다면? 당연히 객체가 생성되지 않는다.

생성자는... 내가 만들지 않더라도 자바가 알아서 만들어 준다고 한다. 솔직히... 아직은 큰 그림을 보지 못하는 상태라 이샛기가 왜 있는지 모르겠다. 왜 얘가 꼭 필요한건가요. 그냥 객체 알아서 생성되는 거 아니었어? ㅠ 그냥 좀 알아서 만들어지라고... 초기화는 다른데서 시키면 안되냐. 엉엉. 변수만 해도 오백만개같아서 머리 아픈데 필드에 생성자에 메소드에 엉엉ㅇ엉엉 클래스 미워

생성자의 목적?

그러니까 나의 오랜 의문은... 이 생성자를 왜 만드냐는 건데! 그 목적은 바로
객체를 생성하고 멤버 변수(필드)를 초기화하기 위함이다.
필드 초기화를 위해서 가장 많이 쓰인다.

기본 생성자 (디폴트 생성자)

자바는 생성자를 자동으로 생성하기 때문에 (라임 지림) 모든 클래스는 생성자를 반드시 갖는다.
만일 우리가 생성자 선언을 생략했다면 컴파일러 코드에 public Constructor() {} 이런 식의 기본 생성자 (디폴트 생성자)가 자동으로 생성된다. (클래스가 public class로 선언되고 클래스 이름이 Constructor인 코드에서)
때문에 생성자를 선언하지 않아도 new연산자 뒤에 기본 생성자를 호출해서 객체를 생성시킬 수 있다.
ex. Car myCar = new Car(); -> Car(): 기본 생성자

디폴트 생성자(Default Constructor)
자바는 생성자를 자동으로 생성한다.

  • 생성자 이름 = 클래스
  • 반환 타입 없음
  • 반환 타입은 없지만 생성한 객체의 주소를 반환한다.

생성자 선언

기본생성자가 아니라 우리가 명시적으로 선언하는 방법도 있다. (명시적으로 선언한 생성자가 하나라도 있으면 기본 생성자가 추가되지 않는다.)

public class Student {
	int studentID;
    String studentName;
    int grade; 
    String address;
    
    // 생성자 - 사람 이름을 매개변수로 입력받아서 생성자 블록 안에 Student 클래스를 생성한다 
	public Student(String studentName) { // 소괄호 안: 매개변수 선언
		studentName = name; // 중괄호 안: 객체의 초기화 코드 
	}
}

생성자는 이런 형태로 작성하면 된다.

  • 메소드와 비슷한 모양이지만 리턴 타입이 없다.
  • 클래스 이름과 동일하다.
  • 블록 내부에 작성되는 객체 초기화 코드에는 필드에 초기값을 저장하거나 메소드를 호출해 객체 사용 전에 필요한 준비를 한다.
public class Car {
	// 생성자
    Car(String color, int cc) {
    }
}

public class CarExample {
	public static void main(String[] args) {
    	Car myCar = new Car("검정", 3000);
        //Car myCar = new Car(); // 이미 명시적으로 생성자가 선언되어 있기 때문에 기본 생성자는 호출 불가. 
        // 반드시 Car(String color, int cc)를 통해서 호출해주어야 한다! 
    }
}

하나씩 뜯어보겠습니다.
1. Car myCar = new Car("검정", 3000): 매개 변수가 두 개 왔음. 하나는 String, 하나는 int 타입으로 왔다. 이 두 개의 매개값을 받기 위해서는 이 두 매개 변수를 받을 수 있는 생성자를 선언해야 한다.
2.Car(String color, int cc) 바로 이렇게!

→ 매개 변수는 생략할 수도 있고 여러 개를 선언해도 된다.
그렇다면 매개변수는 왜 있는걸까? 우리가 new연산자를 통해서 생성자를 호출하게 되면! 매개변수를 통해서 생성자 (블록) 내부로 외부의 값이 전달된다.

필드 초기화

클래스에서 객체가 생성되면 필드는 기본 초기값으로 설정된다. (Null, 0, false)
다른 값으로 초기화를 주고 싶다면?!

  1. 필드 선언 시 초기값을 주는 방법
  2. 생성자에서 초기값을 주는 방법

필드 선언 시 초기값을 주는 건 앞서 이야기를 했기 때문에 여기선 생성자에서 초기값을 주는 방법을 알아볼 것이다.

public class Korean {
	// 필드
    String nation = "대한민국";
    String name;
    Sting ssn;
    
    // 생성자
    public Korean(String n, String s) {
    	this.name = name; // this.필드(name) = 매개변수(name)
        this.ssn = ssn; // this.필드(ssn) = 매개변수(ssn)
        // 매개변수는 관례적으로 동일한 이름을 붙인다. 
    }
}

public class KoreanExample {
	public static void main(String[] args) {
    Korean k1 = new Korean("권수경", "960803-1234567"
    
    System.out.println(k1.name);
    System.out.println(k2.ssn);
    }
}
  1. Korean 클래스에 nation이라는 필드를 선언하면서 "대한민국"이라는 초기값을 준다.
    → k1 객체의 nation 필드에는 "대한민국"이 저장됨.
  2. name과 ssn 필드값은 생성자를 통해서 초기값을 줄 수 있다! name, ssn이라는 매개변수를 통해서 생성자 내부로 외부에 있는 값들을 받아올 수 있다. 어떻게 하냐면!
    • k1이라는 객체 선언! → "권수경"이 매개변수 n을 통해서, "960803-1234567"이 매개변수 s를 통해서 전달된다. 각각 name 필드와 ssn 필드의 초기값이 되는 것!
  3. this.name, this.ssn: 객체 자신을 참조하는 것! this를 통해서 필드명과 매개변수를 구분해준다. 약간... 객체가 자기 자신을 부르는 거라고 생각하면 될 것 같다. 'this'는 '이것'이라는 표현이니까 객체가 "와타시, name..." 하고 스스로를 3인칭으로 부르는 거라고 이해하면... 재밌음. 난 재밌음... "this.필드"를 통해서 필드를 참조 변수로 사용하는 것이다.

생성자 오버로딩

→ 매개 변수를 달리하는 생성자를 여러 개 선언하는 것

=같은 이름의 메소드를 여러 개 선언하는 것이라고도 볼 수 있다.

우리는... 외부에서 다양한 데이터를 끌어올 거쉽니다. 그 데이터들을 이용해서 객체를 초기화하기 위해서는 생성자도 다양화가 되어야 할 필요가 있음!

[Car 객체를 생성할 때]

  • 외부 제공 데이터가 없다면? 기본 생성자로 Car 객체 생성
  • 외부에서 model 데이터가 제공되는 경우
  • 외부에서 model, color 데이터가 제공되는 경우

→ 이 경우들도 모두 Car 객체를 생성할 수 있어야 한다.

하지만 생성자가 하나 뿐이라면 객체의 요구 조건을 수용할 수가 없음. 때문에 자바는 다양한 방법으로 객체를 생성할 수 있도록 생성자 오버로딩(Overloading)을 제공한다.

public class Car { 
	// 1. 필드
	String company = "현대자동차";
	String model;
	String color;
	int maxSpeed;
	
	// 생성자
    Car() { // 생성자 1
    }
	
    Car(String model) { // 생성자 2 
		this.model = model; // this. → 생성자의 객체(객체 데이터)를 가져오는 연산자. 필드에 있는 객체 model! 우측의 model은 생성자의 매개변수 model을 말함. 
	}
	
	Car(String model, String color) { // 생성자 3
    	this.model = model;
		this.color = color;
	}
    
	Car(String model, String color, int maxSpeed) { // 생성자 4
		this.model = model; 
		this.color = color; 
		this.maxSpeed = maxSpeed;
	}
}

public class CarExample {
	public static void main(String[] args) { 
		Car car1 = new Car(); // 생성자 1로 객체 생성 
		System.out.println(car1.company);
        System.out.println();

		Car car2 = new Car("자가용"); // 생성자 2로 객체 생성 
		System.out.println(car2.company;
		System.out.println(car2.model);
		System.out.println();
		
		Car car3 = new Car("자가용", "빨강"); // 생성자 3으로 객체 생성 
		System.out.println(car3.company);
        System.out.println(car3.model);
		System.out.println(car3.color);
		System.out.println();

		Car car4 = new Car("택시", "검정", 200); // 생성자 4로 객체 생성  
		System.out.println(car4.company);
        System.out.println(car4.model);
        System.out.println(car4.color);
        System.out.println(car4.maxSpeed);
        
        //외부에서 필드 데이터 변경하는 방법 
		car4.maxSpeed = 60; // 필드 값 변경  
		System.out.println(car4.maxSpeed); // 수정된 값을 할당했기 때문에 60이 출력된다. 
		
        
         /*[출력결과]
    	[car1]
    	현대자동차
    
    	[car2]
    	현대자동차
    	자가용
    
    	[car3]
    	현대자동차
    	자가용
   	 	빨강
    
    	[car4]
    	현대자동차
    	택시
    	검정
    	200
    	*/
	}
}
  • 생성자 1은 기본 생성자로 객체를 생성한다.

  • new Car("그랜저")Car(String model)생성자로 객체를 생성한다.

  • new Car("그랜저", "흰색")Car(String model, String color)생성자로 객체를 생성한다.

  • new Car("그랜저", "흰색", 300)Car(String model, String color, int maxSpeed)생성자로 객체를 생성한다.
    = Car 생성자를 오버로딩해서 CarExample 클래스에서 다양한 방법으로 Car 객체를 생성한 것!

  • 객체를 생성하면 필드에 접근할 수 있다. 객체가 생성되는 순간 생성자가 실행 됨! 객체 데이터들이 생성되자마자 바로 생성자에 들어가게 된다.

  • 객체 생성 시 변수명: 주소 이름, 번지 수라고 함. 이들은 스택 영역에 저장되고, new 연산자를 통해 생성된 객체는 힙 영역에 실제 값을 저장한다.

  • 주의: 매개 변수의 타입과 개수, 그리고 선언된 순서가 똑같을 경우 매개 변수 이름만 바꾸는 것은 생성자 오버로딩이라고 볼 수 없다.

다른 생성자 호출(this())

생성자 오버로딩이 많아지면 중복된 코드가 발생할 수 있다. 변수의 수만 달리하고 초기화 내용이 비슷하면 이런 현상이 일어나기 쉽다.
→ 필드 초기화 내용을 한 생성자에만 집중적으로 작성하고 나머지 생성자는 초기화 내용을 가지고 있는 생성자를 호출하면 된다!

생성자에서 다른 생성자를 호출할 때는 this()코드를 사용한다.

 Car(String model) { // 생성자 2 
		this.model = model; 
        this.color = "은색";
        this.maxSpeed = 250;
	}
	
	Car(String model, String color) { // 생성자 3
    	this.model = model;
		this.color = color;
	}
    
	Car(String model, String color, int maxSpeed) { // 생성자 4
		this.model = model; 
		this.color = color; 
		this.maxSpeed = maxSpeed;
	}

여길 보면... 전부 다 중복 코드이다!!! 중복되는거 넘 많아!!!
그래서 깔끔하게! this() 코드를 사용해 중복 코드를 제거할 것이다.

↓↓↓

public class Car { 
	// 1. 필드
	String company = "현대자동차";
	String model;
	String color;
	int maxSpeed;
	
	// 생성자
    Car() { // 생성자 1
    }
	
    Car(String model) { // 생성자 2 
		this.(model, "은색", 250); (공통 실행 코드 호출) 
	}
	
	Car(String model, String color) { // 생성자 3 (공통 실행 코드 호출) 
    	this.(model, color, 250);
	}
    
	Car(String model, String color, int maxSpeed) { // 생성자 4 (공통 실행 코드) 
		this.model = model; 
		this.color = color; 
		this.maxSpeed = maxSpeed;
	}
}

앞의 코드를 봤을 때 세 개의 생성자 내용이 비슷하므로 앞의 두 개의 생성자에서 this() 코드를 사용해 마지막 생성자인 Car(String model, String color, int maxSpeed)를 호출하도록 수정한 것.

마지막 코드를 사용한 이유는 중복된 코드가 많을 때 선언한 것이 가장 많은 생성자를 호출하기 때문!

주의할 점

  1. this()는 자신의 다른 생성자를 호출하는 코드로 반드시 생성자의 첫 줄에서만 허용된다.
  2. this()의 매개값은 호출되는 생성자의 매개 변수 타입에 맞게 제공해야 한다.
  3. this() 다음에는 추가적인 실행문들이 올 수 있다.
    = 호출되는 생성자의 실행이 끝나면 원래 생성자로 돌아와서 다음 실행문을 진행함
profile
영차영차 😎

0개의 댓글