[Java 풀스택 과정 강의] 2월 3일

부트캠프 일지/Java 풀스택 과정 강의
2026.02.08
※ TIL와는 별개로 적는 개인 개발 일지라서 말은 좀 편하게하는 페이지입니다.
일지이기 때문에 일기의 성격이 더 강합니다.

 

 

이어서 계속 적는 뒤늦은 일지. java도 곧 끝이 난다. 

 

생성자 상속

생성자 상속은 자식 객체를 만들 때 부모 생성자가 한 번 호출되는 것을 말한다. 

 

class Parent {
	String a, b;
    Parent(String a, String b) {
    	this.a = a;
        this.b = b;
    }
}

class Child extends Parent {
	String c;
    Child(String a, String b, String c) {
        super(a,b);  // 부모 생성자 먼저 호출
        this.c = c; // 그 다음 자식 필드 초기화
    }
}

 

super()형태로 부모의 생성자를 불러온 뒤, 자식 필드를 초기화한다. 이렇게 하면 자식 클래스에서 부모 생성자를 사용할 수 있다.

 

 

메서드 재정의: 오버라이딩

오버라이딩에 대해서는 TIL에서 쓴 적이 있지만, 부모의 메서드를 그대로 상속받지 않고 자식에서 재정의하는 형태를 의미한다. 상속 관계에서 발생하며, 메서드 시그니처가 동일해야 한다(이름, 매개변수, 반환형). 부모 클래스의 메소드의 접근 제어자 보다 접근 범위가 좁아서는 안된다. @Override를 붙이지 않아도 오버라이딩은 동작하지만, 대부분 검증을 위해 적어주는 것이 권장된다.

 

다음과 같은 형태가 된다.

class Parent {
	public void show() { System.out.println("차가 달립니다"); }
}

class Child extends Parent {
@Override
	public void show() { System.out.println("벤츠가 달립니다"); } //변경한 내용
}

 

이런 것을 하는 이유는 한 부모가 여러 자식을 두고 있을 때, 메서드가 정의된다면 구현을 자식에 맞게 구체화하기 좋기 위해서다. 위 코드에서 차가 달립니다가 벤츠가 달립니다가 되는 것과 비슷하다. 만약 이렇게 Override를 했음에도 부모의 메서드를 쓰고 싶으면 super.메서드명(); 을 사용하면 된다.

 

 

다형성

여기서는 다형성을 다루었는데, 다형성은 하나의 타입에 여러 객체를 대입할 수 있는 것을 의미한다. 부모 타입에는 모든 자식 객체의 대입이 가능하며, 자식의 경우는 부모 타입으로 자동적으로 변환된다.

 

// 부모
class Animal {
    void speak() {
        System.out.println("...");
    }
}

// 자식들
class Dog extends Animal {
    @Override
    void speak() {
        System.out.println("멍멍");
    }
}

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

// 사용
public class Main {
    public static void main(String[] args) {
        // 부모 타입 변수에 자식 객체 대입 (다형성)
        Animal a1 = new Dog();   // Animal 타입에 Dog 대입
        Animal a2 = new Cat();   // Animal 타입에 Cat 대입

        a1.speak();  // 멍멍  (실제 객체가 Dog라서 Dog의 메서드 실행)
        a2.speak();  // 야옹  (실제 객체가 Cat이라서 Cat의 메서드 실행)

        // 메서드 인자도 부모 타입으로 받으면 여러 자식 전달 가능
        letSpeak(a1);  // 멍멍
        letSpeak(a2);  // 야옹
    }

    static void letSpeak(Animal animal) {
        animal.speak();  // 넘어온 실제 객체(Dog/Cat)의 speak() 호출
    }
}

 

다만, 부모는 자식 타입(객체)을 써도 되지만, 자식(타입 변수)이 부모 객체를 쓰는 건 불가능하다.

 

추상 메서드

추상 메서드란 메서드가 필요하지만 메서드가 무엇을 할지 구체적으로 정하지 못할 때 사용한다. 이 경우 추상 메서드로 먼저 만든 후, 자식에서 Override를 사용하여 메서드의 내용을 구성한다. 

 

다음처럼 사용하면 된다.

public abstract class AbstractClass { //추상 키워드 abstract를 사용한다
    public String abstractField;    
    public abstract void abstractMethod(); //추상 메서드가 된다
}

public class Child extends AbstractClass { //자식에서 추상 메서드를 오버라이드해서 사용
	@Override
    public void abstractMethod(){ System.out.println("추상 메서드 사용입니다."); }
}

 

 

 

인터페이스

인터페이스란 객체의 사용 방법을 정의한 타입을 말한다. 클래스가 갖춰야 할 기능을 정의해둔 설계도나 표준 규격에 가깝다. 이 인터페이스들은 추상 메서드로 채워나가는 것이 일반적이다.

 

인터페이스의 특징이 몇 가지 있다.

  1. 객체 생성 불가(구현되지 않은 추상 메서드)
  2. 생성자 불가
  3. 인터페이스 간 상속 가능
    일반 부모-자식 상속과 달리 여러 상속이 가능하다.
  4. 인터페이스 내의 변수는 public static final이 된다. 그렇기 때문에 인터페이스 이름만으로 접근이 가능해진다.

대체로 인터페이스의 경우 이름을 I로 시작하는 것이 관례다.

public interface IInterface {
	public void IInterfaceMethod(); //추상 메서드를 사용하는 것이 일반적이다.
}

public class interfaceChild implements IInterface { //implements 키워드로 인터페이스의 구현이 가능
	public void IInterfaceMethod(){
    	System.out.println("인터페이스 사용입니다.")
    }
}

 

 

인터페이스 메서드 선언

인터페이스 메서드는 세 가지 종류가 존재한다. 추상 메서드(abstract), 디폴트 메서드(default), 정적 메서드(static)가 있다. 기본적으로 인터페이스 내의 추상 메서드의 경우 abstract 선언을 하지 않아도 자동으로 abstract로 처리된다.

 

디폴트 메서드(default)의 경우 인터페이스 안에 구현이 있는 메서드를 넣을 때 쓴다.

    default void interfaceDefaultMethod() {
        System.out.println("인터페이스 디폴트 메서드입니다.");
    }

 

 

정적 메서드(static)의 경우 인터페이스를 통해 호출된다.

static void interfaceStaticMethod() {
        System.out.println("인터페이스 static 메서드입니다.");
 }

 

default와 static은 언뜻 비슷해보이지만 차이가 있다.

구분 default 메서드 static 메서드
호출 객체로 호출된다. obj.메서드(); 메인 등에서 인터페이스 이름으로 호출된다. 인터페이스명.메서드();
인스턴스 접근 있음 없음
오버라이드 구현 클래스에서 가능 불가능

 

즉 static은 인터페이스 자체에 귀속되고, default는 구현 클래스의 인스턴스를 통해 호출된다. 일반적인 메서드 사용과 비슷한건 default라고 할 수 있다.

 

인터페이스 상속

인터페이스끼리는 다중 상속이 가능하다. 다음 예제를 보자.

 

public interface InterfaceA {
    public void methodA();
}

public interface InterfaceB {
    public void methodB();
}

public interface InterfaceC extends InterfaceA, interfaceB {
	public void methodC();	//methodA,B 또한 상속받는다.
}

 

이 상태에서 클래스에서 InterfaceC를 implemetns하면 override를 통해 methodA(), methodB(), methodC()를 구현화할 수 있다.