자바의 상속에 대해 학습하세요.
상속이란 부모클래스의 변수와 메서드를 자식 클래스가 물려받는 것을 말한다.
상속을 이용하면 코드의 중복이 제거되어 코드의 간결성을 확보해준다.
상속을 해주는 클래스를 상위 클래스, 부모 클래스, 슈퍼 클래스라고 한다.
상속을 받는 클래스를 하위 클래스, 자식 클래스, 서브 클래스라고 한다.
상속을 통해 모든 변수와 메서드를 사용할 수 있는것은 아니다. 이전 학습에서 배웠던 접근제어자에 따라 사용할 수 있는 메서드와 필드들이 나누어진다.
아래와 같은 방법으로 상속 받을 수 있다.
class 자식클래스명 extends 부모클래스명{}
상속과 다형성은 객체지향개념의 중요한 특징으로 서로 깊은 관계가 있다.
다형성이란 여러가지 형태로 변할 수 있는 것을 말하며 부모 클래스의 타입의 참조 변수로 자식 클래스의 인스턴스를 참초 할 수 있는것 을 말한다.
public static void main(String[] args) {
Parents parents1 = new Parents();
Parents parents2 = new Child();
//Child가 Parents로 형변환되는 것을 명시적으로 표현
Parents parents3 = (Parents) new Child();
}
static class Parents {
}
static class Child extends Parents{
}
super 키워드는 부모의 주소값 참조이다. 이전에 배웠던 this와 비슷하게 사용할 수 있다.
public static void main(String[] args) {
Child child = new Child();
System.out.println(child.getSuperInt()); //10
}
static class Parents {
int a = 10;
}
static class Child extends Parents{
int a = 20;
int getSuperInt() {
return super.a;
}
}
Child클래스의 getSuperInt() 메서드를 보면 super.a를 리턴하는데 이는 부모 클래스인 Parents의 a를 리턴하는 것을 확인 할 수 있다.
오버라이딩은 부모로부터 상속받은 메서드를 재정의해서 사용하는 것이다.
아래 코드는 사람의 정의되어있는 work메서드를 재정의해서 사용한 예제이다.
public static void main(String[] args) {
Student student = new Student();
student.work(); //공부를 합니다.
}
static class people {
String name;
int age;
public void work() {
System.out.println("일을 합니다.");
}
}
static class Student extends people{
@Override
public void work() {
System.out.println("공부를 합니다.");
}
}
Student클래스를 보면 work()메서드 위에 @Override 어노테이션을 붙인것을 볼 수 있다. 해당 어노테이션은 메서드를 재정의하겠다는 의미이다.
메서드 디스패치는 어떤 메서드를 호출할 지 결정하여 실행시키는 과정을 말한다.
정적 메서드 디스패치, 동적 메서드 디스패치, 더블 디스패치 세 가지가 존재하며 이에 대해 알아보겠다.
정적 메서드 디스패치는 어떤 인스턴스의 메서드가 호출된다와 같이 직관적으로 호출될 메서드를 알 수 있다.
아래의 코드를 보면 child의 print가 호출 될 것을 알 수 있다.
이러한 것을 정적 메서드 디스패치라고 한다.
public static void main(String[] args) {
Child child = new Child();
child.print(); // 자식입니다!
}
static class Parents {
void print(){
System.out.println("부모입니다!");
}
}
static class Child extends Parents{
@Override
void print() {
System.out.println("자식입니다!");
}
}
동적 메서드 디스패치는 컴파일러가 어떠한 메서드가 실행될 지 런타임 시점에 알 수 있는 것을 말한다.
아래의 예제를 보면 메인메서드에서 Child클래스에 whoParents를 호출하는데
해당 메서드 내부의 Parents객체의 print();를 호출하는 부분이 있다.
Parents는 메인 메서드에서 ParentsTwo 객체를 새로 생성해서 주입해주기 전까지는
어느 Parents의 print()를 호출해야되는지 알 수 없는데 이러한 것을
동적 메서드 디스패치
라고 부른다.
public static void main(String[] args) {
Child child = new Child(new ParentsTwo());
child.whoParent();
}
static interface Parents {
void print();
}
static class ParentsOne implements Parents{
@Override
public void print() {
System.out.println("첫번째 부모입니다!");
}
}
static class ParentsTwo implements Parents{
@Override
public void print() {
System.out.println("두번째 부모입니다.");
}
}
static class Child {
private Parents parents;
public Child(Parents parents) {
this.parents = parents;
}
public void whoParent(){
System.out.println("누가 부모입니까?");
parents.print();
}
}
더블 디스패치는 다이나믹 디스패치를 2번 이용한 것이라하는데 이를 이용한 패턴이 방문자 패턴(Visitor Pattern)이라한다.
이번 스터디를 마치고 나면 디자인 패턴을 공부할 예정이라 추후에 진행하기로 했다.
추상 클래스는 클래스를 만들기 위한 설계도로 반드시 하나 이상의 추상 메서드를 포함하고 있어야하며 인스턴스를 생성 할 수 없다.
이를 사용하기 위해서는 무조건 자식 클래스를 생성해서 자식 클래스의 인스턴스를 이용해 사용해야 한다.
abstract class 클래스이름{
abstract 리턴타입 메서드이름();
}
추상 클래스 내부에 선언된 추상 메서드를 보면 구현부가 없는것을 확인할 수 있다.
이처럼 추상 메서드는 선언부만 있고 구현부는 상속받은 자식 클래스에서 반드시 구현해주어야 한다. 단 자식 또한 추상 클래스이면 구현을 안해도 된다.
final 키워드는 말 그대로 마지막 즉 한번만이라는 뜻을 의미한다.
변수, 메서드, 클래스와 같이 사용할 수 있는데 아래 표를 통해 어떤 기능을 가지는 지 알아보겠다.
대상 | 설명 |
---|---|
변수 | 상수를 의미하며 생성자나 대입 연산자를 통해 한번만 초기화가 가능 |
메서드 | 오버라이드 또는 접근제어자를 private, protected으로 설정할 수 없다 |
클래스 | 해당 클래스를 더 이상 상속할 수 없다. |
모든 클래스들의 최상위 부모로써 Object 클래스에 정의된 메서드들은 모든 클래스에서 사용이 가능하다.
메소드 | 설명 |
---|---|
boolean equals(Object obj) | 두 객체가 같은 지 비교한다.(같으면 true, 틀리면 false) |
String toString() | 객체의 문자열을 반환한다. |
protected Object clone() | 객체를 복사한다. |
protected void finalize() | 가비지 컬렉션 직전에 객체의 리소스를 정리할때 호출한다. |
Class getClass() | 객체의 클레스형을 반환한다. |
int hashCode() | 객체의 코드값을 반환한다. |
void notify() | wait된 스레드 실행을 재개할 때 호출한다. |
void notifyAll() | wait된 모든 스레드 실행을 재개할 때 호출한다. |
void wait() | 스레드를 일시적으로 중지할 때 호출한다. |
void wait(long timeout) | 주어진 시간만큼 스레드를 일시적으로 중지할 때 호출한다. |
void wait(long timeout, int nanos) | 주어진 시간만큼 스레드를 일시적으로 중지할 때 호출한다. |