상속을 사용하면 자식 클래스가 부모 클래스의 필드와 메서드를 물려받는다. 자식 클래스는 부모 클래스의 기능을 재사용할 수 있고, 필요에 따라 추가하거나 재정의(오버라이딩)할 수 있다.
// 부모 클래스 (Super Class)
class Animal {
String name;
void eat() {
System.out.println(name + " is eating.");
}
}
// 자식 클래스 (Sub Class)
class Dog extends Animal {
// 추가적인 필드
String breed;
// 메서드 오버라이딩 (부모 클래스의 메서드를 재정의)
@Override
void eat() {
System.out.println(name + " (Dog) is eating.");
}
// 자식 클래스의 새로운 메서드
void bark() {
System.out.println(name + " is barking.");
}
}
// 자식 클래스 (Sub Class)
class Cat extends Animal {
// 메서드 오버라이딩 (부모 클래스의 메서드를 재정의)
@Override
void eat() {
System.out.println(name + " (Cat) is eating.");
}
// 자식 클래스의 새로운 메서드
void meow() {
System.out.println(name + " is meowing.");
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
// Dog 객체 생성
Dog dog = new Dog();
dog.name = "Buddy";
dog.breed = "Golden Retriever";
dog.eat(); // 오버라이딩된 메서드 호출
dog.bark();
// Cat 객체 생성
Cat cat = new Cat();
cat.name = "Whiskers";
cat.eat(); // 오버라이딩된 메서드 호출
cat.meow();
}
}
Buddy (Dog) is eating.
Buddy is barking.
Whiskers (Cat) is eating.
Whiskers is meowing.
dog와 Cat 클래스는 Animal 클래스를 상속받음Animal 클래스에 정의된 name 필드와 eat() 메서드는 Dog와 Cat이 상속받아 사용 가능Dog와 Cat 클래스는 eat() 메서드를 오버라이딩하여 자신만의 방식으로 정의Dog는 bark()라는 메서드를 추가했고, Cat은 meow() 메서드를 추가다형성은 하나의 객체가 여러 타입을 가질 수 있는 성질이다, 상속을 통해 부모 클래스 타입의 참조 변수로 자식 클래스의 객체를 참조할 수 있으며, 동일한 메서드 호출이 참조하는 객체에 따라 다르게 동작할 수 있다.
// 부모 클래스 (Super Class)
class Animal {
String name;
void eat() {
System.out.println(name + " is eating.");
}
}
// 자식 클래스 (Sub Class)
class Dog extends Animal {
@Override
void eat() {
System.out.println(name + " (Dog) is eating.");
}
void bark() {
System.out.println(name + " is barking.");
}
}
// 자식 클래스 (Sub Class)
class Cat extends Animal {
@Override
void eat() {
System.out.println(name + " (Cat) is eating.");
}
void meow() {
System.out.println(name + " is meowing.");
}
}
// 메인 클래스
public class Main {
public static void main(String[] args) {
// 다형성 적용: 부모 클래스 타입으로 자식 객체 참조
Animal myDog = new Dog();
myDog.name = "Buddy";
myDog.eat(); // Dog 클래스의 eat() 메서드가 호출됨
Animal myCat = new Cat();
myCat.name = "Whiskers";
myCat.eat(); // Cat 클래스의 eat() 메서드가 호출됨
// 배열을 사용한 다형성 적용
Animal[] animals = new Animal[2];
animals[0] = new Dog(); // Dog 객체
animals[1] = new Cat(); // Cat 객체
animals[0].name = "Rex";
animals[1].name = "Mittens";
for (Animal animal : animals) {
animal.eat(); // 각 객체의 eat() 메서드가 호출됨
}
}
}
Buddy (Dog) is eating.
Whiskers (Cat) is eating.
Rex (Dog) is eating.
Mittens (Cat) is eating.
myDog과 myCat은 Animal 타입의 참조 변수지만, 각각 Dog와 Cat 객체를 참조한다. 참조된 객체에 따라 오버라이딩된 메서드가 호출된다.animals 배열에 Dog와 Cat 객체를 추가하고, 루프를 통해 각각의 eat() 메서드를 호출하면 객체의 실제 타입에 맞는 메서드가 실행된다.✅ 상속: 부모 클래스의 속성과 메서드를 자식 클래스가 물려받아 재사용하거나 오버라이딩할 수 있는 기능
✅ 다형성: 하나의 객체가 여러 타입을 가질 수 있으며, 동일한 메서드 호출이 참조하는 객체에 따라 다르게 동작하는 성질
클래스가 부모 클래스를 상속받을 때는 오버라이딩을 꼭 해줄 필요는 없다. 즉, 부모 클래스에서 상속받은 메서드를 그대로 사용할 수도 있고, 필요하다면 자식 클래스에서 해당 메서드를 오버라디잉(재정의)할 수 있다. 오버라이딩은 선택 사항이지 필수는 아니다.
자식 클래스는 부모 클래스에서 제공하는 기본 기능을 그대로 사용할 수 있다. 이 경우, 오버라이딩을 하지 않고 부모 클래스의 메서드를 사용하면 된다.
class Parent {
void greet() {
System.out.println("Hello from Parent!");
}
}
class Child extends Parent {
// greet() 메서드를 오버라이딩하지 않음
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.greet(); // 부모 클래스의 greet() 메서드가 호출됨
}
}
Hello from Parent!
자식 클래스가 부모 클래스의 메서드를 자신만의 방식으로 동작하도록 변경하려면 오버라이딩을 할 수 있다. 이 경우, 자식 클래스에서 메서드의 구현을 다시 작성하게 된다.
class Parent {
void greet() {
System.out.println("Hello from Parent!");
}
}
class Child extends Parent {
@Override
void greet() {
System.out.println("Hello from Child!");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.greet(); // 자식 클래스의 greet() 메서드가 호출됨
}
}
Hello from Child!
부모 클래스에서 메서드를 abstract로 정의하면, 해당 메서드는 자식 클래스에서 반드시 오버라이딩해야 한다. 추상 클래스를 상속받은 경우에 오버라이딩이 필수가 된다.
abstract class Parent {
// 추상 메서드, 구현부가 없음
abstract void greet();
}
class Child extends Parent {
@Override
void greet() {
System.out.println("Hello from Child!");
}
}
public class Main {
public static void main(String[] args) {
Child child = new Child();
child.greet(); // 자식 클래스에서 구현한 greet() 메서드가 호출됨
}
}
Parent 클래스의 greet() 메서드는 추상 메서드이므로 자식 클래스에서 반드시 구현해주어야 한다. 추상 메서드를 오버라이딩하지 않으면 컴파일 오류 발생
캡슐화는 객체 지향 프로그래밍의 중요한 개념 중 하나로, 객체의 내부 상태(필드)를 외부로부터 보호하고 객체의 속성에 대한 접근을 제한하여 데이터의 무결성을 보장하는 방법이다. 캡슐화를 통해 객체의내부 구현을 감추고, 외부와의 인터페이스를 통해서만 데이터를 처리하게 한다.
예를 들어, 사용자가 직접 필드를 수정하지 못하도록 막고, 검증 로직을 통해 유효한 값만 저장하도록 할 수 있다.필드를 public으로 선언하여 외부에서 직접 접근하게 한다면, 데이터의 유효성을 보장할 수 없게 되고, 객체의 내부 상태가 쉽게 손상될 수 있다. 예를 들어, 나이를 음수로 설정하는 등의 문제가 발생할 수 있다.
public class Person {
public int age; // 공개 필드, 외부에서 직접 접근 가능
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
person.age = -5; // 음수 나이 설정 가능, 데이터 무결성 파괴
System.out.println("Age: " + person.age);
}
}
캡슐화는 클래스의 필드를 private으로 선언하고, 외부에서 접근할 수 있도록 public 메서드를 제공하여 필드에 간접적으로 접근하게 한다. 이를 통해 객체의 상태를 보호하고, 유효성 검사를 적용할 수 있다.
Account 클래스public class Account {
// private 필드, 외부에서 직접 접근 불가
private String accountNumber;
private double balance;
// Constructor to initialize the account
public Account(String accountNumber, double initialBalance) {
this.accountNumber = accountNumber;
if (initialBalance >= 0) {
this.balance = initialBalance;
} else {
System.out.println("Initial balance cannot be negative.");
}
}
// Getter for accountNumber
public String getAccountNumber() {
return accountNumber;
}
// Getter for balance
public double getBalance() {
return balance;
}
// Setter for balance with validation
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
} else {
System.out.println("Deposit amount must be positive.");
}
}
// Withdraw method with validation
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) {
balance -= amount;
} else {
System.out.println("Invalid withdraw amount.");
}
}
}
메인 클래스
public class Main {
public static void main(String[] args) {
// Account 객체 생성, 초기 잔액은 1000
Account account = new Account("123-456", 1000);
// 현재 계좌 번호와 잔액 확인
System.out.println("Account Number: " + account.getAccountNumber());
System.out.println("Balance: " + account.getBalance());
// 입금 시도
account.deposit(500);
System.out.println("After deposit, Balance: " + account.getBalance());
// 유효하지 않은 입금 시도
account.deposit(-200); // 오류 메시지 출력
// 출금 시도
account.withdraw(300);
System.out.println("After withdrawal, Balance: " + account.getBalance());
// 유효하지 않은 출금 시도
account.withdraw(1500); // 오류 메시지 출력
}
}
출력 결과:
Account Number: 123-456
Balance: 1000.0
After deposit, Balance: 1500.0
Deposit amount must be positive.
After withdrawal, Balance: 1200.0
Invalid withdraw amount.
accountNumber와 balance는 private으로 선언되어 외부에서 직접 접근할 수 없다.getAccountNumber()와 getBalance() 메서드를 통해 필드 값을 읽을 수 있다.deposit()과 withdraw() 메서드에서 유효성 검사를 수행한다. 금액이 양수인지, 출금 시 잔액이 충분한지 등을 확인하여 잘못된 데이터가 입력되지 않도록 방지한다.deposit(), withdraw() 메서드를 통해서만 계좌 잔액을 변경할 수 있다. 잔액을 직접 수정하지 못하도록 막고, 안전하게 데이터를 처리한다.Getter와 Setter는 객체의 캡슐화된 필드에 접근하고 수정하는 메서드이다. 보통 클래스의 필드를 private으로 선언하여 외부에서 직접 접근하지 못하도록 하고, public 접근 제한자를 가진 getter와 setter 메서드를 제공하여 필드에 대한 간접 접근을 허용합니다.
public class Person {
// private 필드 - 외부에서 직접 접근 불가
private String name;
private int age;
// Getter for name
public String getName() {
return name;
}
// Setter for name
public void setName(String name) {
this.name = name;
}
// Getter for age
public int getAge() {
return age;
}
// Setter for age with validation
public void setAge(int age) {
if (age >= 0) { // 나이는 0 이상이어야 한다는 조건을 추가
this.age = age;
} else {
System.out.println("Invalid age!");
}
}
}
public class Main {
public static void main(String[] args) {
Person person = new Person();
// Setter를 사용해 값 설정
person.setName("John");
person.setAge(25);
// Getter를 사용해 값 가져오기
System.out.println("Name: " + person.getName());
System.out.println("Age: " + person.getAge());
// 유효하지 않은 값 설정
person.setAge(-5); // Invalid age! 출력
}
}
Name: John
Age: 25
Invalid age!
Person 클래스의 name과 age 필드는 private으로 선언되어 외부에서 직접 접근할 수 없다.setName()과 setAge() 메서드를 통해 name과 age 필드에 값을 설정할 수 있다.setAge() 메서드에서는 나이가 0보다 작지 않도록 유효성 검사를 추가하여, 잘못된 값을 입력하려고 할 때는 경고 메시지를 출력하고 필드를 수정하지 않는다.getName()과 getAge() 메서드를 통해서만 필드 값을 읽을 수 있다.private으로 선언하고, Getter/Setter를 사용함으로써 필드에 대한 직접적인 접근을 차단할 수 있다. 이를 통해 객체의 무결성을 보장할 수 있다.| 특징 | Getter | Setter |
|---|---|---|
| 역할 | 필드 값을 읽고 반환 | 필드 값을 설정하거나 변경 |
| 메서드 명명 규칙 | get 접두사를 사용해 getFieldName 형식으로 작성 | set 접두사를 사용해 setFieldName 형식으로 작성 |
| 리턴 타입 | 필드의 데이터 타입과 동일한 값을 반환 | 반환 값이 없으며, 일반적으로 void 타입 |
| 인수 | 인수를 받지 않음 | 필드의 값을 설정하기 위한 인수를 받음 |
| 읽기 전용 vs 쓰기 전용 | 필드에 읽기 접근 권한만 제공 | 필드에 쓰기 접근 권한을 제공 |
| 사용 목적 | 외부에서 필드의 값을 확인할 때 사용 | 외부에서 필드의 값을 변경하고자 할 때 사용 |
| 유효성 검사 여부 | 일반적으로 유효성 검사가 필요하지 않음 | 유효성 검사를 포함할 수 있어 값이 잘못 설정되는 것을 방지 |
BankAccount 클래스 예제
public class BankAccount {
// Private 필드: 외부에서 직접 접근 불가
private String accountHolder; // 계좌 소유자
private double balance; // 잔액
// 생성자: 초기 잔액을 설정
public BankAccount(String accountHolder, double initialBalance) {
this.accountHolder = accountHolder;
if (initialBalance >= 0) { // 초기 잔액이 0 이상일 때만 설정
this.balance = initialBalance;
} else {
System.out.println("Initial balance cannot be negative.");
this.balance = 0;
}
}
// Getter for accountHolder: 소유자 이름을 읽기 전용으로 제공
public String getAccountHolder() {
return accountHolder;
}
// Getter for balance: 잔액을 읽기 전용으로 제공
public double getBalance() {
return balance;
}
// Setter for balance: 유효성 검사 포함하여 잔액 추가 (입금 기능)
public void deposit(double amount) {
if (amount > 0) { // 입금액이 양수일 때만 허용
balance += amount;
} else {
System.out.println("Deposit amount must be positive.");
}
}
// Withdraw 메서드: 유효성 검사 포함하여 잔액 감소 (출금 기능)
public void withdraw(double amount) {
if (amount > 0 && amount <= balance) { // 잔액 범위 내에서만 출금 허용
balance -= amount;
} else {
System.out.println("Invalid withdraw amount.");
}
}
}
메인 클래스
public class Main {
public static void main(String[] args) {
// 계좌 생성
BankAccount account = new BankAccount("Alice", 1000);
// 계좌 소유자 확인: Getter 사용
System.out.println("Account Holder: " + account.getAccountHolder());
// 초기 잔액 확인: Getter 사용
System.out.println("Initial Balance: " + account.getBalance());
// 입금: Setter의 역할 (간접적 데이터 수정)
account.deposit(500);
System.out.println("Balance after deposit: " + account.getBalance());
// 잘못된 입금 시도: 음수 입금 (Setter 유효성 검사)
account.deposit(-100); // 유효성 검사 실패
// 출금: Setter의 역할 (간접적 데이터 수정)
account.withdraw(300);
System.out.pintln("Balance after withdrawal: " + account.getBalance());
// 잘못된 출금 시도: 잔액 초과 출금 (Setter 유효성 검사)
account.withdraw(2000); // 유효성 검사 실패
}
}
출력 결과
Account Holder: Alice
Initial Balance: 1000.0
Balance after deposit: 1500.0
Deposit amount must be positive.
Balance after withdrawal: 1200.0
Invalid withdraw amount.
Getter (getAccountHolder, getBalance):
getAccountHolder()와 getBalance()는 읽기 전용 메서드로, 계좌 소유자와 잔액을 확인하는 데 사용된다.balance와 accountHolder 필드에 직접 접근할 수 없기 때문에, Getter 메서드를 통해 필드 값을 안전하게 가져올 수 있다.Setter (deposit, withdraw):
deposit()과 withdraw()는 간접적으로 잔액을 수정하는 Setter 메서드로, 쓰기 전용 메서드이다.deposit()에서는 양수 금액만 입금 가능하고, withdraw()에서는 출금액이 잔액보다 클 수 없도록 유효성 검사가 포함되어 있다.Getter는 읽기 전용 접근을 제공하며, 필드 값을 변경하지 않고 가져오기만 한다.
Setter는 쓰기 전용 접근을 제공하며, 필드 값에 대한 유효성 검사를 통해 데이터 무결성을 유지하면서 안전하게 값을 변경할 수 있게 한다.
✅ 캡슐화와 getter, setter 둘은 서로 연관되어 있으며, getter와 setter가 캡슐화를 구현하는 도구로 자주 사용된다.
private으로 선언하고, 필요한 경우 getter와 setter 메서드를 사용해 외부에서 필드에 접근할 수 있도록 한다.age 필드는 0 이상이어야 한다는 조건을 setter 메서드에 설정하여 잘못된 데이터가 저장되지 않도록 한다.| 캡슐화 | Getter/Setter |
|---|---|
| 클래스의 필드를 외부로부터 보호하여 정보 은닉을 실현하는 개념적 기법입니다. | 캡슐화된 필드에 간접적으로 접근하기 위해 사용하는 메서드입니다. |
| 데이터의 무결성을 보장하고, 필드에 대해 불필요한 접근을 제한합니다. | 특정 조건에 따라 필드 값을 읽거나 수정하는 데 사용됩니다. |
주로 필드를 private으로 선언하고, 필요 시 메서드로 접근을 제한합니다. | get 메서드는 필드 값을 반환하고, set 메서드는 필드 값을 수정합니다. |
| 필드에 직접 접근하지 못하게 하고, 필요한 검증을 통해 안전하게 데이터를 관리합니다. | Getter와 Setter로 필드 값에 대한 제어가 가능하며, 유효성 검사 등을 추가할 수 있습니다. |
| 캡슐화를 통해 객체의 내부 구현을 변경해도 외부에 영향이 적게 만듭니다. | 외부에서는 getter와 setter만 호출하므로, 필드의 변경 사항이 덜 영향을 미칩니다. |
하나 이상의 추상 메서드를 포함하고 있으며, 객체를 직접 생성할 수 없는 클래스이다. 추상 클래스는 구현해야 할 메서드를 자식 클래스에서 반드시 재정의하도록 강제하여, 코드의 일관성을 유지하고 유연성을 제공하는 역할을 한다.
abstract 키워드를 사용해 선언하며, 객체를 직접 생성할 수 없다. 주로 상속을 통해 하위 클래스에서 구현을 공유하고, 일부 메서드는 추상적으로 남겨둬 하위 클래스가 고유의 기능을 정의하도록 한다.abstract 키워드를 사용한다.추상 클래스는 다음과 같은 경우에 유용하다:
추상 클래스는 abstract 키워드로 정의되며, 추상 메서드는 구현 없이 선언만 되어 있다. 추상 클래스를 상속하는 모든 하위 클래스는 추상 메서드를 반드시 오버라이드해야 한다.
Animal 추상 클래스 예제
아래 예제는 Animal이라는 추상 클래스를 만들고, 이를 상속받는 Dog와 Cat 클래스를 통해 추상 클래스의 활용을 보여준다.
// 추상 클래스 Animal
abstract class Animal {
private String name;
public Animal(String name) {
this.name = name;
}
// 추상 메서드: 하위 클래스에서 구현해야 함
public abstract void makeSound();
// 구현된 메서드: 모든 하위 클래스에서 공통으로 사용할 수 있음
public void sleep() {
System.out.println(name + " is sleeping.");
}
}
// Dog 클래스는 Animal을 상속받아 추상 메서드를 구현
class Dog extends Animal {
public Dog(String name) {
super(name);
}
// 추상 메서드 구현
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
// Cat 클래스는 Animal을 상속받아 추상 메서드를 구현
class Cat extends Animal {
public Cat(String name) {
super(name);
}
// 추상 메서드 구현
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
메인 클래스
public class Main {
public static void main(String[] args) {
// Animal animal = new Animal("Unknown"); // 에러! 추상 클래스는 인스턴스화 불가
Animal dog = new Dog("Buddy");
Animal cat = new Cat("Kitty");
dog.makeSound(); // "Woof!"
dog.sleep(); // "Buddy is sleeping."
cat.makeSound(); // "Meow!"
cat.sleep(); // "Kitty is sleeping."
}
}
makeSound()라는 추상 메서드를 선언하여 하위 클래스에서 해당 메서드를 구현하도록 강제한다.sleep() 메서드는 구현된 메서드로, 모든 하위 클래스에서 공통적인 동작을 제공한다.Dog와 Cat 클래스는 Animal을 상속받아 각각 makeSound() 메서드를 구현한다.makeSound() 메서드는 Dog에서는 "Woof!"를, Cat에서는 "Meow!"를 출력하도록 한다.Dog와 Cat 객체는 각각 Animal 타입으로 참조된다. 이는 다형성을 사용해 다양한 Animal 객체를 처리할 수 있게 한다.Animal 객체를 직접 생성하려고 하면 에러가 발생한다.sleep() 같은 메서드를 여러 하위 클래스에서 중복 구현할 필요 없이 부모 클래스에서 공통으로 제공할 수 있다.Animal 클래스는 반드시 makeSound() 메서드를 구현해야 하므로, 하위 클래스 간의 일관성이 유지된다.Animal 객체를 Animal 타입으로 처리할 수 있어 유연한 코드 설계가 가능하다.메서드의 선언만 있고, 구현은 없는 메서드이다. 즉, 메서드 이름, 반환형, 매개변수만 정의하고, 실제 메서드가 해야 할 작업은 하위 클래스가 구체적으로 구현하도록 강제한다. 추상 메서드는 추상 클래스 또는 인터페이스 안에서만 선언할 수있으며, abstract 키워드를 사용하여 정의된다.
{} 대신 세미콜론 ; 으로 끝난다.추상 메서드는 구체적인 구현을 자식 클래스가 수행하도록 설계한다. 이를 통해 상위 클래스는 메서드의 형태(이름, 반환형 등)만 정의하고, 구체적인 내용은 필요에 따라 다양하게 구현할 수 있어 유연성과 일관성을 모두 제공한다.
Animal이라는 추상 클래스와, 그 안에 선언된 추상 메서드 makeSound()를 보여준다. Dog와 Cat 클래스는 Animal을 상속받아 각각 makeSound()를 자신에 맞게 구현한다.
// 추상 클래스 Animal
abstract class Animal {
// 추상 메서드: 구현이 없음, 하위 클래스에서 오버라이딩해야 함
public abstract void makeSound();
// 일반 메서드: 모든 하위 클래스에서 공통적으로 사용할 수 있음
public void sleep() {
System.out.println("Animal is sleeping.");
}
}
// Dog 클래스는 Animal을 상속받아 추상 메서드를 구현
class Dog extends Animal {
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
// Cat 클래스는 Animal을 상속받아 추상 메서드를 구현
class Cat extends Animal {
@Override
public void makeSound() {
System.out.println("Meow!");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
Animal cat = new Cat();
// 추상 메서드 makeSound()의 구현 호출
dog.makeSound(); // "Woof!"
cat.makeSound(); // "Meow!"
// 일반 메서드 sleep() 호출 (공통 동작)
dog.sleep(); // "Animal is sleeping."
cat.sleep(); // "Animal is sleeping."
}
}
makeSound():Dog와 Cat 클래스는 각각 makeSound()를 고유하게 오버라이드하여 동물마다 다른 소리를 낸다.sleep():sleep() 메서드는 Animal 클래스에 구현되어, 모든 하위 클래스에서 공통적으로 사용할 수 있다.dog.sleep()과 cat.sleep()은 동일한 출력을 가지며, 각 하위 클래스에서 다시 구현할 필요 없이 공통 동작을 재사용한다.추상 메서드는 공통 인터페이스를 제공하면서도 구체적인 동작을 하위 클래스에서 구현하도록 하여 유연하고 일관성 있는 객체 지향 구조를 설계하는 데 매우 유용하다.
추상 클래스와 인터페이스는 모두 자바에서 추상화를 지원하는 기법이다. 하지만 두 개념은 상속 구조와 기능 구현 방식, 사용할 수 있는 메서드와 필드 등에 있어 차이점이 있다.
default와 static 메서드를 통해 일부 구현이 가능하지만, 기본적으로는 모든 메서드가 추상 메서드이다.public static final 상수만 선언할 수 있으며, 모든 필드는 자동으로 상수로 취급된다.private, protected, public 등).public이며, public abstract로 간주된다.추상 클래스 예제
abstract class Animal {
// 필드와 구현된 메서드를 가질 수 있음
private String name;
public Animal(String name) {
this.name = name;
}
// 추상 메서드: 하위 클래스에서 구현
public abstract void makeSound();
// 구현된 메서드
public void sleep() {
System.out.println(name + " is sleeping.");
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println("Woof!");
}
}
인터페이스 예제
interface Flyable {
// 모든 필드는 public static final
int MAX_ALTITUDE = 10000;
// 추상 메서드
void fly();
}
interface Swimmable {
void swim();
}
class Bird implements Flyable, Swimmable {
@Override
public void fly() {
System.out.println("Bird is flying.");
}
@Override
public void swim() {
System.out.println("Bird is swimming.");
}
}
| 특성 | 추상 클래스 | 인터페이스 |
|---|---|---|
| 상속 개수 제한 | 단일 상속 | 다중 구현 가능 |
| 메서드 | 추상 메서드와 구현된 메서드 모두 포함 가능 | 자바 8부터 default, static 메서드 포함 가능 |
| 필드 | 인스턴스 변수와 상수 모두 가능 | public static final 상수만 가능 |
| 생성자 | 생성자 선언 가능 | 생성자 선언 불가 |
| 목적 | 공통된 속성 및 기능 상속 | 클래스 간 공통 행위 정의 |
따라서, 클래스들 간에 공통된 속성과 일부 구현이 필요하면 추상 클래스를, 클래스 간의 행위의 표준화가 필요하다면 인터페이스를 선택하는 것이 좋다.