자바의 상속에 대해 학습하세요.
- 자바 상속의 특징
- super 키워드
- 메소드 오버라이딩
- 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
- 추상 클래스
- final 키워드
- Object 클래스
자바 상속의 특징
상속
- 상위 클래스는 하위 클래스보다 일반적인 의미를 가짐
- 하위 클래스는 상위 클래스보다 구체적인 의미를 가짐
클래스 상속 문법
class B extends A {
}
Protected 접근 제어자
- 상속 관계에 있을 때, 다른 패키지에 있더라도 Protected 접근 제어자를 이용하여 접근 가능하다.
- private > default(같은 패키지) > protected > public 순서
상속의 특징
- 상속은 단일 상속만 가능하다.
- 자바의 계층 구조 최상위에는 java.lang.Object 클래스가 존재한다.
- 자바에서는 상속의 횟수에 제한을 두지 않는다.
class A{};
class B extends A{};
class C extends B{}; //상속에는 횟수제한이 없다.
- 부모의 메소드와 변수만 상속되며 생성자는 상속되지 않는다.
- 부모의 메소드는 재정의하여 사용 가능하다 - 오버라이딩
- 부모의 private 접근 제한을 갖는 멤버는 상속 불가능
- final 클래스는 상속할 수 없고, final 메소드는 오버라이딩 불가능
super 키워드
상속에서 클래스의 생성 과정
- 하위 클래스가 생성될 때 상위 클래스가 먼저 생성된다.
- 상위 클래스의 생성자가 호출되고 하위 클래스의 생성자가 호출된다.
- 하위 클래스 생성자에서는 무조건 상위 클래스의 생성자가 호출되어야 한다.
- 아무것도 없는 경우 컴파일러는 상위 클래스 기본 생성자를 호출하기 위한 super()를 코드에 넣어준다
- super() 가 호출되는 생성자는 상위 클래스의 기본 생성자이다.
- 만약 상위 클래스의 기본 생성자가 없는경우 (매개변수가 있는 생성자만 존재하는 경우) 하위 클래스는 명시적으로 상위 클래스를 호출해야 한다.
상속에서의 메모리 상태
- 상위 클래스의 인스턴스가 먼저 생성이 되고, 하위 클래스의 인스턴스가 생성됨
super 예약어
- 하위 클래스가 상위 클래스에 대한 주소를 가짐
- 하위 클래스가 상위 클래스에 접근할 때 사용 가능
메소드 오버라이딩
- 메소드를 오버라이딩 할 경우 자식 객체의 메소드 호출 시, 부모 기능은 숨겨지고 자식이 재정의한 기능이 실행됨
class Parent {
void method1() {}
void method2() {}
}
class Child extends Parent {
@Override
void method2() {}
void method3() {}
}
class Example {
public static void main(String[] args) {
Child child = new Child();
child.method1(); // 상속 받은 부모 메소드 호출
child.method2(); // 재정의한 자식 메소드 호출
child.method3(); // 자식 메소드 호출
}
}
오버라이딩 주의사항
부모의 메소드와 동일한 메소드 시그니처(메소드 이름, 매개변수 리스트) 사용
새로운 예외 추가 불가능
부모 메소드의 접근 제한자보다 강한 제한 사용 불가능
- 부모 public -> 자식 public 가능
- 부모 default -> 자식 public protected default 가능
다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
메소드 디스패치 (Method Dispatch)
- 어떤 메소드를 호출할지 결정하여 실제로 실행시키는 과정
- 자바는 런타임 시 객체를 생성하고 컴파일 시 생성할 객체 타입에 대한 정보만 보유한다.
스태틱 메소드 디스패치 (Static Method Dispatch)
- 구현 클래스를 통해 컴파일 시점에 컴파일러가 어떤 메소드를 호출할지 명확하게 알고있는 경우
- 컴파일 시 생성된 바이트 코드에도 정보가 남아있으며, 애플리케이션 실행 전에 호출할 메소드 결정
- 메소드를 오버로딩하면 매개변수 타입과 개수에 따라 어떤 메소드를 호출할지 알 수 있는 경우
- 상위 클래스가 있더라도 하위 클래스 ( 구현 클래스 ) 로 선언을 하고 하위 클래스의 인스턴스를 생성
다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
- 인터페이스나 추상 클래스에 정의된 추상 메소드를 호출하는 경우
- 호출되는 메소드가 런타임 시 동적으로 결정 되는 것
- 인터페이스 또는 추상 클래스로 선언하고 구현/상속 받은 하위 클래스의 인스턴스 생성
- 컴파일러가 알고 있는 타입에 대한 정보를 토대로 런타임 시 해당 객체를 생성하고 메소드를 호출
public interface Animal {
String method();
}
public class Dog implements Animal {
@Override
public String method() { . . . }
public void bark() { . . . }
}
public class Cat implements Animal {
@Override
public String method() { . . . }
public void meow() { . . . }
}
// 예시 1
public static void main(String[] args) {
// 다이나믹 메소드 디스패치
Animal animal = new Dog();
System.out.println(animal.method());
}
// 예시 2
public class Example {
private Animal animal;
public Example(Animal animal) {
this.animal = animal;
}
public void print() {
// 다이나믹 메소드 디스패치
System.out.println(animal.method());
}
}
- 런타임 전에는 객체 생성이 되지 않기에 Animal animal = new Dog();를 해도 컴파일러는 Dog가 생성됨을 알 수 없으므로 Animal이 정의한 method()만 접근 가능
- Example 클래스의 생성자 인자로 넘어오는 매개값의 타입이 Dog 인지 Cat인지는 실행 시 확인 가능함
업캐스팅
- 상위클래스 형으로 변수를 선언하고 하위클래스 인스턴스를 생성할 수 있다.
- 하위클래스는 상위 클래스 타입을 내포하고 있으므로 상위클래스로의 묵시적 형변환이 가능하다.
- Customer vc = new VIPCustomer(); 에서 vc가 가르키는건 무엇일까?
- VIPCustomer() 생성자의 호출로 인스턴스는 모두 생성되었지만 타입이 Customer이므로 접근할 수 있는 변수나 메소드는 Customer의 변수와 메소드이다.
다운캐스팅
- 하위클래스가 상위클래스로 형변환 되는 것은 묵시적
- 원래 자료형인 하위 클래스로 형변환 하려면 명시적으로 다운캐스팅을 해야 함
- 원래 인스턴스의 타입을 체크하는 예약어 instanceof 사용
추상 클래스
- 추상 메서드를 포함한 클래스
- 추상 메서드는 구현코드 없이 메서드의 선언만 있다.
- abstract 예약어 사용
- 추상 클래스는 인스턴스화 할 수 없다.
- 생성자와 멤버변수, 일반 메서드 모두를 가질 수 있다.
//추상클래스 선언방법
abstract class 클래스이름{
}
추상 메서드
- 메서드의 선언부만 작성하고 구현부는 미완성인 채로 남겨두는 메소드
- 자식클래스는 반드시 추상 메서드를 구현해야 하며, 만약 구현하지 않은 경우 자식 클래스도 추상클래스가 되어야 한다.
- 추상 메서드의 접근 지정자에는 private를 사용할 수 없다.
abstract 리턴타입 메서드이름();
final 키워드
- final 변수는 값이 변경될 수 없는 상수이며 오직 한 번만 값을 할당할 수 있다.
- final 메서드는 하위클래스에서 재정의 할 수 없다.
- final 클래스는 더 이상 상속되지 않는다.
Object 클래스
- 자바의 최상위 클래스
- 클래스 선언 시 다른 클래스를 상속받지 않으면 암시적으로 java.lang.Object 클래스를 상속받는다.
- 모든 객체는 Object로 자동 타입변환이 가능하다.
객체 비교 (equals())
public boolean equals(Object obj) {
return (this == obj);
}
- 두 객체의 논리적인 값을 비교
- 객체가 저장하고 있는 데이터를 비교함
- 기본 타입의 경우 실제 데이터
- 참조 타입의 경우 참조하는 객체의 주소값
객체 해시코드 (hashcode())
public native int hashCode();
- 객체를 식별할 하나의 정수 값
- 객체의 메모리 주소값을 이용해 해시코드를 만들어 리턴하기 때문에 객체마다 다른 값을 가지고 있음
객체 문자정보 (toString())
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
- 객체를 문자열로 표현한 값 반환
- 기본값 "클래스명@16진수해시코드" 반환
- System.out.Println(); 은 매개값이 기본 타입이면 그대로 출력, 참조 타입 객체면 toString() 호출하여 반환값 출력
객체 복제 (clone())
protected native Object clone() throws CloneNotSupportedException;
- 원본 객체의 필드값과 동일한 값을 가지는 새로운 객체를 생성하는 것
- 원본 객체의 데이터가 훼손되지 않도록 안전하게 보호하기 위해 객체 복제
- 이 메소드를 사용하여 객체를 복제하려면 반드시 Cloneable 인터페이스를 구현해야 한다.
얕은 복제(thin clone)
- 단순히 필드 값을 복사해서 객체를 복제
- 기본 타입 필드는 값 복사, 참조 타입 필드는 객체 주소값 복사
- Object의 clone() 메소드는 얕은 복제된 객체 리턴
깊은 복제(deep clone)
- 참조 타입 필드의 경우 얕은 복제 사용 시 문제 발생
- 참조 타입은 객체의 주소값을 복사하므로 원본 객체의 참조 필드와 복제 객체의 참조 필드가 같은 위치를 가리키게 되어 복제 객체의 참조 필드 값을 변경하면 원본 객체에서도 변경됨
- 깊은 복제를 하려면 Object의 clone()을 재정의하여 사용
public class Member implements Cloneable {
public String id;
public int[] scores;
public Car car;
public Member(String id, int[] scores, Car car) {
this.id = id;
this.scores = scores;
this.car = car;
}
public Member getMember() {
Member cloned = null;
try {
cloned = (Member) clone();
} catch(CloneNotSupportedException e) { }
return cloned;
}
@Override
public Oject clone() throws CloneNotSupportedException {
// 얕은 복제 가능한 필드 복제
Member cloned = (Member) super.clone();
// 각 참조 타입 필드 깊은 복제
cloned.scores = Arrays.copyOf(this.scores, this.scores.length);
cloned.car = new Car(this.car.model);
return cloned;
}
@Override
public String toString() {
return id + ", " + scores + ", " + car;
}
}
public class Example {
public staic void main(String[] args) {
Member member = new Member("111", new int[] {10, 20}, new Car("아반떼"));
Member cloned = member.getMember();
cloned.scores = new int[] {20, 5};
cloned.car = new Car("소나타");
System.out.println(member);
System.out.println(cloned);
}
}
- Member의 참조 타입 필드인 scores와 car의 값을 변경해도 원본 객체 데이터 유지
'Back-end > java' 카테고리의 다른 글
스터디 8주차 - 인터페이스 (0) | 2021.07.01 |
---|---|
스터디 7주차 - 패키지 (0) | 2021.07.01 |
스터디 5주차 - 클래스 (0) | 2021.06.30 |
스터디 4주차 - 제어문 (0) | 2021.06.28 |
스터디 3주차 - 연산자 (0) | 2021.06.28 |