본문 바로가기

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

[2024-2 Java 스터디] 김태일 #4주차

반응형

05-5 상속

- 상속 : 자식 클래스가 부모 클래스의 기능을 그대로 물려받는 것

- extends 키워드를 사용하여 상속

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {  // Animal 클래스를 상속한다.
}

public class Sample {
    public static void main(String[] args) {
        Dog dog = new Dog();
        dog.setName("poppy");
        System.out.println(dog.name);
    }
}

 

 

1. 자식 클래스의 기능 확장하기

- Dog 클래스에 sleep 메서드를 추가

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name+" zzz");
    }
}

 

2. IS-A 관계

- Dog 클래스가 Animal 클래스에 상속됨 -> Dog is a Animal 과 같이 말할 수 있는 관계 == IS-A 관계

  • 자식클래스의 객체는 부모 클래스의 자료형인 것처럼 사용 가능
Animal dog = new Dog();

 

  • 반대의 경우에는 컴파일 오류가 남
Dog dog = new Animal();  // 컴파일 오류

 

3. 메서드 오버라이딩

- 같은 메서드를 다른 클래스에 구현

  •  sleep메서드를 HouseDog 클래스에 추가해주어 우선순위에 따라 HouseDog 클래스의 sleep 메서드가 호출함
class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Dog extends Animal {
    void sleep() {
        System.out.println(this.name + " zzz");
    }
}

class HouseDog extends Dog {
    void sleep() {
        System.out.println(this.name + " zzz in house");
    }
}

public class Sample {
    public static void main(String[] args) {
        HouseDog houseDog = new HouseDog();
        houseDog.setName("happy");
        houseDog.sleep();  // happy zzz in house 출력
    }
}

 

4. 메서드 오버로딩

- 메서드 이름은 동일하지만 메서드의 입력 항목이 다른 경우

class HouseDog extends Dog {
    void sleep() {
        System.out.println(this.name + " zzz in house");
    }

    void sleep(int hour) {
        System.out.println(this.name + " zzz in house for " + hour + " hours");
    }
}

 

5. 다중 상속이란?

- 다중 상속 : 클래스가 동시에 하나 이상의 클래스를 상속받는 것

- 자바에서는 불가능 -> 어떤 클래스가 우선순위인지 모르기 때문에 불명확함

class A {
    public void msg() {
        System.out.println("A message");
    }
}

class B {
    public void msg() {
        System.out.println("B message");
    }
}

class C extends A, B {
    public void static main(String[] args) {
        C test = new C();
        test.msg();
    }
}

 

 

05-6 생성자

- 객체 변수에 값을 무조건 설정해야만 객체가 생성될 수 있도록 강제하는 것

- 생성자 규칙

  • 클래스명과 메서드명이 같다
  • 리턴 타입을 정의하지 않는다 (void도 사용하지 않음)
...

class HouseDog extends Dog {
    HouseDog(String name) {
        this.setName(name);   // name 객체 변수에 값 설정
    }
}

public class Sample {
    public static void main(String[] args) {
        HouseDog dog = new HouseDog("happy");
        System.out.println(dog.name);
    }
}  //happy 출력

 

1. 디폴트 생성자

- 디폴트 생성자 : 입력 항목이 없고, 내부가 비어있는 생성자

  •  Dog 클래스의 객체가 만들어질때 디폴트 생성자 Dog() 실행
class Dog extends Animal {
    Dog() {
    }

    void sleep() {
        System.out.println(this.name + " zzz");
    }
}

 

2. 생성자 오버로딩

- 입력값을 다르게하여 메서드와 마찬가지로 오버로딩이 가능

class HouseDog extends Dog {
    HouseDog(String name) {
        this.setName(name);
    }

    HouseDog(int type) {
        if (type == 1) {
            this.setName("yorkshire");
        } else if (type == 2) {
            this.setName("bulldog");
        }
    }
}
HouseDog happy = new HouseDog("happy");  // 문자열로 생성
HouseDog yorkshire = new HouseDog(1);  // 숫자값으로 생성

 

05.7 인터페이스

- interface 키워드로 작성

- 다른 클래스를 작성할 때 기본이 되는 틀 제공

- 클래스 사이의 중간 매개 역할을 담당하는 추상 클래스 (다중 상속 지원)

- implements로 상속 가능

class Animal {
    String name;

    void setName(String name) {
        this.name = name;
    }
}

class Tiger extends Animal {
}

class Lion extends Animal {
}

class ZooKeeper {
    void feed(Tiger tiger) {  // 호랑이가 오면 사과를 던져 준다.
        System.out.println("feed apple");
    }

    void feed(Lion lion) {  // 사자가 오면 바나나를 던져준다.
        System.out.println("feed banana");
    }
}

public class Sample {
    public static void main(String[] args) {
        ZooKeeper zooKeeper = new ZooKeeper();
        Tiger tiger = new Tiger();
        Lion lion = new Lion();
        zooKeeper.feed(tiger);  // feed apple 출력
        zooKeeper.feed(lion);  // feed banana 출력
    }
}

위와 같은 코드를 작성할 때 동물이 추가될 때마다 ZooKeeper클래스의 오버로딩되는 메서드가 추가될 것이다.

 

이 코드를 간편화하기 위해서 interface를 사용하면 다음과 같이 변한다.

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";

}

...

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

ZooKeeper의 코드가 훨씬 간단화된것을 볼 수 있다.

 

 ZooKeeper 클래스가 동물 클래스에 의존적인 클래스에서 동물 클래스와 상관없는 독립적인 클래스가 되었다는 점이 핵심!!

 

 

05-8 다형성

 

동물 울음소리를 출력하는 Bouncer 클래스를 만들어보자

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

동물들이 많아지면 if-else 문이 많아진다

 

위에서 했던대로 인터페이스를 사용하면,

interface Barkable {
    void bark();
}

...

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

...

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

...

class Bouncer {
    void barkAnimal(Barkable animal) {  // Animal 대신 Barkable을 사용
        animal.bark();
    }

Bouncer클래스가 간단화된것을 볼 수 있다.

 

만약 Tiger tiger = new Tiger(); 를 통해 tiger객체를 만들면 이 객체는 여러개의 자료형 타입을 가진다.

 

이를 다형성이라고 한다.

 

 

05-9 추상 클래스

- 인터페이스의 역할도 하면서 클래스의 기능도 가지고 있는 클래스- 클래스 앞에 abstract로 표기

- 디폴트 메서드를 더 이상 사용할 수 없으므로 default 키워드를 삭제해야함

abstract class Predator extends Animal {
    abstract String getFood();

    void printFood() {  // default 를 제거한다.
        System.out.printf("my food is %s\n", getFood());
    }
}

(... 생략 ...)

 

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. 다형성의 주요 특징은 무엇인가요?

답 : a) 여러 형태로 객체를 생성할 수 있다

 

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

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

 

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

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

 

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

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

 

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

c) abstract

 

 

서술형 문제

 

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

답 : 추상클래스 선언된 추상메서드는 상속을 통해 실행시키는 코드를 다시 짜줘야한다

 

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

답 : 상속과 구현을 모두 활용할 수 있기 때문에

 

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

답 : 추상 메서드는 아직 구현되지 않았으므로 자식 클래스에서 그 메서드를 구현해주어야 한다

 

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

답 : 일반 메서드는 이미 구현이 된 것이고, 추상 메서드는 자식 클래스에서 구현을 해 주어야 구현이 된다.

 

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

답 : 상속은 상속받는 클래스가 변경 없이 일관된 동작을 할 필요가 있을 때 적합하고, 구성은 변경 가능성을 고려하거나 여러 독립적인 기능을 조합해야 할 때 유용하다.

 

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

답 : this 키워드는 필드에서 선언된 변수에 접근하도록 하는 것이고, super는 상위 클래스에 메서드에 접근하는 것이다.

 

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

답 : 추상 클래스는 다중 상속을 지원하지 않는 반면, 인터페이스는 다중 상속을 지원한다.

 

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

답 : 하나의 객체가 여러개의 자료형 타입을 갖는 것을 다형성이라고 한다. 이는 복잡한 형태의 코드를 간단하게 처리할 수 있도록 한다.

 

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

답 : 오버로딩은 입력받는 값이 다른 것이고 오버라이딩은 입력받는 값은 같고, 자식 클래스에서 같은 메서드를 생성하는 것이다. 

 

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

답 : 동적 바인딩은 프로그램 실행 중 메서드 호출이 실제 어떤 클래스의 메서드를 호출할 지 결정하는 방식. 동적바인딩은 실행 시점에 객체의 실제 타입에 맞는 메서드를 호출하게 하여 다형성을 실현하는 기법.

 

 

코딩 테스트 문제

[1~7] 아래 코드를 실행했을 때 출력되는 결과를 적으시오.

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);
    }
}

1. generic sound

 

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();
    }
}

2. Child

 

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);
    }
}

3. 10

 

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();
    }
}

4.

A's constructor

B's constructor

 

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();
    }
}

5.

A's no-arg constructor

B's no-arg constructor

 

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();
    }
}

6. C's display

 

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();
    }
}

7. B

반응형