Claire's Study Note

[Java 기본] 4. 생성자

by Hi.Claire
반응형

🖥️ 김영한의 실전 자바 - 기본편 (김영한, 인프런)

 

섹션4. 생성자

4-1. this

회원 객체를 생성하고, 회원의 이름, 나이, 등급을 초기화하는 경우를 생각해보자.

회원 클래스의 멤버 변수로 이름, 나이, 등급을 사용한다.

-> String name, int age, int grade 필드 사용

회원 인스턴스를 생성할 때마다 각 회원의 멤버 변수의 초기값을 설정하는 로직이 반복적으로 사용되므로 메서드를 사용하여 해당 기능을 구현한다.

-> initMember(String name, int age, int grade) 메서드 사용

객체 지향 프로그래밍을 위해서는 속성과 기능을 객체 안에 포함시키는 것이 좋다.

즉, 회원 객체가 자기 자신의 데이터(속성)을 변경하는 기능(메서드)를 제공하면 된다.

 

이때 회원 객체의 멤버 변수 name, age, grade와 initMember() 메소드의 매개변수 name, age, grade의 이름을 같게 두는 경우를 생각해보자.

이럴 경우 initMember() 메서드 내부에서는 멤버 변수와 매개변수 이름을 어떻게 구분해야 할까?

메서드 입장에서는 멤버 변수보다 매개변수가 코드 블럭의 더 안쪽에 있기 때문에 매개변수가 우선순위를 가진다.

따라서 initMember() 메서드 안에서 name이라고 적으면 매개변수에 접근하게 된다.

멤버 변수에 접근하려면 앞에 this.을 붙여주면 된다.

여기서 this 인스턴스 자신의 참조값을 가리킨다.

 

(예시1.1) 회원 객체

package construct;

public class MemberInit {
    String name;
    int age;
    int grade;

    void initMember(String name, int age, int grade) {
        this.name = name;
        this.age = age;
        this.grade = grade;
    }
}

 

(예시1.2) 회원 객체 사용

package construct;

import construct.MemberInit;

public class MethodInit {
    public static void main(String[] args) {
        MemberInit member1 = new MemberInit();
        member1.initMember("user1", 15, 1);

        MemberInit member2 = new MemberInit();
        member2.initMember("user2", 16, 2);

        MemberInit[] members = {member1, member2};

        for (MemberInit member : members) {
            System.out.println("이름:" + member.name + " 나이:" + member.age + " 등급:" + member.grade);
        }
    }
}

이름:user1 나이:15 등급:1
이름:user2 나이:16 등급:2

 

4-2. 생성자

프로그래밍을 할 때 객체를 생성하고 이후에 바로 초기값을 할당해야 하는 경우가 많다.

따라서 앞의 예시에서 살펴본 initMember()와 같은 메서드를 매번 만들어야 한다.

 

그러나 대부분의 객체 지향 언어는 객체를 생성하자마자 즉시 필요한 기능을 좀 더 편리하게 수행할 수 있도록 생성자라는 기능을 제공한다.

생성자를 사용하면 객체를 생성하는 시점에 즉시 필요한 기능을 수행할 수 있다.

 

생성자와 메서드의 차이

  • 생성자의 이름은 클래스의 이름과 같아야 한다. 따라서 첫 글자도 대문자로 시작한다.
  • 생성자는 반환 타입이 없다.

 

생성자를 호출하려면 new 명령어 다음에 생성자 이름과 매개변수에 맞는 인수를 전달하면 된다.

생성자를 호출하면 인스턴스를 생성하고 즉시 해당 생성자를 호출한다.

(예시) 생성자 호출

new 생성자이름(생성자에 맞는 인수 목록)

 

(예시2.1) 회원 객체 - 생성자 사용

package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

    MemberConstruct(String name, int age, int grade) {
        System.out.println("생성자 호출 name=" + name + " age=" + age + " grade" + grade);
        this.name = name;
        this.age = age;
        this.grade = grade;
    }
}

 

(예시 2.2) 회원 객체 사용

package construct;

public class ConstructMain {
    public static void main(String[] args) {
        MemberConstruct member1 = new MemberConstruct("user1", 15, 1);
        MemberConstruct member2 = new MemberConstruct("user2", 16, 2);

        MemberConstruct[] members = {member1, member2};

        for (MemberConstruct member : members) {
            System.out.println("이름:" + member.name + " 나이:" + member.age + " 등급:" + member.grade);
        }
    }
}

생성자 호출 name=user1 age=15 grade1
생성자 호출 name=user2 age=16 grade2
이름:user1 나이:15 등급:1
이름:user2 나이:16 등급:2

 

생성자 장점

  • 중복 호출 제거

생성자를 사용하기 전에는 객체를 생성하고 필요한 작업을 수행하기 위해 메서드를 한 번 더 호출해야 했다.

생성자를 사용하면 객체를 생성하면서 동시에 생성 직후에 필요한 작업을 한번에 처리할 수 있다.

 

(예시) 생성자 사용 후 중복 호출 제거

// 생성자 사용 전
MemberInit member1 = new MemberInit();
member1.initMember("user1", 15, 1);

// 생성자 사용 후
MemberConstruct member1 = new MemberConstruct("user1", 15, 1);

 

  • 제약 : 생성자 호출 필수, 필수값 입력 보장

위의 예시에서 생성자 사용 전 코드를 보자.

MemberInit 인스턴스 생성 후에 initMember() 메서드를 실수로 호출하지 않아도 프로그램은 작동한다. 

하지만 이름, 나이, 등급 데이터가 없는 유령 회원이 시스템 내부에 등장하게 된다.

생성자의 진짜 장점은 객체를 생성할 때 직접 정의한 생성자를 반드시 호출해야 한다는 점에 있다.

직접 정의한 생성자를 호출하지 않으면 다음과 같이 컴파일 오류가 발생한다.

물론 클래스에 개발자가 직접 정의한 생성자가 없다면 생성자를 호출하지 않아도 된다.

 

(예시) 직접 정의한 생성자를 호출하지 않는 경우 컴파일 오류 발생

MemberConstruct member3 = new MemberConstruct();

java: constructor MemberConstruct in class construct.MemberConstruct cannot be applied to given types;
  required: java.lang.String,int,int
  found:    no arguments
  reason: actual and formal argument lists differ in length

 

즉, 생성자를 사용하면 필수값 입력을 보장할 수 있다.

 

좋은 프로그램은 무한한 자유가 주어지는 프로그램이 아니라 적절한 제약이 있는 프로그램이다.

 

4-3. 기본 생성자

기본 생성자

  • 매개변수가 없는 생성자
  • 클래스에 생성자가 하나도 없으면 자바 컴파일러는 매개변수가 없고 작동하는 코드가 없는 기본 생성자를 자동으로 추가한다.
    • (참고) 자바가 자동으로 생성해주는 기본 생성자는 클래스와 같은 접근 제어자를 가진다.
  • 생성자가 하나라도 있으면 자바 컴파일러는 기본 생성자를 만들지 않는다.

 

(예시3.1) 생성자가 없는 클래스

package construct;

public class MemberDefault {
    String name;
}

 

(예시3.2) 생성자가 없는 클래스의 인스턴스 생성

package construct;

public class MemberDefaultMain {
    public static void main(String[] args) {
        MemberDefault memberDefault = new MemberDefault();
    }
}

 

위의 프로그램을 실행하면 자바는 다음과 같은 기본 생성자를 만들어준다. (우리 눈에는 보이지 않는다.)

(예시3.3) 기본 생성자

package construct;

public class MemberDefault {
    String name;
    
    // 기본 생성자
    public MemberDefault() {}
}

 

만약 자바에서 기본 생성자를 만들어주지 않는다면 생성자 기능이 필요하지 않은 경우에도 모든 클래스에 개발자가 직접 기본 생성자를 정의해야 할 것이다.

기본 생성자 덕분에 생성자 기능이 필요할 때에만 명시적으로 생성자를 정의하면 된다.

 

4-4. 생성자 오버로딩(overloading)과 this()

생성자도 메서드 오버로딩처럼 매개변수만 다르게 해서 여러 생성자를 제공할 수 있다.

 

(예시4.1) 회원 객체 - 생성자 오버로딩

package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

    // 생성자 오버로딩
    MemberConstruct(String name, int age) {
        System.out.println("생성자 오버로딩 name=" + name + " age=" + age);
        this.name = name;
        this.age = age;
        this.grade = 3;
    }

    MemberConstruct(String name, int age, int grade) {
        System.out.println("생성자 호출 name=" + name + " age=" + age + " grade" + grade);
        this.name = name;
        this.age = age;
        this.grade = grade;
    }
}

새로 추가한 생성자는 grade를 받지 않는다.

대신 해당 생성자를 통해 생성된 모든 인스턴스의 등급은 3이 된다.

 

(예시4.2) 회원 객체 사용

package construct;

public class MemberConstructMain2 {
    public static void main(String[] args) {
        MemberConstruct member1 = new MemberConstruct("user1", 15, 1);
        MemberConstruct member2 = new MemberConstruct("user2", 16, 2);
        MemberConstruct member3 = new MemberConstruct("user3", 17);

        MemberConstruct[] members = {member1, member2, member3};

        for (MemberConstruct member : members) {
            System.out.println("이름:" + member.name + " 나이:" + member.age + " 등급:" + member.grade);
        }
    }
}

생성자 호출 name=user1 age=15 grade1
생성자 호출 name=user2 age=16 grade2
생성자 오버로딩 name=user3 age=17
이름:user1 나이:15 등급:1
이름:user2 나이:16 등급:2
이름:user3 나이:17 등급:3

 

위의 예시를 보면 코드가 중복되는 부분이 있다.

MemberConstruct(String name, int age) {
    this.name = name;
    this.age = age;
    this.grade = 3;
}

 

MemberConstruct(String name, int age, int grade) {
    this.name = name;
    this.age = age;
    this.grade = grade;
}

 

중복 코드

this.name = name;
this.age = age;

 

이때 this()를 사용하면 생성자 내부에서 자신의 생성자를 호출할 수 있다.

this는 인스턴스 자신의 참조값을 가리키므로 this()는 자신의 생성자를 호출한다고 생각하면 된다.

단, this()는 생성자 코드의 첫 줄에만 작성할 수 있다는 점에 주의하자.

 

(주의) this()를 생성자 코드의 첫 줄에 작성하지 않을 경우 컴파일 오류 발생

Call to 'this()' must be first statement in constructor body

 

(예시4.3) 중복 코드 수정

package construct;

public class MemberConstruct {
    String name;
    int age;
    int grade;

    // 생성자 오버로딩
    MemberConstruct(String name, int age) {
        this(name, age, 3);
    }

    MemberConstruct(String name, int age, int grade) {
        System.out.println("생성자 호출 name=" + name + " age=" + age + " grade" + grade);
        this.name = name;
        this.age = age;
        this.grade = grade;
    }
}

 

4-5. 문제와 풀이

(생략)

반응형

블로그의 정보

Claire's Study Note

Hi.Claire

활동하기