본문 바로가기

WINK-(Web & App)/JAVA 스터디

[2024-2 Java 스터디] 김민서 #4주차

반응형
상속

 

상속 - 자식 클래스가 부모 클래스의 기능을 그대로 물려받을 수 있는 기능, extends를 사용해 클래스 상속을 한다

* 부모 클래스를 상속받은 자식 클래스는 부모 클래스의 기능에 더하여 더 많은 기능을 갖도록 할 수 있다

 

IS-A 관계

객체 지향 프로그래밍에서 상속 관계를 설명하는 개념 (ex. Dog 클래스가 Animal 클래스를 상속받으면, Dog는 Animal의 하위 개념이 됨)

-> 자식 클래스의 객체는 부모 클래스의 자료형으로 사용할 수 있다

 

Object 클래스
자바의 모든 클래스는 자동으로 Object 클래스를 상속받는다.

-> 자바에서 생성되는 모든 클래스는 Object 자료형으로 사용할 수 있다

 

메서드 오버라이딩

부모 클래스의 메서드를 자식 클래스가 동일한 형태로 또 다시 구현하는 행위

 

메서드 오버로딩

이름이 같고 매개변수가 다른 메서드를 여러개 정의하는 것을 메서드 오버로딩이라 한다

메서드의 이름이 같아도 매개변수의 타입 및 순서가 다르면 오버로딩을 할 수 있다

 

* 자바는 다중 상속을 지원하지 않는다

 

 

생성자

객체 변수에 값을 무조건 설정해야만 객체가 생성될 수 있도록 강제할 때 사용

생성자의 규칙

  • 클래스명과 메서드명이 같다
  • 리턴 타입을 정의하지 않는다 (void도 사용하지 않는다)
new 클래스명(입력인수, ...) // new 키워드가 사용될 때 생성자가 호출된다

 

디폴트 생성자

생성자의 입력 항목이 없고 생성자 내부에 아무 내용이 없는 생성자

클래스에 생성자가 하나도 없으면 자동으로 컴파일러가 디폴트 생성자를 추가함

 

생성자 오버로딩

입력 항목이 다른 생성자를 여러 개 만드는 것

 

 

인터페이스

 

인터페이스 - 일종의 규칙으로, 클래스들이 특정 메서드를 반드시 구현하도록 강제
인터페이스를 사용하면 한 번의 구현으로 다양한 상황에 대처할 수 있음

Predator 인터페이스를 만들어서 getFood()라는 메서드를 정의한 후, 이를 구현하는 모든 클래스에서 이 메서드를 반드시 구현하게 만들 수 있다

interface Predator {
    String getFood();
}

class Tiger extends Animal implements Predator {
    public String getFood() { return "apple"; }
}

class Lion extends Animal implements Predator {
    public String getFood() { return "banana"; }
}


ZooKeeper 클래스는 이제 feed 메서드를 하나만으로 다양한 육식동물을 처리할 수 있다

class ZooKeeper {
    void feed(Predator predator) {
        System.out.println("feed " + predator.getFood());
    }
}


새로운 육식동물이 추가되더라도 해당 클래스가 Predator 인터페이스를 구현하면 feed 메서드를 변경할 필요가 없다

디폴트 메서드
자바 8 이후로 인터페이스는 default 키워드를 사용해 기본 구현이 포함된 메서드를 가질 수 있다.

-> 인터페이스를 구현하는 클래스에서 선택적으로 오버라이딩할 수 있다.

interface Predator {
    String getFood();

    default void printFood() {
        Systehttp://m.out.printf("my food is %s\n", getFood());
    }
}



다형성

 

객체 지향 프로그래밍에서 다형성은 동일한 메서드가 다양한 방식으로 동작할 수 있도록 하는 기능
코드의 유연성과 확장성을 높이며, 복잡한 조건문을 줄여 가독성과 유지보수를 용이하게 한다.

class Bouncer {
    void barkAnimal(Animal animal) {
        if (animal instanceof Tiger) {
            System.out.println("어흥");
        } else if (animal instanceof Lion) {
            System.out.println("으르렁");
        }
    }
}


Bouncer 클래스는 instanceof 를 사용하여 객체 타입을 검사하고 각 타입에 맞는 출력을 한다
-> 새로운 동물 클래스가 추가될 때마다 조건문을 추가해야 하므로 비효율적

인터페이스를 활용한 개선

interface Barkable {
    void bark();
}

class Tiger extends Animal implements Barkable {
    public void bark() {
        System.out.println("어흥");
    }
}

class Lion extends Animal implements Barkable {
    public void bark() {
        System.out.println("으르렁");
    }
}

class Bouncer {
    void barkAnimal(Barkable animal) {
        animal.bark();
    }
}


Bouncer 클래스는 barkAnimal 메서드에서 Barkable 인터페이스를 통해 bark() 메서드만 호출하면 된다
-> 새로운 동물이 추가될 때 Barkable 인터페이스를 구현하면 Bouncer 코드를 수정할 필요가 없다
* 객체의 선언된 자료형에 따라 사용할 수 있는 메서드가 제한된다. Barkable로 선언된 객체는 bark() 메서드만 사용할 수 있음

인터페이스 상속

interface BarkablePredator extends Predator, Barkable {}

class Lion extends Animal implements BarkablePredator {
    public void bark() {
        System.out.println("으르렁");
    }
    public String getFood() {
        return "banana";
    }
}

BarkablePredator라는 인터페이스를 통해 두 개의 인터페이스를 상속받아 두 기능 모두 구현 가능

 

추상 클래스

 

추상 클래스 - 인터페이스 역할도 하면서 클래스의 기능도 가지고 있음, class앞과 인터페이스의 메서드와 같은 역할을 하는 메서드에 abstract 표기

* 일반 클래스와 달리 단독으로 객체 생성 불가

* 인터페이스와 달리 일반 클래스처럼 객체 변수, 생성자, private 메서드 등을 가질 수 있다


 

Quiz

1. 자바에서 한 클래스가 다른 클래스를 상속받을 때 사용하는 키워드는 무엇인가요?

답 - b) extends

 

2. 상속을 통해 자식 클래스가 부모 클래스에서 얻을 수 있는 이점은 무엇인가요?

답 - a) 코드 재사용성

 

3. 다중 상속이 허용되지 않는 이유 중 하나는 무엇인가요?

답 - c) 다이아몬드 문제로 인한 모호성

 

4. 상속된 메서드를 자식 클래스에서 재정의하려면 어떤 키워드를 사용하나요?

답 - d) 없음, 자동으로 재정의된다.

 

5. 자바에서 모든 클래스의 상위 클래스는 무엇인가요?

답 - a) Object

 

6. 생성자의 기본 역할은 무엇인가요?

답 - b) 클래스의 객체를 초기화한다.

 

7. 생성자는 언제 호출되나요?

답 - b) 객체가 생성될 때

8. 생성자를 오버로딩할 때 중요한 조건은 무엇인가요?

답 - b) 서로 다른 매개변수를 가져야 한다

 

9. 인터페이스의 특징으로 옳은 것은 무엇인가요?

답 - b) 메서드의 구현을 포함할 수 없다

 

10. 인터페이스의 메서드 구현을 클래스에서 강제하기 위한 키워드는 무엇인가요?

답 - a) implements

 

11. 다형성의 주요 특징은 무엇인가요?

답 - c) 동일한 인터페이스로 다양한 구현을 사용할 수 있다

 

12. 오버라이딩과 오버로딩의 차이점으로 올바른 것은 무엇인가요?

답 - c) 오버라이딩은 상속 관계에서 이루어진다

 

13. 추상 클래스의 주요 특징으로 옳은 것은 무엇인가요?

답 - c) 인스턴스를 만들 수 없다

 

14. 추상 클래스와 인터페이스의 공통점은 무엇인가요?

답 - a) 둘 다 객체를 생성할 수 없다

 

15. 추상 메서드는 어떤 키워드를 사용하여 선언하나요?

답 - c) abstract

 

서술형 문제

1. 추상 클래스와 일반 클래스의 차이점을 설명하세요.

답 - 추상 클래스는 인스턴스를 생성할 수 없고 추상 메서드를 가질 수 있는 클래스이며, 일반 클래스는 인스턴스 생성이 가능하고 모든 메서드가 구현되어야 한다.

2. 추상 클래스와 인터페이스를 함께 사용하는 이유와 예를 설명하세요.

답 - 추상 클래스와 인터페이스를 함께 사용하면 클래스 계층에서 공통 기능(추상 클래스)과 다중 기능 구현(인터페이스)을 모두 활용할 수 있다.

3. 추상 클래스를 상속받은 자식 클래스에서 추상 메서드를 구현해야 하는 이유를 설명하세요.

답 - 자식 클래스는 부모 추상 클래스의 추상 메서드를 구현해야 클래스가 완전해지고 객체로 생성될 수 있다.

4. 추상 클래스에서 선언된 일반 메서드와 추상 메서드의 차이점을 설명하세요.

답 - 추상 클래스의 일반 메서드는 구현된 메서드이며, 추상 메서드는 구현 없이 선언만 되어 자식 클래스에서 구현해야 한다.

 

5. 상속과 구성(Composition)의 차이점과 각각의 장단점을 비교하세요.

답 - 상속은 클래스 간 IS-A 관계를 나타내며 코드 재사용이 쉽지만 유연성이 떨어지며, 구성은 유연성이 높고 의존성을 줄일 수 있다.

6. 생성자에서 this와 super 키워드의 차이점에 대해 설명하세요.

답 - this는 현재 클래스의 인스턴스를 참조하며, super는 부모 클래스의 멤버를 참조한다.

7. 자바에서 인터페이스와 추상 클래스의 차이점을 설명하세요.

답 - 인터페이스는 다중 상속이 가능하고 모든 메서드가 기본적으로 추상적이며, 추상 클래스는 단일 상속만 가능하고 추상 메서드와 일반 메서드를 같이 가질 수 있다.

8. 다형성의 개념과 객체지향 프로그래밍에서의 중요성을 설명하세요.

답 - 다형성은 객체가 여러 타입으로 참조될 수 있는 성질이며, 객체지향 프로그래밍에서 코드의 유연성과 확장성을 높인다.

9. 메서드 오버로딩과 오버라이딩의 차이점을 비교하세요.

답 - 메서드 오버로딩은 같은 이름의 메서드를 매개변수로 구분하며, 오버라이딩은 부모 클래스의 메서드를 자식 클래스에서 재정의한다.

10. 동적 바인딩의 개념을 설명하고 다형성과의 관계를 설명하세요.

답 - 동적 바인딩은 실행 시점에 메서드 호출이 결정되는 것을 의미하며, 다형성을 통해 상속받은 메서드를 동적으로 호출할 수 있게 한다.

 

코딩테스트 문제

1.

class Animal {
    String sound = "generic sound";
}

class Dog extends Animal {
    String sound = "bark";
}

public class Main {
    public static void main(String[] args) {
        Animal a = new Dog();
        System.out.println(a.sound);
    }
}

generic sound

 

2.

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

class Child extends Parent {
    void show() {
        System.out.println("Child");
    }
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        p.show();
    }
}

Child

 

3.

class Parent {
    int x = 10;
}

class Child extends Parent {
    int x = 20;
}

public class Main {
    public static void main(String[] args) {
        Parent p = new Child();
        System.out.println(p.x);
    }
}

10

 

4.

class A {
    A() {
        System.out.println("A's constructor");
    }
}

class B extends A {
    B() {
        System.out.println("B's constructor");
    }
}

public class Main {
    public static void main(String[] args) {
        B obj = new B();
    }
}

A's constructor

B's constructor

 

5.

class A {
    A() {
        System.out.println("A's no-arg constructor");
    }
}

class B extends A {
    B() {
        System.out.println("B's no-arg constructor");
    }
}

public class Main {
    public static void main(String[] args) {
        A a = new B();
    }
}

A's no-arg constructor

B's no-arg constructor

 

6. 

interface A {
    void display();
}

interface B extends A {
    void display();
}

class C implements B {
    public void display() {
        System.out.println("C's display");
    }
}

public class Main {
    public static void main(String[] args) {
        A obj = new C();
        obj.display();
    }
}

C's display

 

7. 

interface A {
    default void show() {
        System.out.println("A");
    }
}

class B implements A {
    public void show() {
        System.out.println("B");
    }
}

public class Main {
    public static void main(String[] args) {
        A obj = new B();
        obj.show();
    }
}

B

반응형