github.com/whiteship/live-study/issues/6
- 자바 상속의 특징
- super 키워드
- 메소드 오버라이딩
- 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
- 추상 클래스
- final 키워드
- Object 클래스
자바 상속의 특징
상속이란 부모의 멤버를 자식에게 물러주는 것으로
사용 용도는 코드 중복을 방지하고 공통 코드를 재사용하기다
다음과 같은 Car 클래스가 존재한다.
public class Car {
public String model;
public String color;
public String maxSpeed;
public Car() {
this("승용차", "검정", "200");
}
public Car(String model, String color, String maxSpeed) {
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed;
}
public void move() {
System.out.println(this.model + "가 이동중");
}
}
상속방법은 간단하다
자식클래스 뒤에 extends 로 상속받을 부모클래스를 선택한다
// 자식클래스 뒤에 extends 로 부모클래스 선택
public class SportsCar extends Car {
}
상속을 받았다. 생성자를 추가해보자.
public class SportsCar extends Car {
public SportsCar() {
this("페라리", "빨강", "300");
}
public SportsCar(String model, String color, String maxSpeed) {
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed;
}
}
SportsCar 내부를 보면 model, color, maxSpeed 를 선언한적이 없는데 사용이 가능하다
부모의 멤버를 물러받았기에 선언을 안해도 가능한 것이다.
주의사항!
자바에서 여러 개의 클래스를 동시에 상속받을 수 없다. extends 뒤에는 하나의 클래스만 와야한다.
public class multiCar extends Car, SportsCar {
// 다중 상속으로 인한 컴파일 에러 발생
}
메소드 오버라이딩
부모의 메소드를 자식에 맞게 재정의하여 사용하는 기법
다음은 부모의 메소드 move 를 실행한다.
// 6주차 : 상속
public class JavaStudy06 {
public static void main(String[] args) {
// 부모 클래스
Car car = new Car();
car.move();
}
}
이제 자식에서 오버라이드를 사용해보자
public class SportsCar extends Car {
public SportsCar() {
this("페라리", "빨강", "300");
}
public SportsCar(String model, String color, String maxSpeed) {
this.model = model;
this.color = color;
this.maxSpeed = maxSpeed;
}
// 메소드 오버라이드란? 부모의 메소드를 자식에 맞게 재정의하여 사용하는 기법
// @Overrid 생략이 가능한 어노테이션
@Override
public void move() {
System.out.println(this.color + " " + this.model + "가 시속 " + maxSpeed + "로 달리고 있다");
}
}
public class JavaStudy06 {
public static void main(String[] args) {
// 부모 클래스
Car car = new Car();
car.move();
// 자식 클래스
SportsCar sportsCar = new SportsCar();
sportsCar.move();
}
}
오버라이딩이 되면 부모의 메소드는 실행이 안되고 재정의한 자식 메소드가 실행되었다.
super 키워드
메소드 오버라이딩을 언급했다. 만약에 오버라이딩 된 메소드가 아닌 부모의 메소드를 호출해야한다면?
명시적으로 super 키워드를 사용해서 부모 메소드를 호출할 수 있다.
super 는 부모 객체를 참조한다.
public class SportsCar extends Car {
...
public void carMove() {
super.move();
}
}
부모 메소드를 호출하기위한 carMove 메소드를 선언한다.
super 키워드를 이용해서 부모를 접근한다.
// 6주차 : 상속
public class JavaStudy06 {
public static void main(String[] args) {
....
sportsCar.carMove();
}
}
메소드 디스패치
메소드 디스패치란 어떤 메소드를 실행할지 결정하고 실행하는 과정을 말한다.
메소드 디스패치는 정적 메소드 디스패치와 동적 메소드 디스패치로 나눠진다
정적 메소드 디스패치(static method dispatch)
구현을 통해 컴파일 시점에 어떤 메소드가 실행될지 명확하게 아는 경우다. 다음 예제를 보자.
public class Dispatch {
static class Service {
void run() {
System.out.println("run()");
}
}
public static void main(String[] args) {
new Service().run();
}
}
누가봐도 뭐가 실행될지 알 수 있다
public class Dispatch {
static class Service {
void run() {
System.out.println("run()");
}
void run(String msg) {
System.out.println("run(" + msg +")");
}
}
public static void main(String[] args) {
new Service().run();
new Service().run("Dispatch");
}
}
오버로딩으로 같은 이름의 메소드를 추가하고 인자를 하나 넣었다. 이것도 뭐가 실행될지 누구나 알 수 있다
동적 메소드 디스패치(dynamic method dispatch)
인터페이스나 추상클래스로 정의된 추상 메서드를 호출하는 경우에 런타임이 실행 후 실행될 메서드가 결정된다. 다음 예제를 보자
public class Dispatch {
// dynamic method dispatch 예시
static abstract class Service {
abstract void run();
}
static class MyService1 extends Service {
@Override
void run() {
System.out.println("run1");
}
}
static class MyService2 extends Service {
@Override
void run() {
System.out.println("run2");
}
}
public static void main(String[] args) {
Service svc = new MyService1();
svc.run(); // receiver parameter
}
}
svc.run() 을 보자 svc 는 추상클래스고 run 은 추상메소드라 구현부도 없다.
컴파일 시점에서도 뭐가 실행될지 모른다.
런타임이 되면? 변수에 new MyService1() 를 받아서 MyService1.run() 이 실행이 된다.
모든 클래스에 this 가 존재한다. this 에 해당되는 오브젝트가 receiver parameter 라고 들어가있다.
이 receiver parameter 가 메소드의 첫번째 파라미로 들어가 있기에 (보이진 않지만)
그걸로 런타임시점에 MyService1 로 판단하고 MyService1.run() 으로 실행이 결정된다.
추상 클래스
실체간의 공통적인 특징을 뽑아내는 것을 추상이라 한다
실체 클래스가 공통적으로 가져야 할 필드와 메소드를 정의해 놓은 걸 추상클래스라 한다
class 앞에 abstract 키워드를 붙이면 추상클래스가 된다
public abstract class Bird {
abstract void sound();
}
추상클래스는 인스턴스를 만들 수 없다.
추상클래스는 공통적인 특징을 가진 클래스들을 통일하려는 목적으로 상속을 받아 사용한다
위의 추상클래스 Bird 를 보면 메소드 앞에 abstract 키워드가 붙는걸 볼 수 있다.
추상 메소드로 구현부가 없는 특징이 있고. 상속받은 자식클래스가 반드시 재정의해야한다
class 독수리 extends Bird {
@Override
void sound() {
System.out.println("독수리");
}
}
class 참새 extends Bird {
@Override
void sound() {
System.out.println("참새");
}
}
class 비둘기 extends Bird {
@Override
void sound() {
System.out.println("비둘기");
}
}
Final
final 키워드는 해당 선언이 최종 상태고 수정될 수 없다는 뜻을 가진다.
필드와 메소드, 클래스에 따라 해석이 조금씩 틀려진다
final 필드
public final String name = "현철";
위의 final 필드를 선언하고 값을 초기화한다.
값을 초기화 안하면 에러가 발생한다.
final 필드의 값을 변경하려고 하면 에러가 발생한다.
final 메소드
위에 예제로 언급한 추상클래스에
final 메소드를 추가해보자
public abstract class Bird {
abstract void sound();
final void fly() {
System.out.println("날아간다");
}
}
상속을 받은 자식에서 final 키워드가 붙은 메소드를 오버라이드해보면
final 메소드는 재정의할 수 없다고 에러가 발생한다.
부모의 메소드를 그대로 사용해야한다.
final 클래스
class 앞에 final 키워드를 붙여보자
final class BigBird {
void sound() {
System.out.println("지저귄다");
}
}
일반적인 클래스처럼 보인다
public static void main(String[] args) {
BigBird bb = new BigBird();
bb.sound();
}
인스턴스 생성도 되고 사용도 된다.
final 클래스는 한가지 특징이 있다. 최종으로 사용해야해서 상속을 할 수 없다.
즉 자식을 가질 수 없다는 뜻이다.
Object 클래스
자바에서는 클래스를 선언할 때 암묵적으로 Object 클래스를 상속을 받게해준다.
따라서 자바의 모든 클래스는 Object 의 자식이며, Object 는 최상위 부모 클래스이다.
위의 예제에서 BigBird 클래스를 선언했고 메소드로 sound 하나만 가지고있다.
하지만 인스턴스를 생성해서 내용을 보면 많은 메소드를 가진 것을 볼 수 있다.
sound 메소드를 제외한 나머지 메소드는 암묵적으로 상속받은 Object 클래스의 메소드다.
다음은 자바 공식문서에서 제공하는 Object 의 메소드 내역이다
이것이 자바다, 신용권 [한빛미디어]
토비의봄, Toby Lee
자바공식문서
'개발 & 방법론 > Java' 카테고리의 다른 글
자바 8주차 : 인터페이스 (0) | 2021.02.13 |
---|---|
7주차 : 패키지 (0) | 2021.01.01 |
5주차 : 클래스(Class) (0) | 2020.12.21 |
4주차 : 제어문 (0) | 2020.12.12 |
3주차 과제 : 연산자 (0) | 2020.11.27 |