상속 - 시작
상속관계가 왜 필요할까 ??
public class CarMain {
public static void main(String[] args) {
ElectricCar car = new ElectricCar();
car.move();
car.charge();
}
}
public class ElectricCar {
public void move(){
System.out.println("차를 이동");
}
public void charge(){
System.out.println("충전 중");
}
}

상속관계
상속은 객체지향 프로그램의 핵심 요소 중 하나. 기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해준다. 이름 그래도 기존 클래스의 속성과 기능을 그대로 물려받음. 상속을 사용하려면 extends 키워드를 사용
부모 클래스 (슈퍼 클래스) : 상속을 통해 자신의 필드와 메서드를 다른 클래스에 제공
자식 클래스 (서브 클래스) : 부모 클래스로 부터 필드와 메서드를 상속받는 클래스
public class Car {
public void move(){
System.out.println("차 이동");
}
}
public class CarMain {
public static void main(String[] args) {
ElectricCar electricCar = new ElectricCar();
electricCar.charge();
electricCar.move();
GasCar gasCar = new GasCar();
gasCar.fillUp();
gasCar.move();
}
}
public class ElectricCar extends Car{
public void charge(){
System.out.println("충전");
}
}
public class GasCar extends Car{
public void fillUp(){
System.out.println("기름을 주유");
}
}
상속 구조도

상속은 부모의 기능을 자식이 물려받는 것. 따라서 자식이 부모의 기능을 물려받아서 사용 가능.
반대로 부모클래스는 자식 클래스에 접근 불가능. 자식 클래스는 부모 클래스의 기능을 물려 받기 때문에 접근 할 수 있지만. 그 반대는 아니다.
단일 상속
자바는 다중 상속을 지원하지 않는다. 그래서 extends 대상은 하나만 선택 가능.
상속과 메모리 구조

new ElectricCar()를 호출하면 ElectricCar 뿐만 아니라 상속 관계에 있는 Car 까지 함께 포함해서 인스턴스를 생성. 참조값은 하나, Car ElectricCar라는 두가지 클래스 정보가 공존
상속이라고 해서 단순하게 부모의 필드와 메서드만 물려 받는것이 아니다. 상속 관계를 사용하면 부모클래스도 포함해서 생성.
electricCar.charge() 호출

참조값을 확인해서 .charge를 호출. 따라서 주소값을 찾아서 charge를 호출하면 된다.
그러나 상속 관계의 경우 내부에 부모와 자식이 모두 존재. 이때 부모인 Car을 통해서 charge를 찾을 건지 ElectricCar을 통해서 charge를 찾을지 선택
호출 변수의 타입을 기준으로 선택.

ElectriceCar에는 move 메서드 없다. 상속관계에서는 자식 타입에 해당하는 기능이 없다면 부모타입으로 올라가서 찾는다.
상속과 기능을 추가
public class Car {
public void move(){
System.out.println("차 이동");
}
public void openDoor(){
System.out.println("문 열림");
}
}
public class CarMain {
public static void main(String[] args) {
ElectricCar electricCar = new ElectricCar();
electricCar.move();
GasCar gasCar = new GasCar();
gasCar.move();
}
}
public class ElectricCar extends Car {
@Override
public void move(){
System.out.println("차 빠르게 이동");
}
public void charge(){
System.out.println("충전");
}
}
public class GasCar extends Car {
public void fillUp(){
System.out.println("기름을 주유");
}
}
public class HydrogenCar extends Car{
public void fillHydrogen(){
System.out.println("수소를 충전");
}
}

상속 관계 덕분에 중복은 줄고, 새로운 차종을 편리하게 확장
상속과 메서드 오버라이딩
부모 타입의 기능을 자식에서는 다르게 재정의
부모에게서 상속 받은 기능을 자식이 재정의 하는 것을 메서드 오버라이딩이라고 한다.
public class Car {
public void move(){
System.out.println("차 이동");
}
public void openDoor(){
System.out.println("문 열림");
}
}
public class CarMain {
public static void main(String[] args) {
ElectricCar electricCar = new ElectricCar();
electricCar.charge();
electricCar.move();
electricCar.openDoor();
GasCar gasCar = new GasCar();
gasCar.fillUp();
gasCar.move();
gasCar.openDoor();
HydrogenCar hydrogenCar = new HydrogenCar();
hydrogenCar.fillHydrogen();
hydrogenCar.move();
hydrogenCar.openDoor();
}
}
public class ElectricCar extends Car {
public void charge(){
System.out.println("충전");
}
}
public class GasCar extends Car {
public void fillUp(){
System.out.println("기름을 주유");
}
}
public class HydrogenCar extends Car {
public void fillHydrogen(){
System.out.println("수소를 충전");
}
}
ElectricCar는 부모인 Car의 move 기능을 그대로 사용 하고 싶지 않다. 메서드 이름은 동일하지만 새로운 기능을 사용 -> move 메서드를 ElectricCar에 새롭게 재정의
부모의 기능을 자식이 새롭게 재정의 하는 것을 메서드 오버라이딩
@Override
상위 클래스의 메서드를 오버라이드 하는 것을 알 수 있다.
오버라이딩 조건을 만족 X -> compileError

오버라이딩과 메모리 구조

electriceCar.move를 호출
호출한 electricCar타입은 ElectricCar. -> 따라서 인스턴스 내부의 ElectricCar 타입에서 시작.
move 메서드가 있다. 해당 메서드 실행 / 부모타입 찾지 않는다.
Overloading & Overriding
오버로딩 : 메서드 이름 동일, 메개벼누가 다름 메서드를 여러개 정의
오버라이딩 : 메서드 오버라이딩은 하위 클래스에서 상위 클래스의 메서드를 재정의, 상속관계에서 주로 많이 사용. 부모의 기능를 자식이 다시 정의.
메서드 오버라이딩 조건 (참고)
1. 메서드 이름 동일
2. 메서드 매개변수 타입, 순서, 개수 동일
3. 반환 타입이 하위클래스 타입일 수 있다.
4. 접근 제어자 : 오버라이딩 메서드의 접근제어자는 상위 클래스의 메서드보다 제한적이면 X
5. 예외 : 오버라이딩 메서드는 상위 클래스의 메서드보다 더 많은 체크 예외를 throws로 선언 가능. 하지만 적거나 같은 수의 예외 또는 하위 타입의 예외는 선언 ㅒ
6. static, final, private 키워드가 붙은 메서드는 오버라이딩 X
-> static은 클래스 level에서 작동, 인스턴스 레벨에서 사용하는 오버라이딩 의미X
-> final 메서드는 재정의 X
-> private 메서드는 해당 클래스만 접근 가능
7. 생성자는 오버라이딩 X
상속과 접근제어

public class Parent {
public int publicVal;
protected int protectedVal;
int defaultVal;
protected int privateVal;
public void publicVal(){
System.out.println("Parent.publicMethod");
}
protected void protectedVal(){
System.out.println("protectedVal = " + protectedVal);
}
void defaultVal(){
System.out.println("defaultVal = " + defaultVal);
}
private void privateMethod(){
System.out.println("privateVal = " + privateVal);
}
public void printParent(){
System.out.println("Parent 안");
System.out.println("publicVal = " + publicVal);
System.out.println("protectedVal = " + protectedVal);
System.out.println("defaultVal = " + defaultVal);
System.out.println("privateVal = " + privateVal);
//부모 메서드 안에서 모두 접근 가능
defaultVal();
privateMethod();
}
}
public class Child extends Parent {
public void call(){
publicVal = 1;
protectedVal = 1;
//defaultVal = 1; -> 같은 페키지 안에 있어야 호출 가능
//privateVal = 1; -> 접근 불가
publicVal();
protectedVal();
printParent(); //public -> 자기 자신 꺼기에 호출 가능
}
}
public class ExtendsAccessMain {
public static void main(String[] args) {
Child child = new Child();
child.call();
}
}
<실행결과>
Parent.publicMethod
Parent.protectedMethod
==Parent 메서드 안==
publicValue = 1
protectedValue = 1
defaultValue = 0
privateValue = 0
Parent.defaultMethod
Parent.privateMethod
Child.call -> Parent.printParent 순서로 호출
Child 부모의 public.protected 필드나 메서드만 접근 가능
접근 제어와 구조

본인 타입에 없으면 부모타입에서 기능을 찾는다. 이떄 접근제어자가 영향을 준다.
왜냐면 객체 내부에서는 자식과 부모가 구분.
Super 부모 참조
부모와 자식의 필드명이 같거나 메서드가 오버라이딩 되어 있다면, 자식에서 부모의 필드나 메서드를 호출 할 수 X
이때 super 키워드를 사용하면 부모를 참조할 수 있다. super는 이름 그대로 부모 클래스에 대한 참조

public class Child extends Parent {
public String val = "child";
@Override
public void hello() {
System.out.println("Child.hello");
}
public void call(){
System.out.println("this val = " + this.val); //현재 내 타입
System.out.println("super val = " + super.val); //부모 타입에서 찾는것
this.hello();
super.hello();
}
}
public class Parent {
public String val = "Parent";
public void hello(){
System.out.println("Parent.Hello");
}
}
public class SuperMain {
public static void main(String[] args) {
Child child = new Child();
child.call();
}
}
call 메서드
this는 자기 자신의 참조를 의미
super는 부모클래스에 대한 참조
필드 이름과 메서드 이름 같지만 super를 사용해서 부모 클래스에 있는 기능을 사용 가능

Super 생성자
public class ClassA {
public ClassA(){
System.out.println("ClassA 생성자");
}
}
public class ClassB extends ClassA{
public ClassB(int a){
this(a, 0);
System.out.println("a = " + a);
}
public ClassB(int a, int b){
super();
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
public class ClassC extends ClassB{
//B의 경우 기본생성자 X
public ClassC(){
super(10, 20); // 생략 불가능
System.out.println("ClassC 생성자 ");
}
}
public class SuperMain2 {
public static void main(String[] args) {
ClassC c = new ClassC();
ClassB b = new ClassB(200);
}
}
실행해보면 A -> B -> C 순서로 실행. 생성자의 실행 순서가 결과적으로 최상위 부모부터 실행, 하나씩 아래로 내려온다

C 인스턴스를 생성할때 먼저 호출 되는 것이 맞긴 하지만 C의 생성자는 먼저 super를 통해 B의 생성자, B도 super에 의해 A 생성자 우선 호출
this를 사용해도 한번은 super를 호출해야 한다.
public ClassB(int a){
this(a, 0);
System.out.println("a = " + a);
}
public ClassB(int a, int b){
super();
System.out.println("a = " + a);
System.out.println("b = " + b);
}
문제풀이
1.
public class Item {
private String name;
private int price;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public int getPrice(){
return price;
}
public void print(){
System.out.println("이름 : " + name + " 가격 : " + price);
}
}
public class Book extends Item{
private String author;
private String isbn;
public Book(String name, int price, String author, String isbn) {
super(name, price);
this.author = author;
this.isbn = isbn;
}
@Override
public void print(){
super.print();
System.out.println("- 저자 " + author + " isbn " + isbn);
}
}
public class Album extends Item{
private String artist;
public Album(String name, int price, String artist) {
super(name, price);
this.artist = artist;
}
@Override
public void print(){
super.print();
System.out.println(" - 아티스트 : " + artist);
}
}
public class Movie extends Item{
private String director;
private String actor;
public Movie(String name, int price, String director, String actor) {
super(name, price);
this.director = director;
this.actor = actor;
}
@Override
public void print(){
super.print();
System.out.println(" - 배우 : " + actor);
}
}
public class ItemMain {
public static void main(String[] args) {
Item book = new Book("Java", 10000, "han", "12345");
Item album = new Album("앨범1", 5000, "터진입");
Item movie = new Movie("영화1", 10000, "감독1", "배우1");
book.print();
album.print();
movie.print();
int sum = book.getPrice() + album.getPrice() + movie.getPrice();
System.out.println("상품 가격 합 " + sum);
}
}