[Java] 생성자

szlee·2023년 12월 7일
0

Java

목록 보기
13/23

< 김영한의 실전 자바 - 기본편 > 강의를 보고 이해한 내용을 바탕으로 합니다.





생성자

객체를 생성하는 시점에 뭔가를 하고 싶을 때 생성자를 사용하면 된다.

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);

        }
    }
}

객체를 생성하고 나면 name, age, grade 같은 변수에 초기값을 설정한다.
위 코드에서 회원의 초기값을 설정하는 부분이 계속 반복된다.

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 객체의 멤버 변수를 사용한다.
이런 경우에 속성과 기능을 한 곳에 두는 것이 더 낫다!
MemberInit이 자기 자신의 데이터를 변경하는 기능(메서드)을 제공하는 것이 좋다.





this

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

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

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

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

initMember는 MemberInit에 초기값 설정 기능을 제공하는 메서드이다.
member1.initMember("user1", 15, 90); 이렇게 메서드를 호출하면 객체의 멤버 변수에 인자로 넘어온 값을 채운다.

this는 뭐냐고?

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

이 부분을 보면 메서드의 매개변수에 정의한 String nameMemberInit의 멤버변수 name의 이름이 똑같다. (나머지 변수도 마찬가지)
이럴 경우에 둘을 어떻게 구별할까?
이 경우에 멤버 변수보다 매개변수가 코드 블럭의 더 안쪽에 있기 때문에 매개변수가 우선순위를 가진다. 따라서 initMember()메서드 안에서 name이라고 적으면 매개변수에 접근하게 된다.
따라서 멤버 변수에 접근하려면 this를 붙여주면 된다.
this는 인스턴스 자신의 참조값을 가리킨다.

  • 매개변수의 이름과 멤버 변수의 이름이 같은 경우 this를 사용해서 둘을 명확하게 구분해야한다.
  • this는 인스턴스 자신을 가리킨다.

this 생략 가능한가?

그렇다. 매개변수 이름과 멤버변수 이름이 다르면 생략할 수 있다.
그런데 멤버변수에 접근할 때는 항상 this를 적는 코딩 스타일도 있다.
그런데, 요즘엔 IDE가 둘을 잘 구별해주기 때문에 무조건적으로 this를 쓰진 않는다ㅎㅎ







생성자 도입

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

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

생성자는 메서드와 비슷하게 생겼지만 차이가 있다.

  • 생상자의 이름은 클래스 이름과 같아야한다. 따라서 첫 글자도 대문자로 시작한다.
  • 생성자는 반환 타입이 없다. 비워두어야 한다.
  • 나머지는 메서드와 같다.

생성자 호출

생성자는 인스턴스를 생성하고 나서 즉시 호출된다.
생성자를 호출하는 방법은 new 명령어 다음에 생성자 이름과 매개변수에 맞추어 인수를 전달하면 된다.
new 생성자이름(생성자에 맞는 인수)
new 클래스이름(생성자에 맞는 인수)
생성자 이름 = 클래스 이름.

new MemberConstruct("user1", 15, 90);
인스턴스를 생성하고 즉시 해당 생성자를 호출한다.

new 키워드를 사용해서 객체를 생성할 때 마지막에 괄호()도 포함하는 이유가 바로 생성자 때문이다. 객체를 생성하면서 동시에 생성자를 호출한다는 의미를 포함!


생성자의 장점

중복 호출 제거
생성자 없이는 생성 직후 메서드를 직접 한번 더 호출해야 했지만, 생성자 덕분에 객체를 생성하면서 동시에 필요한 작업을 한번에 처리할 수 있게 되었다.

//1. 생성자 등장 전
MemberInit member1 = new MemberInit();
initMember(member1, "user1", 15, 90);
        
        
//2. 생성자 등장 후
MemberConstruct member1 = new MemberConstruct("user1", 15, 90);

1번의 경우 initMember()를 실수로 호출하지 않아도 프로그램은 작동한다. 하지만 데이터가 없기 때문에 이 값들을 필수로 반드시 입력해야 한다면 시스템에 큰 문제가 발생할 수 있다.
결국 아무 정보가 없는 유령 회원이 시스템 내부에 등장하게 된다.

생성자의 진짜 장점은 객체를 생성할 때 직접 정의한 생성자가 있다면 직접 정의한 생성자를 반드시 호출해야 한다는 점이다.
생성자를 메서드 오버로딩처럼 여러개 정의할 수 있는데 이 경우에는 하나만 호출하면 된다.
따라서 2번의 경우에 인자를 넣지 않으면, 즉 직접 정의한 생성자를 호출하지 않으면 IDE에서 컴파일 오류가 난다.
따라서 생성자를 사용하면 필수값 입력을 보장할 수 있다.



기본 생성자

MemberInit member1 = new MemberInit();

이면 MemberInit 클래스에

MemberInit() {
}

이렇게 생성자가 필요할 것 같은데 코드에는 없다. 없어도 문제가 없다.
왜냐면 기본생성자 때문!

  • 매개변수가 없는 생성자를 기본 생성자라 한다.
  • 클래스에 생성자가 하나도 없으면 자바 컴파일러는 매개변수가 없고 작동하는 코드가 없는 기본 생성자를 자동으로 만들어준다.
  • 생성자가 하나라도 있으면 자바는 기본 생성자를 만들지 않는다.

자바는 왜 기본 생성자를 자동으로 만들어줄까?

만약 기본 생성자를 만들어주지 않는다면 생성자 기능이 필요하지 않은 경우에도 모든 클래스에 개발자가 직접 기본 생성자를 정의해야한다. 생성자 기능을 사용하지 않는 경우도 많기 때문에 이런 편의 기능을 제공한다.

  • 생성자는 반드시 호출 되어야한다.
  • 생성자가 없으면 기본 생성자가 제공된다.
  • 생성자가 하나라도 있으면 기본 생성자가 제공되지 않는다. --> 개발자가 정의한 생성자를 직접 호출해야 한다.




생성자 오버로딩과 this

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

public class MemberConstruct {
    String name;
    int age;
    int grade;
    
    
    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;
    }
}

이렇게 생성자 두개 만들어서(오버로딩) 쓸 수 있다.

MemberConstruct member2 = new MemberConstruct("user2", 16);
// 결과 : user2, 16, 50 

주의할 점 : 초기화 로직을 생성하는거지 객체를 여러개 생성하는 것이 아님!


위 코드에서 this()를 활용하여 중복되는 부분을 지울 수 있다.
this()를 사용하면 생성자 내부에서 자신의 생성자를 호출할 수 있다.
(this는 인스턴스 자신의 참조값을 가리킨다. 그래서 자신의 생성자를 호출한다고 이해할 수 있다.)

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

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

MemberConstruct(String name, int age){
        System.out.println("go");
        this(name, age, 50);
    }   

위 코드는 규칙위반으로 컴파일 오류가 발생한다.

profile
🌱

0개의 댓글