Class
패키지
접근 제어자
- 클래스나 일부 멤버 (필드, 메서드) 에 외부에서의 접근 제한을 걸어두는 것
- public
- 같은 클래스 ⭕ 같은 패키지 ⭕ 자식 클래스 ⭕ 전체 ⭕
- protected
- 같은 클래스 ⭕ 같은 패키지 ⭕ 자식 클래스 ⭕
- default
- private
static 멤버 (변수, 메서드)
- 객체마다 생성 ❌ 클래스당 하나만 생성 ⭕
- 클래스가 생성되는 순간에 메모리를 할당 받음
- 객체를 생성하지 않아도 static 멤버에 접근 ⭕
- 동일한 클래스의 모든 인스턴스에 공유됨 (같은 메모리 공간 공유)
- non-static의 경우, 객체가 생성될 때마다 멤버 공간을 새로 생성
→ 메모리 공간 공유 ❌
static 활용
- static 변수 : 공유의 목적으로 많이 사용됨
- static 메서드 : 유틸리티성 메서드를 작성할 때 많이 사용됨
- ex) java.lang 패키지의 Math 클래스, 오늘의 날짜 구하기, 숫자에 콤마 추가하기 등
static 제약
- static 메서드 → non-static 멤버 접근 ❌
- 객체가 생성되지 않은 상태일 때, non-static 멤버는 만들어져 있지 않기 때문
- non-static 메서드 → static 멤버 접근 ⭕
- static 메서드에서는 this 키워드 사용 ❌
상속
- 부모 클래스에서 정의된 필드와 메서드를 자식 클래스가 물려받는 것
- 장점
- 멤버의 중복 작성 제거
- 클래스의 계층적 분류 가능
- 클래스의 재사용과 확장성 용이
class Person {
private int age;
private String name;
public Person(String name) {
setName(name);
}
public void setName(String name) {
this.name = name;
}
}
public class Student extends Person {
private String school;
public Student(int age, String name) {
super(name);
setAge(age);
}
}
- super() : 하위 클래스에서 상위 클래스 생성자를 호출할 때 사용
- super 키워드는 상위 클래스에서 오버라이드된 메서드를 호출할 때 사용
Overriding
- 부모 클래스의 메서드를 자식 클래스에서 재정의 하는 것
- 메서드 이름, 매개변수 타입 및 개수, return type 등 모든 것을 동일하게 작성
Overloading vs Overriding
Overloading
- 같은 클래스 내에 같은 이름을 가진 메서드가 존재하더라도 매개변수 타입 또는 개수가 다르면, 같은 이름을 사용하여 메서드를 정의할 수 있음
- 반환 값만 다른 것은 오버로딩 ❌
- 같은 기능을 하는 메서드를 하나의 이름으로 사용 ⭕
- 메서드의 이름을 절약할 수 있음
구분 | Overriding | Overloading |
---|
접근 제어자 | 부모 클래스 메서드의 접근 제어자보다 더 넓은 범위의 접근 제어자 | 모든 접근 제어자 |
return type | 동일해야 함 | 달라도 됨 |
메서드명 | 동일해야 함 | 동일해야 함 |
매개변수 | 동일해야 함 | 달라야 함 |
적용 범위 | 상속 관계 | 같은 클래스 내부 |
출처
추상 클래스
- 추상 메서드 : 선언은 되어 있으나 구현이 되어 있지 않은 메서드
- 추상 메서드를 갖는 클래스는 추상 클래스가 됨
- 추상 클래스를 상속받는 자식 클래스에서 추상 메서드를 오버라이딩 해주어야 함
추상 클래스 사용
- 설계와 구현 분리
- 부모 클래스에서는 개념 정의
- 자식 클래스에서는 구체적 행위 구현
- 자식 클래스마다 목적에 맞게 추상 메서드를 다르게 구현
- 상속 시, extends 키워드 사용
public abstract class Person {
private int age;
private String name;
abstract public void printName();
}
Interface
- 추상 메서드의 모음
- 여러 구현체에서 공통적인 부분을 추상화하는 역할
- 자식 클래스가 같은 동작을 한다는 것을 보장하기 위해 사용
- 장점
- 추상화 : 메서드를 선언하여 상속받은 클래스에서 사용하게 함
- 다중 상속 : 하나의 클래스는 여러 인터페이스를 구현할 수 있음
Interface 사용
- 필드 (변수) 선언 ❌
- 단, public 상수는 선언 ⭕ (final 키워드 사용)
- 인터페이스를 상속받는 클래스는 인터페이스의 모든 추상 메서드를 구현해야 함
- 상속 시, implements 키워드 사용
- 다른 인터페이스 상속 ⭕
public interface Phone {
public final String CATEGORY = "phone";
public abstract void sendCall();
public abstract void receiveCall();
public abstract void sendMessage();
public abstract void receiveMessage();
}
final
- 클래스에서 사용 : 상속 ❌
ex) final class MyClass{ }
- 메서드에서 사용 : 오버라이딩 ❌
- 변수에서 사용 : 값 변경 ❌ (상수가 됨)
- 매개변수에서 사용 : 매개변수 값 변경 ❌
ex) void Func(final int x) { }
추상 클래스 vs Interface
공통점
- 객체를 생성 (인스턴스화) ❌
- 추상 메서드를 포함함
- 상속받는 클래스에서 추상 메서드를 재정의하여 구현해야 함
차이점
- 상속
- 추상 클래스 : 하나의 클래스는 하나의 추상 클래스만 상속
- 인터페이스 : 하나의 클래스는 여러 인터페이스를 구현 가능
- 멤버 변수
- 추상 클래스 : 멤버 변수를 가질 수 있음
- 인터페이스 : 상수만 가질 수 있음
- 구성 요소
- 추상 클래스 : 추상메서드, 생성자, 변수
- 인터페이스 : 추상메서드, 상수, default 메서드
- 사용
- 추상 클래스 : 상속 관계에서 공통의 기능이나 상태를 유지할 때 사용
- 인터페이스 : 여러 클래스에서 공통으로 필요한 기능을 정의할 때 사용
- 생성자
- 추상 클래스 : 생성자와 초기화 ⭕
- 인터페이스 : 생성자와 초기화 ❌
다형성
- 하나의 인터페이스나 클래스를 여러 방식으로 동작하게 하는 것
- 상속과 인터페이스를 활용하여 구현
- 다형성의 장점
- 재사용성 : 기존 코드를 재사용하여 새로운 클래스 생성
- 확장성 : 기존 코드를 수정하지 않고 기능 추가 및 확장
- 다형성의 단점
- 복잡성 증가 : 여러 객체가 동일한 인터페이스나 부모 클래스를 상속받을 때, 실제 어떤 객체의 메서드가 호출되는지 파악하기 어려울 수 있음
- 디버깅 어려움 : 여러 클래스가 동일 메서드를 오버라이딩할 경우, 디버깅할 때 실제로 어떤 클래스의 메서드가 실행되는지 파악하기 어려울 수 있음
- 유지 보수의 어려움 : 다형성을 과도하게 사용할 경우, 새로운 클래스나 메서드가 추가될 때 유지 보수가 어려워질 수 있음
- 다형성은 각 클래스나 인터페이스의 참조 변수를 사용하여 다양한 구현을 가진 객체를 참조
class Animal {
public void sound() {
System.out.println("동물은 소리를 낸다.");
}
}
class Dog extends Animal {
@Override
public void sound() {
System.out.println("멍멍");
}
}
class Cat extends Animal {
@Override
public void sound() {
System.out.println("야옹");
}
}
Animal dog = new Dog();
Animal cat = new Cat();
dog.sound();
cat.sound();
- dog, cat은 Animal 타입의 참조 변수
- dog, cat은 Dog와 Cat의 객체를 참조