본문 바로가기

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

[2024-2 Java 스터디] 김재승 #4주차

반응형

목차

  • Default Constructor
  • Constructor Overloading
  • Interface
  • Polymorphism
  • Abstract Class

디폴트 생성자, 생성자 오버로딩, 인터페이스, 다형성, 그리고 추상 클래스에 대한 내용을 알아보자.


1. 디폴트 생성자 (Default Constructor)

자바에서 생성자가 없을 때 컴파일러가 자동으로 제공하는 생성자를 바로 디폴트 생성자라고 부른다.

디폴트 생성자는 매개 변수가 없으며, 객체 생성 시 초기값을 설정하지 않아도 사용할 수 있다는 특징이 있다.

public class Car {
    // 필드
    private String model;
    private int year;

    // 디폴트 생성자
    public Car() {
        this.model = "Unknown";
        this.year = 2000;
    }

    public void displayInfo() {
        System.out.println("모델: " + model + ", 연도: " + year);
    }

    public static void main(String[] args) {
        Car car = new Car();
        car.displayInfo();  // 모델: Unknown, 연도: 2000
    }
}
  • Car 클래스에서 생성자가 없을 경우, 컴파일러가 디폴트 생성자를 자동으로 추가함.
  • Car 객체가 생성될 때 model과 year에 기본값이 설정됨.

2. 생성자 오버로딩 (Constructor Overloading)

생성자 오버로딩은 동일한 클래스 내에서 매개 변수의 수나 타입을 다르게 하여 여러 생성자를 정의하는 것을 말한다.

public class Car {
    private String model;
    private int year;

    // 디폴트 생성자
    public Car() {
        this.model = "Unknown";
        this.year = 2000;
    }

    // 오버로딩된 생성자
    public Car(String model, int year) {
        this.model = model;
        this.year = year;
    }

    public void displayInfo() {
        System.out.println("모델: " + model + ", 연도: " + year);
    }

    public static void main(String[] args) {
        Car defaultCar = new Car();
        defaultCar.displayInfo();  // 모델: Unknown, 연도: 2000

        Car customCar = new Car("Toyota", 2021);
        customCar.displayInfo();  // 모델: Toyota, 연도: 2021
    }
}
  • Car 클래스는 매개변수가 없는 디폴트 생성자와 String 및 int 타입의 매개변수를 받는 생성자가 존재함.
  • 메인 메서드를 보면, 객체 생성 시 상황에 맞게 원하는 생성자를 호출해서 쓸 수 있음.

3. 인터페이스 (Interface)

인터페이스는 클래스가 실행해하는 동작, 메서드를 반복해서 사용하는 것을 줄이기 위해

해당 메서드의 집합을 정의해주는 역할을 한다고 보면 된다.

 
interface Driveable {
    void startEngine();
    void stopEngine();
}

public class Car implements Driveable {
    @Override
    public void startEngine() {
        System.out.println("엔진이 시작됩니다.");
    }

    @Override
    public void stopEngine() {
        System.out.println("엔진이 멈춥니다.");
    }

    public static void main(String[] args) {
        Car car = new Car();
        car.startEngine();  // 엔진이 시작됩니다.
        car.stopEngine();   // 엔진이 멈춥니다.
    }
}
  • Driveable 인터페이스는 startEngine과 stopEngine 메서드를 선언하고, Car 클래스는 이 인터페이스를 구현하여 각 메서드를 정의함.
  • implements 를 써서 해당 인터페이스를 구현한 클래스라는 뜻을 표시함.

4. 다형성 (Polymorphism)

다형성은 부모 타입의 참조 변수가 자식 클래스 객체를 참조할 수 있도록 하여,

동일한 메서드 호출이 다른 행동을 하도록 만드는 특징이다.

 
class Animal {
    public void sound() {
        System.out.println("동물이 소리를 냅니다.");
    }
}

class Dog extends Animal {
    @Override
    public void sound() {
        System.out.println("멍멍!");
    }
}

class Cat extends Animal {
    @Override
    public void sound() {
        System.out.println("야옹!");
    }
}

public class Main {
    public static void main(String[] args) {
        Animal dog = new Dog();
        Animal cat = new Cat();

        dog.sound();  // 멍멍!
        cat.sound();  // 야옹!
    }
}
  • Animal 클래스에는 sound 메서드가 정의되어 있음.
  • Dog와 Cat은 Animal을 상속받아 sound 메서드를 오버라이딩함.
  •  Animal 타입의 변수가 어떤 객체를 참조하는지에 따라 각기 다른 행동을 하게 됨 >> 다형성!!

5. 추상 클래스 (Abstract Class)

추상 클래스는 인스턴스를 생성할 수 없으며, 반드시 서브클래스가 추상 메서드를 정의해야 한다.

추상 클래스는 인터페이스의 역할도 하면서 클래스의 기능도 하는 클래스라고 볼 수 있다.

 
abstract class Shape {
    abstract void draw();
}

class Circle extends Shape {
    @Override
    void draw() {
        System.out.println("원을 그립니다.");
    }
}

class Rectangle extends Shape {
    @Override
    void draw() {
        System.out.println("사각형을 그립니다.");
    }
}

public class Main {
    public static void main(String[] args) {
        Shape circle = new Circle();
        Shape rectangle = new Rectangle();

        circle.draw();     // 원을 그립니다.
        rectangle.draw();  // 사각형을 그립니다.
    }
}
  • class 앞에 abstract (추상적인) 이라는 단어를 붙여서 추상 클래스임을 보임.
  • Shape는 추상 클래스이며, draw 메서드는 추상 메서드로 선언되어 있음.
  • 서브클래스인 Circle과 Rectangle 클래스는 draw 메서드를 구현해야 함!!
  • 추상 클래스를 통해 공통적인 행동을 정의하고, 구체적인 구현은 서브클래스가 정의하는 방식임.

객관식 문제

 

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

  • a) super
  • b) extends
  • c) inherits
  • d) this

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

  • a) 코드 재사용성
  • b) 객체 간의 독립성 강화
  • c) 코드 복잡도 증가
  • d) 상속된 필드의 자동 초기화

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

  • a) 코드의 재사용성
  • b) 메모리 절약
  • c) 다이아몬드 문제로 인한 모호성
  • d) 컴파일 시간 단축

cf.다이아몬드 문제

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

  • a) super
  • b) abstract
  • c) override
  • d) 없음, 자동으로 재정의된다.

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

  • a) Object
  • b) Base
  • c) Class
  • d) Parent

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

  • a) 클래스의 메서드를 초기화한다
  • b) 클래스의 객체를 초기화한다
  • c) 클래스의 인터페이스를 정의한다
  • d) 클래스의 속성을 변경한다

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

  • a) 클래스가 선언될 때
  • b) 객체가 생성될 때
  • c) 프로그램이 종료될 때
  • d) 메서드가 호출될 때

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

  • a) 같은 매개변수를 사용해야 한다
  • b) 서로 다른 매개변수를 가져야 한다
  • c) 이름이 달라야 한다
  • d) 반환 타입이 있어야 한다

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

  • a) 필드와 메서드를 포함한다
  • b) 메서드의 구현을 포함할 수 없다
  • c) 객체를 생성할 수 있다
  • d) final 키워드로 선언해야 한다

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

  • a) implements
  • b) extends
  • c) interface
  • d) super

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

  • a) 여러 형태로 객체를 생성할 수 있다
  • b) 여러 클래스를 상속할 수 있다
  • c) 동일한 인터페이스로 다양한 구현을 사용할 수 있다
  • d) 모든 필드가 자동으로 초기화된다

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

  • a) 오버라이딩은 메서드 이름이 다르다
  • b) 오버로딩은 반환 타입이 다르다
  • c) 오버라이딩은 상속 관계에서 이루어진다
  • d) 오버로딩은 상속 관계에서 이루어진다

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

  • a) 객체 생성이 가능하다
  • b) 모든 메서드를 추상 메서드로 선언해야 한다
  • c) 인스턴스를 만들 수 없다
  • d) static 메서드를 가질 수 없다

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

  • a) 둘 다 객체를 생성할 수 없다
  • b) 둘 다 모든 메서드를 구현해야 한다
  • c) 둘 다 필드를 가질 수 없다
  • d) 둘 다 final로 선언해야 한다.

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

  • a) static
  • b) void
  • c) abstract
  • d) final

서술형 문제

  • 추상 클래스와 일반 클래스의 차이점을 설명하세요.
더보기

추상 클래스는 인스턴스를 생성할 수 없는 클래스이며, 추상 메서드를 포함할 수 있고 추상 메서드는 자식 클래스에서 반드시 구현해야 하는 메서드임. 반면, 일반 클래스는 인스턴스를 생성할 수 있으며 모든 메서드가 구현되어 있음.

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

추상 클래스와 인터페이스를 함께 사용하면 클래스가 상속을 통해 코드의 재사용성을 가지면서도 인터페이스 구현을 통해 다형성을 극대화할 수 있다. 예를 들어, Animal이라는 추상 클래스와 Swimmable이라는 인터페이스가 있다면, 상어와 같은 클래스는 Animal을 상속하고 Swimmable을 구현해 다양한 기능을 가질 수 있다.

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

추상 클래스는 공통적인 기능을 정의하면서도, 특정 기능을 자식 클래스에서 각각 구현하도록 강제하는 용도로 사용됨.

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

추상 메서드는 구현이 없는 메서드로, 자식 클래스에서 반드시 구현해야 하지만 , 일반 메서드는 이미 구현된 메서드로, 자식 클래스에서 바로 사용할 수 있다.

  • 상속과 구성(Composition)의 차이점과 각각의 장단점을 비교하세요.*
더보기

상속은 is-a 관계를 나타내며 코드 재사용성이 뛰어나지만, 클래스 간의 강한 결합으로 인해 유연성이 떨어질 수 있다. 구성은 has-a 관계를 나타내며, 하위 클래스가 상위 클래스를 상속받는 것이 아닌 단순히 하위 클래스가 상위 클래스를 private으로 참조받아서 사용하는 것이으로 이 이후에 상위 클래스에서 필요한 메서드를 가져다 사용하는 방식이다. 객체를 포함하여 사용함으로써 유연성이 높고 클래스 간 결합이 낮아 재사용성이 높아진다.

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

this는 현재 객체를 참조하는 키워드로, 같은 클래스 내의 다른 생성자나 필드를 참조할 때 사용됨. super는 부모 클래스를 참조하며, 부모 클래스의 생성자나 메서드를 호출할 때 사용됨.

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

인터페이스는 다중 구현이 가능하며, 기본적으로 모든 메서드가 추상 메서드임. 반면, 추상 클래스는 단일 상속만 가능하고, 추상 메서드와 일반 메서드 둘 다 가질 수 있음.

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

다형성은 동일한 인터페이스로 다양한 구현을 사용할 수 있게 하는 기능으로, 코드의 유연성과 확장성을 높여준다. 객체지향 프로그래밍에서 중요한 이유는, 상위 클래스나 인터페이스를 통해 다양한 객체를 다룰 수 있어 유지보수성과 확장성이 향상되기 때문.

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

오버로딩은 같은 이름의 메서드를 다른 매개변수로 여러 개 정의하는 것이고, 오버라이딩은 상속받은 메서드를 자식 클래스에서 재정의하는 것. 오버로딩은 같은 클래스 내에서 발생하고, 오버라이딩은 상속 관계에서 발생.

  • 동적 바인딩의 개념을 설명하고 다형성과의 관계를 설명하세요.*
더보기

동적 바인딩은 실행 시점에 메서드 호출을 결정하는 방식. 다형성에서 중요하며, 상위 타입의 참조 변수가 하위 타입의 메서드를 호출할 수 있게 해줌.


코테

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 (필드는 타입에 따라 접근됨. 여기서 타입은 Animal이고 Dog라는 객체를 참조하는 것)

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 (메서드는 실제 객체의 타입에 따라 호출되는데 여기서 p는 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 (1번과 same)

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

(B 클래스의 객체를 생성하면 부모 클래스의 생성자가 먼저 호출된 후 B의 생성자가 호출됨. 상속의 기본적인 특징)

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

(B 객체를 생성하기 때문에 먼저 A 호출 이후 B 호출)

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 (C객체를 생성 후 display 메서드 호출하고 있는데 C 클래스에서 display 메서드 구현 중임 따라서 A,B가 동일한 이름의 메서드를 가지고 있더라도 실제 구현된 C 클래스의 메서드가 실행됨)

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 (B 객체 생성 후 B클래스의 show 메서드 실행하므로 B 출력)

반응형