1. 상속과 다형성
1.1 상속이란?
상속은 부모 클래스의 필드와 메서드를 자식 클래스가 물려받아 사용할 수 있는 기능입니다. 이를 통해 코드의 중복을 줄이고 유지보수를 용이하게 만들 수 있습니다.
✅ extends 키워드를 사용하여 부모 클래스를 상속받습니다.
class Animal {
void cry() {
System.out.println("동물이 소리를 냅니다.");
}
}
class Dog extends Animal {
void cry() { // 메서드 오버라이딩
System.out.println("멍멍!");
}
}
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.cry(); // "멍멍!"
}
}
Dog 클래스는 Animal을 상속받아 cry() 메서드를 재정의(오버라이딩)합니다.
1.2 상속의 특징
- 부모 클래스의 필드와 메서드를 상속받아 사용 가능
- 코드 중복을 방지하여 유지보수가 쉬워짐
- 자식 클래스는 부모 클래스의 생성자를 호출하여 초기화 가능 (super 키워드 사용)
- 자바에서는 다중 상속이 불가능하며, 단일 상속만 지원됨
자바는 다중 상속을 허용하지 않기 때문에 다음 코드는 불가능합니다.
class A {}
class B {}
class C extends A, B {} // 오류 발생
1.3 생성자와 상속
부모 클래스의 생성자는 상속되지 않지만, 자식 클래스에서 super()를 사용해 호출할 수 있습니다.
class Animal {
String name;
Animal(String name) {
this.name = name;
}
}
class Dog extends Animal {
Dog(String name) {
super(name); // 부모 생성자 호출
}
}
super(name);을 호출하지 않으면 기본 생성자가 실행되며, 기본 생성자가 없으면 오류가 발생합니다.
1.4 private 멤버는 상속되지 않음
부모 클래스의 private 멤버는 직접 접근할 수 없고, getter/setter 메서드를 통해 접근해야 합니다.
class Parent {
private int secret = 100;
public int getSecret() {
return secret;
}
}
class Child extends Parent {
void printSecret() {
System.out.println(getSecret()); // private 변수 직접 접근 불가
}
}
1.5 메서드 오버라이딩(Method Overriding)
부모 클래스에서 상속받은 메서드를 자식 클래스에서 재정의(덮어쓰기) 하는 것
즉, 기존 기능을 유지하면서 새로운 방식으로 동작하도록 변경하는 것을 의미합니다.
✅ 메서드 이름, 매개변수, 리턴 타입이 부모와 동일해야 함
✅ 접근 제어자는 부모보다 넓거나 같아야 함 (public → protected 가능, private 불가능)
✅ @Override 어노테이션을 사용해 컴파일러가 제대로 오버라이딩했는지 확인 가능
✅ 런타임 시점에 어떤 메서드를 실행할지 결정됨 (동적 바인딩)
class Parent {
void show() {
System.out.println("부모 클래스");
}
}
class Child extends Parent {
@Override
void show() {
System.out.println("자식 클래스");
}
}
오버라이딩은 부모 클래스의 메서드를 자식 클래스에서 변경하고 싶을 때 사용합니다
(예: toString(), equals() 등의 메서드는 기본적으로 오버라이딩된다)
접근 제어자 규칙:
public | public |
protected | protected, public |
default (생략) | default, protected, public |
private | 오버라이딩 불가 |
1.6 업캐스팅(Upcasting) & 다운캐스팅(Downcasting)
업캐스팅(Upcasting)
Animal myDog = new Dog();
myDog.makeSound(); // "멍멍!"
myDog 변수는 Animal 타입이지만, Dog 객체를 참조하기 때문에
오버라이딩된 makeSound()가 실행됩니다. (다형성 활용)
다운캐스팅(Downcasting)
부모 타입 변수를 자식 타입으로 변환 (명시적 형변환 필요)
Animal myAnimal = new Dog();
Dog myDog = (Dog) myAnimal;
myDog.bark();
myAnimal은 Animal 타입이므로,
강제 변환을 통해 Dog 타입의 기능(bark())을 사용 가능합니다
⚠️ 잘못된 다운캐스팅은 ClassCastException 오류 발생 가능
Animal myAnimal = new Animal();
Dog myDog = (Dog) myAnimal; // 오류 발생
해결 방법: instanceof 키워드로 확인 후 변환
if (myAnimal instanceof Dog) {
Dog myDog = (Dog) myAnimal;
}
1.7 super 키워드와 super() 생성자 호출
✅ 부모 클래스의 필드, 메서드 또는 생성자를 참조할 때 사용
class Parent {
void sayHello() {
System.out.println("Hello from Parent");
}
}
class Child extends Parent {
void sayHello() {
super.sayHello(); // 부모 메서드 호출
System.out.println("Hello from Child");
}
}
super.sayHello();를 호출하면 부모의 메서드 실행 후 자식의 코드 실행
1.8 final 키워드
✅ 변경할 수 없는(고정된) 값, 메서드, 클래스를 정의할 때 사용
🔹 final 변수 (상수)
class Example {
final int VALUE = 100;
}
final 변수는 값 변경 불가
🔹 final 메서드 (오버라이딩 금지)
class Parent {
final void show() {
System.out.println("부모 클래스");
}
}
class Child extends Parent {
// void show() { // 오류 발생! final 메서드는 오버라이딩 불가
// System.out.println("자식 클래스");
// }
}
🔹 final 클래스 (상속 금지)
final class Parent {}
// class Child extends Parent {} // 오류 발생! final 클래스는 상속 불가
2. Object 클래스와 주요 메서드
모든 클래스는 기본적으로 Object 클래스를 상속받습니다. Object 클래스는 자바의 최상위 클래스이며, 모든 클래스는 암묵적으로 이를 상속받습니다. 따라서 Object 클래스의 메서드들은 모든 클래스에서 사용 가능합니다.
2.1 주요 메서드
1) toString() 메서드
toString() 메서드는 객체의 문자열 표현을 반환하는 메서드입니다. 기본적으로 Object 클래스에서는 객체의 해시 코드 값을 반환하지만, 이를 오버라이딩하여 객체의 정보를 의미 있게 출력할 수 있습니다.
class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "이름: " + name + ", 나이: " + age;
}
}
public class Main {
public static void main(String[] args) {
Person p = new Person("홍길동", 25);
System.out.println(p.toString()); // "이름: 홍길동, 나이: 25"
}
}
toString()을 오버라이딩하면 객체를 출력할 때 유용하게 활용할 수 있습니다.
2) equals() & hashCode() 오버라이딩
equals()는 두 객체가 논리적으로 동일한지 비교하는 메서드입니다. 기본적으로 Object 클래스의 equals()는 메모리 주소를 비교하지만, 논리적 비교를 원하면 이를 오버라이딩해야 합니다.
class Person {
String name;
Person(String name) {
this.name = name;
}
@Override
public boolean equals(Object obj) {
if (obj instanceof Person) {
return this.name.equals(((Person) obj).name);
}
return false;
}
}
public class Main {
public static void main(String[] args) {
Person p1 = new Person("홍길동");
Person p2 = new Person("홍길동");
System.out.println(p1.equals(p2)); // true (내용 비교)
}
}
equals()를 오버라이딩할 때는 객체의 필드를 기준으로 비교하는 것이 일반적입니다.
3) hashCode() 오버라이딩
hashCode()는 객체의 해시 코드 값을 반환하는 메서드입니다. equals()를 오버라이딩하면 hashCode()도 함께 오버라이딩해야 HashMap, HashSet과 같은 자료구조에서 올바르게 동작합니다.
@Override
public int hashCode() {
return this.name.hashCode();
}
hashCode()와 equals()를 함께 오버라이딩하면 해시 기반 컬렉션(HashMap, HashSet 등)에서 일관성 있게 동작합니다.
3. 인터페이스(Interface)와 추상 클래스(Abstract Class)
3.1 인터페이스란?
인터페이스는 클래스가 구현해야 하는 메서드의 집합입니다. 다중 상속이 가능하며, 모든 메서드는 기본적으로 public abstract입니다.
interface Animal {
void sound(); // 추상 메서드
}
class Dog implements Animal {
public void sound() {
System.out.println("멍멍!");
}
}
Dog 클래스는 Animal 인터페이스를 구현(implements) 해야 하며, 모든 메서드를 반드시 오버라이딩해야 합니다.
3.2 추상 클래스란?
추상 클래스는 추상 메서드(구현되지 않은 메서드)와 일반 메서드를 포함할 수 있는 클래스입니다.
abstract class Animal {
abstract void sound(); // 추상 메서드
void eat() {
System.out.println("먹이를 먹습니다.");
}
}
class Dog extends Animal {
void sound() {
System.out.println("멍멍!");
}
}
Animal 클래스는 sound() 메서드를 추상 메서드로 정의하여 Dog 클래스에서 오버라이딩해야 합니다.
3.3 추상 클래스 vs 인터페이스
비교 항목 | 추상 클래스(abstract class) | 인터페이스(interface) |
상속 방식 | extends (단일 상속) | implements (다중 상속 가능) |
필드 | 인스턴스 변수 포함 가능 | public static final 변수만 가능 |
메서드 | 일반 메서드 & abstract 메서드 가능 | abstract 메서드만 가능 (Java 8부터 default 메서드 추가) |
객체 생성 | 직접 객체 생성 불가능 | 직접 객체 생성 불가능 |
언제 인터페이스를 사용하고, 언제 추상 클래스를 사용할까?
- 공통된 기능을 여러 클래스에서 제공할 경우 → 추상 클래스 사용
- 여러 개의 클래스를 공통된 타입으로 묶어야 할 경우 → 인터페이스 사용
4. 예외 처리(Exception Handling)
4.1. 예외란?
예외(Exception)는 프로그램 실행 중 발생할 수 있는 오류를 의미하며, 이를 적절히 처리하지 않으면 프로그램이 비정상 종료될 수 있습니다.
✅ 예외가 발생할 가능성이 있는 코드를 try-catch-finally 문으로 감싸서 처리
try {
int result = 10 / 0;
} catch (ArithmeticException e) {
System.out.println("0으로 나눌 수 없습니다.");
} finally {
System.out.println("예외 처리 완료!");
}
catch 블록을 통해 발생한 예외를 처리하고, finally 블록은 예외 발생 여부와 관계없이 항상 실행됩니다.
4.2. throws 키워드로 예외 전가
✅ 예외를 호출한 곳으로 넘김
void divide() throws ArithmeticException {
int result = 10 / 0; // 예외 발생
}
throws를 사용하면 예외 처리를 강제할 수 있습니다.
4.3. 사용자 정의 예외 만들기
✅ 개발자가 직접 예외 클래스를 만들어 예외 상황을 커스텀할 수 있음.
class MyException extends Exception {
MyException(String message) {
super(message);
}
}
class Example {
void check(int number) throws MyException {
if (number < 0) throw new MyException("음수는 입력할 수 없습니다.");
}
}
MyException 클래스를 정의하여, 특정 조건(예: 음수 입력)을 예외로 처리할 수 있습니다.
5. 마무리
이번 강의를 통해 상속, 다형성, 인터페이스, 예외 처리 등의 개념을 복습했습니다.
'Experience > LG CNS AM Inspire Camp 1기' 카테고리의 다른 글
[LG CNS AM Inspire Camp] 13. 자바 실습 - 끝말잇기 게임 (with CLI) (0) | 2025.02.10 |
---|---|
[LG CNS AM Inspire Camp] 12. 자바 제네릭, 컬렉션, 함수형 프로그래밍, 람다식, 스트림 (0) | 2025.02.10 |
[LG CNS AM Inspire Camp] 10. 자바(Java) 객체지향 프로그래밍 (배열, String, 클래스, 메서드, static) (0) | 2025.02.10 |
[LG CNS AM Inspire Camp] 9. React 컴포넌트와 상태 관리 (0) | 2025.02.10 |
[LG CNS AM Inspire Camp] 8. Node.js와 JavaScript 핵심 개념 (0) | 2025.02.10 |