
부모 클래스에서 사용하던 변수와 메소드(public, protected)를 자식 클래스에서
재사용하기 위해 사용
자식 클래스의 객체를 생성하면 자동으로 부모의 생성자가 먼저 호출되고 자식의 생성자가 호출됨다중 상속 불가능코드를 재사용하여 중복된 코드를 작성하지 않아도 됨계층 구조를 분류할 수 있음유지보수가 쉬워짐자식 클래스 생성자 가장 첫 줄에 'super()'가 만들어짐부모 클래스에 기본 생성자가 없다면 에러가 남부모 클래스 기본 생성자를 만듦
super(...)를 이용해 부모 클래스 생성자를 명시
public class InheritanceClass {
public static void main(String[] args) {
ChildClass child = new ChildClass();
}
}
class ParentClass {
// public ParentClass() {
// System.out.println("ParentClass constructor");
// }
public ParentClass(String name) {
System.out.println("Parent name is " + name);
}
}
class ChildClass extends ParentClass {
ChildClass() {
super("HI"); //부모 클래스의 생성자를 명시적으로 지정
System.out.println("ChildClass constructor");
}
}
결과
Parent name is HI
ChildClass constructor
부모 클래스의 메소드와
동일한 메소드 시그니처,리턴 타입을 갖는 메소드를 자식 클래스에서 재정의하는 것
💡 Overloading - 확장
→ 메소드 시그니처가 다른 서로 다른 메소드들을 만들어 확장함 (자식>부모)
Overriding - 덮어 씀
→ 메소드 시그니처가 같은 메소드를 만들어 부모 클래스의 기능은 무시하고 덮어 씀
자식의 접근 제어자 범위가 더 커야함부모 - default
자식 - default 이상 (public, protected, default)
- 부모가 공개한다는데 자식이 더 좁은 범위만 보여준다고? 안됨!
cf) 부모 클래스의 메소드가 private
- 자식 클래스의 메소드는 어떤 접근 제어자를 써도 상관 없음
- private이 가장 작은 범위의 접근 제어자이기 때문
예외 처리 시 주의사항checked exception을 던질 수 없음package org.example;
public abstract class Parent {
public void myMethod(String a) {
}
}package org.example;
public class Child extends Parent {
// 불가능
// @Override
// public void myMethod(String a) throws Exception {
// super.myMethod(a);
// }
// 가능
@Override
public void myMethod(String a) throws RuntimeException {
super.myMethod(a);
}
}checked exception을 던지는 경우더 큰 범위의 예외를 던질 수 없음ThirdException → SecondException → FirstException → Exception
package org.example;
public abstract class Parent {
public void myMethod(int a) throws SecondException {
throw new SecondException();
}
}
package org.example;
public class Child extends Parent {
// 큰 범위 예외 -> 불가능
@Override
public void myMethod(int a) throws FirstException {
}
// 같은 예외 -> 가능
@Override
public void myMethod(int a) throws SecondException {
}
// 작은 범위 예외 -> 가능
@Override
public void myMethod(int a) throws ThirdException {
}
// 예외x -> 가능
@Override
public void myMethod(int a) {
}
}
unchecked exception을 던지는 경우 (= 부모 클래스가 예외를 던지지 않는 경우)커도 상관없음checked exception은 던질수 없음!ThirdException → SecondException → FirstException → RuntimeException
package org.example;
public abstract class Parent {
public void myMethod(int a) throws SecondException {
throw new SecondException();
}
}
package org.example;
public class Child extends Parent {
// 큰 범위 예외 -> 가능
@Override
public void myMethod(int a) throws FirstException {
}
// 같은 예외 -> 가능
@Override
public void myMethod(int a) throws SecondException {
}
// 작은 범위 예외 -> 가능
@Override
public void myMethod(int a) throws ThirdException {
}
// 예외x -> 가능
@Override
public void myMethod(int a) {
}
// checked 예외 -> 불가능
@Override
public void myMethod(int a) throws Exception {
}
}
어떤 메소드를 실행할 지 결정하고 실제로실행시키는 과정
컴파일 시어떤 메소드가 실행될 지결정됨
class Human {
int a;
public void printA() {
System.out.println("사람 메소드 : " + a);
}
}
class Parent extends Human {
@Override
public void printA() {
System.out.println("부모 메소드 : " + a);
}
}
class Child extends Human {
@Override
public void printA() {
System.out.println("자식 메소드 : " + a);
}
}
public class InheritanceTest {
public static void main(String[] args) {
Parent parent = new Parent();
Child child1 = new Child();
parent.printA(); // 부모 클래스 타입의 printA() 메소드
child1.printA(); // 자식 클래스 타입의 printA() 메소드
}
}
런타임 시어떤 메소드가 실행될 지결정됨
런타임 시 메소드의 클래스 타입이 정해져 호출되는 것을 다이나믹 메소드 디스패치라고 함메소드의 클래스 타입public class InheritanceTest {
public static void main(String[] args) {
Human whoIsIt = new Child();
whoIsIt.printA(); // 컴파일 시 (눈으로 보기에) 어떤 클래스 타입의 메소드인지 알 수 없음
// 실행해보면 자식 클래스 타입의 printA() 메소드가 호출됨
}
}
런타임 시객체와매개변수에 따라 호출할메소드가 결정됨
방문자와 방문자 공간을 분리하여 방문 공간이 방문자를 맞이할 때 이 후에 대한 행동을 방문자에게 위임하는 패턴
package org.example.double_dispatch;
/**
* 방문자 패턴
* 방문자 구현체(apple, chicken)가 실제 로직을 수행함
*/
public interface FoodVisitor {
void printBadOrGood(Man man);
void printBadOrGood(Woman woman);
}public class Apple implements FoodVisitor {
@Override
public void printBadOrGood(Man man) {
System.out.println("[bad] man eats " + this.getClass().getSimpleName());
}
@Override
public void printBadOrGood(Woman woman) {
System.out.println("[bad] woman eats " + this.getClass().getSimpleName());
}
}public class Chicken implements FoodVisitor {
@Override
public void printBadOrGood(Man man) {
System.out.println("[good] man eats " + this.getClass().getSimpleName());
}
@Override
public void printBadOrGood(Woman woman) {
System.out.println("[good] woman eats " + this.getClass().getSimpleName());
}
}public interface Human {
public void eat(FoodVisitor foodVisitor);
// public void eat(Chicken chicken);
// public void eat(Apple apple);
}public class Man implements Human {
// @Override
// public void eat(Chicken chicken) {
// System.out.println("[good] man eats " + chicken.getClass().getName());
// }
//
// @Override
// public void eat(Apple apple) {
// System.out.println("[bad] man eats " + apple.getClass().getName());
// }
// 만약 새로운 Food가 추가된다면?
// -> Man과 Woman 모두에 새로운 Food를 처리하는 로직을 추가해야 함
// @Override
// public void eat(FoodVisitor food) {
// if (food instanceof Chicken) {
// System.out.print("[good] ");
// }
//
// if (food instanceof Apple) {
// System.out.print("[bad] ");
// }
//
// System.out.println("man eats " + food.getClass().getSimpleName());
// }
@Override
public void eat(FoodVisitor foodVisitor) {
// 이 후 로직은 foodVisitor(visitor)에게 맡김
// **Food가 추가되어도 Human의 코드는 수정할 필요 없음**
foodVisitor.printBadOrGood(this);
}
}public class Woman implements Human {
// @Override
// public void eat(FoodVisitor food) {
// if (food instanceof Chicken) {
// System.out.print("[good] ");
// }
//
// if (food instanceof Apple) {
// System.out.print("[bad] ");
// }
//
// System.out.println("woman eat " + food.getClass().getSimpleName());
// }
@Override
public void eat(FoodVisitor foodVisitor) {
foodVisitor.printBadOrGood(this);
}
}public class DoubleDispatchMain {
public static void main(String[] args) {
Human human = new Man();
FoodVisitor foodVisitor = new Chicken();
/**
* 정적 메소드 디스패치가 되면 안됨!
* Human에서 Food를 받아야 함 -> 동적 디스패치 1
* eat() 메소드에서 전달받은 food로 이 후 로직 수행해야 함
* foodVisitor.printBadOrGood(this) -> 동적 디스패치 2
*/
human.eat(foodVisitor);
}
}[good] man eats Chicken실제 코드는 작성하지 않더라도
어떤 메소드들이 있어야 하는지를 정의해 놓은 것
public static final 필드static 메소드와 default 메소드가 추가되었음!여러 개의 인터페이스를 구현할 수도 있음다중 상속 가능
일부 완성되어 있는 클래스
상속받은 클래스는 반드시 추상 메소드를 구현해야 함단독으로 객체를 만들 수 없음static이나 final이 아닌 변수가 있어도 됨static이나 final 메소드 있어도 됨public abstract class AbsClass {
int a;
public AbsClass(int a) {
this.a = a;
}
public void printA() {
System.out.println(a);
}
}
public static void main(String[] args) {
/**
* 1. 추상 클래스는 단독으로 인스턴스화 할 수 없음
* -> 익명 클래스를 만들고 객체를 생성함
* 2. 추상 클래스에 추상 메소드가 없어도 에러는 안남
* 3. 추상 메소드가 있다면 그 메소드는 무조건 오버라이딩 해야함
*/
AbsClass absClass = new AbsClass(10) {
@Override
public void printA() {
super.printA();
}
};
}
변수, 메소드 선언의 격차를 줄일 수 있음선언과 구현을 구분할 수 있음인터페이스를 사용하는 경우추상 클래스를 사용하는 경우
더 이상 상속해 줄 수 없음더 이상 오버라이딩 할 수 없음 → 누군가 메소드의 기능을 바꿀 수 없음바꿀 수 없는 변수 → 상수매개 변수 & 지역 변수는 반드시 선언할 때 초기화 할 필요 없음매개 변수
지역 변수
- 선언된 중괄호 내에서만 참조되므로
→ 단, 둘 다 메소드 내부에서 다시 값을 할당하면 안 됨
public void test(final int b) {
final int a;
a = 1;
System.out.println(a);
a = 2; //불가
b = 1; //불가
}
참조 자료형 변수모든 자바 클래스의 부모 클래스
클래스의 기본적인 행동을 정의해 놓은 클래스
필드는 가지지 않으며 11개의 메소드로만 구성
메소드 종류

객체를 처리하기 위한 메소드쓰레드를 처리하기 위한 메소드패키지.클래스@해시코드값' 리턴함getClass().getName() + ‘@’ + Integer.toHexString(hashCode())println() 메소드에 매개변수로 들어가는 경우
객체에 더하기 연산을 하는 경우
public class ToStringMethod {
public static void main(String[] args) {
Banana banana = new Banana();
Apple apple = new Apple();
System.out.println(banana);
System.out.println("HI "+apple);
System.out.println("Hello "+apple.toString());
}
}
class Banana {}
class Apple {
public String toString() {
return "I am Apple";
}
}
결과
chapter11.Banana@36baf30c
HI I am Apple
Hello I am Apple
객체를 비교할 때는 equals() 메소드를 오버라이딩하여 사용해야 함import java.util.Objects;
class Banana {
int count;
Banana() {
}
Banana(int number) {
count = number;
}
//cmd+N 단축키를 이용해 자동으로 만듦
@Override
public String toString() {
return "Banana{" +
"count=" + count +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Banana banana = (Banana) o;
return count == banana.count;
}
@Override
public int hashCode() {
return Objects.hash(count);
}
}
public class EqualsMethod {
public static void main(String[] args) {
Banana banana1 = new Banana(1);
Banana banana5 = new Banana(5);
Banana banana10 = new Banana(1);
System.out.println(banana1);
System.out.println(banana5);
System.out.println(banana10);
if (banana1.equals(banana5)) {
System.out.println("same fruit");
} else {
System.out.println("different fruit");
}
if (banana1.equals(banana10)) {
System.out.println("same fruit");
} else {
System.out.println("different fruit");
}
}
}
결과
Banana{count=1}
Banana{count=5}
Banana{count=1}
different fruit
same fruit
객체의 메모리 주소를 16진수로 변환하여 리턴하는 메소드
두 객체가 동일하면 hashCode() 값이 똑같아야 함오버라이딩 규칙자바 애플리케이션이 실행되는 동안 hashCode()는 항상 동일한 int 값을 리턴해야 함
equals() 결과가 true인 경우 두 객체의 hashCode() 결과값은 항상 동일해야 함
equals() 결과가 false라고 해서 무조건 hashCode() 결과값이 달라야 하는 것은 아님
직접 오버라이딩 하는것은 권장하지 않음. IDE가 알아서 잘 해준다.
방문자 패턴
casting object
더블 디스패치
[Java] 더블 디스패치(Double Dispatch)
더블 디스패치