객체지향 프로그래밍 -2

cutiepazzipozzi·2023년 4월 25일
0

지식스택

목록 보기
23/35
post-thumbnail

[생성자(Constructor)]

생성자?

= 인스턴스가 생성될 때 호출되는 인스턴스 초기화 메서드
(인스턴스 초기화 = 인스턴스 변수를 초기화)

생성자의 조건

  1. 생성자 이름 = 클래스 이름
  2. return 값이 없어야 함
class TV {
	TV() {//기본생성자}
    TV(int remote) {//매개변수 있는 생성자}
}

위의 생성자를 보면 우리가 그동안 new 연산자를 활용해 인스턴스를 선언했던 부분은 생성자를 호출하는 코드였던 것이었던 것이었다..!!

※ 여기서 주의해야 할 점은
new연산자가 인스턴스를 생성하는 것이지, 생성자가 인스턴스를 생성하는 것은 아니다! 생성자도 그저 하나의 특수한 메서드일뿐,,

기본 생성자

모든 클래스에는 하나 이상의 생성자가 정의되어야 한다.
그러나 사실 우리가 이 단원을 배우기 전까지는 생성자를 정의하지 않았던 것 같은데, 어떻게 생성자가 알아서 정의되었던 것일까?

이건 컴파일러가 제공하는 기본 생성자 덕택이었다
컴파일 시 .java 클래스에 생성자가 없다면 자동적으로 위의 생성자 조건을 갖춘 기본 생성자를 컴파일러가 추가해준다. 만약 특별히 생성자를 만들어 줄 필요가 없다면, 기본 생성자를 불러와도 된다.

그러나 기본 생성자는 생성자를 따로 선언해주지 않는 경우에만 만들어지기 때문에, 생성자를 하나라도 선언했다면 그 형태에 맞게 선언해줘야 한다! 아니라면 오류가 발생한다. 아래의 예시를 보자.

class Data1 {int value;}
class Data2 {
	int value;
    Data2(int x) {value = x;}
}
class Ex1 {
	public static void main(String[] args) {
    	Data1 d1 = new Data1();
        Data2 d2 = new Data2(); //이거 무조건 컴파일 에러!!!!!
    }
}

매개변수가 있는 생성자

호출 시 값을 넘겨 받아 인스턴스를 생성해야 하는 생성자도 있다.

class Car {
	String color;
    String gearType;
    int door;
    
    Car() {}
    Car(String c, String g, int d) {
    	color = c;
        gearType = g;
        door = d;
    }
}

public static void main(String[] args) {
	Car car1 = new Car();
    car.color = "yellow";
    car.gearType = "auto";
    car.door = 4;
    
    Car car2 = new Car("yello", "auto", 4);
    
    //2개의 생성자를 활용해 만든 두 인스턴스는 사실 같은 속성을 갖지만
    //누가 봐도 생성하는데 두번째가 더 간편하지 않은가?
}

생성자에서 다른 생성자 호출 -> this(), this

= 생성자 끼리도 호출이 가능하단 말씀!
= this: 인스턴스 자신을 가리키는 참조변수
(this를 통해 인스턴스변수에 접근)
= this(): 생성자(같은 클래스의 다른 생성자 호출 시)

  • 생성자의 이름으로 클래스이름 대신 this 사용
  • 한 생성자에서 다른 생성자 호출 시 반드시 첫 줄에서만!!
class Car {
	String color;
    String gearType;
    int door;
    
    Car() {this("white", "auto", 4);}
    Car(String color) {
    	this(color, "auto", 4);
    }
    Car(String color, String gearType, int door) {
    	this.color = color;
        this.gearType = gearType;
        this.door = door;
    }
}

class Ex1 {
	public static void main(String[] args) {
    	Car c1 = new Car();
        Car c2 = new Car("blue");
        
        //c1은 white, auto, 4
        //c2는 blue, auto, 4
    }
}

(생성자 내에서 초기화 작업도중 다른 생성자를 호출하게 되면, 호출된 다른 생성자 내에서도 멤버변수들의 값을 초기화 할 것이므로 다른 생성자를 호출하기 이전의 작업이 무의미해질 수도 있어서,,)

생성자를 이용한 인스턴스의 복사

-> 현재 사용되고 있는 인스턴스와 같은 상태의 새로운 인스턴스를 만들고 싶을 때!
(두 인스턴스가 같은 상태이다 = 모든 인스턴스 변수가 같은 값을 갖는다)

//위의 예시에서 인스턴스 복사를 위한 새로운 생성자를 하나 더 만든다
Car(Car c) {
	color = c.color;
    gearType = c.gearType;
    door = c.door;
}
  • 하나의 클래스로부터 생성된 모든 인스턴스에서 각기 다른 값을 가질 수 있는 것은 인스턴스 변수 뿐이다!
  • 그렇지만 서로 독립적으로 메모리공간에 존재하는 별도의 인스턴스이기 대문에 원래의 인스턴스 값이 변경되어도 새로운 인스턴스 값은 변경되지 않는다.

인스턴스 생성 시 고려점

  1. 클래스 -> 어떤 클래스를 생성할 것인가?
  2. 생성자 -> 선택한 클래스의 어떤 생성자로 인스턴스를 생성할 것인가?

[변수의 초기화]

-> 변수를 선언하고 처음으로 값을 저장하는 것
-> 가능하면 선언과 동시에 적절한 값으로 초기화 하는 것이 Good!

class Ex1 {
	int x; // 인스턴스 변수
    int y = x;
    
    void method() {
    	int i;
        int j = i; // 이러면 컴파일 에러!! 
    }
    //지역변수인 i를 초기화하지 않은 상태에서 j에도 i 값을 부여했으므로
}

멤버변수(클래스 변수, 인스턴스 변수)와 배열의 초기화는 선택적이지만, 지역변수는 반드시 사용 전에 초기화를 해주어야 함

멤버변수의 초기화 방법

  1. 명시적 초기화
  2. 생성자
  3. 초기화 블럭 (인스턴스 초기화 블럭, 클래스 초기화 블럭)

명시적 초기화

명시적 초기화 = 변수를 선언과 동시에 초기화하는 것

class Car {
	int door = 4;
    Engine e = new Engine();
}

초기화 블럭

  1. 클래스 초기화 블럭
  2. 인스턴스 초기화 블럭
class InitBlock {
	static {//클래스 초기화 블럭}
    {//인스턴스 초기화 블럭}
}
  • 클래스 초기화 블럭은 클래스가 메모리에 처음 로딩될 때 한번만 수행되고, 인스턴스 초기화 블럭은 인스턴스가 생성될 때 마다 수행된다.
  • 생성자 보다 인스턴스 초기화 블럭이 먼저!!!!!
  • 보통은 인스턴스 초기화 대신 생성자를 사용하지만, 모든 생성자가 공통적으로 수행해야 하는 코드가 있다면 그것을 인스턴스 초기화 블럭에 넣으면 좋다!!
    => 재사용성을 높이고 중복을 제거 = 객체지향프로그래밍의 목표!!

위의 설명이 코드로 그려질만한 간단한 아래의 예제를 보자.

class Ex1 {
	static {System.out.println("static { }");}
    {System.out.pritnln("{ }");
    
    public Ex1() {System.out.println("생성자");}
    
    public static void main(String[] args) {
    	System.out.println("Ex1 e1 = new Ex1();");
        Ex1 e1 = new Ex1();
        
        System.out.println("Ex1 e2 = new Ex1();");
        Ex1 e2 = new Ex1();
    }
}

//출력결과
static { }
Ex1 e1 = new Ex1();
{ }
생성자
Ex1 e2 = new Ex1();
{ }
생성자

클래스 초기화 블럭 -> main 메서드 -> 인스턴스 생성 후 초기화 블럭

멤버변수의 초기화 시기와 순서

클래스 변수의 초기화 시점: 클래스가 처음 로딩될 때 단 한번
인스턴스 변수의 초기화 시점: 인스턴스가 생성될 때마다 초기화
클래스 변수의 초기화 순서: 기본값 -> 명시적 -> 클래스 초기화 블럭
인스턴스 변수의 초기화 순서: 기본값 -> 명시적 -> 인스턴스 초기화 블럭
-> 생성자

  • 프로그램 도중 클래스에 대한 정보가 요구된다면, 클래스는 메모리에 로딩된다. 그러나 이미 메모리에 있다면, 초기화 및 로딩 X !!
class InitTest {
	static int cv = 1;
    int iv = 1; //여가까지는 명시적 초기화
    
    static {cv = 2;} //클래스 초기화 블럭
    {iv = 2;} //인스턴스 초기화 블럭
    
    InitTest() { //생성자
    	iv = 3;
    }
}
  • 클래스 초기화(클래스가 처음 메모리에 로딩될 때 수행)
    기본값: cv = 0
    명시적 초기화: cv = 1
    클래스 초기화 블럭: cv = 2
  • 인스턴스 초기화(인스턴스 생성 시 차례대로 수행)
    기본값: cv = 2, iv = 0
    명시적 초기화: cv = 2, iv = 1
    인스턴스 초기화 블럭: cv = 2, iv = 2
    생성자: cv = 2, iv = 3

연습문제

6-10)
(b) 객체를 생성 = new 연산자, 객체 초기화 = 생성자

6-11)
(b) this 키워드는 인스턴스 메서드 내에서만

6-16)
(e) 지역변수는 호출스택 영역에 생겼다가 소멸됨
** heap 영역에는 인스턴스 변수가!

6-17)
(f) 호출 스택에서 가장 상위 메서드가 아닌 메서드들은 대기중!

6-19)

public static void change(String str) {
	str += "456";
}
public static void main(String[] args) {
	String str = "ABC123";
    System.out.println(str);
    change(str);
    System.out.println("After change:"+str);
}
//둘 모두 출력 결과는 ABC123

String은 불변 자료형이기 때문에 change 메서드에서 주어진 매개변수에 456을 추가하게 되면 새로운 문자열이 생성된다. 그러나 마지막 코드에서 불러오는 str은 원래의 주솟값이기 때문에 원래 문자열 그대로 출력된다!!

profile
노션에서 자라는 중 (●'◡'●)

0개의 댓글