Java공부(코딩)

코딩초보의 자바(Java)공부 11일차 { 상속 }

동곤일상 2024. 12. 21. 18:49
반응형
3~4일만에 글 씁니다...
학원상담,자취방 등
해결할건 다 해결하고왔어요!
상속관계에 대해서
알아보겠습니다 !
좋은 하루 되세요!!

 


1) 상속관계

기존 클래스의 필드와 메서드를 새로운 클래스에서 재사용하게 해준다.

이름 그대로 기존 클래스의 속성과 기능을 그대로 물려받는 것이다.

상속을 사용하려면 `extends` 키워드를 사용하면 된다.

그리고 `extends` **대상은 하나만 선택** 있다.

(단일 상속만 지원 !! )

1-1) 용어정리

부모클래스 :

상속을 통해 자신의필드 , 메서드를

다른클래스에게 제공하는 클래스

 

자식클래스:

부모클래스로부터

필드,메서드를 상속받는 클래스

 

코드로 한번 상속관계를 나타내볼게요.


부모클래스 : Car

자식클래스 : GasCar 

package extend1;

public class Car {

 

Car(){ //기본생성자

System.out.println("차를 뭐 탈까?");

}

public void move() {

System.out.println("차가 이동합니다");}

public void charge() {

System.out.println("차를 충전합니다");

}}

package extend1;

 

public class GasCar extends Car{ //Car class를 상속받음

 

GasCar(){ //부모가 생성자가있다면 자식도 생성자를 만들어야함\

// 상속 관계를 사용하면 자식 클래스의 생성자에서 부모 클래스의 생성자를 반드시 호출해야 한다.(규칙)**

// 상속 관계에서 자식 클래스의 생성자 첫줄에 반드시 `super(...)` 를 호출해야 한다. 단 기본 생성자

// (`super()` )인 경우 생략할 수 있다.

System.out.println("** 가스차를 탑니다 **");

}

 

@Override

public void move() {//오버라이딩 하지않았다면 부모클래스의charge를 사용하게 됨

System.out.println("가스차가 이동합니다");

}

@Override

public void charge() { //오버라이딩 하지않았다면 부모클래스의charge를 사용하게 됨

System.out.println("가스ㅡ충전");

}

public void name() {

System.out.println("K5 Lpg");

}}

 

**@Override**

Car에게 물려받은 필드or메서드(charge , move)를

그대로 사용하고싶지 않다면

메서드를 재정의해 사용하는 방법 !

컴파일러는 애노테이션을 보고 메서드가 정확히 오버라이드 되었는지 확인한다.

오버라이딩 조건을 만족시키지 않으면 컴파일 에러를 발생시킨다.

ex)부모에 없는 메서드를 오버라이딩했다고

표기하는 경우 오류발생!

 

**메서드 오버라이딩 조건**

**부모 메서드와 같은 메서드를 오버라이딩 있다 정도로 이해하면 충분한다.**

**메서드 이름**: 메서드 이름이 같아야 한다.

**메서드 매개변수(파라미터)**: 매개변수(파라미터) 타입, 순서, 개수가 같아야 한다.

**반환 타입**: **반환 타입이 같아야 한다**. 반환 타입이 하위 클래스 타입일 있다.

**접근 제어자**: 오버라이딩 메서드의 접근 제어자는 상위 클래스의 메서드보다 제한적이어서는 안된다. 예를

, 상위 클래스의 메서드가 `protected` 선언되어 있으면

하위 클래스에서 이를 `public` 또는`protected` 오버라이드할 있지만,

`private` 또는 `default` 오버라이드 없다.

 

**예외**: 오버라이딩 메서드는 상위 클래스의 메서드보다 많은 체크 예외를 `throws`

선언할 없다.

하지만

적거나 같은 수의 예외, 또는 하위 타입의 예외는 선언할 있다.

예외를 학습해야 이해할 있다. 예외는 뒤에서 다룬다.

 

`static` , `final` , `private` : 키워드가 붙은 메서드는 오버라이딩 없다.

 

`static` 클래스 레벨에서 작동하므로 인스턴스 레벨에서 사용하는 오버라이딩이 의미가 없다. 쉽게

야기해서 그냥 클래스 이름을 통해 필요한 곳에 직접 접근하면 된다.

 

`final` 메서드는 재정의를 금지한다.

 

`private` 메서드는 해당 클래스에서만 접근 가능하기 때문에 하위 클래스에서 보이지 않는다.

따라서 오버라이딩 없다.

 

**생성자 오버라이딩**: 생성자는 오버라이딩 없다.

 

 

Gascar 인스턴스를 생성해서 각 기능을 호출해보자

package extend1;

public class CarMain {

 

public static void main(String[] args) {

 

GasCar car = new GasCar();

car.name();

car.charge();

car.move();

}

 

 

출력:

차를 뭐 탈까? //Car(부모)의 생성자

K5 Lpg

가스ㅡ충전

가스차가 이동합니다

 

 

GasCar를 생성했는데

왜 Car의 생성자가 호출이되는거지??

라는 의문이 들 수 있다.

 

상속 관계를 사용하면 자식 클래스의 생성자에서

부모 클래스의 생성자를 반드시 호출해야 한다.(규칙)**

기본생성자는 자동으로 생성되므로

호출하지않아도 자동호출이 되는 것.

 


2) 상속과 메모리구조

 

GasCar car = new GasCar();

 

이것을 생성할 떄 GasCar만 생성되는 것이 아닌

상속해준 Car클래스도 포함해서

인스턴스가 생성 된다.

 

 

이런식으로 부모클래스까지 생성이된다 

(참조값은 하나임)

 

변수 호출 시

자신의 타입에서 먼저 찾는다.

 

없으면 부모로 올라가서 찾는다.

호출하는 변수의 타입(클래스) 기준으로 선택

위에 그림은 electricCar를 호출함

 

하지만

 

부모클래스의 인스턴스를 생성하면

자식클래스는 생성되지 않음.

즉 아래에서 위로는 찾지만

위에서 아래로는 찾지 못함.

 

이 구조를 정확하게 이해해야함 !!


3) 상속과 접근제어

**접근 제어자의 종류**

`private` : 모든 외부 호출을 막는다.

`default` (package-private): 같은 패키지안에서 호출은 허용한다.

`protected` : 같은 패키지안에서 호출은 허용한다. 패키지가 달라도 상속 관계의 호출은 허용한다.

`public` : 모든 외부 호출을 허용한다

 

다양한 접근제어자를 사용해

서로다른패키지에서

상속관계를 하나 만들게요!

 

 

모두 패키지가 다르다는점을 알고 보세요!


부모클래스

package extend1.access.parent;

 

public class Parent {

public int publicValue;

protected int protectedValue;

int defaultValue;

private int privateValue;

public void publicMethod() {

System.out.println("Parent.publicMethod");

}

protected void protectedMethod() {

System.out.println("Parent.protectedMethod");

}

void defaultMethod() {

System.out.println("Parent.defaultMethod");

}

private void privateMethod() {

System.out.println("Parent.privateMethod");

}

public void printParent() {

System.out.println("==Parent 메서드 안==");

System.out.println("publicValue = " + publicValue);

System.out.println("protectedValue = " + protectedValue);

System.out.println("defaultValue = " + defaultValue); //부모 메서드 안에서 접근 가능

System.out.println("privateValue = " + privateValue); //부모 메서드 안에서 접근 가능

//부모 메서드 안에서 모두 접근 가능

defaultMethod();

privateMethod();}}

 


자식클래스

package extend1.access.child;

 

import extend1.access.parent.Parent;

 

public class Child extends Parent {

public void call() {

publicValue = 1;

protectedValue = 1;

//defaultValue = 1; //다른 패키지 접근 불가, 컴파일 오류

//privateValue = 1; //접근 불가, 컴파일 오류

 

publicMethod();

protectedMethod(); //상속 관계 or 같은 패키지

//defaultMethod(); //다른 패키지 접근 불가, 컴파일 오류

//privateMethod(); //접근 불가, 컴파일 오류

printParent();

}}


메인코드

package extend1.access;

 

import extend1.access.child.Child;

 

public class AccessMain {

 

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` 부모의 `public` , `protected` 필드나 메서드만 접근할 있다.

반면에 `Parent.printParent()` 경우 `

public이며

Parent` 안에 있는 메서드이기 때문에

`Parent` 자신의 모든 필드와 메서드에 얼마든지 접근할 있다.

 


4) super

부모와 자식의 필드명이 같거나 메서드가 오버라이딩 되어 있으면,

자식에서 부모의 필드나 메서드를 호출할 없다.

(오버라이딩이 된 필드,메서드가 우선순위1위)

 

이때 `super` 키워드를 사용하면 부모를 참조할 있다.

`super` 이름 그대로 부모 클래스에 대한 참조를 나타낸다.

 

코드로한번 보자

 

부모클래스

package extend1.super1;

 

public class Parent {

 

public int value = 99;

 

public void name() {

System.out.println("* 부모 * ");

}}

 

자식클래스

package extend1.super1;

 

public class Child extends Parent{

 

public int value = 1;

 

@Override

public void name() {

super.name(); //부모클래스의 name호출

System.out.println("자식");

}

public void call() {

System.out.println(this.value); //this생략해도 되긴 함

System.out.println(super.value);}}

 

 

메인코드

package extend1.super1;

 

public class SuperMain {

 

public static void main(String[] args) {

Child child = new Child();

child.name();

child.call();}

}

 

출력결과

* 부모 *

자식

1

99

 

간단하게 그림으로

이해 하시길 바랍니다!!

 


4-1) super - 생성자

상속관계에서

자식인스턴스를 만들면

부모의 인스턴스까지 만들어지는 것을

알 수 있을겁니다.

즉 각각의 생성자를 호출!

 

상속 관계를 사용하면 자식 클래스의 생성자에서]

부모 클래스의 생성자를 반드시 호출해야 한다.(규칙)**

 

이것도 예를 들어 확인해보자

//////////////////

부모클래스

 

package extend1.super2;

 

public class ClassA {

 

public ClassA() {

System.out.println("최상위부모생성자");

}

}

 ///////////////////////////////

자식클래스

 

public class ClassB extends ClassA{

 

public ClassB(int a,int b ) {

//super(); 생략가능

System.out.println(""자식생성자"+" a : "+a+" b : "+b");

}}

 

////////////////

최하위 자식 클래스

package extend1.super2;

 

public class ClassC extends ClassB{

 

public ClassC() {

super(10,20); //부모인ClassB는 기본생성자가없으므로 생략하거나 super();호출 불가

System.out.println("최하위 자식 생성자");

}}

 

//////////////////////////

메인코드

 

package extend1.super2;

 

public class Super2Main {

public static void main(String[] args) {

ClassC super_class = new ClassC();}}

 

//////////////

출력

 

최상위부모생성자

자식생성자 a : 10 b : 20

최하위 자식 생성자

 

new ClassC()` 통해 `ClassC` 인스턴스를 생성한다.

이때 `ClassC()` 생성자가 먼저 호출되는 것이 맞다.

지만 `ClassC()` 성생자는 가장 먼저 `super(..)` 통해 `ClassB(...)` 생성자를 호출한다. `

ClassB()` 생성자도 부모인 `ClassA()` 생성자를 가장 먼저 호출한다.

 

 

그림으로 나타내볼게요 !

 

정리**

상속 관계의 생성자 호출은 결과적으로 부모에서 자식 순서로 실행된다.

따라서 부모의 데이터를 먼저 초기화하고

다음에 자식의 데이터를 초기화한다.

상속 관계에서 자식 클래스의 생성자 첫줄에 반드시 `super(...)` 호출해야 한다.

기본 생성자

(`super()` ) 경우 생략할 있다.


 

final)문제와풀이

지금까지 배운것으로

문제한번 만들어 보겠습니다 !

 

문제)

`Book` , `Album` , `Movie` 이렇게 3가지 상품을 클래스로 만들어야 한다.

코드 중복이 없게 상속 관계를 사용하자.

부모 클래스는 `Item` 이라는 이름을 사용하면 된다.

공통 속성: `name` , `price`

`Book` : 저자(`author` ), isbn(`isbn` )

`Album` : 아티스트(`artist` )

`Movie` : 감독(`director` ), 배우(`actor` )

 

package extend1.ex;

 

public class MainCode {

public static void main(String[] args) {

book book = new Book("채식주의자", 10000, "han", "12345");

Album album = new Album("Love Dive", 23600,"IVE");

Movie movie = new Movie("소방관", 15000,"곽경택","곽도원");

 

book.print();

album.print();

movie.print();

int sum = book.getPrice() + album.getPrice() + movie.getPrice();

System.out.println("상품 가격의 합: " + sum);

}}

 

코드를 한번 보여드리겠습니다 !!

 


Item클래스(부모)

package extend1.ex;

 

public class Item {

public String name;

public int price;

 

public Item(String name,int price) {

this.name = name;

this.price = price;

}

 

public int getPrice() {

return this.price;

}

 

public void print() {

System.out.print("이름 : "+this.name+" 가격 : "+this.price+" ");}}

 

Book클래스

package extend1.ex;

 

public class Book extends Item{

 

public String author;

public String isbn;

 

public Book(String name, int price, String author , String isbn) {

super(name, price);

this.author = author;

this.isbn = isbn;

}

 

@Override

public int getPrice() {

return this.price;

}

 

@Override

public void print() {

super.print();

System.out.println("저자 : "+author + " ISBN : "+isbn);}

}

 

Album 클래스

package extend1.ex;

 

public class Album extends Item{

 

public String artist;

 

 

public Album(String name, int price, String artist) {

super(name, price);

this.artist = artist;

}

 

@Override

public int getPrice() {

return this.price;

}

@Override

public void print() {

super.print();

System.out.println("아티스트 : "+artist);

}}

 

 

Movie 클래스

 

package extend1.ex;

 

public class Movie extends Item{

 

public String director;

public String actor;

 

public Movie(String name, int price, String director , String actor) {

super(name, price);

this.director = director;

this.actor = actor;

}

 

@Override

public int getPrice() {

return this.price;

}

 

@Override

public void print() {

super.print();

System.out.println("감독 : "+director+" 배우 : "+actor);

}}

 

 

출력 코드

이름 : 채식주의자 가격 : 10000 저자 : han ISBN : 12345

이름 : Love Dive 가격 : 23600 아티스트 : IVE

이름 : 소방관 가격 : 15000 감독 : 곽경택 배우 : 곽도원

상품 가격의 합: 48600


 

정리

클래스와 메서드에 사용되는 final

**클래스에 `final` **상속 !

`final` 선언된 클래스는 확장될 없다.

다른 클래스가 `final` 선언된 클래스를 상속받을 없다.

: `public final class MyFinalClass {...}`

 

**메서드에 `final` **

오버라이딩 !

`final` 선언된 메서드는 오버라이드 없다.

상속받은 서브 클래스에서 메서드를 변경할 없다.

: `public final void myFinalMethod() {...}`