1. abstract
abstract는 자바에서 추상 클래스나 추상 메서드를 정의할 때 사용하는 키워드입니다. 추상 클래스는 객체를 직접 생성할 수 없고, 공통적인 속성과 동작을 정의하여 상속을 통해 구체화되도록 설계된 클래스입니다. 이 안에 선언된 추상 메서드는 본체(구현부)가 없는 메서드로, 자식 클래스에서 반드시 오버라이딩(재정의)해야 합니다. 즉, abstract 키워드는 구체적인 동작을 미리 정하지 않고, 자식 클래스가 각자의 방식으로 구현하도록 강제하는 역할을 합니다. 이를 통해 객체지향 프로그래밍에서 다형성과 설계 유연성을 높일 수 있습니다.
1. 추상 클래스 (abstract class)
- 인스턴스를 생성할 수 없는 클래스입니다.
- 다른 클래스들이 공통적으로 가져야 할 속성과 메서드를 정의합니다.
- 하위 클래스에서 상속하여 구체화됩니다.
abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
// 추상 메서드
abstract void sound();
// 일반 메서드
void sleep() {
System.out.println(name + "이(가) 잠을 잡니다.");
}
}
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
void sound() {
System.out.println(name + "이(가) 멍멍 짖습니다.");
}
}
public class Main {
public static void main(String[] args) {
// Animal a = new Animal("동물"); // ❌ 에러! 추상 클래스는 객체 생성 불가
Animal dog = new Dog("루시");
dog.sound(); // 루시이(가) 멍멍 짖습니다.
dog.sleep(); // 루시이(가) 잠을 잡니다.
}
}
2. 추상 메서드 (abstract method)
- 메서드 선언만 있고, 구현(바디)가 없는 메서드입니다.
- 반드시 추상 클래스 내에서만 정의할 수 있습니다.
- 이 메서드는 자식 클래스에서 반드시 오버라이딩해야 합니다.
abstract class Shape {
abstract double getArea(); // 추상 메서드: 면적 계산
}
class Circle extends Shape {
double radius;
Circle(double radius) {
this.radius = radius;
}
@Override
double getArea() {
return Math.PI * radius * radius;
}
}
class Rectangle extends Shape {
double width, height;
Rectangle(double width, double height) {
this.width = width;
this.height = height;
}
@Override
double getArea() {
return width * height;
}
}
public class AreaTest {
public static void main(String[] args) {
Shape s1 = new Circle(3);
Shape s2 = new Rectangle(4, 5);
System.out.println("원 면적: " + s1.getArea());
System.out.println("사각형 면적: " + s2.getArea());
}
}
주의사항
- 추상 클래스 내에는 일반 메서드, 필드, 생성자도 정의할 수 있습니다.
- 추상 클래스를 상속한 클래스는 모든 추상 메서드를 구현해야 합니다. 단, 구현하지 않는다면 그 클래스도 추상 클래스로 선언해야 합니다.
- 인터페이스와는 달리 생성자, 상태(필드)를 가질 수 있습니다.
2. 종합예제
콘솔 기반 턴제 동물 배틀 게임 만들기
- 동물(Animal)클래스를 기반으로 한 콘솔 기반 턴제 배틀 게임을 구현합니다.
- 추상 클래스와 상속, 메서드 오버라이딩을 활용하여 동물들의 다양한 능력을 표현합니다.
- 플레이어와 적 팀 각각 2마리의 동물로 구성되어 있으며, 서로 공격하고 교체하며 승패를 겨루게 됩니다.
1. 추상 클래스 Animal
- 이름(name), 체력(hp), 최대 체력(maxHp), 공격력(power) 등의 공통 속성을 가짐
- 다음 메서드를 abtract로 정의:
- attack(): 일반 공격 출력
- getAttackPower(): 랜덤 공격력 반환
- getSkillName(): 스킬 이름 반환
- useSkill(Animal enemy):적에게 스킬 사용
2. 3종의 동물 클래스 만들기 (Animal을 상속)
- 예: Dragon, Wolf, Bear
- 각 동물은:
- 서로 다른 기본 체력
- 서로 다른 일반 공격력 범위
- 서로 다른 스킬 구현
- 서로 다른 스킬 사용 가능 횟수

3. 플레이어 팀 구성
- 시작 시 2마리의 동물을 선택하도록 구현
- 선택한 동물은 각각 독립적인 객체로 생성해야 함
4. 적 팀 구성
- 컴퓨터가 2마리 동물을 무작위로 선택(중복 없음)
5. 턴제 전투 시스템
- 플레이어와 적이 번갈아 가며 공격
- 한 쪽 동물의 체력이 0이 되면 다음 동물로 교체
- 두 마리 모두 쓰러지면 패배
6. 행동 선택 (플레이어 턴)
- 매 턴 다음 중 하나 선택 가능
- 일반 공격
- 스킬 사용(남은 횟수 있을 경우만 사용 가능)
- 다른 동물로 교체(이미 쓰러진 동물은 교체 불가)
7. AI 턴 로직 (컴퓨터 턴)
- 다음 기준에 따라 커뮾터가 행동 결정:
- 체력이 60 이하이고 스킬 가능 -> 70% 확률로 스킬 사용
- 그렇지 않으면 일반 공격
8. 전투 종료 조건
- 한 팀의 동물 2마리 모두 쓰러지면 전투 종료
- 결과 메시지를 출력:
- 🎉 승리!
- 💀 패배...
import java.util.Random;
import java.util.Scanner;
abstract class Animal {
String name;
int maxHp;
int hp;
int power;
int maxSkillCount;
int skillCount;
public Animal(String name, int power, int maxHp, int maxSkillCount) {
this.name = name;
this.power = power;
this.maxHp = maxHp;
this.hp = maxHp;
this.maxSkillCount = maxSkillCount;
this.skillCount = maxSkillCount;
}
abstract void attack();
abstract int getAttackPower();
abstract String getSkillName();
abstract void useSkill(Animal enemy);
boolean isAlive() {
return hp > 0;
}
boolean canUseSkill() {
return skillCount > 0;
}
void takeDamage(int damage) {
hp -= damage;
if (hp < 0) hp = 0;
}
void printStatus() {
System.out.println(name + " 체력: " + hp + "/" + maxHp + " | 스킬 남은 횟수: " + skillCount + "/" + maxSkillCount);
}
}
class Dragon extends Animal {
public Dragon() {
super("드래곤", 30, 180, 3); // 체력 상향, 스킬 3회
}
@Override
void attack() {
System.out.println("🔥 드래곤이 불을 내뿜는다!");
}
@Override
int getAttackPower() {
return power + new Random().nextInt(20); // 30~49
}
@Override
String getSkillName() {
return "파이어 브레스";
}
@Override
void useSkill(Animal enemy) {
if (canUseSkill()) {
skillCount--;
int damage = 40 + new Random().nextInt(20); // 40~59
System.out.println("🔥 드래곤의 스킬! 파이어 브레스!! (불꽃 데미지 " + damage + ")");
enemy.takeDamage(damage);
} else {
System.out.println("❌ 드래곤은 더 이상 스킬을 사용할 수 없습니다!");
}
}
}
class Wolf extends Animal {
public Wolf() {
super("늑대", 25, 160, 2); // 체력 상향, 스킬 2회
}
@Override
void attack() {
System.out.println("🌕 늑대가 어둠 속에서 덮친다!");
}
@Override
int getAttackPower() {
return power + new Random().nextInt(15); // 25~39
}
@Override
String getSkillName() {
return "달빛 송곳니";
}
@Override
void useSkill(Animal enemy) {
if (canUseSkill()) {
skillCount--;
int damage = 35 + new Random().nextInt(15); // 35~49
System.out.println("🌙 늑대의 스킬! 달빛 송곳니!! (강력 데미지 " + damage + ")");
enemy.takeDamage(damage);
} else {
System.out.println("❌ 늑대는 더 이상 스킬을 사용할 수 없습니다!");
}
}
}
class Bear extends Animal {
public Bear() {
super("곰", 20, 200, 1);
}
@Override
void attack() {
System.out.println("🐻 곰이 앞발로 내려친다!");
}
@Override
int getAttackPower() {
return power + new Random().nextInt(10); // 20~29
}
@Override
String getSkillName() {
return "대지 강타";
}
@Override
void useSkill(Animal enemy) {
if (canUseSkill()) {
skillCount--;
int damage = (int)(enemy.hp * 0.3);
if (damage < 25) damage = 25;
System.out.println("🌍 곰의 스킬! 대지 강타!! (적 체력의 30% 데미지: " + damage + ")");
enemy.takeDamage(damage);
} else {
System.out.println("❌ 곰은 더 이상 스킬을 사용할 수 없습니다!");
}
}
}
// === 메인 게임 ===
public class AnimalBattleGame {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
Animal[] allAnimals = { new Dragon(), new Wolf(), new Bear() };
Animal[] playerTeam = new Animal[2];
Animal[] enemyTeam = new Animal[2];
Random rand = new Random();
System.out.println("🧙 전설의 포켓동물 배틀에 오신 걸 환영합니다!");
System.out.println("두 마리의 포켓동물을 선택하세요:");
for (int i = 0; i < 2; i++) {
System.out.println("1. 드래곤 🐉\n2. 늑대 🐺\n3. 곰 🐻");
System.out.print((i + 1) + "번째 동물 선택: ");
int choice = sc.nextInt();
playerTeam[i] = copyAnimal(allAnimals[choice - 1]);
}
// 적 팀 랜덤 구성
for (int i = 0; i < 2; i++) {
while (true) {
Animal enemy = copyAnimal(allAnimals[rand.nextInt(3)]);
boolean exists = false;
for (int j = 0; j < i; j++) {
if (enemy.name.equals(enemyTeam[j].name)) {
exists = true;
break;
}
}
if (!exists) {
enemyTeam[i] = enemy;
break;
}
}
}
int playerIdx = 0;
int enemyIdx = 0;
while (true) {
Animal player = playerTeam[playerIdx];
Animal enemy = enemyTeam[enemyIdx];
System.out.println("\n====================================");
System.out.println("👊 현재 전투 중인 포켓동물");
player.printStatus();
enemy.printStatus();
// 플레이어 턴
System.out.println("\n[당신의 턴]");
System.out.println("1. 일반 공격");
System.out.println("2. 스킬 사용 (" + player.getSkillName() + ")");
System.out.println("3. 동물 교체");
System.out.print("선택 >> ");
int action = sc.nextInt();
if (action == 1) {
player.attack();
int damage = player.getAttackPower();
System.out.println("💥 공격력: " + damage);
enemy.takeDamage(damage);
} else if (action == 2) {
player.useSkill(enemy);
} else if (action == 3) {
int otherIdx = 1 - playerIdx;
if (!playerTeam[otherIdx].isAlive()) {
System.out.println("⚠️ 다른 동물이 전투불능입니다!");
continue;
}
playerIdx = otherIdx;
System.out.println("🔁 포켓동물 교체!");
continue;
}
if (!enemy.isAlive()) {
System.out.println("✅ 적 " + enemy.name + "이(가) 쓰러졌습니다!");
enemyIdx++;
if (enemyIdx >= 2) {
System.out.println("🎉 당신이 승리했습니다!!");
break;
}
}
// 적 턴
Animal currEnemy = enemyTeam[enemyIdx];
Animal currPlayer = playerTeam[playerIdx];
System.out.println("\n[👾 적의 턴: " + currEnemy.name + "]");
int enemyChoice;
if (currEnemy.hp < 60 && currEnemy.canUseSkill() && rand.nextInt(100) < 70) {
enemyChoice = 1;
} else {
enemyChoice = 0;
}
if (enemyChoice == 0) {
System.out.println("👾 적이 일반 공격을 선택합니다!");
currEnemy.attack();
int damage = currEnemy.getAttackPower();
System.out.println("💥 공격력: " + damage);
currPlayer.takeDamage(damage);
} else {
System.out.println("👾 적이 스킬 '" + currEnemy.getSkillName() + "'을(를) 사용합니다!");
currEnemy.useSkill(currPlayer);
}
if (!currPlayer.isAlive()) {
System.out.println("☠️ 당신의 " + currPlayer.name + "이(가) 쓰러졌습니다!");
if (!playerTeam[1 - playerIdx].isAlive()) {
System.out.println("💀 모든 포켓동물이 쓰러졌습니다. 패배...");
break;
} else {
playerIdx = 1 - playerIdx;
System.out.println("🔁 자동으로 다른 포켓동물로 교체합니다!");
}
}
}
sc.close();
}
public static Animal copyAnimal(Animal original) {
if (original.name.equals("드래곤")) return new Dragon();
else if (original.name.equals("늑대")) return new Wolf();
else return new Bear();
}
}