클래스의 생성자(Constructor) 알아보기
국비지원수업 13일차
1. 생성자란?
생성자 (Constructor)
이전에 다뤘던 메소드와 조금 다르게 별도로 정의하지 않아도 클래스 내부에 존재하는 메소드로, 클래스 이름과 동일한 이름을 갖는 멤버 메소드이다. 리턴 값이 없어 메소드 정의 시 void, int 등의 리턴 자료형을 쓰지 않으며 객체가 생성될 때마다 호출된다.
class A{
A(){
}
class A
A a = new A();
main 클래스, A의 객체 생성
new 키워드와 함께 객체를 저장하기 위해 Heap 메모리에 공간을 할당하며, 할당된 공간에 멤버 변수를 만든다. 즉, 생성자는 new Instance를 만드는 역할이라 할 수 있다. 본래의 역할 이외에 추가적인 코드를 더 기술해서 '객체 생성 시'에 실행하게 할 수도 있다.
2. 생성자의 형태
class Aclass{
private int age;
Aclass(){
// 보통 멤버 변수를 초기화하는 역할
age = 100;
System.out.println("객체 생성! Aclass의 생성자 호출! 멤버변수 값 : " + age);
}
보통 생성자는 멤버 변수를 초기화하는 역할로 사용한다.
외부 패키지에서 가져다 쓰지 않는다면 public을 생략할 수 있으며, 리턴 값이 없기 때문에 리턴 자료형도 생략한다.
메소드 내부에 별다른 명령이 쓰여지지 않는다면 숨어있는 채로 있는 것과 다르지 않다.
class Aclass{
private int age;
Aclass(){
age = 100;
System.out.println("객체 생성! Aclass의 생성자 호출! 멤버변수 값 : " + age);
}
// 생성자가 클래스 내부에 기술된다고 해서 다른 메소드에 영향을 미치는 것은 아님
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
생성자도 별도의 기능이 있는 특별한 메소드일 뿐, 생성자가 클래스 내부에 기술된다고 해서 다른 메소드에 영향을 미치지 않아 기존처럼 자유로운 메소드 생성이 가능하다.
2-1. 기본 형태 (디폴트 생성자)
Bclass(){ }
매개변수가 없는 생성자
생성자가 클래스 내부에 아무것도 쓰여지지 않고 감춰져 있는 상태라면 객체 생성시에 감춰져 있는 생성자를 자동으로 호출한다.
생성자는 리턴값이 없을 뿐 매개변수는 사용할 수 있다.
아래와 같이 매개변수가 있는 생성자를 클래스 내부에 정의했다면 ,매개변수가 있는 생성자가 "추가"된 것이 아니라 감춰져 있던 Bclass(){ } 가 생성자를 대체한 것으로 인식한다.
class Bclass{
private int age;
// 매개변수가 있는 생성자
Bclass(int age){
this.age = age;
}
}
Bclass 클래스
public static void main(String[] args) {
// Bclass b1 = new Bclass( ); // 에러 Bclass b1 = new Bclass( 10 );
Bclass b2 = new Bclass( 100 );
}
main 클래스
따라서 Bclass b1 = new Bclass() 형태로 객체를 생성하려고 하면, 매개변수의 자료형이 맞지 않는 생성자를 호출한 것으로 인식되어 에러가 발생한다. 이러한 에러를 해결하기 위해서는
1. 객체 생성시에 생성자 호출(괄호)에 전달 인수를 넣거나 (Bclass(int age){ ... } 형태에 맞춰서)
2. 반드시 디폴트 생성자를 호출해야 한다면, 클래스 내부에 호출하고 싶은 형태로 디폴트 생성자를 오버로딩한다.
class Bclass{
private int age;
Bclass(int age){
this.age = age;
}
// 오버로딩
Bclass(){
}
}
2-2. 오버로딩 (Overloading)
class Cclass{
Cclass(){ System.out.println("Default Constructor 호출"); }
Cclass(int n){ System.out.println("int 매개변수가 있는 Constructor 호출"); }
Cclass(double d){ System.out.println("double 매개변수가 있는 Constructor 호출"); }
Cclass(String s){ System.out.println("String 매개변수가 있는 Constructor 호출"); }
}
Cclass 클래스
public static void main(String[] args) {
Cclass c1 = new Cclass();
Cclass c2 = new Cclass(10);
Cclass c3 = new Cclass(10.123);
Cclass c4 = new Cclass("String Data");
}
main 클래스
3. this 키워드
3-1. 객체를 구분하는 this
클래스의 객체가 생성될 때 각 개체마다 멤버 변수의 공간은 별도로 생성되지만, 멤버 메소드는 한개만 생성되어 멤버 메소드를 이용하는 객체들에게 공유되어진다. 이때, 멤버 메소드 입장에서 자신을 호출하는 객체를 구분하기 위해 this 라는 참조변수를 사용한다.
노출되지 않는 디폴트 생성자처럼 this 또한 감춰져 있음. 즉, 따로 생성하는 명령이 없다.
class ThisA{
private int num;
ThisA( /* ThisA this */ ){
this.num = 100;
}
void init(int i /*, ThisA this */) {
this.num = i;
}
public int getNum(/* ThisA this */) {
return num;
}
public void setNum(int num /*, ThisA this */) {
this.num = num;
}
public void copy1(ThisA t /*, ThisA this */) {
this.num = t.num; // this <- a3 t <- a
}
public ThisA copy2( /* ThisA this */) {
ThisA temp = new ThisA();
temp.num = this.num;
return temp; // temp -> a4 a2 --> (숨겨진)this
}
}
ThisA 클래스
public static void main(String[] args) {
ThisA a1 = new ThisA();
a1.init(333);
System.out.println("a1의 num값 : " + a1.getNum());
ThisA a2 = new ThisA();
a2.setNum(500); // a2 -> this 전달
System.out.println("a2의 num값 : " + a2.getNum());
ThisA a3 = new ThisA();
a3.copy1(a1);
System.out.println("a1의 num값 : " + a1.getNum());
System.out.println("a3의 num값 : " + a3.getNum());
ThisA a4 = a2.copy2();
System.out.println("a2의 num값 : " + a2.getNum());
System.out.println("a4의 num값 : " + a4.getNum());
}
main 클래스
3-2. 생성자 호출 this
호출하는 형태(매개변수 형태)대로 생성자가 오버로딩 되어 있는 경우, 오버로딩 된 생성자들 간에 재호출을 위한 이름으로 사용된다.
class ThisB{
private int num1;
ThisB(int i){
num1 = i;
System.out.println("ThisB 클래스의 생성자1이 호출되었습니다.");
}
// 생성자2
ThisB(int n1, int n2){
// ThisB(n1); // 생성자는 new와 함께 객체가 생성될 때 호출되어야 함, 그 외에 임의로 호출할 수 없음
this(n1); // 객체 생성은 안 하고, 형제 생성자의 코드만 실행하려 호출
num2 = n2;
System.out.println("ThisB 클래스의 생성자2가 호출되었습니다.");
}
}
ThisB 클래스
ThisB b1 = new ThisB(10);
System.out.println();
ThisB b2 = new ThisB(20, 30);
System.out.println();
main 클래스
클래스의 이름을 불러 ThisB(n); 식으로의 호출은 제한되어 있다.
생성자 내에서 다른 오버로딩된 생성자를 호출하기 위해서는 this(n); 과 같이 매개변수의 순서와 갯수를 맞춰 호출한다.
class ThisB{
private int num1;
private int num2;
private int num3;
// 생성자1
ThisB(int i){
num1 = i;
System.out.println("ThisB 클래스의 생성자1이 호출되었습니다.");
}
// 생성자2
ThisB(int n1, int n2){
this(n1);
num2 = n2;
System.out.println("ThisB 클래스의 생성자2가 호출되었습니다.");
}
ThisB(int n1, int n2, int n3){
this(n1, n2); // num1 = n1, num2 = n2;
// 생성자 안에서 호출되는 this()는 다른 명령보다 항상 위에 있어야 함
// 순서가 맞지 않으면 에러를 발생
// Constructor call must be the first statement in a constructor
num3 = n3;
System.out.println("ThisB 클래스의 생성자3가 호출되었습니다.");
}
}
ThisB 클래스
ThisB b3 = new ThisB(40, 50, 60);
System.out.println();
main 클래스
또한 생성자 안에서 호출되는 this()는 다른 명령보다 항상 위에 있어야 한다. 순서가 맞지 않으면 ' Constructor call must be the first statement in a constructor ' 오류를 발생시킨다.
3-3. 응용 예제
Student2 클래스에서 디폴트 생성자를 포함한 3종류의 생성자를 제작하고, 오버로딩된 생성자 내에서 매개변수에 차이를 두어 다른 생성자를 호출한다.
class Student2{
private String name;
private int[] score;
// score 배열의 정수 3칸의 공간을 할당하는 디폴트 생성자
Student2(){
score = new int[3]; // 4
System.out.println("디폴트 생성자"); // 5
}
Student2(String name) {
this(); // 3
this.name = name; // 6
System.out.println("String data가 전달되는 생성자"); // 7
}
Student2(String name, int kor, int eng, int mat){
this(name); // 2
score[0] = kor; // 8
score[1] = eng;
score[2] = mat;
System.out.println("String, int, int, int data가 전달되는 생성자"); // 9
}
}
Student2 클래스
public static void main(String[] args) {
Student2 std2 = new Student2("홍길동");
System.out.println();
Student2 std3 = new Student2("홍길남", 98, 87, 89); // 실행순서 1
System.out.println();
}
main 클래스
Std3 객체에 대해서는 실행 순서를 주석으로 표시해봤다.
'Backend > Java' 카테고리의 다른 글
[JAVA] 싱글톤 패턴(Singleton Pattern)과 초기화 블록 (0) | 2024.10.14 |
---|---|
[JAVA] static과 인스턴스(instance) (0) | 2024.10.14 |
[JAVA] 클래스(Class) 생성과 사용 (0) | 2024.10.07 |
[JAVA] 메소드 (Method) (0) | 2024.09.27 |
[JAVA] 배열 (Array), 2차원 배열 (0) | 2024.09.27 |