상속을 받은 클래스가 인스턴스를 생성할 때 어떤 일이 벌어지는가 알아본다.

 

public class OOPTest {
	public static void main(String[] args) {
		B1 b1 = new B1();	
		System.out.println("Good Day, dear my lord!");
	}
}
class A1 {

	int ID;
	String name;
	int naturalNumber;
	
	A1(){
		System.out.println("A1() Called");
	}
}
class B1 extends A1{
	
	double realNumber;
	
	B1(){
		// super();
		System.out.println("B1() Called");
	}
}

클래스 B1은 클래스 A1을 상속한다. main 메소드에서 상속한 B1을 생성했다.

 

결과는 부모클래스 A1이 먼저 생성되고 B1이 생성된다.

 

생성자 호출 순서

힙메모리에서 보면 A1이 먼저 생성되고 그 다음에 B1의 멤버변수가 들어온다. 상속을 하게 되면

 

*부모 클래스의 멤버변수 + 자손 클래스의 멤버변수 = 클래스의 멤버변수

 

가 되고 자손 멤버변수의 개수는 항상 부모와 같거나 많게 된다.

 

class A1 {
	int ID;
	String name;
	int naturalNumber;	
	A1(){
		System.out.println("A1() Called");
	}
}
class B1 extends A1{

	double realNumber;
	
	B1(){
		super(); // A1 () 생성자를 호출한다.
		System.out.println("B1() Called");
	}
}

 

B1은 상위 클래스의 생성자를 호출하는데 컴파일러가 자동으로 super ( ); 를 추가한다. super 는 this 와 관계가 있다.  this 는 자기 자신을 참조하는 것이고 super 는 상위클래스 (부모)를 참조하는 것이다.

 

즉 다음과 같이 생성자가 호출된다.

 

super ( ) ;

B1 ( ) ; 

 

실질적으로

 

super( );

this( );

 

라 볼 수도 있다. 이제 부모의 매개변수를 바꿔 새로운 생성자를 만들어 보자.

 

super ( )

부모의 생성자 A1 ( int,String,int)를 새로 정의했다. 그랬더니 컴파일 오류가 난다. B1을 생성하려고 할 때 super( ) 생성자를 호출하는데 매개변수가 없기 때문이다. 이제 B1에서 생성자를 정의하고, main 메서드에서 매개변수를 추가한다. 코드가 정상으로 작동하는 것을 볼 수 있다.

 

public class OOPTest {
	public static void main(String[] args) {
		B1 b1 = new B1(2020,"my Lord",100,101.5);	
		B1 b2 = new B1();
		System.out.println("Good Day, dear my lord!");
	}
}
class A1 {

	int ID;
	String name;
	int naturalNumber;
	
	A1(){
		System.out.println("old A1");
	}
	A1(int ID, String name, int naturalNumber){
		this.ID = ID;
		this.name = name;
		this.naturalNumber = naturalNumber;	
		System.out.println("A1() new Called");
	}
}
class B1 extends A1{	
	double realNumber;	
	B1(){
		System.out.println("old B1");
	}
	B1(int ID, String name, int naturalNumber,double realNumber){	
		super(ID, name, naturalNumber);
		this.realNumber = realNumber;
		System.out.println("B1() new Called");
	}
}

결과를 비교해보면 확실히 알 수 있다.

 

super인 A1( ) new 가 먼저 생성되고 B1( ) 이 따른다.

 

매개변수가 없는 기존 B1은 여전히 super( )는 생략되어 있으나 super( )를 먼저 생성한다.

 

결론적으로 자손이 생성될때 부모가 먼저 생성된다는 것을 알 수 있다. super( ) -> this( )

 

 super 연산자로 부모의 멤버변수나 메소드를 참조할 수 있다. 예를 들어 super.ID 멤버변수에 접근, super.메소드( ) 처럼 사용가능하다. 하나의 인스턴스에서 멤버 변수는 동일 참조값이다. 메소드의 경우 자손을 오버라이드 했다면 부모와 다를 수 있다. 예전 메서드를 사용하기 위해서 super 연산자를 사용할 수 있다. 이 부분은 오버라이드에서 다룰 것이다.

 

*클래스 형 변환 (상위 클래스)

여기까지 봤으면 왜 자손클래스의 범위가 부모클래스의 범위보다 큰지 이해할 수 있을 것이다. B1 extends A1은 A1보다 확장한다는 뜻이다. 멤버변수와 메서드 측면에서 확장된다.

 

B1 extends A1

그렇다는 것은 B1의 인스턴스를 생성하면 A1 참조변수로 사용할 수 있다는 것이다. B1의 확장된 영역은 사용하지 못하겠지만 자기 범위인 A1클래스의 멤버면수와 메소드에는 접근할 수 있다. 다시 말해

 

A1 a1 = new B1( );

 

이 가능하다. 이것을 클래스 형 변환이라고 한다.

 

반대로 A1의 인스턴스를 B1이 접근할 수는 없다. double 형 참조는 가리킬 곳이 없다. 이것은 불가능하다. 아무리 A1의 인스턴스를 만들어도 B1의 double 참조공간은 계속 비어있을텐데 사용할 이유가 없다.

 

A1을 참조 변수로 B1의 인스턴스를 생성했더니 기존 A1 클래스의 변수들만 사용할 수 있다. B1의 double realNumber는 사용 못한다.

 

그런데 다시 B1형으로 형변환을 하니 realNumber가 사용가능해졌다.  옆에 붙어있는 표시가 어느 클래스의 변수인지를 말해준다. A1, B1, Object

 

최초 new B1( ) 생성시기에 메모리상에 double realNumber도 이미 생성되었다는 것을 의미한다. 참조 자료형이 A1으로 범위가 B1보다 적었기 때문에 접근을 못한 것 뿐이었다. 다시 B1형으로 변환하면 접근할 수 있다. (다시 자손으로 내려가서 다운캐스팅이라 한다)

 

 

공유하기

facebook twitter kakaoTalk kakaostory naver band