오늘의하루

[Java Note] 객체지향 개념 기초 요약본 본문

JAVA

[Java Note] 객체지향 개념 기초 요약본

오늘의하루_master 2022. 8. 29. 17:52

객체지향

객체지향은 코드의 재사용성을 높이고 관리를 수월하게 해서 신뢰성 높은 프로그램의 개발이 가능하다.

객체지향 - 클래스, 객체

클래스란 객체를 정의해 놓은 것이며 객체는 실제로 존재하는 개념이다.

객체와 인스턴스

객체(Object)는 인스턴스(Instance)를 포함하는 일반적인 의미이다.

인스턴스화(Instantiate)

클래스로부터 인스턴스를 생성하는 것을 말한다.

클래스명 참조 변수명; // 객체를 다루기 위한 참조 변수 선언
클래스명 참조 변수명 = new 클래스명( ); // 객체 생성 후 해당 객체의 주소를 참조 변수에 저장

하나의 인스턴스 주소를 여러 개의 참조 변수들이 가지는 것은 가능

여러 개의 인스턴스 주소를 하나의 참조 변수가 가지는 것은 불가능

객체의 구성요소 - 속성과 기능

객체는 변수(속성)와 메서드(기능)로 이루어져 있습니다.

객체지향 - 변수와 메서드

변수는 선언 위치에 따라 종류와 범위(Scope)를 결정한다.

class Test {
    클래스 영역 시작
    int iv;  =  인스턴스 변수
    static int cv;  =  클래스 변수(static 변수, 공유 변수)
    메서드 영역 시작
    void method(){
        int lv;  =  지역 변수
    }
    메서드 영역 종료
    클래스 영역 종료
}
변수의 종류 선언 위치 생성시기
클래스 변수 클래스 영역 클래스가 메모리에 올라갈때
인스턴스 변수 인스턴스 생성시
지역 변수 메서드 영역 메서드 호출 후 변수 선언문 수행시

변수들의 특징

변수의 종류 특징
인스턴스 변수 인스턴스 생성 후 "참조변수.인스턴스변수명"으로 접근
참조변수가 없으면 GC(가비지 컬렉터)에 의해 소멸된다.
클래스 변수 인스턴스 생성 없이 "클래스명.클래스변수명"으로 접근
클래스 로딩시 생성되며 프로그램 종료 시 소멸된다.
지역 변수 메서드 호출 시 생성되며 메서드 종료시 소멸된다.

메서드(Method)

작업을 수행하기 위한 명령문의 집합을 의미하며 클래스 영역에서만 정의할 수 있고 선언부와 구현부로 나뉩니다.

리턴 타입 메서드명 ( 타입 변수명.... ) = 선언부
{ 구현부 }

JVM의 메모리 구조

명칭 특징
Method Area (메서드 영역) 클래스 정보와 클래스 멤버가 저장되는 공간
Call Stack (호출 스택) 메서드의 작업공간
메서드가 호출되면 메모리 공간을 할당 받고 메서드 종료되면 사용하던 메모리 공간 소멸
Heap (힙) 인스턴스가 생성되는 공간
new 연산자에 의해서 생성되는 배열과 객체는 모두 여기에 생성

Call Stack - 호출 스택

메서드가 호출되면 수행에 필요한 메모리를 스택에 할당되며 수행을 마치면 메모리를 반환한다.

Call Stack 이해하기

아래 있는 메서드가 바로 위에 있는 메서드를 호출한 메서드이며 호출 스택의 제일 

  1. 가장 처음 main 메서드가 실행된다.
  2. main 메서드 안에서 FirstMethod가 호출되면 위에 쌓인다.
  3. FristMethod가 종료되면 Call Stack에서 소멸된다.

기본형, 참조형 매개변수

종류 특징
기본형 매개변수 변수의 값을 읽기만 가능하다. (Read Only)
종류 : boolean, char, byte, short, int, float, long, double
참조형 매개변수 변수의 값을 읽고 변경할 수 있다. (Read & Write)
종류 : 기본형 매개변수 외에 모든 것

클래스 메서드와 인스턴스 메서드

인스턴스 메서드(Instance Method)

  • 인스턴스 생성 후 " 참조 변수명. 메서드명() "으로 호출할 수 있다.
  • 메서드 내에서 인스턴스 변수를 사용할 수 있다.
  • 메서드 내에서 클래스 메서드를 사용할 수 있다.

클래스 메서드(static Method)

  • 인스턴스 생성 없이 " 클래스명. 메서드명() "으로 호출할 수 있다.
  • 메서드 내에서 인스턴스 멤버를 사용할 수 없다.
class Test{
    long a, b;
    long add() {return a+b;} // 인스턴스 메서드
    static long add(long a, long b) {return a+b;} // 클래스 메서드
}

System.out.println(Test.add(20L,10L)); // 30L 클래스 메서드 호출

Test t = new Test(); // 인스턴스 생성
t.a = 20L;
t.b = 10L;
System.out.println(t.add()); // 30L 인스턴스 메서드 호출

 

메서드 오버 로딩(Method Overloading)

하나의 클래스에 같은 이름의 메서드를 여러 개 정의하는 것을 의미합니다.

  • 기존에 없는 새로운 메서드를 정의하는 것

오버 로딩의 조건

  1. 메서드 이름이 같아야 한다.
  2. 매개변수의 개수 또는 타입이 달라야 한다.
  3. 매개변수는 같고 리턴 타입이 다른 경우는 성립되지 않는다.

생성자(constructor)

  • 모든 클래스에는 반드시 하나 이상의 생성자가 있어야 한다!

인스턴스가 생성될 때마다 호출되는 인스턴스 초기화 메서드를 의미합니다.

Test t = new Test();
  1. new 연산자에 의해서 메모리(heap)에 Test 클래스의 인스턴스가 생성된다.
  2. 생성자 Test()가 호출되어 수행된다.
  3. new 연산자의 결과로 생성된 Test 인스턴스의 주소가 참조 변수 t에 저장된다.

생성자의 조건

  1. 생성자의 이름은 클래스의 이름과 같아야 한다.
  2. 생성자는 리턴 값이 없다.

기본 생성자(default constructor)

매개변수가 없는 생성자를 말하며, 만약 클래스에 생성자가 하나도 없다면 컴파일러가 기본 생성자를 추가한다.

class Test1{
    int value;
    // Test1(){} -> 컴파일러가 생성자가 없기 때문에 추가해준다.
}

class Test2{
    int value;
    Test2(int x){ // 매개변수가 있는 생성자
        value = x;
    }
}

Test1 t1 = new Test1(); // OK
// Test1() 생성자를 호출한다.

Test t2 = new Test2(); // Error!
// Test2() 생성자를 호출하는데 Test2 클래스에는 Test2() 생성자가 없다.
Test t2 = new Test2(10); // OK

다른 생성자 호출하는 this( )

같은 클래스의 다른 생성자를 호출할 때 사용하며 다른 생성자 호출은 생성자의 첫 문장에서만 가능하다.

class Test{
    String color;
    int door;
    
    Test(){
        this("Red", 4);
        // Test("Red", 4); 와 같다.
    }
    
    Test(String c, int d){
        color = c;
        door = d;
    }
}

참조 변수 this

인스턴스 자신을 가리키는 참조 변수이며 인스턴스 주소가 저장되어있다.

class Test{
    String color;
    int door;
    
    Test(String color, int door){
        this.color = color;
        this.door = door;
        // 인스턴스 변수와 지역 변수를 구별하기 위해 참조변수 this를 사용
    }
}

변수의 초기화

변수를 선언하고 처음으로 값을 저장하는 것을 말한다.

  • 지역 변수의 경우는 사용 전에 꼭 초기화를 해주어야 한다.
자료형 기본값 자료형 기본값
boolean false int 0
char '\u0000' long 0L
byte 0 float 0.0f
short 0 double 0.0
참조형 변수 null

멤버 변수의 초기화

1. 명시적 초기화 (explicit initialization)

class Test{
    int door = 4; // 기본형 변수의 초기화
    Engine e = new Engine(); // 참조형 변수의 초기화
}

2. 생성자 (constructor)

Test(String color, int door){
    this.color = color;
    this.door = door;
}

3. 초기화 블록 (initialization block)

{} // 인스턴스 초기화 블록 (생성자 보다 먼저 실행된다.)
static {} // 클래스 초기화 블록 (클래스가 로딩될 때 실행된다.)

멤버 변수의 초기화 시기와 순서

class InitTest{
    static int cv = 1;
    int iv = 2;
    
    static { cv = 3; }
    { iv = 4; }
    
    InitTest(){ iv = 5; }
}
  1. 클래스 변수 cv 기본값 0
  2. 클래스 변수 cv 명시적 초기화 1
  3. 클래스 변수 cv 초기화 블록 3
  4. 인스턴스 변수 iv 기본값 0
  5. 인스턴스 변수 iv 명시적 초기화 2
  6. 인스턴스 변수 iv 초기화 블록 4
  7. 인스턴스 변수 iv 생성자 5

상속 (inheritance)

  • Java에서는 단일 상속만을 허용한다!

기존의 클래스를 재사용해서 새로운 클래스를 작성하는 것이며 두 클래스는 부모와 자식으로 관계를 맺게 된다.

  • 자식 클래스는 부모 클래스의 모든 멤버를 상속받는다.
    • 단, 생성자와 초기화 블록은 상속되지 않는다.

클래스 간의 상속 관계

상속은 " extends "로 만들 수 있으며 부모 클래스의 변경은 자식 클래스에게 영향을 미치지만 자식 클래스의 변경은 부모 클래스에게 아무런 영향을 미치지 못합니다.

class Parent{}
class Child1 extends Parent{}
class Child2 extends Parent{}
상속 관계 " ~은 ~이다 " (is ~a)

포함(composite)

한 클래스의 멤버 변수로 다른 클래스를 선언하는 것이다.

class Point{
    int x;
    int y;
}

class Circle{
    Point p = new Point(); // 포함 관계
    int r;
}
포함 관계 " ~은 ~을 가지고 있다. " (has ~a)

Object 클래스

조상이 없는 클래스는 자동적으로 Object 클래스를 상속받게 된다.

  • Object 클래스는 11개의 메서드를 상속한다.
    • toString(), equals(Object obj), hashCode() 등등

오버 라이딩(overriding)

부모 클래스로부터 상속받은 메서드의 내용을 상속받는 클래스에 맞게 변경하는 것을 오버 라이딩이라고 한다.

  • 상속받은 메서드의 내용을 변경하는 것
class Point{
    int x;
    int y;
    
    String getLocation(){
        return "x = " + x + "y = " + y;
    }
}

class Point3D extends Point{
   int z;
   
   String getLocation(){ // 오버라이딩
       return "x = " + x + "y = " + y + "z = " + z;
   }
}

오버 라이딩의 조건

  1. 선언부(메서드명, 매개변수, 리턴 타입)가 같아야 한다.
  2. 접근제어자를 좁은 범위로 변경할 수 없다.
    • 범위 순서 : public > protected > default > private
  3. 부모 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
class Parent{
    default void parentMethod() throws IOException, SQLException{/* ... */}
}

class Child extends Parent{
    private void parentMethod() throws IOException{/* ... */}
    // Error! 접근제어자는 좁은범위로 변경 불가능
    
    public void parentMethod() throws Exception{/* ... */} // OK
}

참조 변수 super

참조 변수 super는 부모의 멤버와 자신의 멤버를 구별하는 데 사용된다.

부모의 변수를 참조

class Parent{
    int x = 10;
}

class Child extends Parent{
    int x = 20;
    void method(){
        System.out.println("x = " + x); // 20 (가까운 x -> Child 클래스에 x가 없다면 부모의 x를 가리킨다.)
        System.out.println("this.x = " + this.x); // 20 (자신의 x)
        System.out.println("super.x = " + super.x); // 10 (부모의 x)
    }
}

부모의 메서드를 참조

class Point{
    int x;
    int y;
    
    String getLocation(){
        return "x : " + x + ", y : " + y;
    }
}

class Point3D extends Point{
     int z;
     String getLocation(){
          return super.getLocation() + ", z : " + z; // 부모 메서드 호출
     }
}

class Point3D extends Point{
     int z;
     String getLocation(){ // 오버라이딩
          return "x : " + x + ", y : " + y + ", z : " + z;
     }
}

부모의 생성자 super( )

부모의 멤버들을 초기화해야 하기 때문에 자식의 생성자의 첫 문장에서는 부모의 생성자를 호출해야 한다.

  • Object클래스를 제외한 모든 클래스의 생성자 첫 줄에는 (1) 생성자를 호출해야 한다.
    • (1) : 같은 클래스의 다른 생성자 또는 부모의 생성자
  • 생성자를 호출하지 않으면 컴파일러가 자동적으로 " super( ); "를 생성자의 첫 줄에 삽입한다.
class Point{
    int x;
    int y;
    
    Point(){this(0,0);}
    Point(int x, int y){
        // super(); 컴파일러가 자동 삽입한다.
        this.x = x;
        this.y = y;
    }
}

상속에서 super( ) 사용

class Parent{
    int x;
    
    Parent(int x){
        //super();
        this.x = x;
    }
}

class Child extends Parent{ 
    int y;
    
    Child(int x, int y){
        super(x);
        //super(); // Error! 부모의 생성자에 기본 생성자가 없다!
        this.y = y; 
    }
}

제어자(modifiers)

제어자는 크게 접근 제어자와 그 외 제어자로 나뉜다.

구분 종류
접근 제어자 public, protected, default, private
그 외 제어자 static, final, abstract, native, transient, synchronized, volatile, strictfp

그 외 제어자 알아보기

static(공통적인)

static이 사용될 수 있는 곳은 멤버 변수, 메서드, 초기화 블록이다.

class StaticTest{
    static int width = 100;
    staitc int height = 200;
    
    static{}
    
    static int max(int a, int b){return a > b ? a : b;}
}

final(마지막의)

final이 사용될 수 있는 곳은 클래스, 메서드, 멤버 변수, 지역변수이다.

final class FinalTest{
    final int MAX_SIZE = 10;
    
    final void getMaxSize(){
        final LV = MAX_SIZE;
        return MAX_SIZE;
    }
}
구분 의미
클래스 부모가 될 수 없는 클래스
메서드 오버라이딩을 할 수 없는 메서드 ( 오버로딩과 상관 없음 )
멤버변수 값을 변경할 수 없는 상수
※ 상수는 선언과 초기화를 동시에 하는게 좋다.
지역변수

abstract(추상의)

abstract가 사용될 수 있는 곳은 클래스, 메서드이다.

abstract class AbstractTest{
    abstract void move();
}
구분 의미
클래스 클래스 내부에 추상 메서드가 있다는 의미
메서드 선언부만 있고 구현부가 없는 미완성된 메서드

접근 제어자 알아보기

멤버 또는 클래스에 사용되며 외부로부터의 접근을 제한하는 역할을 합니다.

구분 특징
public 접근 제한이 없다. (사용가능한 곳 : 클래스, 멤버)
protected 같은 패키지, 다른 패키지의 자식 클래스에서 접근 가능 (사용가능한 곳 : 멤버)
default (생략 가능) 같은 패키지에서만 접근 가능 (사용가능한 곳 : 클래스, 멤버)
private 같은 클래스에서만 접근 가능 (사용가능한 곳 : 멤버)
// 캡슐화

class Time{
    private int hour; // 외부에서 직접 접근하는것이 불가능하다.
    
    Time(int hour){
        setHour(hour); // 메서드를 통해서 초기화 된다.
    }
    
    public int setHour(int hour){
         if(hour < 0 || hour > 23){
             return;
         }
         this. hour = hour;
    }
    
    public int getHour(){ // 메서드를 통해 값을 불러온다.
        return hour;
    }
}

Time t = new Time(12);
t.get(); // 12

생성자의 접근 제어자

일반적으로 생성자의 접근 제어자는 클래스의 접근 제어자와 일치하며 생성자에 접근 제어자를 사용함으로써 인스턴스 생성을 제한할 수 있다.

class Test{
    private int x;
    
    private static Test t = null;
    
    private Test(int x){this.x = x;}
    
    public static Test getInstance(int a){
        if(t == null){
            t = new Test(a);
        }
        return t;
    }
    public int getX(){
        return this.x;
    }
}

Test t = Test.getInstance(1);
System.out.println(t.getX()); // 1

다형성(Polymorphism)

부모의 참조 변수로 자식 타입의 객체를 다룰 수 있는 것을 다형성이라고 부른다.

  • 반대로 자식의 참조 변수는 부모의 객체를 다룰 수 없다!
class Tv{
    int channel;
    Tv(int x){
        channel = x;
    }
    void channelUp(){++channel;}
}

class SmartTv extends Tv{
    String Text;
    SmartTv(int x, String str){
         super(x);
         Text = str;
    }
    void caption(){
        System.out.println(Text);
    }
}

Tv t = new SmartTv(1, "wow");
t.channelUp(); // OK 2
t.caption(); // Error! Tv타입의 참조변수 t에는 caption이 없다.

참조 변수의 형 변환

서로 상속관계에 있는 타입 간의 형 변환만 가능하다.

class Car{
    String color;
    void drive(){System.out.println("rrrrr~");}
}

class FireEngine extends Car{
    void water(){System.out.println("water!!");}
}

class Ambulance extends Car{
    void siren(){System.out.println("siren!!");}
}

Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = new FireEngine();

fe.water(); // water!!
car = fe; // Car car = (Car)fe;
car.water(); // Error!! Car타입에는 water메서드가 없다.

fe2 = (FireEngine)car; // FireEngine fe2 = (FireEngine)car;
fe2.water(); // water!!

부모 타입의 참조 변수에서 자식 타입의 참조 변수로 형 변환을 할 때는 (자식 타입)을 생략할 수 없으며 자식 타입의 참조 변수에서 부모 타입의 참조 변수로 형 변환할 때는 (부모 타입)을 생략할 수 있다.

구분 특징
자식 타입에서 부모 타입으로 형변환 생략가능
부모 타입에서 자식 타입으로 형변환 생략불가

instanceof 연산자

참조 변수 형 변환을 하기 전에 형 변환이 가능한지 확인하는 연산자이다.

  • 참조 변수가 참조하는 인스턴스의 실제 타입을 체크한다.
class Car{}
class FireEngine extends Car{}

FireEngine fe = new FireEngine();
Car c = new FireEngine(); // 다형성

if (fe instanceof FireEngine){ // True
    System.out.println("this is a fireEngine instance");
}

if (fe instanceof Car){ // True
    System.out.println("this is a car instance");
}

if (fe instanceof Object){ // True
    System.out.println("this is a Object instance");
}

System.out.println(c instanceof FireEngine); // true

A instanceof B = A 인스턴스의 실제 타입이 B의 자식 타입이면 true를 반환하고 아니라면 에러가 발생한다.

참조 변수와 인스턴스 변수의 연결

구분 특징
멤버 변수 중복 참조 변수 타입에 따라 멤버 변수가 달라진다.
메서드 중복 참조 변수 타입과 상관없이 실제 인스턴스의 타입에 메서드가 호출된다.
class Parent{
    int x = 100;    
    void method(){ System.out.println("Parent Method"); }
}

class Child{
    int x = 200;
    void method(){ System.out.println("Child Method"); }
}

Parent p = new Child(); // 다형성
Child c = new Child();

System.out.println("p.x = " + p.x); // 100
System.out.println("c.x = " + c.x); // 200
p.method(); // Child Method
c.method(); // Child Method

매개변수의 다형성

참조형 매개변수는 메서드 호출 시 자신과 같은 타입 또는 자식 타입의 인스턴스를 넘겨줄 수 있다.

class Product{
    int price;
}

class Tv extends Product{}
class Audio extends Product{}

// 매개변수 다형성 사용하지 않았을 경우
class Buyer{
    int money = 100;
    
    void Buy(Tv t){
        this.money = this.money - t.price;
    }
    void Buy(Audio a){ // 오버로딩
        this.money = this.money - a.price;
    }
}

// 매개변수 다형성 사용하는 경우
class Buyer{
    int money = 100;
    
    void Buy(Product p){
        this.money = this.money - p.price;
    }
}

Buyer b = new Buyer();
Tv t = new Tv();
Audio a = new Audio();

b.buy(t); // Product p = (Product)t 참조변수 형변환 
b.buy(a); // Product p = (Product)a 참조변수 형변환

여러 종류의 객체를 하나의 배열로 다루기

부모 타입의 배열에 자식 타입의 객체들을 담을 수 있다.

class Product{}
class Tv extends Product{}
class Audio extends Product{}
class Computer extends Product{}

Product[] p = new Product[3];
p[0] = new Tv(); // Product p = new Tv(); 다형성
p[1] = new Audio(); // Product p = new Audio(); 다형성
p[2] = new Computer(); // Product p = new Computer(); 다형성

추상 클래스 (abstract class)

클래스 내부에 추상 메서드를 가진 클래스를 의미하며 미완성된 클래스이기 때문에 인스턴스를 생성할 수 없다.

  • 추상 클래스를 상속받는 자식 클래스에서는 모든 추상 메서드의 구현부를 완성해서 사용한다.
abstract class Player{
    int currentPos;
    
    Player(){ // 추상 클래스에도 생성자가 있어야 한다.
        currentPos = 0;
    }
    
    abstract void play(int pos); // 추상 메서드
    
    void stop(){ 
        play(currentPos); // 일반 메서드에서 추상 메서드를 사용할 수 있다.
    }
}

추상 클래스를 사용하는 이유

여러 클래스에 공통적으로 사용되는 메서드명이지만 각기 다른 구현부를 가질 때 그 부분을 뽑아서 만든다.

class Marine{
    int x, y;
    void move(int x, int y){System.out.println("Marine move = " + x + ", " + y);}
}
class Tank{
    int x, y;
    void move(int x, int y){System.out.println("Tank move = " + x + ", " + y);}
}

// 추상 클래스를 만들어서 사용
abstract class Unit{
    int x, y;
    abstract void move(int x, int y); // 추상 메서드
}

class Marine extends Unit{
    void move(int x, int y){System.out.println("Marine move = " + x + ", " + y);}
} 
class Tank extneds Unit{
    void move(int x, int y){System.out.println("Tank move = " + x + ", " + y);}
}

Unit[] group new Unit[2];
group[0] = new Marine();
group[1] = new Tank();

for(int i = 0; i < group.length; i++){
    group[i].move(100,200); 
}
// Marine move = 100, 200
// Tank move = 100, 200

인터페이스 (interface)

실제 구현된 것이 없는 기본 설계도이며 추상 메서드와 상수, 디폴트 메서드만을 멤버로 가질 수 있다.

  • 인스턴스를 생성할 수 없으며 표준을 제시하는 데 사용된다.
interface interfaceName{
    public static final int NUM = 1; // 상수
    public abstract void method(); // 추상 메서드
    default void Hello(){System.out.println("Hello Im default Method~");};
}

// interface는 implements로 받을 수 있으면 이는 " 구현 "이라고 부른다.
class Car inplements interfaceName{
    void method(){System.out.println("추상 메서드 구현부 완성");}
}
// 인터페이스에 있는 추상 메서드를 모두 구현하지 않으면 추상 클래스가 된다.

class Child extends Parent implements Interface{} // 상속과 구현을 동시에 할 수 있다.
구분 interface의 특징
멤버 변수 모든 멤버 변수가 public static final이기 때문에 생략 가능
추상 메서드 모든 추상 메서드가 public abstract이기 때문에 생략가능

인터페이스의 상속

인터페이스는 메서드가 선언 부만 있기 때문에 클래스와 다르게 다중 상속이 가능하다.

interface A{
    void move();
}
interface B{
    void attack();
}

interface C extends A, B{}

인터페이스를 이용한 다형성

인터페이스 타입의 변수로 인터페이스를 구현한 클래스의 인스턴스를 참조할 수 있다.

class Fighter extends Unit implements Fightable{
    public void move(int x, int y){System.out.println("Move = " + x + ", " + y);}
    public void attack(Fightable f){/* 내용생략 */}
    // 매개변수로 인터페이스를 지정할 수 있다.
    // Fightable을 구현한 클래스만 들어오게 된다.
    
    // 인터페이스를 메서드의 리턴타입으로 지정할 수 있다.
    Fightable method(){ // Fightable 인스턴스를 구현한 클래스의 인스턴스를 반환한다.
        return new Fighter();
    }
}

Fighter f = new Fighter();
Fightable f2 = new Fighter();

디폴트 메서드

JDK 1.8 이후 인터페이스에서 디폴트 메서드(일반 메서드), static 메서드를 사용할 수 있게 되었다.

  • 디폴트 메서드가 기존의 메서드와 충돌하는 경우 아래와 같이 해결된다.
    1. 여러 인터페이스의 디폴트 메서드 간의 충돌
      • 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버 라이딩해야 한다.
    2. 디폴트 메서드와 부모 클래스의 메서드 간의 충돌
      • 부모 클래스의 메서드가 상속되고 디폴트 메서드는 무시된다.
interface Test{
    void method();
    default void get(){System.out.println("interface");}
}

class Parent{
    public void get(){System.out.println("Parent");}
}

class Child extends Parent implements Test{
    public void method(){System.out.println("hi");}
}

Child c = new Child();        
c.get(); // Parent
Comments