객체를 생성하는 시점에 어떤 작업을 하고 싶다면 생성자(Constructor)를 이용하면 된다.
package construcct;
public class MemberInit {
String name;
int age;
int grade;
}
package construcct;
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 m : members) {
System.out.println("name : " + m.name + " age : " + m.age + " grade : " + m.grade);
}
}
}
위 코드는 회원의 초기값 설정하는 부분이 계속 반복되기 때문에 메서드를 사용해서 반복을 제거해본다.
package construcct;
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 m : members) {
System.out.println("name : " + m.name + " age : " + m.age + " grade : " + m.grade);
}
}
static void initMember(MemberInit member, String name, int age, int grade) {
member.name = name;
member.age = age;
member.grade = grade;
}
}
initMember 메서드를 사용해서 반복을 제거했다. 이 메서드는 MemberInit 객체의 멤버 변수를 사용한다. 이것보다, MemberInit이 자기 자신의 데이터를 변경하는 기능(메서드)를 제공하는 게 훨씬 깔끔하고 편리할 것.
package construcct;
public class MemberInit {
String name;
int age;
int grade;
void iniitMember(String name, int age, int grade) {
this.name = name;
this.age = age;
this.grade = grade;
}
}
package construcct;
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 m : members) {
System.out.println("name : " + m.name + " age : " + m.age + " grade : " + m.grade);
}
}
}
(클래스 Member의 코드를 다시 보자.)
멤버 변수와 메서드의 배개변수의 이름이 같으면 둘을 어떻게 구분해야 할까?
initMember(String name,...) 메서드 안에서 name이라고 적으면 매개변수에 접근하게 된다. this.라고 해주면 된다. this.는 인스턴스 자신의 참조값을 뜻한다.this를 사용하지 않으면,
this.name = name
name = name
name은 둘다 매개변수를 뜻하게 된다. 즉, 멤버변수의 갑이 변경되지 않는다.
this를 사용해서 둘을 명확하게 구분해야 한다. this는 인스턴스 자신을 가리킨다.this는 생략할 수 있다. 변수를 찾을 때 가까운 지역변수(매개변수도 지역변수임)를 먼저 찾고 없으면 그 다음으로 멤버변수를 찾는다. 멤버변수도 없으면 오류 발생.
this를 꼭 생략하지 않고 사용해도 된다. this를 사용하면 이 코드가 멤버 변수를 사용한다는 것을 눈으로 쉽게 확인할 수 있다. (매개변수와 멤버변수를 눈에 보이도록 구분)
객체를 생성하고 이후에 바로 초기값을 할당해야 하는 경우가 많다. initMember(...)와 같은 메서드를 매번 만들어야 한다.
객체를 생성하자마자 즉시 필요한 기능을 수행할 수 있도록 생성자라는 기능을 제공한다. 생성자를 사용하면 객체를 생성하는 시점에 즉시 필요한 기능을 수행할 수 있다.
package construcct;
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;
}
}
다음이 생성자이다.
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 construcct;
public class ConstructMain1 {
public static void main(String[] args) {
MemberConstruct member1 = new MemberConstruct("user1", 15, 90);
MemberConstruct member2 = new MemberConstruct("user1", 16, 80);
MemberConstruct[] members = {member1, member2};
for (MemberConstruct m : members) {
System.out.println("name : " + m.name + " age : " + m.age + " grade : " + m.grade);
}
}
}
생성자는 인스턴스를 생성하고 나서 즉시 호출된다.
생성자를 호출하는 방법은 new 명령어 다음에 생성자 이름과 함께 매개변수에 맞춰 인수를 전달.
➡️ 인스턴스를 생성하고 즉시 해당 생성자르 호출한다.
참고로 new 키워드를 사용해서 객체를 생성할 때 마지막에 괄호 () 도 포함해야 하는 이유가 바로 생성자 때문이다. 객 체를 생성하면서 동시에 생성자를 호출한다는 의미를 포함한다.
생성자가 없던 시절에는 생성 직후에 어떤 작업을 수행하기 위해 다음과 같이 메서드를 직접 한번 더 호출해야 했다. 생성자 덕분에 객체를 생성하면서 동시에 생성 직후에 필요한 작업을 한번에 처리할 수 있게 되었다.
//생성자 등장 전
MemberInit member = new MemberInit(); member.initMember("user1", 15, 90);
//생성자 등장 후
MemberConstruct member = new MemberConstruct("user1", 15, 90);
initMember(...) 를 실수로 호출하지 않으면 어떻게 될까? 이 메서드를 실수로 호출하지 않아도 프로그램은 작동한다. 하지만 회원의 이름과 나이, 성적 데이터가 없는 상태로 프로그 램이 동작하게 된다. 만약에 이 값들을 필수로 반드시 입력해야 한다면, 시스템에 큰 문제가 발생할 수 있다.
생성자의 진짜 장점은 객체를 생성할 때 직접 정의한 생성자가 있다면 직접 정의한 생성자를 반드시 호출해야 한다는 점 이다. 참고로 생성자를 메서드 오버로딩 처럼 여러개 정의할 수 있는데, 이 경우에는 하나만 호출하면 된다.
MemberConstruct(String name, int age, int grade) {...}
직접 정의한 생성자를 호출하지 않으면 컴파일 오류가 발생한다.

생성자를 사용하면 필수값 입력을 보장할 수 있다
public class MemberInit {
String name;
int age;
int grade;
}
public class MethodInitMain1 {
public static void main(String[] args) {
MemberInit member1 = new MemberInit();
... }
}
여기서 new MemberInit() 이 부분은 매개변수가 없는 다음과 같은 생성자가 필요하다.
public class MemberInit {
String name;
int age;
int grade;
MemberInit() { // 기본 생성자
}
package construcct;
public class MemberDefault {
String name;
}
package construcct;
public class MemberDefaultMain {
public static void main(String[] args) {
MemberDefault memberDefault = new MemberDefault();
}
}
MemberDefault 클래스에는 생성자가 하나도 없으므로 자바는 자동으로 다음과 같은 기본 생성자를 만들어준다. (우리 눈에 보이지는 X)
package construct;
public class MemberDefault {
String name;
//기본 생성자
public MemberDefault() {
}
}
c.f 자바가 자동으로 생성해주는 기본 생성자는 클래스와 같은 접근 제어자를 가진다.(public)
생성자 기능을 사용하지 않는 경우도 많기 때문이다.
생성자도 메서드 오버로딩처럼 매개변수만 다르게 해서 여러 생성자를 제공할 수 있다.
package construcct;
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;
}
}
새로 추가한 생성자는 grade를 받지 않는다. (대신 grade는 50이 됨)
package construcct;
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 m : members) {
System.out.println(" name : " + m.name + " age : " + m.age + " grade : " + m.grade);
}
}
}

생성자를 오버로딩 함 - 성적 입력이 꼭 필요한 경우에는 grade 가 있는 생성자를 호출, 그렇지 않은 경우에는 grade 가 없는 생성자를 호출하면 된다. grade 가 없는 생성자를 호출하면 성적은 50 점이 된다.
근데 두 생성자의 코드 중복을 제거하려면?
이때 this() 라는 기능을 사용하면 생성자 내부에서 자신의 생성자를 호출할 수 있다. 참고로 this 는 인스턴스 자신 의 참조값을 가리킨다. 그래서 자신의 생성자를 호출한다고 생각하면 된다.
package construcct;
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;
}
}
위와 같이 첫 번째 생성자 내부에서 두 번째 생성자를 호출한다.
MemberConstruct(String name, int age) -> MemberConstruct(String name, int age,
int grade)
this()는 생성자 코드의 첫줄에만 작성할 수 있다.package construcct.ex;
public class Book {
String title;
String author;
int page;
Book() {
this("", "", 0);
}
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 : " + this.title + ", author : " + this.author + ", page : " + this.page);
}
}
package construcct.ex;
public class BookMain {
public static void main(String[] args) {
Book book1 = new Book();
book1.displayInfo();
Book book2 = new Book("hello java", "seo");
book2.displayInfo();
Book book3 = new Book("jpa programming", "kim", 700);
book3.displayInfo();
}
}
생성자는 객체 생성 직후 객체를 초기화 하기 위한 특별한 메서드이다.