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() {...}`
'Java공부(코딩)' 카테고리의 다른 글
코딩초보의 자바(Java)공부 13일차 { 다형성 활용 } (0) | 2024.12.23 |
---|---|
코딩초보의 자바(Java)공부 12일차 { 다형성 } (0) | 2024.12.22 |
코딩초보의 자바(Java) 공부 10일차 { 메모리구조와 static } (5) | 2024.12.15 |
코딩초보의 자바(Java)공부 9일차-2 { 접근 제어자 } (0) | 2024.12.14 |
코딩초보의 자바(Java)공부 9일차 { 패키지 } (0) | 2024.12.14 |