기존의 클래스로 새로운 클래스를 작성하는 것 (코드의 재사용)
두 클래스를 부모와 자식으로 관계를 맺어주는 것
class 자식클래스 extends 부모클래스 {
// ...
}
포함(composite)
클래스의 멤버로 참조변수를 선언하는 것
상속관계 : ~은 ~이다. (is - a)
포함관계 : ~은 ~을 가지고 있다. (has - a)
대부분의 경우 포함관계를 사용한다. 90% 정도.
상속은 여러가지 제약이 있기 때문에 꼭 필요할 때만 사용함.
-상속받은 조상의 메소드를 자신에 맞게 변경하는 것
class Point {
int x;
int y;
String getLocation() {
return "x :" + x + ", y :" + y;
}
}
class Point3D entends Point {
int z;
String getLocation() { // 오버라이딩
return "x :" + x + ", y :" + y + ", z :" +z;
}
}
조건
오버로딩 :
오버라이딩 :
class Ex7_2 {
public static void main(String args[]) {
Child c = new Child();
c.method();
}
}
class Parent { int x=10; }
class Child extends Parent {
int x=20;
void method() {
System.out.println("x=" + x);
System.out.println("this.x=" + this.x);
System.out.println("super.x="+ super.x);
}
}
// x=20
// this.x=20
// super.x=10
class Ex7_3 {
public static void main(String args[]) {
Child2 c = new Child2();
c.method();
}
}
class Parent2 { int x=10; } // super.x 와 this.x 둘 다 가능
class Child2 extends Parent2 {
void method() {
System.out.println("x=" + x);
System.out.println("this.x=" + this.x);
System.out.println("super.x="+ super.x);
}
}
// x=10
// this.x=10
// super.x=10
자손의 생성자는 자신이 생성한 것만 초기화 해야한다.
class Point {
int x, y;
Point(int x, int y) {
this.x = x;
this.y = y;
}
}
class Point3D extends Point {
int z;
Point3D(int x, int y, int z) {
super(x, y); // 조상클래스의 생성자 Point(int x, int y)를 호출
this.z = z; // 자신의 멤버를 초기화
}
}
접근 제어자 : public
,protected
,(default)
,private
제어자 : static
,final
,abstact
. . .
접근 제어자를 사용하는 이유
조상 타입 참조 변수로 자손 타입 객체를 다루는 것
자손 타입의 참조변수로 조상 타입의 객체를 가리킬 수 없다.
조상 타입 참조변수로 자손 타입 인스턴스 참조
부모의 참조변수로 자식 인스턴스를 가르키면 여러 기능 중 자식의 기능만 안쓰면 됨.
그러나 자식의 참조변수로 부모의 인스턴스를 가르키면
실제 가지고 있는 멤버의 개수보다 기능이 많아서 동작 안하는 경우 생김 -> 에러
// 부모 타입 참조변수가 자식 타입 인스턴스 가르키는 것 ! ! ! !
사용할 수 있는 멤버의 갯수를 조절하는 것
-> 객체에 속한 멤버들에 대한 사용범위가 달라진다는 것
조상 자손 관계(상속 관계)의 참조변수는 서로 형변환 가능
자손 타입에서 조상 타입으로 형변환 하는 경우, 형변환 생략가능
자손타입 -> 조상타입 (Up-casting) : 형변환 생략가능
조상타입 -> 자손타입 (Down-casting) : 형변환 생략불가
생략 가능, 불가 / 업캐스팅, 다운캐스팅의 개념이 있지만 신경쓰지 말고
조상 자손 관계에 형변환 가능한 것에 집중하자
// Person 클래스 정의 (부모 클래스)
public class Person {
String name;
int age;
public void speak() {
System.out.println(name + ": 안녕하세요");
}
}
// Dancer 클래스 정의 (자식 클래스)
public class Dancer extends Person {
public void dance() {
System.out.println(name + ": 춤을 춥니다");
}
}
// 객체 생성 및 실행
public class HelloWorld {
public static void main(String[] args) {
//==================================
System.out.println("==== 예시1 ====");
//==================================
Person p1 = new Dancer();
p1.name = "홍길동";
p1.speak();
// p1.dance(); // 사용불가
Dancer d1 = (Dancer) p1; // 다운캐스팅
d1.name = "이순신";
d1.speak();
d1.dance();
//==================================
System.out.println("==== 예시2 ====");
//==================================
Dancer d2 = new Dancer();
d2.name = "제갈공명";
d2.speak();
d2.dance();
Person p2 = (Person) d2; // 업캐스팅 - (Person) 생략가능
p2.name = "신사임당";
p2.speak();
// p2.dance(); // 사용불가
}
}
// 실행결과
==== 예시1 ====
홍길동: 안녕하세요
이순신: 안녕하세요
이순신: 춤을 춥니다
==== 예시2 ====
제갈공명: 안녕하세요
제갈공명: 춤을 춥니다
신사임당: 안녕하세요
장점
부모클래스 타입으로 형변환하여 사용하게 되면, 실제 생성된 인스턴스는 여러 자식클래스 중에 어느 것인지 알아야 하는 경우가 생긴다.
instanceof 연산자는 해당 참조 변수가 형변환이 가능한지 여부를 확인할 때 사용된다.
형변환 전에 반드시 instanceof 로 확인!!!
// 클래스 정의
public class Person { } // 부모
class Dancer extends Person { } // 자손
class Actor extends Person { } // 자손
// 객체 생성 및 실행
public class HelloWorld {
public static void main(String[] args) {
Dancer d1 = new Dancer();
Person p1 = (Person) d1;
System.out.println("Person? " + (p1 instanceof Person));
System.out.println("Dancer? " + (p1 instanceof Dancer));
System.out.println("Actor? " + (p1 instanceof Actor));
}
}
// 실행결과
Person? true
Dancer? true
Actor? false
수정 예정
이해가 가지 않음
추상 클래스와 인터페이스는 따로 정리