Java 객체지향 핵심 문법 정리

프로그래밍 언어는 기계어에서 시작해 점진적으로 발전해왔습니다. 기계어는 0과 1로 이루어진 이진수 명령어로 구성되며, 하드웨어가 직접 해석할 수 있는 최저수준 언어입니다. 이후 등장한 어셈블리어는 인간이 이해하기 쉬운 기호를 도입했지만 여전히 기계 구조에 밀접하게 연결되어 있었습니다. 고급 언어는 이러한 기계적 세부사항을 숨기고 더 높은 수준의 추상화를 제공했으며, 특히 1960년대 구조적 프로그래밍 패러다임은 제어 구조의 체계화와 모듈성을 강조했습니다. 객체지향 패러다임은 이를 한 단계 발전시켜, 문제 영역에 존재하는 실체를 직접적으로 표현하는 것을 목표로 합니다.

Java의 핵심 철학은 객체지향에 있습니다. 절차지향과의 차이를 예로 들면, "장소 A에서 B로 이동한다"는 과제를 절차지향으로 접근하면 "엔진 시동 → 기어 변속 → 가속 페달 → 방향 조작" 등 세부 단계를 나열하게 니다. 반면 객체지향은 "탈 것(Vehicle) 객체를 생성하고 목적지를 전달"하는 방식으로, 구체적인 이동 메커니즘은 해당 객체 내부에 캡슐화됩니다. 이처럼 객체지향은 속성(상태)기능(행위)을 하나의 단위로 묶는 클래스(Class)를 중심으로 설계됩니다.

1. 클래스와 객체의 개념

객체(Object)는 소프트웨어로 모델링하려는 대상을 의미하며, 필드(상태)메서드(행위)로 구성됩니다. 클래스(Class)는 동일한 유형의 객체들에 대한 설계도이며, 객체는 클래스의 구체적 실현체(Instance)입니다.

2. 클래스 정의와 객체 생성

다음은 동물 정보를 표현하는 클래스와 이를 활용하는 예제입니다.

public class Creature {
    // 인스턴스 변수
    private int lifespan;
    private String species;
    
    // 기본 생성자
    public Creature() {
        System.out.println("기본 생성자 호출됨");
    }
    
    // 매개변수 생성자
    public Creature(int lifespan) {
        this.lifespan = lifespan;
        System.out.println("매개변수 생성자 호출됨: " + lifespan);
    }
    
    // 접근자와 설정자
    public int getLifespan() {
        return lifespan;
    }
    
    public void setLifespan(int lifespan) {
        this.lifespan = lifespan;
    }
    
    public String getSpecies() {
        return species;
    }
    
    public void setSpecies(String species) {
        this.species = species;
    }
    
    // 일반 메서드
    public void displayInfo() {
        int bonus = 500;  // 지역 변수, 초기화 필수
        System.out.println("종: " + species + ", 수명: " + lifespan + "년");
    }
}

객체를 생성하고 활용하는 코드:

public class CreatureDemo {
    public static void main(String[] args) {
        // 객체 생성 및 생성자 호출
        Creature cat = new Creature();
        Creature dog = new Creature(15);
        
        // 메서드 호출
        cat.setSpecies("페르시안 고양이");
        cat.setLifespan(12);
        
        System.out.println(cat.getSpecies() + "의 예상 수명: " + cat.getLifespan());
        cat.displayInfo();
    }
}

3. 핵심 키워드와 접근 제어

키워드의미
this현재 인스턴스 참조, 필드와 매개변수 구분
static클래스 수준 멤버, 모든 인스턴스 공유
package네임스페이스 계층, 충돌 방지
import외부 패키지 클래스 참조

접근 제한자는 다음 네 가지가 있습니다: private(클래스 내부), default(동일 패키지), protected(패키지 + 상속), public(모든 범위).

4. 상속 메커니즘

Java는 단일 상속만을 허용하며, extends 키워드로 부모 클래스의 특성을 확장합니다.

부모 클래스:

public class Gadget {
    protected int cost;
    
    public Gadget() {
        System.out.println("Gadget 생성자 실행");
    }
    
    public void powerOn() {
        System.out.println("전원 켜짐");
    }
}

자식 클래스:

public class Smartphone extends Gadget {
    private String modelCode;
    
    public Smartphone() {
        // super()가 자동 삽입됨
        System.out.println("Smartphone 생성자 실행");
    }
    
    @Override
    public void powerOn() {
        System.out.println("스마트폰 부팅 중...");
    }
}

실행 검증:

public class InheritanceTest {
    public static void main(String[] args) {
        Gadget item = new Smartphone();  // 업캐스팅
        item.powerOn();  // "스마트폰 부팅 중..." 출력
    }
}

5. 메서드 재정의(Override)와 다중 정의(Overload)

재정의(Override)는 상위 클래스의 메서드를 하위 클래스에서 새로 구현하는 것이고, 다중 정의(Overload)는 동일한 이름으로 매개변수 구성만 다르게 하는 것입니다.

public class Calculator {
    
    public void add(int x, int y) {
        System.out.println("정수 합: " + (x + y));
    }
    
    public void add(int x, double y) {
        System.out.println("혼합 합: " + (x + y));
    }
    
    public void add(double x, int y) {
        System.out.println("혼합 합(역): " + (x + y));
    }
    
    public void add(double x, double y) {
        System.out.println("실수 합: " + (x + y));
    }
}

테스트:

public class CalcTest {
    public static void main(String[] args) {
        Calculator calc = new Calculator();
        calc.add(3, 4);        // 정수, 정수
        calc.add(3, 4.5);      // 정수, 실수
        calc.add(3.5, 4);      // 실수, 정수
        calc.add(3.5, 4.5);    // 실수, 실수
    }
}

6. final 키워드와 Object 클래스

final은 변경 불가능성을 선언합니다. 변수에 적용하면 상수가 되며, 클래스에 적용하면 상속 금지, 메서드에 적용하면 재정의 금지가 됩니다. java.lang.Object는 모든 클래스의 최상위 조상이며, toString(), equals(), hashCode() 등 기본 메서드를 제공합니다.

7. 다형성과 타입 변환

다형성은 세 가지 조건을 만족할 때 성립합니다: 상위-하위 관계, 메서드 재정의, 그리고 상위 참조로 하위 객체 가리키기.

public class PolymorphismDemo {
    public static void main(String[] args) {
        Mammal mammal = new Canine();  // 업캐스팅
        
        // 안전한 다운캐스팅
        if (mammal instanceof Canine) {
            Canine canine = (Canine) mammal;
            canine.bark();
        }
        
        if (!(mammal instanceof Feline)) {
            System.out.println("해당 객체는 Feline 유형이 아님");
        }
    }
}

8. 추상 클래스

abstract로 선언된 클래스는 직 인스턴스화할 수 없으며, 하위 클래스에서 확장되어야 합니다. 추상 메서드를 포함하는 경우, 해당 클래스는 반드시 추상 클래스여야 하고 하위 클래스에서 추상 메서드를 구현(오버라이드)해야 합니다.

9. 인터페이스

인터페이스는 완전한 추상화 단위로, 상수와 추상 메서드의 집합입니다. Java 8 이후 defaultstatic 메서드도 포함 가능해졌습니다.

public interface Flyable {
    double MAX_ALTITUDE = 10000.0;
    
    void takeOff();
    void fly();
    void land();
}

구현체:

public class Drone implements Flyable {
    
    @Override
    public void takeOff() {
        System.out.println("드론 이륙");
    }
    
    @Override
    public void fly() {
        System.out.println("드론 비행 중");
    }
    
    @Override
    public void land() {
        System.out.println("드론 착륙");
    }
}

태그: java OOP Class Object Inheritance

5월 21일 16:51에 게시됨