7일차(1) - 객체지향(다형성,static, final,abstract,interface)

은채의 성장통·2025년 6월 5일

KCC정보통신

목록 보기
9/30

노션정리

객체지향 프로그래밍


4. 다형성 (Polymorphism)

다형성은 하나의 객체가 여러 유형으로 사용될 수 있다는 개념이에요. 자바에서는 상속을 기반으로 동작하며, 부모 클래스로 선언된 변수에 자식 클래스의 인스턴스를 할당할 수 있습니다. 이를 활용하면 코드의 재사용성과 유지보수성이 높아지고, 다양한 객체를 유연하게 사용할 수 있습니다.


4.1 다형적 객체

비유: 부모 클래스를 "컵"이라고 생각해 봅시다. 컵은 "물"도, "주스"도, "커피"도 담을 수 있죠. 컵이 어떤 종류의 액체를 담고 있든지 컵으로써의 기능(잡고 마시는 기능)은 변하지 않습니다.

마찬가지로 Person 타입의 변수는 Student, Teacher, Employee 객체를 담을 수 있습니다. 하지만 Person의 기능을 따르는 방식으로 사용됩니다.

코드 예시

Person p; // p는 Person 타입이므로 하위 객체를 담을 수 있음
p = new Student("허현수", 17, "20160001");
p = new Teacher("허현준", 22, "Java Programming");
p = new Employee("허현정", 23, "교무처");

4.2 Virtual Method Invocation (가상 메서드 호출)

비유: 부모 클래스는 "TV 리모컨"이고, 자식 클래스는 "TV 기기"라고 생각해 봅시다.

리모컨으로 TV를 조작하면, 어떤 TV를 연결했느냐에 따라 TV의 화면이 달라지는 것처럼

자식 객체가 부모 변수에 저장되었을 때 부모가 가진 메서드를 자식이 재정의하면, 실행 시 자식 클래스의 메서드가 호출됩니다.

코드 예시

Person p = new Student("허현수", 17, "20160001");
System.out.println(p.getDetails()); // Student의 getDetails() 실행됨!

p = new Teacher("허현준", 22, "Java Programming");
System.out.println(p.getDetails()); // Teacher의 getDetails() 실행됨!

텍스트 그림

메모리 구조 (Virtual Method Invocation)

                 Person p 참조 변수 (Stack)
                 ┌──────────────────────────┐
                 │  참조값 → Student 객체   │
                 └──────────────────────────┘

                 Heap 메모리
                 ┌──────────────────────────┐
                 │ Student 객체             │
                 │ getDetails() 재정의됨    │ <-- 호출
                 └──────────────────────────┘

4.3 이종 모음 (Heterogeneous Collection)

비유: 다양한 종류의 물건을 담을 수 있는 "가방"이라고 생각해 봅시다.

보통 "가방" 안에는 책, 연필, 노트 같은 같은 종류의 물건이 들어갑니다. 하지만 가방을 범용적으로 만든다면 책, 연필뿐만 아니라 노트북, 물통, 간식도 넣을 수 있죠!

즉, Person[] 배열도 원래는 Person 객체만 담을 수 있지만, 다형성을 이용하면 다양한 객체를 담을 수 있습니다!

코드 예시

Person[] pArr = new Person[4];
pArr[0] = new Person("홍길동", 20);
pArr[1] = new Student("허현수", 17, "20160001");
pArr[2] = new Teacher("허현준", 22, "Java Programming");
pArr[3] = new Employee("허현정", 23, "교무처");

for(Person p : pArr) {
    System.out.println(p.getDetails());
}

텍스트 그림

    pArr 배열 (Person 타입 배열)
    ┌───────────────────┬───────────────────┬───────────────────┬───────────────────┐
    │ pArr[0] (Person) │ pArr[1] (Student) │ pArr[2] (Teacher) │ pArr[3] (Employee) │
    ├───────────────────┼───────────────────┼───────────────────┼───────────────────┤
    │ "홍길동", 20      │ "허현수", 17, 학번 │ "허현준", 22, 과목 │ "허현정", 23, 부서 │
    └───────────────────┴───────────────────┴───────────────────┴───────────────────┘

4.6 다형적 인자 (Polymorphic Argument)

비유: 영화관에서 티켓을 검사할 때를 생각해봅시다!

직원은 “티켓을 가진 사람”이면 누구든지 입장할 수 있도록 합니다.

그런데 영화 티켓을 가진 사람은 일반 관객, VIP, 특별 초대 손님일 수도 있죠!

즉, 다형적 인자를 이용하면 메서드 하나로 다양한 유형의 객체를 처리할 수 있습니다.

코드 예시

public static void printPersonInfo(Person p) {
    System.out.println(p.getDetails()); // 다양한 객체를 처리 가능!
}

printPersonInfo(new Student("허현수", 17, "20001234"));
printPersonInfo(new Teacher("허현준", 22, "Java Programming"));
printPersonInfo(new Employee("허현정", 23, "교무처"));

4.7 instanceof (객체 타입 확인)

💡 비유: 여러 종류의 동물을 관찰할 때, 동물마다 특징이 다르죠!

동물 중 일부는 ‘날개가 있다’ → 새일 가능성이 높음

동물 중 일부는 ‘땅을 파고 다닌다’ → 두더지일 가능성이 높음

이를 컴퓨터적으로 검사하는 방법이 instanceof입니다.

코드 예시

if (p instanceof Student) {
    System.out.println("이 객체는 Student 타입입니다!");
} else if (p instanceof Teacher) {
    System.out.println("이 객체는 Teacher 타입입니다!");
}

텍스트 그림

p가 참조하는 객체 타입 체크

    p instanceof Student   → true ✅
    p instanceof Teacher   → false ❌

4.8 형변환 Casting

형변환은 부모 타입의 객체를 자식 타입으로 변환하여 자식 클래스의 메서드나 속성에 접근할 수 있도록 하는 과정이에요.

📌 코드 예시

Student stu = (Student) p; // Person 타입을 Student 타입으로 변환
stu.applyForClasses();     // Student 클래스에서 정의한 메서드 호출

📌 텍스트 그림

[ Person p ]
┌──────────┐
│ Student 객체 │  ← (Student) 형변환 가능 ✅
└──────────┘

💡 왜 형변환이 필요한가?

  • pPerson 타입이기 때문에, Person이 제공하는 기능만 사용할 수 있어요.
  • 하지만 p가 실제로 Student 객체를 참조하고 있다면, Student의 기능을 사용하려면 형변환을 해야 합니다!

4.9 instanceof와 함께 사용하는 형변환 (자바 12 이후)

자바 12부터 instanceof를 사용할 때 형변환을 별도로 할 필요 없이 한 번에 처리할 수 있도록 변경되었습니다.

📌 기존 방식 (자바 12 이전)

if (p instanceof Student) {
    Student stu = (Student) p; // 형변환 필요
    stu.applyForClasses();
}

📌 새로운 방식 (자바 12 이후)

if (p instanceof Student stu) { // stu 변수를 자동으로 형변환하여 사용 가능!
    System.out.println("************ Student Info ***********");
    stu.applyForClasses(); // 형변환 없이 직접 메서드 호출 가능!
}

💡 이 방식의 장점

  • instanceof 검사와 형변환을 한 줄로 처리할 수 있어 코드가 더 깔끔해집니다.
  • 형변환 후 별도의 변수를 선언하지 않아도 stu를 즉시 사용할 수 있습니다.

📌 텍스트 그림

[ Person p ]
┌──────────┐
│ Student 객체 │  ← `instanceof Student stu`로 자동 형변환 ✅
└──────────┘

5. static – 클래스 기반의 공유 메커니즘

  • 자바에서 static 키워드는 객체 간 공유되는 변수와 메서드를 정의할 때 사용됩니다.
  • 즉, static이 붙은 변수나 메서드는 모든 객체가 동일한 값을 공유하며, 클래스 이름으로 직접 접근할 수 있습니다.

📌 핵심 개념

  1. static 변수: 모든 객체가 공유하는 값 → 객체 없이 클래스 이름으로 접근 가능
  2. static 메서드: 객체 없이 호출 가능 → non-static 변수 및 메서드를 직접 참조할 수 없음
  3. static 초기화 블록: 클래스 로드 시 한 번 실행 → 프로그램에서 한 번만 실행됨
  4. 싱글톤 패턴: static을 활용해 인스턴스를 하나만 유지하는 설계 패턴

5.1 static 변수 – 공유되는 변수

일반 변수(non-static)와 static 변수 비교

일반 변수: 객체마다 각각 다른 값을 가짐

static 변수: 모든 객체가 공통된 값을 공유

코드 예시

package static_;

public class Count {
    public int a = 0; // 일반 변수
    public static int b = 0; // static 변수
}

static 변수 활용

package static_;

public class StaticVarExample {
    public static void main(String[] args) {
        Count c1 = new Count();
        c1.a++; // 객체마다 별개의 변수 증가
        c1.b++; // 모든 객체가 공유하는 static 변수 증가

        Count c2 = new Count();
        c2.a++; // c2의 인스턴스 변수 증가
        c2.b++; // static 변수 증가 (모든 객체 공유)

        Count.b++; // 클래스 이름으로 직접 접근 가능!

        System.out.println("c1.b: " + c1.b);
        System.out.println("c2.b: " + c2.b);
        System.out.println("Count.b: " + Count.b);
    }
}

텍스트 그림 – 메모리 구조

[ Heap 영역 (각 객체 별도 저장) ]
┌─────────────┬─────────────┐
│Count객체(c1)│Count 객체(c2)│
│ a = 1       │ a = 1       │
└─────────────┴─────────────┘

[ Static 영역 (모든 객체 공유) ]
┌───────────────┐
│ static int b = 3 │  <-- 모든 객체가 공유 ✅
└───────────────┘

결론: b는 모든 객체가 공유하는 값이므로 Count.b++를 호출하면 모든 객체에 영향을 줍니다!


5.2 static 메서드 – 인스턴스 없이 호출 가능

  • static 메서드는 객체를 생성하지 않고 호출할 수 있습니다.
  • 하지만 non-static 멤버(변수 & 메서드)를 직접 참조할 수 없습니다!

📌 코드 예시

package static_;

public class Count {
    public int a = 0;
    public static int b = 0;

    public static int doIt() {
        // return ++a; // ❌ 오류 발생 (non-static 변수 접근 불가)
        return ++b;   // ✅ static 변수 접근 가능
    }
}

📌 static 메서드 호출

package static_;

public class StaticMethodExample {
    public static void main(String[] args) {
        System.out.println("Count.doIt(): " + Count.doIt()); // 객체 없이 호출 가능
        System.out.println("Count.doIt(): " + Count.doIt());
        System.out.println("Count.doIt(): " + Count.doIt());
    }
}

📌 텍스트 그림

  [ Count 클래스 ]
┌───────────────────┐
│ static int b      │
│ static doIt() {   │
│    return ++b;    │ ← static 변수만 접근 가능 ✅
│ }                 │
└───────────────────┘

static 메서드는 non-static 변수(a)를 직접 접근할 수 없습니다!


5.3 static 초기화 블록 – 클래스 로드 시 한 번 실행

  • static 블록은 클래스가 로딩될 때 한 번만 실행됩니다.
  • 프로그램에서 단 한 번만 실행되어야 하는 설정 값 초기화에 사용됩니다.

📌 코드 예시

package static_;

public class StaticInit {
    static {
        System.out.println("static initializer가 수행됨!");
    }

    public StaticInit() {
        System.out.println("Constructor 호출됨!");
    }
}

📌 static 블록 실행 확인

package static_;

public class StaticInitExample {
    public static void main(String[] args) {
        StaticInit s1 = new StaticInit(); // 객체 생성 시 생성자 실행
        StaticInit s2 = new StaticInit();
        System.out.println("main() 메서드 종료");
    }
}

📌 출력 결과

static initializer가 수행됨! ✅ (클래스 로딩 시 한 번 실행)
Constructor 호출됨!
Constructor 호출됨!
main() 메서드 종료

💡 static 블록은 main()보다 먼저 실행되며, 클래스 로딩 시 한 번만 실행됩니다!


5.4 Singleton Design Pattern – 하나의 인스턴스 유지

  • 싱글톤 패턴은 static을 활용해 인스턴스를 하나만 유지하는 패턴입니다!

📌 코드 예시

package static_;

public class Singleton {
    private static Singleton instance = new Singleton(); // static 인스턴스

    private Singleton() {
        System.out.println("instance created!");
    }

    public static Singleton getInstance() { // static 메서드
        return instance; // 모든 요청에서 동일한 객체 반환
    }
}

📌 싱글톤 패턴 실행

package static_;

public class SingletonExample {
    public static void main(String[] args) {
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();

        System.out.println(s1);
        System.out.println(s1 == s2); // true! ✅
        System.out.println(s2);
    }
}

📌 텍스트 그림

싱글톤 패턴 메모리 구조

Singleton 클래스  │ private static instance     │ ← 모든 요청에서 같은 객체 반환 ✅
Singleton.getInstance() 호출
s1: ┌───────────┐
    │ Singleton 객체 │  ← 동일한 객체! ✅
    └───────────┘
s2: ┌───────────┐
    │ Singleton 객체 │  ← 동일한 객체! ✅
    └───────────┘

싱글톤 패턴을 사용하면 getInstance()를 호출할 때마다 동일한 객체를 반환합니다!


profile
인생 별거 없어

0개의 댓글