10 분 소요



[김영한의 실전 자바 - 기본편↗️]을 듣고 정리한 글입니다.



1. 생성자 - 필요한 이유

1.1 생성자란?

객체 지향 프로그래밍에서 생성자는 객체가 생성될 때 초기화 작업을 자동으로 수행하는 메서드이다.

객체를 생성하는 시점에 어떤 작업을 하고 싶다면 생성자(Constructor)를 이용하면 된다.


1.2 초기값 설정의 필요성

아래 코드는 수동으로 초기값을 설정하는 예시이다.

package construct;

public class MemberInit {
    String name;
    int age;
    int grade;
}
package construct;

public class MethodInitMain1 {
    public static void main(String[] args) {
        MemberInit member1 = new MemberInit();
        member1.name = "user1";
        member1.age = 15;
        member1.grade = 90;

        MemberInit member2 = new MemberInit();
        member2.name = "user2";
        member2.age = 16;
        member2.grade = 80;

        MemberInit[] members = {member1, member2};
        for (MemberInit s : members) {
            System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
        }
    }
}

위 코드는 MemberInit 객체를 생성한 후, 각 필드에 초기값을 설정하는 과정에서 반복적인 코드가 발생하기 때문에, 관리하기 어렵고 오류가 발생할 가능성이 높다.


1.3 메서드를 통한 초기값 설정

반복적인 초기값 설정을 줄이기 위해, 초기화를 수행하는 메서드를 추가할 수 있다.

package construct;

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

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

        MemberInit[] members = {member1, member2};
        for (MemberInit s : members) {
            System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
        }
    }

    static void initMember(MemberInit member, String name, int age, int grade) {
        member.name = name;
        member.age = age;
        member.grade = grade;
    }
}
  • 위 코드에서는 initMember 메서드를 사용하여 중복 코드를 제거했지만, 여전히 데이터(속성)와 기능(메서드)이 분리되어 있다.
  • MemberInit 객체가 자기 자신의 데이터를 초기화하는 기능(메서드)을 제공하는 것이 더 나은 접근법이다. 이때 생성자를 사용하면 이러한 문제를 해결할 수 있다.


1.4 생성자를 사용한 초기값 설정

이제 MemberInit 클래스에 생성자를 추가하여 객체 생성 시 초기값을 설정할 수 있도록 한다.

  • 멤버 변수와 메서드의 매개변수의 이름이 같으면?
    • 멤버 변수보다 매개변수가 코드 블럭의 더 안쪽에 있기 때문에 매개변수가 우선순위를 가진다.
    • 따라서 initMember(String name,...)메서드 안에서 name이라고 적으면 매개 변수에 접근하게 된다.
    • 멤버 변수에 접근하려면 앞에 this.이라고 해주면 된다. this는 여기 자신의 참조값을 가리킨다.
package construct;

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

    // 생성자 정의
    public MemberInit(String name, int age, int grade) {
        this.name = name;
        this.age = age;
        this.grade = grade;
    }
}


이제 생성자를 사용하여 회원 정보를 초기화하는 메인 클래스를 작성한다.

member1.initMember("user1", 15, 90)와 같이 메서드를 호출하면 객체의 멤버 변수에 인자로 넘어온 값을 채운다.

package construct;

public class MethodInitMain3 {
    public static void main(String[] args) {
        MemberInit member1 = new MemberInit("user1", 15, 90);
        MemberInit member2 = new MemberInit("user2", 16, 80);

        MemberInit[] members = {member1, member2};
        for (MemberInit s : members) {
            System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
        }
    }
}

실행 결과

이름:user1 나이:15 성적:90
이름:user2 나이:16 성적:80


1.5 생성자의 장점

  • 생성자를 사용하면 객체 생성 시 초기값을 간단하게 설정할 수 있어 코드가 간결해진다.
  • 클래스 내부에 데이터를 설정하는 방법을 제공하므로, 데이터와 기능이 한 곳에 모여 유지보수가 용이해진다.
  • 생성자를 통해 필요한 값을 모두 설정하도록 강제할 수 있어 객체의 유효성을 높인다.



2. this

this 키워드는 자바에서 인스턴스 자신의 참조값을 나타내며, 멤버 변수와 메서드 매개변수의 이름이 동일할 때 구분하는 데 유용하다.

2.1 this의 필요성

this를 사용하는 이유는 주로 두 가지이다.

  1. 명확한 구분: 멤버 변수와 메서드 매개변수의 이름이 같을 경우, this를 사용하여 멤버 변수를 명확하게 지정할 수 있다.
  2. 인스턴스 참조: this는 현재 객체의 참조를 나타내며, 인스턴스의 멤버 변수 및 메서드에 접근할 때 사용된다.
package construct;

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

    // 메서드에서 매개변수와 멤버 변수의 이름이 동일
    void initMember(String name, int age, int grade) {
        this.name = name; // this.name은 멤버 변수, name은 매개변수
        this.age = age;
        this.grade = grade;
    }
}
  • 위 코드에서 this.name은 멤버 변수 name을 의미하고, name은 매개변수를 의미한다.
  • 따라서 this를 사용함으로써 어떤 변수를 참조하는지 명확히 할 수 있다.


2.2 this를 제거했을 때의 문제

this를 제거하고 다음과 같이 작성하면

void initMember(String name, int age, int grade) {
    name = name; // 이 경우, 두 변수 모두 매개변수를 가리킴
}

여기서는 name이 매개변수에만 접근하게 되어 멤버 변수 name은 변경되지 않는다. 따라서 멤버 변수에 값을 할당할 수 없게 된다.


2.3 this의 생략

this는 생략할 수 있다. 이 경우, 컴파일러는 지역 변수(매개변수)를 먼저 찾고, 그 후에 멤버 변수를 찾는다. 멤버 변수도 없으면 오류가 발생한다.

package construct;

public class MemberThis {
    String nameField; // 멤버 변수

    void initMember(String nameParameter) { // 매개변수
        nameField = nameParameter; // nameField는 멤버 변수, nameParameter는 매개변수
    }
}

위 코드에서 nameField는 멤버 변수이고, nameParameter는 매개변수이다. 이 경우, this를 생략해도 문제가 발생하지 않는다.


2.4 this 사용의 스타일

코딩 스타일에 따라 this를 사용하는 것이 좋을 수도 있다.

package construct;

public class MemberThis {
    String nameField;

    void initMember(String nameParameter) {
        this.nameField = nameParameter; // this를 사용하여 멤버 변수를 명확하게 표시
    }
}
  • 이처럼 this를 사용하면 코드를 읽을 때 멤버 변수와 매개변수를 쉽게 구분할 수 있다.
  • 과거에는 this를 강제로 사용하는 스타일이 인기가 있었으나, 현재는 IDE가 멤버 변수와 지역 변수를 색상으로 구분해주기 때문에 꼭 필요할 때만 사용하는 것이 일반적이다. (매개변수는 보라색 글씨로 표시해줌)



3. 생성자 - 도입

3.1 생성자의 특징

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

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


생성자는 다음과 같은 특징을 가지고 있다.

  • 이름: 생성자의 이름은 반드시 클래스의 이름과 같아야 한다.
  • 반환 타입: 생성자는 반환 타입이 없으며, 따라서 void도 사용하지 않는다.
  • 매개변수: 생성자는 매개변수를 받을 수 있어, 인스턴스 생성 시 초기값을 쉽게 설정할 수 있다.


3.2 생성자 예제

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;
    }
}
package construct;

public class ConstructMain1 {
    public static void main(String[] args) {
        // 인스턴스를 생성하고 나서 즉시 해당 생성자 호출
        MemberConstruct member1 = new MemberConstruct("user1", 15, 90);
        MemberConstruct member2 = new MemberConstruct("user2", 16, 80);

        MemberConstruct[] members = {member1, member2};
        for (MemberConstruct s : members) {
            System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
        }
    }
}

실행결과

생성자 호출 name=user1, age=15, grade=90
생성자 호출 name=user2, age=16, grade=80
이름:user1 나이:15 성적:90
이름:user2 나이:16 성적:80


3.3 생성자 장점

중복호출 제거: 생성자를 사용함으로써 객체를 생성할 때 필요한 초기화를 한 번의 호출로 수행할 수 있다.

  • 생성자가 없던 시절에는 생성 직후에 어떤 작업을 수행하기 위해 다음과 같이 메서드를 직접 한번 더 호출해야 했다.
  • 이렇게 작성할 경우 initMember 메서드를 호출하지 않는 실수를 할 수도 있다.
// 생성자 등장 전
MemberInit member = new MemberInit();
member.initMember("user1", 15, 90);


  • 생성자를 사용하면 한 번의 호출로 모든 초기화가 완료된다.
// 생성자 등장 후
MemberConstruct member = new MemberConstruct("user1", 15, 90);


필수 값 입력 보장

  • 생성자는 객체를 생성할 때 필수 값을 입력해야 하도록 강제할 수 있다.
  • 만약 생성자를 정의하지 않으면 기본 생성자가 자동으로 제공되지만, 직접 생성자를 정의하면 기본 생성자는 사라지므로 반드시 정의한 생성자를 호출해야 한다.
  • 이로 인해, 객체 생성 시 필수값을 반드시 입력하도록 할 수 있다.
MemberConstruct member3 = new MemberConstruct(); // 컴파일 오류 발생

위 코드와 같이 생성자를 정의하면 컴파일 오류가 발생한다. 따라서 항상 필수 값을 입력해야 하는데, 생성자를 사용하면 필수값 입력을 보장할 수 있다.



4. 기본 생성자

4.1 기본 생성자란?

기본 생성자는 매개변수가 없는 생성자를 의미한다.

자바에서 클래스에 명시적으로 생성자를 정의하지 않으면, 컴파일러가 자동으로 매개변수가 없는 기본 생성자를 만들어 준다.

기본 생성자는 다음과 같은 특징을 가지고 있다.

  1. 클래스에 생성자가 하나도 정의되지 않은 경우, 자바 컴파일러는 자동으로 기본 생성자를 생성한다.
  2. 자동으로 생성된 기본 생성자는 클래스와 동일한 접근 제어자를 가진다.
  3. 만약 클래스에 하나 이상의 생성자가 정의되어 있다면, 기본 생성자는 자동으로 생성되지 않는다.


4.2 기본 생성자 예제

아래 예제를 통해 기본 생성자를 확인해보자.

package construct;

public class MemberDefaultMain {
    public static void main(String[] args) {
        MemberDefault memberDefault = new MemberDefault();  // 기본 생성자 호출
    }
}

위의 MemberDefault 클래스에는 생성자가 명시적으로 정의되어 있지 않으므로, 자바 컴파일러는 자동으로 다음과 같은 기본 생성자를 추가한다.

package construct;

public class MemberDefault {
    String name;

    // 기본 생성자 (자동 생성됨, 개발자 눈에는 보이지 않음)
    public MemberDefault() {
    }
}


4.3 기본 생성자 정의

개발자가 직접 기본 생성자를 정의할 수도 있다.

package construct;

public class MemberDefault {
    String name;

    // 사용자 정의 기본 생성자
    MemberDefault() {
        System.out.println("생성자 호출");  // 생성자 호출 시 출력
    }
}

위와 같은 코드에서 MemberDefault 객체를 생성할 때 기본 생성자가 호출되어 “생성자 호출”이 출력된다.


4.4 기본 생성자의 필요성

기본 생성자가 자동으로 제공되는 이유는 다음과 같다.

  • 편의성
    • 개발자는 모든 클래스에 대해 기본 생성자를 수동으로 작성할 필요가 없다.
  • 유연성
    • 생성자 기능이 필요하지 않은 경우에도 클래스 인스턴스를 생성할 수 있다.
    • 예를 들어, 클래스가 단순히 데이터를 저장하는 역할만 할 때 기본 생성자를 자동으로 제공함으로써 유용하다.



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

5.1 생성자 오버로딩

생성자 오버로딩은 메서드 오버로딩과 유사하게, 같은 이름을 가진 생성자를 매개변수의 개수나 타입에 따라 다르게 정의하는 것이다.

이를 통해 다양한 방법으로 객체를 초기화할 수 있다.

package construct;

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

    // 첫 번째 생성자: 성적 기본값 50으로 설정
    MemberConstruct(String name, int age) {
        this.name = name;
        this.age = age;
        this.grade = 50;  // 기본 성적
    }

    // 두 번째 생성자: 모든 값 입력 받음
    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;
    }
}
package construct;

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

        MemberConstruct[] members = {member1, member2};
        for (MemberConstruct s : members) {
            System.out.println("이름:" + s.name + " 나이:" + s.age + " 성적:" + s.grade);
        }
    }
}

실행 결과

생성자 호출 name=user1, age=15, grade=90
이름:user1 나이:15 성적:90
이름:user2 나이:16 성적:50

생성자를 오버로딩 한 덕분에 성적 입력이 꼭 필요한 경우에는 grade가 있는 생성자를 호출하면 되고, 그렇지 않은 경우에는 grade가 없는 생성자를 호출하면 된다.


5.2 this()의 사용

this()는 같은 클래스의 다른 생성자를 호출할 수 있게 해준다.

위 코드의 두 생성자는 아래와 같이 코드가 중복되는 부분이 존재한다.

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

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


this() 활용하기

위의 예제에서 두 생성자 간에 중복된 코드를 줄이기 위해 this()를 사용하여 아래와 같이 수정할 수 있다.

package construct;

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

    // 첫 번째 생성자
    MemberConstruct(String name, int age) {
        this(name, age, 50);  // 두 번째 생성자 호출
    }

    // 두 번째 생성자
    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;
    }
}


5.3 this() 규칙

첫 줄에만 사용 가능

this()는 반드시 생성자 코드의 첫 번째 줄에 있어야 한다. 그렇지 않으면 컴파일 오류가 발생한다.

public MemberConstruct(String name, int age) {
    System.out.println("go");
    this(name, age, 50);  // 오류: this()는 첫 줄에 있어야 함
}



6. 문제와 풀이

6.1 Book과 생성자

문제 설명

  • BookMain코드가 작동하도록 Book클래스를 완성하세요.
  • 특히 Book클래스의 생성자 코드에 중복이 없도록 주의하세요.
package construct.ex;

public class Book {
    String title; //제목
    String author; //저자
    int page; //페이지 수

    //TODO 코드를 완성하세요.
}
package construct.ex;

public class BookMain {
    public static void main(String[] args) {
        // 기본 생성자 사용
        Book book1 = new Book();
        book1.displayInfo();

        // title과 author만을 매개변수로 받는 생성자
        Book book2 = new Book("Hello Java ", "Seo");
        book2.displayInfo();

        // 모든 필드를 매개변수로 받는 생성자
        Book book3 = new Book("JPA 프로그래밍", "kim", 700);
        book3.displayInfo();
    }
}

실행 결과

제목: , 저자: , 페이지: 0
제목: Hello Java, 저자: Seo, 페이지: 0
제목: JPA 프로그래밍, 저자: kim, 페이지: 700


내 풀이

package _04_constructor.ex;

public class Book {
    String title;
    String author;
    int page;

    // 기본생성자
    Book() {
        this("", "", 0);
    }

    // title과 author만을 매개변수로 받는 생성자
    Book(String title, String author) {
        this(title, author, 0);
    }

    //  모든 필드를 매개변수로 받는 생성자
    Book(String title, String author, int page) {
        this.title = title;
        this.author = author;
        this.page = page;
    }

    void displayInfo() {
        System.out.println("제목: " + title + ", 저자: " + author + ", 페이지: " + page);
    }
}
package _04_constructor.ex;

public class BookMain {
    public static void main(String[] args) {
        // 기본 생성자 사용
        Book book1 = new Book();
        book1.displayInfo();

        // title과 author만을 매개변수로 받는 생성자
        Book book2 = new Book("Hello Java ", "Seo");
        book2.displayInfo();

        // 모든 필드를 매개변수로 받는 생성자
        Book book3 = new Book("JPA 프로그래밍", "kim", 700);
        book3.displayInfo();
    }
}


카테고리:

업데이트:

댓글남기기