스물세 번째 수업

정혅·2024년 3월 13일

더 조은 아카데미

목록 보기
28/76
post-thumbnail

사용자 정의 예외 예시 연습

package com.test.memo;

import java.util.Scanner;

class AgeInputException extends Exception {

    public AgeInputException() {
        System.out.println("올바른 나이의 입력이 아닙니다.");
    }

}

public class Practice1 {
    public static void main(String[] args) throws AgeInputException {

        Scanner sc = new Scanner(System.in);
        System.out.print("나이를 입력하세요: ");

        try {
            int age = sc.nextInt();
            if (age < 0)
                throw new AgeInputException(); //위 클래스가 생성되고 예외를 던진다. > 예외를 처리하는 부분으로 넘어감

            System.out.println("입력된 나이: " + age);
        } catch (AgeInputException e) {//위 예외를 잡아 처리한다.
            e.printStackTrace();
        }

    }
}
  • syso입력된 나이를 출력하는 부분은 예외가 발생하지 않았을 때만 실행된다.
  • 근데 위처럼 코드를 짜면 메인 메서드에 명시되어 있은 throws가 필요없어진다.
package com.test.memo;

import java.util.Scanner;

class AgeInputException extends Exception {//사용자 정의 예외 클래스 
    public AgeInputException() {
        super("유효하지 않은 나이가 입력되었습니다.");
    }
}

public class Practice1 {
    public static void main(String[] args) throws AgeInputException {
        System.out.print("나이를 입력하세요: ");
        int age = readAge();//나이를 다른 메서드에서 값을 받아온다. 
        System.out.println("당신은 " + age + "세입니다.");
    }

    public static int readAge() throws AgeInputException {
        Scanner keyboard = new Scanner(System.in);
        int age = keyboard.nextInt();
        if (age < 0) {
            AgeInputException excpt = new AgeInputException();
            throw excpt;//예외가 발생되면, 해다 예외를 상위 메서드로 전파한다. > main메서드에서도 throws하고있기때문에
        }
        return age;
    }
}
  • 사용자가 음수의 나이를 입력하면, AgeInputException이 발생해 해당 예외의 메시지가 출력되고 프로그램이 종료된다.

오전시험

  1. 다음 클래스를 이용해 객체를 복사하는 예제를 만들자.
package com.test.memo;

class Person implements Cloneable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void show() {
        System.out.println(name + " " + age);
    }
    @Override //어노테이션을 사용하면 메서드가 실제로 오버라이드되고있는지 컴파일러가 체크해주므로, 실수를 방지할 수 있다.
    protected Person clone() throws CloneNotSupportedException {//아예 형변환 시켜서 리턴 
        return (Person) super.clone();
    }//Person클래스를 상속한 하위 클래스에서 clone메서드에 접근하여 오버라이딩 할 수 있도록 
}

public class Practice1 {
    public static void main(String[] args) {
        Person person = new Person("정현지", 24);

        try {
            Person copy = person.clone();
            person.show();
            copy.show();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

  • clone메서드를 상속관계에서 사용하기위해 clone()메서드를 protected로 놓은것이다.

만약 해당 클래스가 CLoneable을 구현했다면, 일반적으로 예외를 던지지 않고 구현할 수 있다.

Cloneable을 구현한 클래스에서 clone메서드를 사용할때 CloneNotSupportedException을 던지지않아도 된다.

class Person implements Cloneable {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public void show() {
        System.out.println(name + " " + age);
    }

    @Override
    protected Object clone() {
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            // CloneNotSupportedException은 발생하지 않을 것이므로 무시해도 됨
            e.printStackTrace();
            return null;
        }
    }

}

public class Practice1 {
    public static void main(String[] args) {
        Person person = new Person("정현지", 24);

        try {
            Person copy = (Person) person.clone();
            person.show();
            copy.show();
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
    }
}
  • 위의 코드에서 clone 메서드에서 예외를 처리하는 것은 일종의 안전장치이지만, 실제로는 해당 예외가 발생할 가능성이 거의 없기 때문에 대부분 무시하거나 로그에 출력하는 정도로 처리됩니다.

더조은 패키지에 클래스끼리 나눠서 저장해놓아봤다.


class Product {
    int price;            // 제품의 가격
    int bonusPoint;        // 제품구매 시 제공하는 보너스점수

    Product(int price) {
        this.price = price;
        bonusPoint =(int)(price/10.0);
    }

    Product() {} // 기본 생성자
}

class Tv extends Product {
    Tv() {
        super(100);    //상위 클래스의 생성자에 100을 넣어주는
    }

    public String toString() { return "Tv"; }
}

class Computer extends Product {
    Computer() { super(200); }

    public String toString() { return "Computer"; }
}

class Audio extends Product {
    Audio() { super(50); }

    public String toString() { return "Audio"; }
}

class Buyer {            // 고객, 물건을 사는 사람
    int money = 1000;    // 소유금액
    int bonusPoint = 0;    // 보너스점수
    Product[] item = new Product[10];    // 구입한 제품을 저장하기 위한 배열
    int i = 0;            // Product배열에 사용될 카운터 > 몇개를 구입했는지 

    void buy(Product p) {
        if(money < p.price) {//p.price처럼 다이렉트로 접근은 좋지 않음 
            System.out.println("잔액이 부족하여 물건을 살수 없습니다.");
            return;
        }

        money -= p.price;            // 가진 돈에서 구입한 제품의 가격을 뺀다.
        bonusPoint += p.bonusPoint;    // 제품의 보너스 점수를 추가한다.
        item[i++] = p;                // 제품을 Product[] item에 저장한다.
        System.out.println(p + "을/를 구입하셨습니다.");//변수를 넣으면 toString()이 자동적으로 호출 
    }

    void summary() {            // 구매한 물품에 대한 정보를 요약해서 보여 준다.
        int sum = 0;             // 구입한 물품의 가격합계
        String itemList ="";     // 구입한 물품목록

        // 반복문을 이용해서 구입한 물품의 총 가격과 목록을 만든다.
        for(int i=0; i<item.length;i++) {
            if(item[i]==null) break;
            sum += item[i].price;
            itemList += item[i] + ", "; 
        }
        System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다.");
        System.out.println("구입하신 제품은 " + itemList + "입니다.");
    }
}
class PolyArgumentTest2 {
    public static void main(String args[]) {
        Buyer b = new Buyer();

        b.buy(new Tv()); //p.price에 tv의 주소값이 들어가있는것 = 100 
        b.buy(new Computer()); 
        b.buy(new Audio()); 
        b.summary();
    }
}
  • 위 코드에서 Product배열로 구입한 제품들을 저장할 수 있도록 했다. > 배열의 크기를 10으로 했기 때문에 11개 이상의 제품을 구입할 수 없다.

    • 그렇다고 배열의 크기를 무조건 크게 설정할 수 만도 없다.
  • 이런 경우, Vector클래스를 사용하면된다.

Vector 클래스

import java.util.Vector 꼭 import 해야한다.

  • 내부적으로 Object 타입의 배열을 가지고 있어서, 이 배열에 객체를 추가하거나 제거할 수 있게 작성할 수 있다. > 가변 크기의 배열을 구현

  • Vector내부에 값이 추가되면 자동으로 크기가 조절되며, 그 다음 객체들은 한 자리씩 뒤로 이동한다.

  • 배열의 크기를 알아서 관리해주기 때문에 저장할 인스턴스의 개수에 신경 쓰지 않아도 된다.

    • 동적으로 크기가 관리되는 객체배열일 뿐이다.!
public class Vector extends AbstractList
        implements List, Cloneable, java.io.Serializable{
    protected Object elementData[];
    ...
}

Vector 선언

Vector v = new Vector();//타입 미설정 Object로 선언된다.
Vector<Student> student = new Vector<Student>(); //타입설정 Student객체만 사용가능
Vector<Integer> num2 = new Vector<Integer>(); //타입설정 int타입만 사용가능
Vector<Integer> num3 = new Vector<>(); //new에서 타입 파라미터 생략가능
Vector<String> v2 = new Vector<String>(10);//초기 용량(capacity)지정
Vector<Integer> v3 = new Vector<Integer>(Arrays.asList(1,2,3)); //초기값 지정

메서드/생성자                      설명

Vector 의 변수.~ 선언하면 된다.

Vector()                                    10개의 객체를 저장할 수 있는 Vector인스턴스를 생성한다. > 기본으로 10개의 배열을 가지고있고,
10개 이상의 인스턴스가 저장되면, 자동적으로 크기가 증가된다. > 10이상이되면 증가된다. >> 그 배열을 복사하는 형태로 커지는것
boolean add(Object o)                  Vector에 객체를 추가한다. 추가에 성공하면 결과적으로 true, 실패하면 false를 반환한다.
boolean remove(Object o)             Vector에 저장되어 있는 객체를 제거한다. 제거에 성공하면 true, 실패하면 false를 반환한다
boolean isEmpty()                      Vector가 비어있는지 검사한다. 비어있으면 true, 비어있지 않으면 false를 반환한다.
Object get(int index)                       지정된 위치(index)의 객체를 반환한다. 반환타입이 Object타입이므로 적절한 타입으로의 형변환이 필요하다.
int size()                                  Vector에 저장된 객체의 개수를 반환한다.

setElementAt(E obj, int index)        주어진 인덱스 위치에 새로운 요소를 설정하는 메서드 > E : Vector에 저장된 요소의 타입 / obj : 설정할 요소 / index : 설정할 위치의 인덱스

Vector클래스 예제

import java.util.Vector;

public class VectorExample {
    public static void main(String[] args) {
        // Vector 생성
        Vector<String> vector = new Vector<>();

        // 요소 추가
        vector.add("Java");
        vector.add("Python");
        vector.add("C++");

        // 요소 출력
        for (String language : vector) {
            System.out.println(language);
        }
    }
}

Vector클래스 예제2 변환시키기 전

package com.test.joeun;

import java.util.Vector;

class PhoneInfo {
    private String name;
    private String phone;

    public PhoneInfo(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }

    public void showPhoneInfo() {
        System.out.println("이름 : " + name);
        System.out.println("전화 : " + phone);
    }

    @Override
    public String toString() {
        return name + " " + phone;
    }
}

public class Extest1 {

    public static void main(String[] args) {
        Vector vec = new Vector(); // 내부적으로 10개의 배열인것
        PhoneInfo info1 = new PhoneInfo("홍길동", "010");
        PhoneInfo info2 = new PhoneInfo("배트맨", "011");
        vec.add(info1);
        vec.add(info2);

        // int vecSize = vec.size(); > 이렇게 size를 따로 변수에 넣어서 for문에 저 변수를 넣는게 더 효율적임

        for (int i = 0; i < vec.size(); i++) {// 배열에 몇개가 등록되어있는지 > 현재 2
            PhoneInfo info = (PhoneInfo) vec.get(i);
            info.showPhoneInfo();
        }
        System.out.println("==================");
        for (Object obj : vec) { // 값을 참조만 할것이라면 for each문을 사용하는게 더 편리
            PhoneInfo info = (PhoneInfo) obj; // Object니까 형변환
            info.showPhoneInfo();
        }
        System.out.println("==================");
        for (Object obj : vec) {
            System.out.println(obj);
        }
    }
}
  • 메인 메소드에서 객체를 생성하고, 밑에서 형변환 시켜서 넣어줬는데, 제네릭 형식을 이용하면 형변환을 할 필요가 없다. (나중에 배운다.)
class VectorTest2 {// 제네릭 형식 > 메인에서 한것과 같은기능

    public static void main(String[] args) {
        Vector<PhoneInfo> vec = new Vector<PhoneInfo>();
        PhoneInfo info1 = new PhoneInfo("홍길동", "010");
        PhoneInfo info2 = new PhoneInfo("배트맨", "011");
        vec.add(info1);
        vec.add(info2);

        for (int i = 0; i < vec.size(); i++) {
            PhoneInfo info = vec.get(i);
            info.showPhoneInfo();
        }
        System.out.println("==================");
        for (PhoneInfo info : vec) {//이렇게 foreach문 쓰는게 효율
            info.showPhoneInfo();
        }
        System.out.println("==================");
        for (PhoneInfo info : vec) {
            System.out.println(info);
        }
    }
}


Vector 클래스 예제 2 변환시키기

  • 제네릭 형태
package com.test.joeun;

import java.util.Vector;

class Product {
    int price; // 제품의 가격
    int bonusPoint; // 제품구매 시 제공하는 보너스점수

    Product(int price) {
        this.price = price;
        bonusPoint = (int) (price / 10.0);
    }

    Product() {
    } // 기본 생성자
}

class Tv extends Product {
    Tv() {
        super(100);
    }

    public String toString() {
        return "Tv";
    }
}

class Computer extends Product {
    Computer() {
        super(200);
    }

    public String toString() {
        return "Computer";
    }
}

class Audio extends Product {
    Audio() {
        super(50);
    }

    public String toString() {
        return "Audio";
    }
}

class Buyer { // 고객, 물건을 사는 사람
    int money = 1000; // 소유금액
    int bonusPoint = 0; // 보너스점수

    Vector<Product> vector = new Vector<>();// 제네릭 형태로 자료형을 넣어줬음 > 내부적으로 10의 배열

    void buy(Product p) {
        if (money < p.price) {
            System.out.println("잔액이 부족하여 물건을 살수 없습니다.");
            return;
        }

        money -= p.price; // 가진 돈에서 구입한 제품의 가격을 뺀다.
        bonusPoint += p.bonusPoint; // 제품의 보너스 점수를 추가한다.
        vector.add(p); // 제품을 Product[] item에 저장한다.
        System.out.println(p + "을/를 구입하셨습니다.");
    }

    void refund(Product p) {//추가시킨 내용 
        if (vector.remove(p)) {
            money += p.price;
            bonusPoint -= p.bonusPoint;
            System.out.println(p + "를 환불하셨습니다.");
        } else
            System.out.println("해당하는 물품이 구매 내역에 없습니다.");
    }

    void summary() { // 구매한 물품에 대한 정보를 요약해서 보여 준다.
        int sum = 0; // 구입한 물품의 가격합계
        String itemList = ""; // 구입한 물품목록

        // 반복문을 이용해서 구입한 물품의 총 가격과 목록을 만든다.
			if (item.isEmpty()) {
				System.out.println("구매한 물품이 없습니다.");
				return;
			}
			for(Product pro : item) {
			sum += pro.price;
			itemList += pro + ", ";
		}
		System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다.");
		System.out.println("구입하신 제품은 " + itemList + "입니다.");
	}
}

public class Extest1 {
    public static void main(String[] args) {
        Buyer b = new Buyer();
        Tv tv = new Tv();
        Computer computer = new Computer();
        Audio audio = new Audio();

        b.buy(tv);
        b.buy(computer);
        b.buy(audio);
        System.out.println();
        b.refund(computer);
        b.summary();
    }
}
  • 메인 메서드에서 각 클래스의 객체를 다 만들어야지만, 저 저장된값들이 이어진다. buy인자에 new Computer한다면, refund할때는 새로운 객체를 또 생성해야하니, 당연히 해당 구매 물품을 찾을 수 없다고 뜬다.

  • 일반 형태
class Buyer { // 고객, 물건을 사는 사람
    int money = 1000; // 소유금액
    int bonusPoint = 0; // 보너스점수
    Vector vector = new Vector();

    void buy(Product p) {
        if (money < p.price) {
            System.out.println("잔액이 부족하여 물건을 살수 없습니다.");
            return;
        }

        money -= p.price; // 가진 돈에서 구입한 제품의 가격을 뺀다.
        bonusPoint += p.bonusPoint; // 제품의 보너스 점수를 추가한다.
        vector.add(p); // 제품을 Product[] item에 저장한다.
        System.out.println(p  + "을/를 구입하셨습니다.");
    }

    void refund(Product p) {
        if (vector.remove(p)) {
            money += p.price;
            bonusPoint -= p.bonusPoint;
            System.out.println(p + "를 환불하셨습니다.");
        } else
            System.out.println("해당하는 물품이 구매 내역에 없습니다.");
    }

    void summary() { // 구매한 물품에 대한 정보를 요약해서 보여 준다.
        int sum = 0; // 구입한 물품의 가격합계
        String itemList = ""; // 구입한 물품목록
        if(vector.isEmpty()) {    // Vector가 비어있는지 확인한다.
            System.out.println("구입하신 제품이 없습니다.");
            return;
        }

        // 반복문을 이용해서 구입한 물품의 총 가격과 목록을 만든다.
        for(int i=0; i<vector.size();i++) {
            Product p = (Product)vector.get(i);    //형변환 필수 
            sum += p.price;
            itemList += (i==0) ? "" + p : ", " + p;
        }
        System.out.println("구입하신 물품의 총금액은 " + sum + "만원입니다.");
        System.out.println("구입하신 제품은 " + itemList + "입니다.");
    }
}
  • vector.size(); 는 배열의 cnt값을 표현하는것과 같다.

    • 현재 내부에 저장되어있는 수를 의미하는것(배열길이x)
  • vector.remove(); 는 Vector에 저장되어있는 객체를 제거한다.

  • vector.inEmpty(); 는 비어있는지 검사한다. 비었으면 true, 안비었으면 false


전화번호부 Vector로 저장하기

package com.test.joeun;

import java.util.Scanner;
import java.util.Vector;

class MenuChoiceException extends Exception {
    private int choice;

    MenuChoiceException(int choice) {
        super("유효하지 않은 메뉴 값입니다.");
        this.choice = choice;
    }

    public void showWrongMenu() {
        System.out.println(choice + "에 해당하는 선택은 존재하지 않습니다.");
        System.out.println("메뉴 선택을 처음부터 다시 진행합니다.");
    }
}

class PhoneInfo {
    private String name;
    private String phone;

    PhoneInfo(String name, String phone) {
        this.name = name;
        this.phone = phone;
    }

    public String getName() {
        return name;
    }

    public void showPhoneInfo() {
        System.out.println("이름 : " + name);
        System.out.println("전화번호 : " + phone);
    }
}

class PhoneUnivInfo extends PhoneInfo {
    private String major;
    private int year;

    PhoneUnivInfo(String name, String phone, String major, int year) {
        super(name, phone);
        this.major = major;
        this.year = year;
    }

    public void showPhoneInfo() {
        super.showPhoneInfo();
        System.out.println("전공 : " + major);
        System.out.println("학년 : " + year);
    }
}

class PhoneCompanyInfo extends PhoneInfo {
    private String company;

    PhoneCompanyInfo(String name, String phone, String company) {
        super(name, phone);
        this.company = company;
    }

    public void showPhoneInfo() {
        super.showPhoneInfo();
        System.out.println("회사 : " + company);
    }
}

class PhoneBook {
    private static PhoneBook pb;
    private Vector<PhoneInfo> pInfoArr;

    private PhoneBook(int sizePhoneInfo) {
        //메모리를 효율적으로 관리, 무한히 커지는 것을 방지하기 위해 크기를 제한할 수 있다.
        //그러나 밑에는 제한하는것이 아닌 초기 크기를 지정하는것이다. 초기크기 이상의 요소가 추가되면 자동으로 크기를 조절한다. 
        pInfoArr = new Vector<>(sizePhoneInfo);
    }

    public static PhoneBook getPhoneBookInst(int sizePhoneInfo) {
        if (pb == null)
            pb = new PhoneBook(sizePhoneInfo);
        return pb;
    }

    public void inputPhoneInfo(PhoneInfo pInfo) {
//        if( size >= sizePhoneInfo) > 계속 저장할 수 있으니까 필요없게됌
//        {
//            System.out.println("더 이상 저장할 수 없습니다.");
//            return;
//        }
        int size = pInfoArr.size();
        int i = 0;
        for (i = 0; i < size; i++) {
            // break문을 통해 해당하는 i의 위치를 찾으면 그 위치에 pInfo를 추가하게끔
            if (pInfoArr.get(i).getName().compareTo(pInfo.getName()) > 0)
                break;

        }
        pInfoArr.add(i, pInfo);// i위치에 pInfo를 추가 > pInfo의 이름이 이미 있는 요소들보다 앞이든 뒤이든 올바르게 처리
        // size++; Vector의 크기를 증가시키는건데 굳이..
    }

    public void searchPhoneInfo(String name) {
        int result = search(name);
        if (result != -1)
            pInfoArr.get(result).showPhoneInfo();// result번째에 있는 객체 주소값 반환 >showPhoneinfo에 있는 정보 출력
        else
            System.out.println("찾으시는 데이터가 없습니다.");
    }

    public void deletePhoneInfo(int idx) {
        pInfoArr.remove(idx);
        System.out.println("삭제가 완료되었습니다.");
    }

    public int search(String name) {
        for (int i = 0; i < pInfoArr.size(); i++)// 저장된 pInfoArr의 개수만큼 반복
        {
            if (pInfoArr.get(i).getName().compareTo(name) == 0)
                return i;
        }
        return -1;
    }

    public void showAllPhoneInfo() {
        for (int i = 0; i < pInfoArr.size(); i++)
            pInfoArr.get(i).showPhoneInfo();
        /*
         * 1번 for(PhoneInfo pInfo: pInfoArr) pInfo.showPhoneInfo(); 이 방법이 가장 효율적
         * 
         * 2번 Iterator<PhoneInfo> itr = pInfoArr.iterator(); >>Iterator 반복자
         * while(itr.hasNext()) >>while문안에는 true/false반환하는애만 들어갈 수 있기때문에 유추할 수 있어야함 >>다음
         * 요소가 있으면 true/ 없으면 false >> 있으면 그 객체의 주소를 반환 itr.next().showPhoneInfo();
         */
    }
}

interface PhoneMenuString {
    int INPUT_PHONEINFO = 1;
    int SEARCH_PHONEINFO = 2;
    int DELETE_PHONEINFO = 3;
    int SHOW_ALL_PHONEINFO = 4;
    int PROGRAM_QUIT = 5;

    int GENERAL = 1;
    int UNIVERCITY = 2;
    int COMPANY = 3;

    int YES = 1;
    int NO = 2;
}

class PhoneUI {
    private static final int MAX_CNT = 100;
    public static Scanner sc = new Scanner(System.in);
    private static PhoneBook pb = PhoneBook.getPhoneBookInst(MAX_CNT);

    public static void mainMenu() {
        System.out.println("선택하세요...");
        System.out.println("1. 데이터 입력");
        System.out.println("2. 데이터 검색");
        System.out.println("3. 데이터 삭제");
        System.out.println("4. 모든 데이터 보기");
        System.out.println("5. 프로그램 종료");
        System.out.print("선택 : ");
    }

    public static void inputMenu() {
        System.out.println("1. 일반, 2. 대학, 3. 회사");
    }

    public static void inputMenuChoice() throws MenuChoiceException {
        int choice = 0;
        choice = sc.nextInt();
        sc.nextLine();
        if (choice < PhoneMenuString.GENERAL || choice > PhoneMenuString.COMPANY)
            throw new MenuChoiceException(choice);
        switch (choice) {
        case PhoneMenuString.GENERAL:
            inputGeneralPhoneInfo();
            break;
        case PhoneMenuString.UNIVERCITY:
            inputUniversityPhoneInfo();
            break;
        case PhoneMenuString.COMPANY:
            inputCompanyPhoneInfo();
            break;
        }
    }

    public static void inputGeneralPhoneInfo() {
        String name;
        String phone;

        System.out.println("데이터 입력을 시작합니다.");
        System.out.print("이름 : ");
        name = sc.nextLine();
        System.out.print("전화번호 : ");
        phone = sc.nextLine();
        System.out.println("데이터 입력이 완료되었습니다.");
        pb.inputPhoneInfo(new PhoneInfo(name, phone));
    }

    public static void inputUniversityPhoneInfo() {
        String name;
        String phone;
        String major;
        int year;

        System.out.println("데이터 입력을 시작합니다.");
        System.out.print("이름 : ");
        name = sc.nextLine();
        System.out.print("전화번호 : ");
        phone = sc.nextLine();
        System.out.print("전공 : ");
        major = sc.nextLine();
        System.out.print("학년 : ");
        year = sc.nextInt();
        sc.nextLine();
        System.out.println("데이터 입력이 완료되었습니다.");
        pb.inputPhoneInfo(new PhoneUnivInfo(name, phone, major, year));
    }

    public static void inputCompanyPhoneInfo() {
        String name;
        String phone;
        String company;

        System.out.println("데이터 입력을 시작합니다.");
        System.out.print("이름 : ");
        name = sc.nextLine();
        System.out.print("전화번호 : ");
        phone = sc.nextLine();
        System.out.print("회사 : ");
        company = sc.nextLine();
        System.out.println("데이터 입력이 완료되었습니다.");
        pb.inputPhoneInfo(new PhoneCompanyInfo(name, phone, company));
    }

    public static void searchPhoneInfo() {
        String name;
        System.out.println("데이터 검색을 시작합니다.");
        System.out.println("검색하시고자 하는 이름을 입력하세요.");
        name = sc.nextLine();
        pb.searchPhoneInfo(name);
    }

    public static void deletePhoneInfo() {
        String name;
        int result = 0;
        int answer = 0;
        System.out.println("검색하시고자 하는 이름을 입력하세요.");
        name = sc.nextLine();
        result = pb.search(name);
        if (result != -1) {
            System.out.println("정말 삭제하시겠습니까? 1. Yes 2. No");
            answer = sc.nextInt();
            sc.nextLine();
            switch (answer) {
            case PhoneMenuString.YES:
                pb.deletePhoneInfo(result);
                break;
            case PhoneMenuString.NO:
                break;
            default:
                System.out.println("잘못 누르셨습니다.");
            }
        } else
            System.out.println("삭제하시려는 데이터가 없습니다.");
    }

    public static void showAllPhoneInfo() {
        pb.showAllPhoneInfo();
    }
}

public class Extest1 {
    public static void main(String[] args) {
        int choice = 0;

        while (true) {
            try {
                PhoneUI.mainMenu();
                choice = PhoneUI.sc.nextInt();
                PhoneUI.sc.nextLine();
                if (choice < PhoneMenuString.INPUT_PHONEINFO || choice > PhoneMenuString.PROGRAM_QUIT)
                    throw new MenuChoiceException(choice);

                switch (choice) {
                case PhoneMenuString.INPUT_PHONEINFO:
                    PhoneUI.inputMenu();
                    PhoneUI.inputMenuChoice();
                    break;
                case PhoneMenuString.SEARCH_PHONEINFO:
                    PhoneUI.searchPhoneInfo();
                    break;
                case PhoneMenuString.DELETE_PHONEINFO:
                    PhoneUI.deletePhoneInfo();
                    break;
                case PhoneMenuString.SHOW_ALL_PHONEINFO:
                    PhoneUI.showAllPhoneInfo();
                    break;
                case PhoneMenuString.PROGRAM_QUIT:
                    return;

                }
            } catch (MenuChoiceException e) {
                System.out.println(e.getMessage());
                e.showWrongMenu();
            }
        }
    }
}
  • phoneBook만 수정하면 되니까 유지보수가 좋음 ! 결합도가 낮다고 한다 > 높으면 유지보수가 좋지 않음

Chapter7 연습문제 - 풀이 포함

  1. 섯다 카드 20장을 포함하는 섯다카드 한 벌(SutdaDeck클래스) 을 정의한 것이다. 섯다 카드 20장을 담는 SutdaCard배열을 초기화 하시오. 단, 섯다카드는 1~10까지의 숫자가 적힌 카드가 한 쌍씩 있고, 숫자가 1, 3, 8인 경우에는 둘 중 한 장은 광이어야 한다. 즉 SutdaCard의 인스턴스 변수 isKwang의값이 true여야한다.

내가 푼 풀이 - 틀림

package com.test.memo;

class SutdaDeck {
    final int CARD_NUM = 20;
    SutdaCard[] cards = new SutdaCard[CARD_NUM];

    SutdaDeck() {

        for (int i = 0; i < CARD_NUM; i++) {//1부터 20까지 할 수도 있지만 배열이므로 0부터 하는게 낫
            int num = i % 10 + 1;
            boolean isKwang = (num == 1 || num == 3 || num == 8);
            cards[i] = new SutdaCard(num, isKwang);
        }
    }
}

class SutdaCard {
    int num;
    boolean isKwang;

    SutdaCard() {
        this(1, true);
    }

    SutdaCard(int num, boolean isKwang) {
        this.num = num;
        this.isKwang = isKwang;
    }

// info() Object toString() . 대신 클래스의 을 오버라이딩했다
    public String toString() {
        return num + (isKwang ? "K" : "");
    }
}

public class Practice1 {
    public static void main(String[] args) {
        SutdaDeck deck = new SutdaDeck();
        for (int i = 0; i < deck.cards.length; i++)
            System.out.print(deck.cards[i] + ",");
    }
}
  • 그런데 모든 패의 138이 다 K가 붙어서 출력 절반으로 어떻게 나누지..?
    • 나는 전체 138이 k가 다 나오는데..
    • 생각해보니까, num이 138이였을떄 참을 줬어야하고, 아니였을때 거짓을 줬어야하는데 그 생각을 못했네..아 난 바보다 걍 저기서 i < 10 같이 줬으면 됐는데

정답

  • 1~10일때 참이니까 저렇게 조건을 함께 연산자와 걸어두면, 10~20 일때는 자동으로 false로 생성

  • AND(&&)가 OR(||)보다 우선순위가 높기 때문에 괄호를 꼭 사용해야 한다.


  1. 문제 의 클래스에 다음에 정의된 새로운 메서드를 추가하고 테스트 하 7-1 SutdaDeck
    시오.
    [ ] 주의 Math.random() . 을 사용하는 경우 실행결과와 다를 수 있음

    1. : shuffle 메서드명
      기 능 배열 에 담긴 카드의 위치를 뒤섞는다 사용 : cards .(Math.random() )
      반환타입 없음 :
      매개변수 없음 :
    2. : pick 메서드명
      기 능 배열 에서 지정된 위치의 를 반환한다 : cards SutdaCard .
      반환타입 : SutdaCard
      매개변수 위치 : int index -
    3. : pick 메서드명
      기 능 배열 에서 임의의 위치의 를 반환한다 사용 : cards SutdaCard .(Math.random() )
      반환타입 : SutdaCard
      매개변수 없음 :

    내가 푼 풀이 - 1번 범위만 틀리고 그 외에는 맞게했다.

package com.test.memo;

class SutdaDeck {
    final int CARD_NUM = 20;
    SutdaCard[] cards = new SutdaCard[CARD_NUM];

    SutdaDeck() {

        for (int i = 0; i < CARD_NUM; i++) {
            int num = i % 10 + 1;
            boolean isKwang = (i <10) && (num == 1 || num == 3 || num == 8);
            cards[i] = new SutdaCard(num, isKwang);
        }
    }

    void shuffle() {
        int ran = (int) (Math.random() * cards.length);//0~19
        for (int i = 0; i < cards.length; i++) {
            SutdaCard temp = cards[i];
            cards[i] = cards[ran];
            cards[ran] = temp;
        }
    }

    public SutdaCard pick(int index) {
        return cards[index];
    }

    public SutdaCard pick() {
        int ran = (int) (Math.random() * cards.length);
        return cards[ran];//이렇게해도 되지만 return pick(ran) 오버라이딩을 호출하는 것이 가능하다. > 다소 비효율적이지만 재사용성을 높이기 위해서다. 
    }
}

class SutdaCard {
    int num;
    boolean isKwang;

    SutdaCard() {
        this(1, true);
    }

    SutdaCard(int num, boolean isKwang) {
        this.num = num;
        this.isKwang = isKwang;
    }

    public String toString() {
        return num + (isKwang ? "K" : "");
    }
}

public class Practice1 {
    public static void main(String[] args) {
        SutdaDeck deck = new SutdaDeck();
        System.out.println(deck.pick(0));
        System.out.println(deck.pick());
        deck.shuffle();
        for (int i = 0; i < deck.cards.length; i++)
            System.out.print(deck.cards[i] + ",");
        System.out.println();
        System.out.println(deck.pick(0));
    }
}
  • 위에 문제 다 못풀어서 k가 138에 전부 붙어서 나온다..
  • 유효성 검사 안했다.

풀이

  1. shuffle메서드에서 for문이 돌때마다 난수를 생성해서 섞어야했는데 for문 밖에서 난수를 생성했다..

  2. 인덱스의 유효성을 검사하지 않았다..

  • pick메서드는 매개변수에 대한 유효성 검사가 필요하다.

    • 그렇지 않으면 배열의 index범위를 넘어서서 ArrayIndexOutOfBounndsException이 발생할 수 있다.

매개변수가 있는 메서드는 반드시 작업 전에 유효성 검사를 해야 한다.


  1. 오버라이딩의 정의와 필요성에 대해서 설명하시오. 필요성 안써서 틀림 처리
  • 자식 클래스에서 부모 클래스의 메서드를 다시 정의하는것을 말한다.

  • 메서드의 이름, 반환 타입, 매개변수 타입 및 개수가 동일해야한다.

  • 필요성은 모르겠당..


  1. 다음 중 오버라이딩의 조건으로 옳지 않은 것은 모두 고르시오 ? ( ) 정답 맞춤

a. . 조상의 메서드와 이름이 같아야 한다
b. . 매개변수의 수와 타입이 모두 같아야 한다
c. . 접근 제어자는 조상의 메서드보다 좁은 범위로만 변경할 수 있다
d. . 조상의 메서드보다 더 많은 수의 예외를 선언할 수 있다

  • c, d

JDK1.5부터 '공변 반환타입(convariant return type)'이 추가되어, 반환타입을 자손 클래스의 타입으로 변경가능하도록 조건이 완화되었다.

  • 조상 클래스의 메서드를 자손 클래스에서 오버라이딩 할 때

    1. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.

    2. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.

    3. 인스턴스 메서드를 static메서드로 또는 그 반대로 변경할 수 없다.


  1. 다음의 코드는 컴파일하면 에러가 발생한다 그 이유를 설명하고 에러를 수정하기 위해서는 코드를 어떻게 바꾸어야 하는가? 맞았긴 한데 풀이랑 틀림
  • 제일 먼저 상위 생성자를 호출해주어야한다.

  • Tv메서드 안에 super(int 숫자) > 를 넣어준다.

  • Product클래스에 기본 생성자 Product()가 없기 때문에 기본 생성자를 추가해줘야한다.

    • 내가 위에서 생각했던 답은 맞는 정의이긴 하나, 애초에 super()는 조상클래스인 Product의 기본 생성자 Product()를 호출하는 것인데, Product클래스에는 기본 생성자 Product()가 정의되어있지않아 에러가 발생한 것이다.

      • Product()클래스에는 이미 Product(int price)라는 생성자가 정의되어 있기 때문에 컴파일러가 자동적으로 추가해주지 않아 직접 추가해야한다.
    1. Tv() {} 안에 super(); 상위 클래스의 default 생성자 호출을 컴파일러가 자동으로 삽입해준다.
    • 보통그러면 Tv(){}안에 super(3);라던가 넣어주면 되는데 > 내가 한 답

    • 책에서는 상위클래스에서 매개변수가 없는 default형태로 생성자를 호출하는 형태로 답을 넣었다.


  1. 자손 클래스의 생성자에서 조상 클래스의 생성자를 호출해야하는 이유는?정답 맞춤
  • 조상 클래스의 멤버 변수나 초기화 블록 등을 올바르게 초기화하기 위해서입니다.

    • 자손 클래스가 생성될 때, 자손 클래스의 생성자가 호출되면 먼저 조상 클래스의 생성자가 호출됩니다.

    • 만약 조상 클래스의 생성자를 명시적으로 호출하지 않으면 자동으로 조상 클래스의 기본 생성자가 호출됩니다. 이때 조상 클래스의 멤버 변수나 초기화 블록이 초기화되지 않으면, 올바른 상태로 객체가 생성되지 않을 수 있습니다.


  1. 다음 코드의 실행했을 때 호출되는 생성자의 순서와 실행결과를 적으시오. 정답 맞춤
class Parent {
    int x = 100;

    Parent() {
        this(200);
    }

    Parent(int x) {
        this.x = x;
    }

    int getX() {
        return x;
    }
}

class Child extends Parent {
    int x = 3000;

    Child() {
        this(1000);
    }

    Child(int x) {
        this.x = x;
    }
}

class Exercise7_7 {
    public static void main(String[] args) {
        Child c = new Child();
        System.out.println("x=" + c.getX());
    }
}
  1. 메인 메서드에서 Child 객체를 만들면, Child 생성자에서 상위 클래스의 생성자가 호출된다.

    • 그래서 Parent클래스의 x는 200이 되고, Child클래스의 x는 1000이 된다.
  2. 메인 메서드에서 c.getx() 했으므로, getx는 상위클래스에 있으므로x = 200; 이라고 출력될것이다.

this()라고 되어있지만 java컴파일러가 super(){}형태로 삽입시켜놓는다. 그러므로 위와같은 결과가 출력되는것이다.


  1. 다음 중 접근제어자를 접근범위가 넓은 것에서 좁은 것의 순으로 바르게 나열한 것은? 정답 맞춤

a. public-protected-(default)-private
b. public-(default)-protected-private
c. (default)-public-protected-private
d. private-protected-(default)-public

  • a

  1. 다음 중 제어자 final 을 붙일 수 있는 대상과 붙였을 때 그 의미를 적은 것이다
    옳지 않은 것은 모두 고르시오 ? ( ) 틀림

a. - 지역변수 값을 변경할 수 없다
b. - 클래스 상속을 통해 클래스에 새로운 멤버를 추가할 수 없다
c. - 메서드 오버로딩을 할 수 없다
d. - 멤버변수 값을 변경할 수 없다

  • b - 애초에 상속이 안되지않나.. > 말을 웃기게 써놓은건 맞다고 하심 (선생님이)

오버로딩이 아닌 오버라이딩을 할 수 없다.!!

정답


  1. MyTv2 isPowerOn, channel, volume 클래스의 멤버변수 을 클래스 외부에서 접근할
    수 없도록 제어자를 붙이고 대신 이 멤버변수들의 값을 어디서나 읽고 변경할 수 있도록
    getter setter . 와 메서드를 추가하라 유효성검사를 하지 않아서 틀림처리 - setChannel
package com.test.memo;

class MyTv2 {
    private boolean isPowerOn;
    private int channel;
    private int volume;
    final int MAX_VOLUME = 100;
    final int MIN_VOLUME = 0;
    final int MAX_CHANNEL = 100;
    final int MIN_CHANNEL = 1;

    public void setChannel(int num) {
        if(num > MAX_CHANNEL || num < MIN_CHANNEL)
        return;
        this.channel = num;
    }

    public int getChannel() {
        return channel;
    }

    public void setVolume(int num) {
        this.volume = num;
    }

    public int getVolume() {
        return volume;
    }

}

class Practice2 {
    public static void main(String[] args) {
        MyTv2 t = new MyTv2();
        t.setChannel(10);
        System.out.println("CH:" + t.getChannel());
        t.setVolume(20);
        System.out.println("VOL:" + t.getVolume());
    }
}
  • 매개변수가 있는 메서드는 반드시 작업 전에 넘겨받은 값의 유효성 검사를 해야한다.

  1. 문제 7-10에서 작성한 MyTv2클래스에 이전 채널(previous channel) 로 이동하는기능의 메서드를 추가해서 실행결과와 같은 결과를 얻도록 하시오. 유효성 검사 안해서 틀림처리, 나랑 조금 다른 풀이

    [Hint] 이전 채널의 값을 저장할 멤버변수를 정의하라.
    메서드명 : gotoPrevChannel
    기 능 현재 채널을 이전 채널로 변경한다 : .
    반환타입 없음 :
    매개변수 없음 :

package com.test.memo;

class MyTv2 {
    private boolean isPowerOn;
    private int channel;
    private int previousCh;//이전 채널 저장할 참조 변수 
    private int volume;
    final int MAX_VOLUME = 100;
    final int MIN_VOLUME = 0;
    final int MAX_CHANNEL = 100;
    final int MIN_CHANNEL = 1;

    public void setChannel(int channel) {//유효성검사ㅠ
        previousCh = this.channel;
        this.channel = channel;

    }

    public int getChannel() {
        return channel;
    }

    public void setVolume(int num) {//여기도 유효성 검사  
        this.volume = num;
    }

    public int getVolume() {
        return volume;
    }

    public void gotoPrevChannel() {//여기서 굳이 저럴 필요없이 바로 setChannel을 호출해주면 됐다. 
        int temp = previousCh;
        previousCh = channel;
        channel = temp;
    }

}

class Practice2 {
    public static void main(String[] args) {
        MyTv2 t = new MyTv2();
        t.setChannel(10);
        System.out.println("CH:" + t.getChannel());
        t.setChannel(20);
        System.out.println("CH:" + t.getChannel());
        t.gotoPrevChannel();
        System.out.println("CH:" + t.getChannel());
        t.gotoPrevChannel();
        System.out.println("CH:" + t.getChannel());
    }
}
//CH:10
//CH:20
//CH:10
//CH:20


  1. 다음 중 접근 제어자에 대한 설명으로 옳지 않은 것은 모두 고르시오 ? ( ) 정답

a. public . 은 접근제한이 전혀 없는 접근 제어자이다
b. (default) , . 가 붙으면 같은 패키지 내에서만 접근이 가능하다
c. . 지역변수에도 접근 제어자를 사용할 수 있다
d. protected , . 가 붙으면 같은 패키지 내에서도 접근이 가능하다
e. protected , . 가 붙으면 다른 패키지의 자손 클래스에서 접근이 가능하다

  • c

  1. Math클래스의 생성자는 접근 제어자가 private이다. 그 이유는 무엇일까? 틀림
  • 캡슐화를 위해, 클래스의 고유성을 위해??


  1. 문제7-1 에 나오는 섯다카드의 숫자와 종류(isKwang) 는 사실 한번 값이 지정되면
    변경되어서는 안 되는 값이다 카드의 숫자가 한번 잘못 바뀌면 똑같은 카드가 두 장이 .
    될 수 도 있기 때문이다 이러한 문제점이 발생하지 않도록 아래의SutdaCard 를 수정하시오 . 상수는 대문자 안해서 틀림 처리
package com.test.memo;

class SutdaCard {
    final int num;// 상수로 지정해서 변경할 수 없도록 > 상수는 대문
    boolean isKwang;

    SutdaCard() {
        this(1, true);
    }

    SutdaCard(int num, boolean isKwang) {
        this.num = num;
        this.isKwang = isKwang;
    }

    public String toString() {
        return num + (isKwang ? "K" : "");
    }
}

class Practice {
    public static void main(String[] args) {
        SutdaCard card = new SutdaCard(1, true);
        System.out.println(card);// toString자동호출
    }
}


  1. 클래스가 다음과 같이 정의되어 있을 때 형변환을 올바르게 하지 않은 것은 ? 틀림

    모두 고르시오

class Unit {}
class AirUnit extends Unit {}
class GroundUnit extends Unit {}
class Tank extends GroundUnit {}
class AirCraft extends AirUnit {}

Unit u = new GroundUnit();
Tank t = new Tank();
AirCraft ac = new AirCraft();

a. u = (Unit)ac;
b. u = ac;
c. GroundUnit gu = (GroundUnit)u;
d. AirUnit au = ac;
e. t = (Tank)u;
f. GroundUnit gu = t;

  • b, d, f

정답

  • e

    조상타입의 인스턴스를 자손타입으로 형변환 할 수 없다.

    • 이런 경우 컴파일 할때는 에러가 나진 않지만 런타임 시킬 때 오류가 난다.

    • Unit클래스는 나머지 네 개 클래스의 조상이므로 형변황이 가능하며, 심지어는 생략할 수 있다.

    • 조상타입의 참조변수로 자손 타입의 인스턴스를 참조하는 것이 가능하기 때문에 e를 제외한 코드는 모두 가능하다.


  1. 연산결과가 true가 아닌 것은? (모두 고르시오) 정답

class Car {}
class FireEngine extends Car implements Movable {}
class Ambulance extends Car {}

FireEngine fe = new FireEngine();

a. fe instanceof FireEngine
b. fe instanceof Movable
c. fe instanceof Object
d. fe instanceof Car
e. fe instanceof Ambulance

  • e

  1. 아래 세 개의 클래스로부터 공통부분을 뽑아서 이라는 클래스를 만들고 이 Unit ,
    클래스를 상속받도록 코드를 변경하시오. 틀림 > 결과는 맞았는데 과정이 : 왜 추상을 생각 못한거니..
class Unit {
    int x, y;

    public Unit(int x, int y) {
        this.x = x;
        this.y = y;
    }

    void move(int x, int y) {

    }

    void stop() {

    }
}

class Marine extends Unit {

    public Marine(int x, int y) {
        super(x, y);
    }

    void move() {
        super.move(x, y);
    }

    void changeMode() {

    }
}

class Dropship extends Unit {

    public Dropship(int x, int y) {
        super(x, y);
    }

    void move() {
        super.move(x, y);
    }

    void load() {

    }

    void unload() {

    }
}
  • 생성자를 굳이 만들 필요는 없었던것 같긴한데,,,

  • 없애봤는데 오류 안나는거 보니까 필요 없는듯


  1. 다음과 같은 실행결과를 얻도록 코드를 완성하시오. 정답 맞춤

[Hint]

instanceof . 연산자를 사용해서 형변환한다
메서드명 : action
기 능 주어진 객체의 메서드를 호출한다 : .
DanceRobot , dance() , 인 경우 를 호출하고
SingRobot , sing() , 인 경우 을 호출하고
DrawRobot , draw() . 인 경우 를 호출한다
반환타입 없음 :
매개변수 인스턴스 또는 의 자손 인스턴스 : Robot r - Robot Robot

package com.test.memo;

class Robot {
}

class DanceRobot extends Robot {
    void dance() {
        System.out.println("춤을 춥니다.");
    }
}

class SingRobot extends Robot {
    void sing() {
        System.out.println("노래를 합니다.");
    }
}

class DrawRobot extends Robot {
    void draw() {
        System.out.println("그림을 그립니다.");
    }
}

class Practice {

    private static void action(Robot r) {// 이부분 생성
        if (r instanceof DanceRobot) {
            ((DanceRobot) r).dance();
        } else if (r instanceof SingRobot) {
            ((SingRobot) r).sing();
        } else {
            ((DrawRobot) r).draw();
        }
    }

    public static void main(String[] args) {
        Robot[] arr = { new DanceRobot(), new SingRobot(), new DrawRobot() };
        for (int i = 0; i < arr.length; i++)
            action(arr[i]);
    }
}
  • 훗 이건 쏘이지 했다..

  1. 다음은 물건을 구입하는 사람을 정의한 Buyer클래스이다 이 클래스는 멤버변수로 돈(money) 과 장바구니(cart) 를 가지고 있다 제품을 구입하는 기능의 buy메서드와 장바구니에 구입한 물건을 추가하는 add메서드 구입한 물건의 목록과 사용금액 그리고 남은 금액을 출력하는 summary메서드를 완성하시오 . 정답 맞췄는데, 풀이가 나랑 좀 다름 - summary메서드

package com.test.memo;

class Buyer {
    int money = 1000;
    Product[] cart = new Product[3]; // 구입한 제품을 저장하기 위한 배열
    int i = 0; // Product cart index 배열 에 사용될

    void buy(Product p) {
        if (money < p.price) {
            System.out.println("잔액이 부족하여" + p + "를 구매할 수 없습니다.");
            return;
        }
        money -= p.price;
        add(p);
    }

    void add(Product p) {
        if (i == cart.length || i > cart.length) {
            Product[] newCart = new Product[cart.length * 2];
            System.arraycopy(cart, 0, newCart, 0, cart.length);
            cart = newCart;
        }
        cart[i++] = p;

    }

    void summary() {
        int sum = 0;
        System.out.print("구입한 물건:");
        for (Product product : cart) {
            System.out.print(product + ",");
            sum += product.price;
        }
        System.out.println();
        System.out.println("사용한 금액: " + sum);
        System.out.println("남은 금액: " + money);
    }
}

class Product {
    int price; // 제품의 가격

    Product(int price) {
        this.price = price;
    }
}

class Tv extends Product {
    Tv() {
        super(100);
    }

    public String toString() {
        return "Tv";
    }
}

class Computer extends Product {
    Computer() {
        super(200);
    }

    public String toString() {
        return "Computer";
    }
}

class Audio extends Product {
    Audio() {
        super(50);
    }

    public String toString() {
        return "Audio";
    }
}

class Practice {

    public static void main(String[] args) {
        Buyer b = new Buyer();
        b.buy(new Tv());
        b.buy(new Computer());
        b.buy(new Tv());
        b.buy(new Audio());
        b.buy(new Computer());
        b.buy(new Computer());
        b.buy(new Computer());
        b.summary();
    }
}
  • add메서드에서 배열 복사가 잠깐 헷갈렸었고(clone메서드를 쓰라는건간?! 싶어서),
  • summary메서드에서 목록을 만들어 출력하라는 말이 잠깐 사고가 정지가 왔었다.


  1. 다음 코드의 실행 결과를 적으시오 정답 맞춤
class Exercise7_20 {
            public static void main(String[] args) {
                Parent p = new Child();
                Child c = new Child();
                System.out.println("p.x = " + p.x);
                p.method();
                System.out.println("c.x = " + c.x);
                c.method();
            }
        }
        class Parent {
            int x = 100;

            void method() {
                System.out.println("Parent Method");
            }
        }
        class Child extends Parent {
            int x = 200;

            void method() {
                System.out.println("Child Method");
            }
        }
    }
  • p.x = 100

    • Child Method
  • c.x = 200

    • Child Method

      rs\혅\AppData\Roaming\marktext\images\2024-03-12-19-55-30-image.png)

  1. 다음과 같이 attack메서드가 정의되어 있을 때 이 메서드의 매개변수로 가능한 것 두 가지를 적으세요. 틀림
interface Movable {
    
void move(int x, int y);
    
}
    
void attack(Movable f) {
    
    /* */ 내용 생략
}
  • x랑 y..?


  1. 아래는 도형을 정의한 Shape 클래스다. 이 클래스를 조상으로 하는 Circle클래스와 Rectangle 클래스를 작성해라. 이때, 생성자도 각 클래스에 맞게 적절히 추가해야한다. 틀림 > 답지봐버렸음..

(1) : Circle 클래스명
조상클래스 : Shape
멤버변수 반지름 : double r -

(2) : Rectangle 클래스명
조상클래스 : Shape
멤버변수 폭 : double width -
double height - 높이

메서드 :

  1. : isSquare 메서드명
    기 능 정사각형인지 아닌지를 알려준다 : .
    반환타입 : boolean
    매개변수 없음 :
abstract class Shape {
    Point p;

    Shape() {
        this(new Point(0, 0));
    }

    Shape(Point p) {
        this.p = p;
    }

    abstract double calcArea(); // 도형의 면적을 계산해서 반환하는 메서드

    Point getPosition() {
        return p;
    }

    void setPosition(Point p) {
        this.p = p;
    }
}

class Circle extends Shape {
    private double r;

    Circle(double r) {
        this(new Point(0, 0), r);// Circle(Point p, double r)를 호출
    }

    public Circle(Point p, double r) {
        super(p); // 상위 클래스의 멤버는 상위 클래스의 생성자가 초기화하도록 한다.
        this.r = r;
    }

    double calcArea() {
        return Math.PI * r * r;
    }
}

class Rectangle extends Shape {
    private double width;
    private double height;

    Rectangle(double width, double height) {//super(){} 형태가 보이지않지만 존재하는
        this(new Point(0, 0), width, height);
    }

    Rectangle(Point p, double width, double height) {
        super(p);
        this.width = width;
        this.height = height;
    }

    boolean isSquare() {
        if (width * height != 0 && width == height) {
            return true;
        }
        return false;
    }

    double calcArea() {
        return width * height;
    }
}

class Point {
    int x;
    int y;

    Point() {
        this(0, 0);
    }

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public String toString() {
        return "[" + x + "," + y + "]";
    }
}
  • abstract 클래스니까 해당 생성자들을 자식 클래스에서 오버라이딩해서 추가시켰다.

  • 추상 클래스를 이용해 오버라이딩 시켜서, 원이면 원의 둘레를, 사각형이면 사각형의 면적을 출력할 수 있게끔 오버라이딩 시킨것이다.

    위의 방법이 아니면 instanceof()를 이용해서 비교해줬어야 한다.


  1. 위 문제에서 정의한 클래스들의 면적을 구하는 메서드를 작성하고 테스트 하시오. 위에가 틀렸으니 얘도 틀림

    1. sumArea 메서드명
      기 능 : 주어진 배열에 담긴 도형들의 넓이를 모두 더해서 반환한다
      반환타입 : double
      매개변수 : Shape[] arr
class Organize {
    private static double sumArea(Shape[] arr) {
        double area = 0.0;
        for (int i = 0; i < arr.length; i++) {
            area += arr[i].calcArea();
        }
        return area;
    }

    public static void main(String[] args) {
        Shape[] arr = { new Circle(5.0), new Rectangle(3, 4), new Circle(1) };
        System.out.println("면적의 합:" + sumArea(arr));
    }
}
  • sumArea 에서 foreach문 이용해서 배열에 넣으려다 실패 for문 사용해줬다.

  1. 다음 중 인터페이스의 장점이 아닌 것은? 틀림

a. . 표준화를 가능하게 해준다
b. . 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다
c. . 독립적인 프로그래밍이 가능하다
d. . 다중상속을 가능하게 해준다
e. . 패키지간의 연결을 도와준다

  • c

정답


  1. Outer클래스의 내부 클래스 Inner의 멤버변수 iv의 값을 출력하시오 정답 맞춤
package com.test.memo;

class Outer {
    class Inner {
        int iv = 100;
    }
}

class Organize {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        System.out.println(inner.iv);

    }
}
  • 내부 클래스는 해당 외부 클래스의 객체를 생성한 후에 그 참조 변수를 이용해서 객체를 생성할 수 있다.

  1. Outer클래스의 내부 클래스 Inner의 멤버변수 iv의 값을 출력하시오 정답 맞춤
package com.test.memo;

class Outer {
    static class Inner {
        int iv = 200;
    }
}

class Organize {
    public static void main(String[] args) {
        Outer.Inner inner = new Outer.Inner();
        System.out.println(inner.iv);

    }
}
  • static 내부 클래스는 따로 외부 클래스 객체 생성없이 바로 생성 가능하다.


  1. 다음과 같은 실행결과를 얻도록 1~4의 코드를 완성하세요정답 맞춤
package com.test.memo;

class Outer {
    int value = 10;

    class Inner {
        int value = 20;

        void method1() {
            int value = 30;
            System.out.println(value);
            System.out.println(this.value);
            System.out.println(Outer.this.value);
        }
    }
}

class Organize {
    public static void main(String[] args) {
        Outer outer = new Outer();
        Outer.Inner inner = outer.new Inner();
        inner.method1();
    }
}
//30, 20, 10
  • 메서드 내 지역변수는 바로 접근가능하니까 변수 바로 써주고

    • 내부 클래스 메서드니까 this를 이용해서 써주고

      • Outer클래스의 자신 객체니까 Outer.this. 을 이용해서 써준다.

  1. 아래 EventHandler 를 익명클래스로 변경하시오. 틀림
  • 모르겠드아..

    혅\AppData\Roaming\marktext\images\2024-03-12-19-59-17-image.png)

  • GUI에서 이렇게 많이 사용

  • 매개변수로 객체를 만들면서 일회용성으로 사용할 수 있다.


  1. 지역 클래스에서 외부 클래스의 인스턴스 멤버와 static 멤버에 모두 접근할 수 있지만, 지역변수는 final이 붙은 상수만 접근할 수 있는 이유는? 틀림 > 내가 생각한 느낌이 ㅏ님
  • 메소드가 종료되어 지역변수가 소멸된 후에도 다른 인스턴스가 소멸된 지역변수를 참조할 수 있도록

정답

  • 우린 항상 main thread를 사용하고있는데, 새로운 thread를 생성해서, 사용하는것이다.

    • thread는 서로 독립적으로 돌아가고있는 것이다.

    • t.start();를 하게되면 스레드에 필요한 공간을 확보하고, run ()메서드를 자동으로 실행한다. > 추후에 배움

      • mSecond로 1000이면 1초를 의미
  • 출력 결과를 보면, 외부 인스턴스의 지역변수는 main이 종료되었는데도 소멸되지않고 계속 사용된다.

    • 스레드에서 참조하려는 값을 변경하려고 하면 에러가 난다. > 무조건 상수여야하기 때문에 > 현재는 생략해도 자동으로 상수로 지정해준다. >> 값 변경하려면 에러

      • 지역변수가 사라진 후의 값이 사라지기 전의 값과 다르면 문제가 발생하기 때문에 final로 선언해주는 것이다.

0개의 댓글