객체지향언어의 3대 특징
1. 상속성
2. 캡슐화
3. 다형성
상속성 - 클래스를 상속받아 클래스를 만든다.
class A {
int data = 0;
void print(){
System.out.println( "print" + this.data );
}
}
class B extends A{ // extends A 가 있을 때와 없을때 차이 .
/* A 상속받은 부분
int data = 0;
void print(){
System.out.println( "print" + this.data );
}
>> 이러한 코드가 보이진 않지만 B 내에 존재하는 상황.
*/
void print2(){
System.out.println("print2");
// >> 조상에서 선언된 함수.변수, B 자체에서 선언된 함수 변수 둘다 이용가능.
}
}
- class B extends A : "class B 는 class A로부터 상속받았다. A는 B의 조상클래스, B는 A의 자손클래스가 된다."
- A 클래스에서 선언된 멤버함수와 멤버변수를 옮겨오는 효과이다. 이것을 상속 이라고 한다.
만약 A에서 상속받은 함수 or 변수와 똑같은 이름의 함수, 변수가 이미 B에 존재한다면 어떻게 될까?
class A{
int data = 100;
void print(){
System.out.println("A print");
}
}
class B extends A{
/* A상속
int data = 100;
void print(){
System.out.println("A print");
}
*/
int data = 200;
void print(){
System.out.println("B print");
}
}
public class Main{
public static void main(String[] args){
B t = new B();
t.print(); // 결과 : B print
}
}
- 원래라면 같은 이름의 변수, 함수( 매개변수도 같을 경우 )를 동시에 '선언'이 불가능하다. 근데 위 구조라면 B 클래스 내부에 int data가 두번, void print가 두번 선언된다.
- 클래스로부터 물려받으면 함수포인터를 물려받게 되는데, 자손에서 똑같이 선언되는 경우에는 새로운 포인터를 생성하지 않고 기존의 물려받은 포인터가 자손에서 선언된 실체( B class의 print() 함수 )를 가리키게 된다.
- 조상에서 선언된 멤버함수를 자손에서 그대로 재정의하면서 갈아엎는 형태를 method overriding 이라고 한다. ( 멤버변수는 오버라이딩 개념이 없음 )
- 이러한 상속을 통해서 개발자는 비효율적인 코드 중복을 피할 수 있다. 각각의 자식 클래스에 부모 클래스의 자원을 일일히 적어주어야 하는 수고를 덜어주기 때문이다.
- 또한, 부모 클래스를 한번만 수정하여 자식 클래스의 전체가 수정이 되기 때문에 유지 보수의 편리성도 얻을 수 있다.
[ CASE ]
class A{
int data = 100;
void print(){ System.out.println(" print "); }
}
class B extends A{
int data = 200;
void print(){ System.out.println("printXX"); }
void print2(){ System.out.println(" print2 "); }
}
public class Main{
public static void main(String[] args){
// case (1)
B t = new B();
t.print();
t.print2();
System.out.println( t.data );
// case(2)
A t1 = new B();
t1.print();
System.out.println( t1.data );
// t1.print2(); >> 에러 발생
//case(3)
B t2 = (B)t1;
t2.print2();
System.out.println( t2.data );
}
}
- case(1)의 경우 : print() . print2() 모두 호출 가능하다. 이 때 print()의 경우 오버라이딩되서 "print" 가 아닌 "printXX"가 출력된다. t.data를 출력하면 class B 에 있는 data 값인 200이 출력된다.
- case(2)의 경우 : 조상(A)에서 선언된 함수( print )만 호출가능하고 자손(B) 에서 선언된 함수 ( print2 )는 호출 불가능하다. t1.data를 출력하면 class A 에 있는 data값인 100 이 출력된다.
- case(3)의 경우 : B t2 = (B)t1; >> A클래스형 t를 B클래스형으로 강제형변환(캐스팅) 시킨다. 이건 아무때나 다 되는게 아니라 t1이 가리키고 있는 B의 인스턴스가 존재해야 성립된다. 즉 처음에 A t1 == new A(); 이렇게 선언 되었다면 이 코드는 실행불가하다.
- 조상형 변수로 자손 클래스의 인스턴스를 가리킬 수 있다.(처럼 보인다)>> 실제로는 여전히 자손안의 '조상'을 가리키는 것이다.
- 왜 이런 형태로 선언하는가? 그 이유는 자손B 의 것으로 오버라이딩 하면서 A 의 멤버변수를 이용해야할 때 이다.
- 메서드 오버라이딩은 객체지향언어 3대 특징 중 하나인 다형성에 속하기도 한다.
[ CASE ]
class A{
A(){ // A의 생성자함수
System.out.println("A Constructor");
}
}
class B extends A{
B(){ // B의 생성자함수
System.out.println("B Constructor");
}
}
public class Main{
public static void main(String[] args){
new B();
// t.B(); >> 생성자는 멤버함수 아니므로 이렇게 호출할 수 없다.
}
}
위 코드를 실행하면 다음과 같은 결과가 나온다.
더보기
A Constructor
B Constructor
- 자손의 인스턴스를 생성하면 "조상의 생성자함수"부터 "자손의 생성자함수"가 차례로 호출된다.
- 그럼 생성자도 '상속'되는 개념인가? >> 생성자는 일단 멤버함수가 아니므로 상속되지 않는다. 포인터를 통해 접근도 안된다. 다만 인스턴스 생성시 호출될 뿐이다.
'JAVA > 정리한 것' 카테고리의 다른 글
| [ JAVA ] 객체지향언어의 특징 - 캡슐화 (0) | 2022.05.15 |
|---|---|
| [ JAVA ] 객체지향언어의 특징 - 다형성 (0) | 2022.05.14 |
| [ JAVA ] 자료구조 - 배열( Array )과 리스트 ( List ) (0) | 2022.05.12 |
| [ JAVA ] 클래스(2) (0) | 2022.05.12 |
| [ JAVA ] 클래스(1) (0) | 2022.04.23 |