본문 바로가기
Programming

JAVA 클래스 고급

by 나무수피아는 지식의 가지를 뻗어가는 공간입니다. 2025. 11. 23.
반응형

📘 클래스 고급 (Advanced Class)

이번 강의에서는 내부 클래스(Inner Class), 익명 클래스(Anonymous Class), final / static 키워드, 그리고 Object 클래스의 핵심 메서드들을 깊이 있게 살펴봅니다. 자바의 객체 지향 프로그래밍에서 이 네 가지 개념은 매우 중요하며, 복잡한 구조를 깔끔하게 설계하는 데 큰 도움을 줍니다. 각 항목별로 개념 설명과 함께 구체적인 예제 코드를 통해 이해도를 높여 보겠습니다.


📌 목차

  1. 내부 클래스(Inner Class)
  2. 익명 클래스 (Anonymous Class)
  3. final, static 키워드 정리
  4. Object 클래스 주요 메서드

🔹 1. 내부 클래스 (Inner Class)

내부 클래스란 클래스 안에 정의된 클래스를 말합니다. 내부 클래스는 외부 클래스의 멤버 변수와 메서드에 쉽게 접근할 수 있어, 복잡한 기능을 모듈화 하거나 외부 클래스와 밀접한 관계를 가진 코드를 작성할 때 매우 유용합니다. 또한, 내부 클래스는 크게 네 가지 종류로 나뉩니다: 인스턴스 내부 클래스, 정적 내부 클래스(static nested class), 지역 클래스(local class), 그리고 익명 클래스(anonymous class)가 있습니다.

가장 기본적인 인스턴스 내부 클래스 예제를 살펴보겠습니다. 아래 예제에서 내부 클래스 Inner는 외부 클래스 Outer의 private 필드 msg에 직접 접근합니다.

public class Outer {
    private String msg = "Hello from Outer!";

    class Inner {
        void showMessage() {
            System.out.println(msg); // 외부 클래스 필드 접근 가능
        }
    }

    public static void main(String[] args) {
        Outer outer = new Outer();
        Inner inner = outer.new Inner();
        inner.showMessage();
    }
}

출력:
Hello from Outer!

위 예제에서 보듯이, 내부 클래스는 외부 클래스 인스턴스가 있어야 생성할 수 있으며, 내부 클래스는 외부 클래스의 private 필드도 마음껏 접근할 수 있습니다. 이런 특성 덕분에 내부 클래스는 외부 클래스와의 밀접한 협력이 필요한 경우 매우 적합합니다.

또한, 내부 클래스는 정적(static)으로 선언할 수도 있습니다. 이 경우 정적 내부 클래스는 외부 클래스의 인스턴스 없이도 사용할 수 있으며, 주로 외부 클래스와 논리적으로 묶이지만 외부 인스턴스 상태에 의존하지 않는 경우에 사용됩니다.

public class OuterStatic {
    static class StaticInner {
        void show() {
            System.out.println("Static Inner Class");
        }
    }

    public static void main(String[] args) {
        StaticInner si = new StaticInner();
        si.show();
    }
}

출력:
Static Inner Class

정적 내부 클래스는 외부 클래스의 인스턴스 변수에 접근할 수 없지만, 외부 클래스의 static 멤버에는 접근 가능합니다. 이렇게 내부 클래스의 특성을 이해하면, 코드 구조를 좀 더 체계적이고 명확하게 만들 수 있습니다.


🔹 2. 익명 클래스 (Anonymous Class)

익명 클래스는 이름이 없는 일회성 클래스입니다. 주로 인터페이스나 추상 클래스를 구현해야 할 때, 별도의 클래스 파일을 만들지 않고 즉석에서 클래스를 정의하고 인스턴스를 생성할 때 사용됩니다. 익명 클래스는 코드가 간결해지고 특정 기능을 일회성으로 구현할 때 유용하지만, 복잡한 로직에는 적합하지 않습니다.

다음 예제는 인터페이스 Greet를 익명 클래스로 구현한 모습입니다.

interface Greet {
    void sayHello();
}

public class AnonymousExample {
    public static void main(String[] args) {
        Greet greet = new Greet() {
            public void sayHello() {
                System.out.println("안녕하세요! 익명 클래스입니다.");
            }
        };
        greet.sayHello();
    }
}

출력:
안녕하세요! 익명 클래스입니다.

위 코드에서 new Greet() { ... } 부분이 익명 클래스 정의이며, 클래스 이름이 없고 즉시 sayHello() 메서드를 구현했습니다. 익명 클래스는 이벤트 처리, 스레드 생성 등 간단한 콜백이나 일회성 처리에 널리 활용됩니다.

참고로, 자바 8부터는 람다(Lambda) 표현식이 도입되어 익명 클래스를 대체하는 경우가 많아졌지만, 람다로 표현할 수 없는 복잡한 익명 클래스는 여전히 사용됩니다.


🔹 3. final, static 키워드 정리

자바에서 finalstatic 키워드는 클래스 설계와 변수, 메서드 선언에 있어서 매우 중요한 역할을 합니다. 각각의 의미와 사용법을 명확히 이해하는 것이 중요합니다.

키워드 의미 사용 예시
final 수정 불가 (상수 선언, 메서드 오버라이딩 금지, 클래스 상속 금지) final int MAX = 10;
final void method() { }
static 클래스 소속 (인스턴스 생성과 무관하게 공유되는 멤버) static int count = 0;
static void print() { }

final 키워드는 변수, 메서드, 클래스에 사용할 수 있습니다. 변수에 붙으면 값이 한 번 초기화되면 변경 불가한 상수가 되고, 메서드에 붙으면 서브클래스에서 해당 메서드를 오버라이딩할 수 없으며, 클래스에 붙으면 그 클래스를 상속할 수 없습니다. 이로써 프로그램의 안정성과 예측 가능성을 높일 수 있습니다.

static 키워드는 변수나 메서드가 인스턴스가 아닌 클래스 자체에 소속됨을 의미합니다. 따라서 모든 인스턴스가 공유하는 값이나 동작을 정의할 때 사용하며, 인스턴스 생성 없이 클래스 이름으로 직접 접근할 수 있습니다. 예를 들어, 유일한 값으로 모든 객체가 공유해야 하는 카운터나 공용 헬퍼 메서드 등이 있습니다.

아래 예시는 finalstatic 키워드를 함께 사용한 간단한 클래스입니다.

public class Constants {
    public static final double PI = 3.14159;

    public final void show() {
        System.out.println("This method cannot be overridden.");
    }
}

위처럼 public static final 조합은 자바에서 상수 선언에 가장 많이 쓰이며, Math.PI처럼 표준 라이브러리에도 광범위하게 적용되어 있습니다.


🔹 4. Object 클래스 주요 메서드

자바의 모든 클래스는 기본적으로 Object 클래스를 상속합니다. Object 클래스에는 모든 객체가 가지는 기본적인 메서드들이 정의되어 있으며, 이를 오버라이딩하여 클래스의 동작을 세밀하게 제어할 수 있습니다. 자주 사용하는 핵심 메서드는 다음과 같습니다.

  • equals(Object obj): 두 객체가 논리적으로 같은지 비교합니다. 기본 구현은 참조 주소 비교지만, 필요에 따라 의미 기반 비교를 위해 오버라이딩합니다.
  • toString(): 객체를 문자열로 표현합니다. 디버깅이나 로그 출력 시 매우 유용하며, 기본 구현은 클래스 이름과 해시코드만 표시합니다.
  • hashCode(): 해시 기반 자료구조(예: HashMap, HashSet)에서 객체를 효율적으로 관리하기 위해 사용됩니다. equals()와 함께 재정의하는 것이 중요합니다.

다음은 Person 클래스에서 위 세 메서드를 오버라이딩한 예제입니다.

class Person {
    String name;

    Person(String name) {
        this.name = name;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) return true;
        if (obj == null || getClass() != obj.getClass()) return false;
        Person person = (Person) obj;
        return name != null ? name.equals(person.name) : person.name == null;
    }

    @Override
    public String toString() {
        return "이름: " + name;
    }

    @Override
    public int hashCode() {
        return name != null ? name.hashCode() : 0;
    }
}

public class ObjectExample {
    public static void main(String[] args) {
        Person p1 = new Person("Tom");
        Person p2 = new Person("Tom");
        Person p3 = new Person("Jerry");

        System.out.println("p1.equals(p2): " + p1.equals(p2));  // true
        System.out.println("p1.equals(p3): " + p1.equals(p3));  // false
        System.out.println("p1.toString(): " + p1.toString());  // 이름: Tom
        System.out.println("p1.hashCode(): " + p1.hashCode());  // Tom의 해시코드 값
        System.out.println("p2.hashCode(): " + p2.hashCode());  // 동일한 이름이므로 같은 해시코드
    }
}

위 예제는 equals()를 논리적 비교 기준인 name 필드 기준으로 오버라이딩했습니다. hashCode()도 같은 필드를 기반으로 구현하여, 동일한 이름을 가진 두 객체가 해시 기반 컬렉션에서 제대로 동작하도록 했습니다. toString()은 객체 내용을 사람이 읽기 쉽게 문자열로 반환합니다.

이처럼 Object 클래스의 메서드를 적절히 재정의하면, 자바 컬렉션과 디버깅 환경에서 클래스 사용성을 크게 향상할 수 있습니다. 특히 equals()hashCode()는 함께 오버라이딩하는 것을 잊지 말아야 합니다. 그렇지 않으면 예상치 못한 동작을 일으킬 수 있습니다.


이번 강의에서는 자바 클래스 설계의 고급 주제인 내부 클래스와 익명 클래스, 그리고 finalstatic 키워드, 마지막으로 Object 클래스의 핵심 메서드를 살펴봤습니다. 각각의 개념과 사용법을 잘 이해하면 복잡한 자바 애플리케이션에서도 효율적이고 견고한 구조를 설계할 수 있습니다. 다음 강의에서는 이 내용을 바탕으로 디자인 패턴이나 고급 객체 지향 개념으로 나아가 보겠습니다.

반응형

'Programming' 카테고리의 다른 글

JAVA 네트워크 프로그래밍  (43) 2025.11.25
JAVA 쓰레드와 동시성  (62) 2025.11.24
JAVA 날짜와 시간 처리  (35) 2025.11.22
JAVA 파일 입출력 (File I/O)  (54) 2025.11.21
JAVA 문자열 처리  (58) 2025.11.20