생성자
클래스의 객체가 생성될 때 호출되는 특별한 메서드로, 객체를 초기화하는 데 사용. 생성자는 클래스 이름과 동일해야 하며, 반환 타입이 없음(예: void조차 쓰지 않음).
특징
자동 호출:
- 객체가 생성될 때 자동으로 호출됨.
오버로딩 가능:
- 생성자도 매개변수의 개수나 타입을 달리하여 여러 개 선언 가능(오버로딩).
기본 생성자:
- 생성자를 하나도 선언하지 않으면, 컴파일러가 매개변수가 없는 기본 생성자를 자동으로 생성함.
역할
객체 초기화:
- 객체 생성 시 필드를 초기화하거나, 필요한 작업을 수행.
코드 간결화:
- 초기화 작업을 한 번에 처리하여, 이후 작업에서 간결한 코드 유지.
생성자 체이닝 (생성자가 다른 생성자를 호출할 수 있다) : 객체 초기화를 단순화하고 중복을 줄이는 데 매우 유용한 패턴.
생성자가 없는 경우
- 생성자를 선언하지 않으면, 컴파일러가 매개변수가 없는 기본 생성자를 자동으로 추가함.
생성자와 메서드의 차이
특징 | 생성자(Constructor) | 메서드(Method) |
반환 타입 | 없음 | 반환 타입 명시 필요 (void 포함) |
호출 시점 | 객체 생성 시 자동 호출 | 명시적으로 호출해야 함 |
이름 | 클래스 이름과 동일 | 원하는 이름으로 정의 가능 |
역할 | 객체 초기화 | 동작(Behavior) 구현 |
결론
- 생성자는 객체를 초기화하고, 초기 상태를 설정하는 데 사용됨.
- 매개변수 생성자와 기본 생성자를 함께 사용하면 유연한 객체 생성이 가능.
- 객체 지향 프로그래밍에서 객체 생성 및 초기화에 중요한 역할을 담당.
상속
기존 클래스(부모 클래스, 슈퍼 클래스)의 속성과 메서드를 새로운 클래스(자식 클래스, 서브 클래스)에서 물려받아 사용하는 기능,계층적 구조를 설계할 수 있음.
상속의 주요 개념
- 부모 클래스 (Super Class):
- 속성과 메서드가 정의되어 있는 기존 클래스.
- 자식 클래스에 의해 상속받을 수 있음.
- 자식 클래스 (Sub Class):
- 부모 클래스를 확장(extends)하여 새로 정의된 클래스.
- 부모 클래스의 속성과 메서드를 물려받아 사용할 수 있음.
- 부모 클래스의 기능을 그대로 사용하거나 재정의(오버라이딩) 가능.
- 키워드:
- extends: 상속을 정의할 때 사용.
- super: 부모 클래스의 멤버를 참조하거나 호출할 때 사용.
상속의 특징
- 단일 상속:
- Java에서는 클래스 간에 단일 상속만 지원함.
- 하나의 자식 클래스는 하나의 부모 클래스만 상속 가능.
- 다중 상속을 지원하지 않는 이유: 다이아몬드 문제(모호성 문제) 방지.
- Object 클래스:
- 모든 클래스는 자동으로 Object 클래스를 상속받음.
- Object 클래스는 toString(), hashCode(), equals()와 같은 메서드를 제공.
- 재정의(Overriding):
- 자식 클래스는 부모 클래스의 메서드를 재정의하여 구현 가능.
- 재정의 시 @Override 애너테이션을 붙이면 컴파일 타임에 확인 가능.
- super 키워드:
- 부모 클래스의 필드와 메서드를 참조하거나, 부모 클래스의 생성자를 호출할 때 사용.
- **super()**를 통해 부모 클래스의 생성자를 호출 가능(생성자의 첫 줄에서만 호출 가능).
상속의 장점
- 다형성(Polymorphism):
- 상속을 활용하면 부모 클래스를 참조해 자식 클래스의 객체를 다룰 수 있음.
- 코드의 유연성과 확장성 증가.
상속의 단점
- 강한 결합:
- 부모 클래스와 자식 클래스 간의 강한 의존성 때문에, 부모 클래스를 변경하면 자식 클래스에도 영향을 줄 수 있음.
- 복잡성 증가:
- 상속 계층이 깊어질수록 코드가 복잡해지고 디버깅이 어려워짐.
- 오용 가능성:
- 상속은 "is-a" 관계(자식 클래스는 부모 클래스의 일종이어야 함)에 적합해야 함.
- 잘못된 관계에서 사용하면 구조가 비논리적일 수 있음.
메서드 오버라이딩
자식 클래스에서 부모 클래스의 메서드를 재정의하는 예제.
// 부모 클래스 class Animal { void sound() { System.out.println("Animals make sounds."); } } // 자식 클래스 class Cat extends Animal { @Override void sound() { System.out.println("Meow!"); } } // 메인 클래스 public class OverrideExample { public static void main(String[] args) { Cat cat = new Cat(); cat.sound(); // "Meow!" 출력 (부모 메서드 재정의) } } |
상속과 메서드 오버라이딩을 활용한 다형성 예제.
// 부모 클래스 class Shape { void draw() { System.out.println("Drawing a shape"); } } // 자식 클래스 1 class Circle extends Shape { @Override void draw() { System.out.println("Drawing a circle"); } } // 자식 클래스 2 class Rectangle extends Shape { @Override void draw() { System.out.println("Drawing a rectangle"); } } // 메인 클래스 public class PolymorphismExample { public static void main(String[] args) { Shape shape1 = new Circle(); // 부모 타입 참조, 자식 객체 생성 Shape shape2 = new Rectangle(); shape1.draw(); // "Drawing a circle" 출력 shape2.draw(); // "Drawing a rectangle" 출력 } } |
추상 클래스와 상속을 활용한 예제.
// 추상 클래스 abstract class Vehicle { abstract void run(); // 추상 메서드 void stop() { System.out.println("Vehicle stopped."); } } // 자식 클래스 class Car extends Vehicle { @Override void run() { System.out.println("Car is running."); } } // 메인 클래스 public class AbstractExample { public static void main(String[] args) { Vehicle car = new Car(); // 추상 클래스 타입으로 자식 객체 참조 car.run(); // "Car is running." car.stop(); // "Vehicle stopped." } } |
상속 vs. 포함(Composition)
- 상속: "is-a" 관계를 나타냄. (예: Dog is an Animal)
- 포함: "has-a" 관계를 나타냄. (예: Car has an Engine)
결론
상속은 객체지향 프로그래밍의 핵심 기능으로, 코드 재사용성과 계층적 설계를 지원하며 다형성을 구현할 수 있음. 그러나 남용 시 코드 결합도가 높아지고 유지보수가 어려워질 수 있으므로, 적절한 "is-a" 관계를 기반으로 설계하는 것이 중요함.
오버라이딩(Overriding)과 오버로딩(Overloading)의 차이점
- 오버라이딩은 부모의 것을 재정의하는 것이고,
- 오버로딩은 이름은 같지만 다양한 매개변수로 동작을 다르게 하는 것임.
1. 오버라이딩(Overriding)
상속 관계에서 부모 클래스의 메서드를 자식 클래스에서 재정의하여 사용하는 것.
특징
- 메서드 이름, 매개변수, 반환 타입이 부모 메서드와 동일해야 함.
- 자식 클래스에서 부모의 동작을 변경하거나 확장하기 위해 사용.
- 부모 메서드의 접근 제어자보다 더 restrictive(제한적인) 접근 제어자를 사용할 수 없음. (e.g., 부모가 public이면 자식도 public이어야 함.)
- @Override 애너테이션을 사용하여 오버라이딩 여부를 명시하면 컴파일러가 확인.
2. 오버로딩(Overloading)
같은 이름의 메서드를 여러 개 정의하되, 매개변수의 타입, 개수, 순서를 다르게 설정하는 것.
특징
- 메서드 이름은 같지만, 매개변수 리스트가 달라야 함.
- 반환 타입은 상관없음. (메서드 호출 시에는 매개변수 리스트로 구분하기 때문.)
- 상속과는 무관하게, 같은 클래스 내에서 정의.
비교
구분 | 오버라이딩 (Overriding) | 오버로딩 (Overloading) |
사용 목적 | 부모 메서드를 재정의 | 같은 이름으로 다양한 메서드 정의 |
상속 여부 | 반드시 상속 관계에서 사용 | 상속 관계가 필요 없음 |
메서드 이름 | 부모 메서드와 동일 | 동일 |
매개변수 | 부모 메서드와 동일 | 매개변수의 개수, 타입, 순서가 다름 |
반환 타입 | 부모 메서드와 동일 | 상관없음 |
애너테이션 사용 | @Override 애너테이션 사용 가능 | 애너테이션 사용 안 함 |