2025-03-25 java

성현규·2025년 3월 25일
post-thumbnail

0. 대화

0-1. UML

  • UML (Unified Modeling Language) = “통합 모델링 언어”
  • 소프트웨어 구조(클래스, 관계, 흐름 등)를 그림으로 표현하는 표준 방식
  • UML 중 가장 중요한 것: 클래스 다이어그램

0-2. F5로 실행한 main의 결과물은 어디로 가는가.

    1. 자바 프로그램은 main()을 실행하면,
    1. JVM이 새로운 메모리 공간(힙 & 스택)을 만들어서
    1. 모든 객체들을 새로 생성하고
    1. 실행이 끝나면 통째로 사라진다.
  • 이를 유지하고 싶으면 DB와 연동해서 데이터를 유지시킬 필요가 있는 것이다.

0-3. Serializable이란

  • 객체를 파일로 저장하거나, 네트워크로 보내기 위해 '바이트(byte) 형태로 변환할 수 있도록 해주는 표시(마커)**
  • 이 클래스는 저장 가능하다!"라고 자바에게 알려주는 신호.
  • 부모가 구현(implements)했으면 자식도 저장 가능하다.
  • 이는 인터페이스 이므로 “해야 할 일을 정해 놓은 명세서(설계도)”이를 따랐음을 말하는 것은 "저장 능력을 보증"하는 것과 같다.

0-4. 직렬화(Serialization)와 역직렬화(Deserialization)

  • 직렬화 = 객체를 저장 가능한 데이터로 바꾸는 작업 (나열된 0101처럼 데이터를 일직선으로 만든다는 의미 (직선배열), serial -> series로 만들다.)
  • 역직렬화 = 그 데이터를 다시 객체로 만드는 작업
  • serialVersionUID: 직렬화된 객체를 역직렬화할 때 저장된 클래스와 현재 클래스가 “같은 클래스인지” 확인하기 위한 버전 ID이다.
  • FileOutputStream("devices.dat")
  • “ devices.dat라는 통 파일 뚜껑을 열고, 쓰기용 호스를 연결해서 그 안에 데이터를 부을 준비를 하는 것!”

1. 자바 코딩테스트

1-1. qr code

  • 문자열 control은 웬만하면 StringBuilder가 빠르다.
public String solution(int q, int r, String code) {
    StringBuilder answer = new StringBuilder();
    for (int i = 0; i < code.length(); i++){
        if (i % q == r){
            answer.append(code.charAt(i));
        }
    }
    return answer.toString();
}

1-2. 수열과 구간 쿼리 4

public int[] solution(int[] arr, int[][] queries) {
    int[] answer = {};
    for (int[] x : queries){
        for (int i = x[0]; i <= x[1]; i++){
            if (i % x[2] == 0){
                arr[i] += 1;
            }
        }
    }
    return arr;
}

1-3. 배열 만들기 6

  • 리스트에서 특정 위치의 원소를 꺼낼 때는 get으로 꺼낸다. 배열만 인덱스로 접근 가능
public int[] solution(int[] arr) {
    List <Integer> stk = new ArrayList<>();
    int i = 0;
    while (i < arr.length){
        if (stk.size() == 0){
            stk.add(arr[i]);
            i += 1;
        }
        else if (stk.size() > 0 && stk.get(stk.size()-1) == arr[i] ){
            stk.remove(stk.size()-1);
            i += 1;
        }

        else if (stk.size() > 0 && stk.get(stk.size()-1) != arr[i]){
            stk.add(arr[i]);
            i += 1;
        }
    }
    if (stk.size() == 0){
        return new int[]{-1};
    }
    return stk.stream().mapToInt(Integer::intValue).toArray();
}

1-4. 왼쪽 오른쪽

public String[] solution(String[] str_list) {
    String[] answer = {};
    for (int i = 0; i < str_list.length; i++){
        if (str_list[i].equals("l")){
            answer = Arrays.copyOf(str_list, i);
            break;
        }

        else if (str_list[i].equals("r")){
            answer = Arrays.copyOfRange(str_list, i + 1, str_list.length);
            break;
        }
    }
    return answer;
}

1-5. 문자 개수 세기

  • char은 그냥 비교해도 아스키로 비교되고 아예 아스키로 연산도 된다는 걸 기억하기, 애초에 아스키랑 매우 가깝다고 생각하자.
    • 빈배열 초기값 0
public int[] solution(String my_string) {
    int[] answer = new int[52];
    for (char i : my_string.toCharArray()){
        if (i <= 'z' && i >= 'a'){
            answer[i - 'a' + 26] += 1;
        }

        else {
            answer[i-'A'] += 1;
        }
    }
    return answer;
}

2. 객체지향 프로그래밍의 이해 (1)

2-1. Override, 객체 형변환

  • @Override는 필수는 아니지만 실수 방지를 위해 항상 쓰는 것이 좋다.
  • 부모로 형변환해도 오버라이딩된 메서드는 자식 기능이 실행되는 것을 동적 바인딩(Dynamic Binding)이라고 한다.
  • 다운캐스팅 전에는 꼭 instanceof를 체크해야 한다.
  • 하위 클래스에서 반드시 구현해야 할 메서드는 abstract로 선언한다.
  • // TODO:는 IDE에서 할 일 추적용으로 많이 씀. 표준 주석 태그는 아니지만 실무에서 많이 사용됨.
Protoss 예제
public class Protoss {
    private String name;
    private int hp;
    private int speed;
    private int dps;

    public Protoss(String name, int hp, int speed, int dps) {
        this.name = name;
        this.hp = hp;
        this.speed = speed;
        this.dps = dps;
        System.out.printf(">> 유닛이 생성되었습니다. --> 이름: %s, 체력: %d, 공격력: %d\n", this.name, this.hp, this.dps);
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getHp() {
        return this.hp;
    }

    public void setHp(int hp) {
        this.hp = hp;
    }

    public int getSpeed() {
        return this.speed;
    }

    public void setSpeed(int speed) {
        this.speed = speed;
    }

    public int getDps() {
        return this.dps;
    }

    public void setDps(int dps) {
        this.dps = dps;
    }

    public void move(String position){
        System.out.printf("%s(이)가 %s까지 이동합니다.\n", this.name, position);
    }

    public void attack(String target){
        System.out.printf("%s(이)가 %s(을)를 공격합니다. >> 데미지: %d\n", this.name, target, this.dps);
    }

    public String toString(){ // 같은 클래스 내부이기 때문에 그냥 name으로 해도 된다.
        return "{"+ 
        " name='" + getName() + "'" + 
    ", hp='" + getHp() + "'" +
    ", speed ='" + getSpeed() + "'" +
    ", dps ='" + getDps()+ "'" +
    "}";
    }
}
Zirot 예제
public class Zilot extends Protoss{
        public Zilot(String name, int hp, int speed, int dps){
            super(name, hp, speed, dps);
        }

        @Override // 부모 클래스에서 상속받은 메서드의 접근 범위를 줄일 수 없다. public으로 설정해놨는데 private로 오버라이딩은 안된다.
        public void move(String target){
            System.out.printf("[Zilot] %s(이)가 %s까지 빠른 속도로 이동합니다.\n", this.getName(), target); // 외부 클래스에서 접근하므로 getter 이용하여 접근한다.
        }

        @Override
        public void attack(String target){
            System.out.printf("[Zilot] %s(이)가 %s를 칼로 찌릅니다. >> 데미지:%d.\n", this.getName(), target, this.getDps());
        }

        public void swardAttack(String target){
            System.out.printf("[질럿의 고유 스킬] %s(이)가 %s를 칼로 공격합니다. >> 데미지:%d.\n", this.getName(), target,
            this.getDps() + 10);
        }
}
Dragon 예제
public class Dragon extends Protoss {
    public Dragon(String name, int hp, int speed, int dps){
        super(name, hp, speed, dps);
    }

        @Override 
        public void move(String target){
            System.out.printf("[Dragon] %s(이)가 %s까지 빠른 속도로 이동합니다.\n", this.getName(), target); 
        }

        @Override
        public void attack(String target){
            super.attack(target); // 부모의 원본을 실행한 후에 추가적으로 실행한다. 부모 원본 가져와서 선언할거면 그냥 안쓰는게 낫고 이렇게 확장할거면 쓰는게 좋다.
            System.out.println("원거리 공격을 위한 포탄 발사");
        }

        public void swardAttack(String target){
            System.out.printf("[드라군의 고유 스킬] %s(이)가 %s에게 포탄을 발사합니다. >> 데미지:%d.\n", this.getName(), target,
            this.getDps() + 10);
        }
}
테스트 예제
public static void main(String[] args) {
        Protoss p1 = new Protoss("프로브1", 100, 20, 10);
        p1.move("테란본진");
        p1.attack("테란본진");
        System.out.println(p1.toString());
        System.out.println("--------------");

        Zilot z1 = new Zilot("질럿1", 500, 300, 120);
        z1.move("테란본진");
        z1.attack("테란본진");
        z1.swardAttack("테란본진");
        System.out.println(z1.toString());
        System.out.println("--------------");

        Dragon d1 = new Dragon("드라군1", 100, 20, 10);
        d1.move("테란본진");
        d1.attack("테란본진");
        d1.fireAttack("테란본진");
        System.out.println(d1.toString());
        System.out.println("--------------");
    }

2-1-1. 객체 형변환

  • 자식 -> 부모 형변환하면 (Upcasting) 오버라이딩한건 자식 기능이 사용된다. 자식이 독자적으로 만든기능은 잠긴다. // 부모랑 비슷한 것만 남는다.
  • Boxing은 int -> integer처럼 기본형이 객체로 될때 많이 쓰인다.
  • 역변환시 최초 자식 클래스로 돌아가야한다.
  • ArrayList<> = new List<>(); 이렇게 부모를 자식으로 바꾸는건 안된다.
객체 형변환 예제
public class Ex10_객체형변환 {
    public static void main(String[] args) {
        Protoss p0 = new Protoss("프로브1호", 50, 30, 2);

        // 자식 객체로 생성 후에 부모형식의 객체에 참조
        Zilot z = new Zilot("질럿 1호", 300, 150, 120);
        Protoss p1 = z;

        // 선언은 부모 형식, 할당은 자식 형식 (위를 축약한 형태이다.) -> boxing
        Protoss p2 = new Dragon("드라군1호", 280, 120, 170);

        // 부모 객체로 변환되더라도 자식 클래스가 Override한 기능은 자식의 기능을 그대로 유지한다.
        p0.move("저그 본진");
        p0.attack("저그 본진");
        System.out.println("----------");

        p1.move("저그 본진");
        p1.attack("저그 본진");
        System.out.println("----------");

        p2.move("저그 본진");
        p2.attack("저그 본진");
        System.out.println("----------");

        // 자식이 새로 만든 기능은 사용할 수 없으나 역변환하여 사용할 수는 있다.

        Zilot rz = (Zilot) p1;
        Dragon rd = (Dragon) p2;
        rz.swardAttack("저그 본진");
        rd.fireAttack("저그 본진");

        // p1.swardAttack() -> 이건 에러

    }
}

2-1-2. 객체 배열

  • 객체 배열은 지정된 클래스의 객체로 할당해야 한다.
  • 서로 다른 클래스의 객체를 하나의 배열에 저장하기 위해서는 같은 부모로 부터 파생된 경우만 가능하고 이때 강제로 upcasting된다.
객체 배열 예제
public class Ex11_객체배열 {
    public static void main(String[] args) {
        Zilot[] z = new Zilot[3];
        z[0] = new Zilot("질럿 1호", 150, 100, 120);
        z[1] = new Zilot("질럿 2호", 160, 90, 140);
        z[2] = new Zilot("질럿 3호", 170, 80, 160);

        for (int i = 0; i < z.length; i++){
            z[i].move("테란 본진");
            z[i].attack("테란 본진");
            z[i].swardAttack("테란 본진");

            if (i + 1 < z.length){ // 변수 + 1이 종료조건보다 작다 = 마지막 회차 생략
                System.out.println("---------------");
            }
        }

        
    }
}
그룹지정 예제
  • instanceof는 원래 해당 클래스의 객체인지 검사한다.
  • 부모 배열에 자식을 넣으면 강제로 업캐스팅된다.
public class Ex12_그룹지정 {
    public static void main(String[] args) {
        Protoss[] p = new Protoss[5];

        p[0] = new Zilot("질럿 1호", 150, 100, 120);
        p[1] = new Dragon("드라군 1호", 150, 80, 200);
        p[2] = new Zilot("질럿 2호", 120, 100, 150);
        p[3] = new Dragon("드라군 2호", 170, 110, 240);
        p[4] = new Zilot("질럿 4호", 110, 130, 120);

        for (int i = 0; i < p.length; i++){
            p[i].move("저그 본진");
            p[i].attack("저그 본진");

            if (p[i] instanceof Zilot) {  // instanceof는 원래 해당 클래스의 객체인지 검사한다.
                Zilot z = (Zilot) p[i];
                z.swardAttack("저그 본진");
            }

            else if(p[i] instanceof Dragon){
                Dragon d = (Dragon) p[i];
                d.fireAttack("저그 본진");
            }
        }
    }

}

2-2. 클래스와 객체 연습문제

2-2-1. 동물원 사파리 대탐험

  • // TODO: 하위 클래스에서 구현 예정 이렇게 하위 클래스에서 구현 예정인 매서드를 관례적으로 표현한다.
public class Animal {
    public void move(){
        // TODO: 하위 클래스에서 구현 예정
    }

    public void makeSound(){
        // TODO: 하위 클래스에서 구현 예정
    }

    public static void main(String[] args) {
        Animal[] a = new Animal[3];

        Lion l = new Lion();
        Elephant e = new Elephant();
        Penguin p = new Penguin();

        a[0] = l;
        a[1] = e;
        a[2] = p;

        for (int i = 0; i < a.length; i++){
            a[i].move();
            a[i].makeSound();

            if (a[i] instanceof Penguin){
                Penguin x = (Penguin) a[i];
                x.swim();
            }
        }
    }
}

class Lion extends Animal{
    @Override
    public void move(){
        System.out.println("사자가 네 발로 달려갑니다.");
    }

    @Override
    public void makeSound(){
        System.out.println("사자가 포효합니다.");
    }
}

class Elephant extends Animal{
    @Override
    public void move(){
        System.out.println("코끼리가 천천히 걷습니다.");
    }

    @Override
    public void makeSound(){
        System.out.println("코끼리가 뿌웁~하고 웁니다.");
    }
}

class Penguin extends Animal{
    @Override
    public void move(){
        System.out.println("팽귄이 미끄러지듯 이동합니다.");
    }

    @Override
    public void makeSound(){
        System.out.println("팽귄이 삐약삐약 웁니다.");
    }

    public void swim(){
        System.out.println("펭귄이 헤엄칩니다!!");
    }
}

2-2-2. 쇼핑몰 상품 클래스 만들기

  • 다운 캐스팅 ((Food)i).checkExpiration(); 이렇게 하면된다.
public class Product_test {
    public static void main(String[] args) {
        Product[] p = new Product[3];

        Electronics e = new Electronics();
        Clothing c = new Clothing();
        Food f = new Food();

        p[0]= e;
        p[1]= c;
        p[2]= f;

        for (Product i : p){
            i.showInfo();

            if (i instanceof Food){
                ((Food)i).checkExpiration();
            }
        }
    }
}

public class Product {
    public void showInfo(){
        System.out.println("상품 정보 출력");
    }
}

public class Electronics extends Product {
    @Override
    public void showInfo(){
        System.out.println("전자체품입니다. 최신기기");
    }
}

public class Clothing extends Product{
    @Override
    public void showInfo(){
        System.out.println("옷 입니다, 계절별 신상품!");
    }
}

public class Food extends Product{
    @Override
    public void showInfo(){
        System.out.println("식품입니다. 유통기한을 확인하세요!");
    }

    public void checkExpiration(){
        System.out.println("유통기한을 확인하는 중입니다...");
    }
}

2-2-3. 음식 주문 시스템

  • 마지막에서 println(i)를 했을 때 %s: 패티와 빵이 조화를 이루는 메뉴\n여기에 \n이 하나 더 붙는 거라 엔터가 한번 더 쳐진다.
  • 따라서 둘 중에 하나를 빼야 엔터없이 딱 붙여서 문자들을 출력할 수 있다.
  • ctrl . 누르면 부모 생성자를 생성할 수 있다. 또 오버라이딩 할 것을 한방에 만들 수도 있다.
public class MenuTest {
    public static void main(String[] args) {
        MenuItem[] m = new MenuItem[3];

        Burger b = new Burger("치즈버거", 4000);
        Pizza p = new Pizza("페퍼로니 피자", 10000);
        Salad s = new Salad("그린 셀러드", 5000);

        m[0]= b;
        m[1]= p;
        m[2]= s;

        int answer = 0;

        for (MenuItem i : m){
            System.out.print(i);
            answer += i.getPrice();
        }
        System.out.printf("총 가격: %d원\n", answer);
        System.out.println("옵션 적용:");

        for (MenuItem i : m){
            if (i instanceof Burger){
                ((Burger) i).addOption();
            }

            else if (i instanceof Pizza){
                ((Pizza) i).addOption();
            }

            else if (i instanceof Salad){
                ((Salad) i).addOption();
            }
        }
    }
}

public class MenuItem {
    private String name;
    private int price;

    MenuItem(String name, int price){
        setName(name);
        setPrice(price);
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return this.price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public void addOption(){
        System.out.println("옵션 추가 기능은 각 음식에 따라 다릅니다.");
    }


    @Override
    public String toString() {
        return String.format("%s: 메뉴설명\n", this.name);
    }

}

public class Burger extends MenuItem{
    Burger(String name, int price){
        super(name, price);
    }

    @Override
    public String toString(){
        return String.format("%s: 패티와 빵이 조화를 이루는 메뉴\n", getName());
    }

    @Override
    public void addOption(){
        System.out.printf("\t%s -> 치즈추가\n", getName());
    }
}

public class Pizza extends MenuItem {
    Pizza(String name, int price){
        super(name, price);
    }

    @Override
    public String toString(){
        return String.format("%s: 치즈 듬뿍, 모두의 인기 메뉴\n", getName());
    }

    @Override
    public void addOption(){
        System.out.printf("\t%s -> 엣지 변경: 치즈 크러스트\n", getName());
    }
}

public class Salad extends MenuItem{
    Salad(String name, int price){
        super(name, price);
    }

    @Override
    public String toString(){
        return String.format("%s: 신선한 채소로 만든 건강식\n", getName());
    }

    @Override
    public void addOption(){
        System.out.printf("\t%s -> 드레싱 선택: 발사믹\n", getName());
    }
}

2-2-4. 추가 실습 : 스마트 헬스케어 센터 관리 시트넴 만들기

import java.util.ArrayList;
import java.util.List;

public class HealthCenterSystem {
    
    // 필드 이렇게 생성해도 되나->이 클래스 내에서 여러 번 쓰면 필드에 정의하고 아니면 생성자 안에 넣어서 지역변수로 사용한다.
    // 지역변수로 사용할땐 코드가 깔끔해지는 장점이 있다. 보니까 getter, setter도 따로 설정하지 않아도 돼서 이게 더 나은 것 같다.

    // 변수 이름은 "객체 자체"가 아니라, 그 객체를 가리키는 "라벨(참조 이름)"일 뿐이다.
    // 리스트에 들어가는 건 실제 객체 자체이고, 변수 이름은 없어도 전혀 문제 없다.

    // 객체: 실제 사람 "김영희"
    // 속성: "이름" = 김영희, "나이" = 25
    // 변수명: 그 사람을 코드 안에서 부르는 별명 같은 것 → Person p1
    // **속성(name="김영희")**이 현실의 "진짜 이름"
    // **변수명(p1)**은 코드에서 그걸 가리키는 "참조자"

    private List <HealthService> chart;
    // private  EquipmentRental chestPress = new EquipmentRental("체스트 프레스", 50000);
    // private  GroupExercise yoga = new GroupExercise("요가", 100000);
    // private  Supplement greenTea = new Supplement("녹차", 5000, 120);
    // private FitnessTest pops = new FitnessTest("팝스", 3000);

    // This는 생성자나 메서드 안에서만 처리할 수 있다.
    // 이제부터는 객체 내부 구조(필드) 초기화할 땐 생성자 안에서 처리하는 습관을 들여야 한다.

    HealthCenterSystem(){
    chart = new ArrayList<>();

    chart.add(new EquipmentRental("체스트 프레스", 50000));  // 이러면 리스트에 들어갈때 정보들만 들어가서 객체를 저장하는 메모리에 저장되고 참조이름은 정해지지 않는다.
    chart.add(new GroupExercise("요가", 100000));           // 하여 이 이름모를 객체를 출력하면 toString에 오버라이딩한 정보가 나온다.
    chart.add(new Supplement("녹차", 5000, 120));
    chart.add(new FitnessTest("팝스", 3000));
    }

    public List<HealthService> getChart() {
        return this.chart;
    }

    public void setChart(List<HealthService> chart) {
        this.chart = chart;
    }

    public void programList(){
        System.out.println("----전체 프로그램 목록----");
        for (HealthService i : chart){
            System.out.println(i);
        }
        System.out.println("------------------------");
    }

    public void allUse(){
        System.out.println("모든 프로그램을 실행합니다.");
        for (HealthService i : chart){
            i.use();
            if (i instanceof EquipmentRental){
                ((EquipmentRental)i).checkAvailable();
            }

            else if (i instanceof GroupExercise){
                ((GroupExercise)i).setInstructorName("김범수");
            }

            else if (i instanceof Supplement){
                ((Supplement)i).checkExpiration();
            }

            else if (i instanceof FitnessTest){
                ((FitnessTest)i).setDurationMinutes(120);
            }
        }
    }

    

}

public class GroupExercise extends HealthService {
    private String instructorName;

    // getter, setter
    public String getInstructorName() {
        return this.instructorName;
    }

    public void setInstructorName(String instructorName) {
        this.instructorName = instructorName;
    }

    // 생성자
    GroupExercise(String name, int price){
        super(name, price);
        System.out.println("헬스 프로그램 신청 프로그램이 신설되었습니다.");
    }

    //메서드

    public void showInfo(){
        System.out.println("헬스 프로그램을 예약합니다.\n");
    }

    public void use(){
        System.out.printf("선생님 이름과 함께 다시 사용해주세요"); // 추상화된 매서드를 오버라이딩할때는 똑같은 형식의 매서드를 정의하고 추가로 해야한다.
    }

    public void use(String name){
        setInstructorName(name);
        System.out.printf("[%s]선생님으로 예약되었습니다.\n", this.instructorName);
    }

    public void reserveSlot(){
        System.out.printf("[%s]선생님으로 예약 가능한 날짜는 총 17일 있습니다.\n", this.instructorName);
    }

    @Override
    public String toString(){
        return String.format("%s: %d원에 운동 프로그램이 구성되어 있습니다. 선생님을 말씀하시고 얼른 신청하세요 !", getName(), getPrice());
    }
}

public class Supplement extends HealthService {
    private int expirationDate;

    public int getExpirationDate() {
        return this.expirationDate;
    }

    public void setExpirationDate(int expirationDate) {
        this.expirationDate = expirationDate;
    }
    
    // 생성자
    Supplement(String name, int price, int expirationDate){
        super(name, price);
        this.expirationDate = expirationDate;
        System.out.println("건강식품 판매 프로그램이 신설되었습니다.");
    }

    //메서드

    public void showInfo(){
        System.out.println("건강 식품을 구매할 수 있습니다.\n");
    }

    public void use(){
        System.out.printf("건강 식품을 한 개 구매하였습니다.\n");
    }

    public void checkExpiration(){
        System.out.printf("해당 건강식품의 유효기간은 모두 %d일 입니다.\n", this.expirationDate);
    }

    @Override
    public String toString(){
        return String.format("%s: %d원에 건강식품을 구매할 수 있습니다! %d일 남았습니다!", getName(), getPrice(), this.expirationDate);
    }
}

public abstract class HealthService { // 추상화 클래스 생성 -> 공통 부분 만들기
    private String name;
    private int price;

    HealthService(String name, int price){
        this.name = name;
        this.price = price;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPrice() {
        return this.price;
    }

    public void setPrice(int price) {
        this.price = price;
    }

    public abstract void showInfo();

    public abstract void use();
    
    @Override
    public String toString(){
        return String.format("%s: %d원", this.name, this.price);
    }

}

public class EquipmentRental extends HealthService{
    private boolean available = true;

    // getter, setter
    public boolean isAvailable() {
        return this.available;
    }

    public void setAvailable(boolean available) {
        this.available = available;
    }

    // 생성자
    EquipmentRental(String name, int price){
        super(name, price);
        System.out.println("운동기구 대여 프로그램이 신설되었습니다.");
    }

    //메서드

    public void showInfo(){
        System.out.println("필요한 헬스기구를 예약합니다.\n");
    }

    public void use(){
        if (available){
            System.out.println("신청하신 물품 대여에 성공하셨습니다. 대여는 1인 1물건만 가능합니다.");
            setAvailable(false);
        }

        else {
            System.out.println("이미 물품을 대여한 상태입니다. 물품을 반납하고 이용해주세요.");
        }
    }

    public void checkAvailable(){
        System.out.printf("회원님의 물품 대여 서비스 이용은 %s 합니다", isAvailable() ? "가능": "불가능");
    }

    @Override
    public String toString(){
        return String.format("%s: %d원에 서비스 예약 가능합니다.", getName(), getPrice());
    }
}

2-2-5. 스마트 홈기기 제어 시스템 만들기

  • 이렇게 자식에서 부모의 기능을 재정의할 거라면 그냥 안해도 알아서 상속받는다.
    @Override
    public void showStatus() {
        super.showStatus();
    }

    @Override
    public void turnOff() {
        super.turnOff();
    }

    @Override
    public void turnOn() {
        super.turnOn();
    }
  • 아래는 직접작성한 정답코드이다.
public class SmartHomeTest {
    public static void main(String[] args) {

    SmartDevice[] devices = new SmartDevice[3];
    devices[0] = new SmartLight("스마트 전등");
    devices[1] = new SmartSpeaker("스마트 스피커");
    devices[2] = new SmartThermostat("스마트 온도조절기");

    // 모든 기기 전원 켜기
    for (int i = 0; i < devices.length; i++) {
    devices[i].turnOn();
    }

    // 상태 출력
    for (int i = 0; i < devices.length; i++) {
    devices[i].showStatus();
    }
    System.out.println();

    // 각 기기의 고유 기능 실행 (다운캐스팅 사용)
    SmartLight light = (SmartLight) devices[0];
    light.changeColor("파란색");
    SmartSpeaker speaker = (SmartSpeaker) devices[1];
    speaker.playMusic("Jazz");

    SmartThermostat thermostat = (SmartThermostat) devices[2];
    thermostat.setTemperature(24);
    }
}

class SmartDevice {
    private String name;
    private boolean isOn;

    SmartDevice(String name){
        this.name = name;
    }


    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }


    public boolean getIsOn() {
        return this.isOn;
    }

    public void setIsOn(boolean isOn) {
        this.isOn = isOn;
    }

    public void turnOn(){
        this.isOn = true;
        System.out.printf("%s가 켜졌습니다.\n", this.name);
    }

    public void turnOff(){
        this.isOn = false;
        System.out.printf("%s가 꺼졌습니다.\n", this.name);
    }

    public void showStatus(){
        System.out.printf("%s의 상태: %s입니다.\n", this.name, this.isOn ? "ON" : "OFF");
    }
}

class SmartLight extends SmartDevice {

    SmartLight(String name){
        super(name);
    }

    public void changeColor(String color){
        if (!getIsOn()){
            System.out.printf("%s가 꺼져있어 색상을 변경할 수 없습니다.\n", getName());
        }
        else {
            System.out.printf("%s의 색상이 %s로 변경되었습니다.\n", getName(), color);
        }
    }
    
}

class SmartSpeaker extends SmartDevice {
    SmartSpeaker(String name){
        super(name);
    }
    
    public void playMusic(String song){
        if (!getIsOn()){
            System.out.printf("%s가 꺼져있어 음악을 틀 수 없습니다.\n", getName());
        }
        else {
            System.out.printf("%s에서 %s를 재생합니다.\n", getName(), song);
        }
    }
}

class SmartThermostat extends SmartDevice {
    SmartThermostat(String name){
        super(name);
    }

    public void setTemperature(int degree){
        if (!getIsOn()){
            System.out.printf("%s가 꺼져있어 온도를 조절할 수 없습니다.\n", getName());
        }
        else {
            System.out.printf("%s가 온도를 %d로 조정합니다.\n", getName(), degree);
        }
}
}
profile
학생

0개의 댓글