minghxx.blog
  • 자바의 정석 연습문제 ch 7 객체지향 프로그래밍(2)
    2023년 08월 31일 17시 59분 43초에 업로드 된 글입니다.
    작성자: 민발자
    728x90

    자바의 정석 3판 기초판 연습문제

    https://github.com/castello/javajungsuk3

     

    GitHub - castello/javajungsuk3: soure codes and ppt files of javajungsuk 3rd edition

    soure codes and ppt files of javajungsuk 3rd edition - GitHub - castello/javajungsuk3: soure codes and ppt files of javajungsuk 3rd edition

    github.com

     

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

    class SutdaDeck{
    	final int CARD_NUM = 20;
    	SutdaCard[] cards = new SutdaCard[CARD_NUM];
    	
    	SutdaDeck() {
    		// 1) 배열 SutdaCard 적절히 초기화
    	}
    }
    
    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 Ex7_1 {
    	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] + ", ");
    		}
    	}
    }
    SutdaDeck() {
        // 1) 배열 SutdaCard 적절히 초기화
        for(int i = 0; i < cards.length; i ++) {
            int num = i%10 + 1;
            boolean isKwang = (i < 10) && (num == 1 || num == 3 || num == 8);
    
            cards[i] = new SutdaCard(num, isKwang);
        }
    }

     

    [7-2] 문제7-1의 SutdaDeck클래스에 다음에 정의된 새로운 메서드를 추가하고 테스트 하시오.

    1.

    메서드명 : shuffle

    기능 : 배열 cards에 담긴 카드의 위치를 뒤섞는다. Math.random() 사용

    반환타입 : 없음

    매개변수 : 없음

     

    2.

    메서드명 : pick

    기능 : 배열 cards에서 지정된 위치의 SutdaCard를 반환한다.

    반환타입 : SutdaCard

    매개변수 : int index - 위치

     

    3.

    메서드명 : pick

    기능 : 배열 cards에서 임의의 위치의 SutdaCard를 반환 Math.random() 사용

    반환타입 : SutdaCard

    매개변수 : 없음

    void shuffle() {
        for(int i = 0; i < cards.length; i++) {
            int idx = (int)Math.random() * cards.length;
    
            SutdaCard tmp = cards[i];
            cards[i] = cards[idx];
            cards[idx] = tmp;
        }
    }
    
    SutdaCard pick(int index) {
        // 유효성 검사
        if(index < 0 || index >= CARD_NUM)
            return null;
        return cards[index];
    }
    
    SutdaCard pick() {
        return cards[(int)(Math.random() * cards.length)];
    }

     

    [7-3] 오버라이딩의 정의와 필요성에 대해서 설명하시오.

    조상 클래스로부터 상속받은 메서드를 자손클래스에 맞에 재정의 하는 것

    그대로 사용할 수 없는 경우가 많이 때문에 자손클래스에 맞게 오버라이딩이 필요

     

    [7-4] 다음 중 오버라이딩의 조건으로 옳지 않은 것은? (모두 고르시오)

    a. 조상의 메서드와 이름이 같아야 한다.

    b. 매개변수의 수와 타입이 모두 같아야 한다.

    c. 접근 제어자는 조상의 메서드보다 좁은 범위로만 변경할 수 있다.

    d. 조상의 메서드보다 더 많은 수의 예외를 선언할 수 있다.

     

    →접근제어자는 좁은 범위로 변경 불가

    조상클래스보다 더 많은 예외 선언 불가

    인스턴스 static 서로 변경 불가

     

    오버라이딩

    이름이 같아야하고, 매개변수가 같아야함, 리턴 타입이 같아야함

     

     

    [7-5] 다음의 코드는 컴파일하면 에러가 발생한다. 그 이유를 설명하고 에러를 수정하기 위해서는 코드를 어떻게 바꾸어야 하는가?

    class Product{
    	int price;
    	int bonusPoint;
    	
    	Product(int price){
    		this.price = price;
    		bonusPoint = (int)(price/10.0);
    	}
    }
    
    class Tv extends Product {
    	Tv() {}
    	
    	public String toString() {
    		return "Tv";
    	}
    }
    
    public class Ex7_5 {
    	public static void main(String[] args) {
    		Tv t = new Tv();
    	}
    }

    Product의 기본 생성자 추가

    Tv 생성자가 조상 생성자 super()를 호출하기 때문에 Product() 생성자를 호출함 → Product() 정의되어 있지 않아 컴파일 에러 발생

     

    [7-6] 자손 클래스의 생성자에서 조상 클래스의 생성자를 호출해야하는 이유는 무엇인가?

    조상에 정의된 인스턴스 변수들이 초기화 되도록 하기 위해

    자손 인스턴스 생성시 상속받은 조상의 인스턴스 변수도 생성됨, 이 조상 인스턴스도 초기화 필요하기 때문에 조상 클래스 생성자 호출.

    자손 생성자에서 초기화하는 것보다 조상 생성자를 호출하면서 초기화하는 것이 바람직

     

    [7-7] 다음 코드의 실행했을 때 호출되는 생성자의 순서와 실행결과를 적으시오.

    class Parent {
    	int x = 100;
    	
    	Parent() {
    		this(200); // Parent(int x) 호출
    	}
    	
    	Parent(int x) {
    		this.x = x;
    	}
    	
    	int getX() {
    		return x;
    	}
    }
    
    class Child extends Parent {
    	int x = 3000;
    	
    	Child() {
    		this(1000); // Child(int x) 호출
    	}
    	
    	Child(int x) {
    		this.x = x;
    	}
    }
    
    public class Ex7_7 {
    	public static void main(String[] args) {
    		Child c = new Child();
    		System.out.println("x=" + c.getX());
    	}
    }

    실행 결과 : x=200

    생성자 순서 : Child() → Child(int x) → Parent() → Parent(int x) → Object()

     

    [7-8] 다음 중 접근제어자를 접근범위가 넓은 것에서 좁은 것의 순으로 바르게 나열한 것은?

    a. pubilc - protected - default - private

     

    → 접근제한자

    public : 접근 제한 없음

    protected : 같은 패키지 내, 다른 패키지 자손 클래스에서 접근 가능

    default : 같은 패키지 내에서만 

    private : 같은 클래스 내에서만 가능

     

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

    a. 지역변수 - 값을 변경할 수 없다.

    b. 클래스 - 상속을 통해 클래스에 새로운 멤버를 추가할 수 없다.

    c. 메서드 - 오버로딩 불가

    d. 멤버변수 - 값을 변경할 수 없다.

     

    [7-10] MyTv2클래스의 멤버변수 isPowerOn, channel, volume을 클래스 외부에서 접근할 수 없도록 제어자를 붙이고 대신 이 멤버변수들의 값을 어디서나 읽고 변경할 수 있도록 getter와 setter메서드를 추가하라.

    class MyTv2 {
    	boolean isPowerOn;
    	int channel;
    	int volume;
    	
    	final int MAX_VOLUME = 100;
    	final int MIN_VOLUME = 0;
    	final int MAX_CHANNEL = 100;
    	final int MIN_CHANNEL = 1;
    }
    
    public class Ex7_10 {
    
    	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());
    	}
    
    }
    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;
    	
    	// 1)
    	public void setPowerOn(boolean isPowerOn) {
    		this.isPowerOn = isPowerOn;
    	}
    	
    	public int getChannel() {
    		return channel;
    	}
    	
    	public void setChannel(int channel) {
    		if(channel > MAX_CHANNEL || channel < MIN_CHANNEL) {
    			return;
    		}
    		this.channel = channel;
    	}
    	
    	public int getVolume() {
    		return volume;
    	}
    	
    	public void setVolume(int volume) {
    		if(volume > MAX_VOLUME || volume < MIN_VOLUME) {
    			return;
    		}
    		this.volume = volume;
    	}
    
    }

     

    [7-11] 문제7-10에서 작성한 MyTv2클래스에 이전 채널(previous channel)로 이동하는 기능의 메서드를 추가해서 실행결과와 같은 결과를 얻도록 하시오.

    [Hint] 이전 채널의 값을 저장할 멤버변수를 정의하라.

    메서드명 : gotoPrevChannel

    기능 : 현재 채널을 이전 채널로 변경한다.

    반환타입 : 없음

    매개변수 :  없음

    class MyTv2 {
    	private boolean isPowerOn;
    	private int channel;
    	private int volume;
    	private int prevChannel;
    	
    	final int MAX_VOLUME = 100;
    	final int MIN_VOLUME = 0;
    	final int MAX_CHANNEL = 100;
    	final int MIN_CHANNEL = 1;
    	
    	// 1)
    	public boolean isPowerOn() {
    		return isPowerOn;
    	}
    	
    	public void setPowerOn(boolean isPowerOn) {
    		this.isPowerOn = isPowerOn;
    	}
    	
    	public int getChannel() {
    		return channel;
    	}
    	
    	public void setChannel(int channel) {
    		if(channel > MAX_CHANNEL || channel < MIN_CHANNEL) {
    			return;
    		}
    		prevChannel = this.channel;
    		this.channel = channel;
    	}
    	
    	public int getVolume() {
    		return volume;
    	}
    	
    	public void setVolume(int volume) {
    		if(volume > MAX_VOLUME || volume < MIN_VOLUME) {
    			return;
    		}
    		this.volume = volume;
    	}
    
    
    	public void gotoPrevChannel() {
    		setChannel(prevChannel);
    	}
    }
    
    public class Ex7_10 {
    	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());
    	}
    }

     

    [7-12] 다음 중 접근 제어자에 대한 설명으로 옳지 않은 것은? (모두 고르시오)

    a. public은 접근제한이 전혀 없는 접근 제어자이다.

    b. default가 붙으면 같은 패키지 내에서만 접근이 가능하다.

    c. 지역변수에도 접근 제어자를 사용할 수 있다.

    d. protected가 붙으면, 같은 패키지 내에서도 접근이 가능하다.

    e. protected가 붙으면, 다른 패키지의 자손 클래스에서 접근이 가능하다.

     

    → 접근제어자

    클래스, 멤버변수, 메서드, 생성자에서 사용가능

     

    [7-13] Math클래스의 생성자는 접근 제어자가 private이다. 그 이유는 무엇인가?

    Math클래스의 모든 메서드가 static메서드이고 인스턴스 변수가 존재하지 않기 때문에 객체를 생성할 필요가 없기 때문이다.

     

    [7-15] 클래스가 다음과 같이 정의되어 있을 때, 형변환을 올바르게 하지 않은 것은? (모두 고르시오.)

    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;

     

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

     

     

    [7-16] 다음 중 연산결과가 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

     

    instanceof 연산자

    조상이나 구현한 인터페이스에 대해 true반환, 형변환이 가능하다는 것을 뜻함

     

    [7-17] 아래 세 개의 클래스로부터 공통부분을 뽑아서 Unit이라는 클래스를 만들고, 이 클래스를 상속받도록 코드를 변경하시오

    class Marine {
    	int x, y;
    	void move(int x, int y) {}
    	void stop() {}
    	void stimPack() {}
    }
    
    class Tank {
    	int x, y;
    	void move(int x, int y) {}
    	void stop() {}
    	void changeMode() {}
    }
    
    class Dropship {
    	int x, y;
    	void move(int x, int y) {}
    	void stop() {}
    	void load() {}
    	void unload() {}
    }
    abstract class Unit {
    	int x, y;
    	abstract void move(int x, int y);
    	void stop() {}
    }
    
    class Marine extends Unit{
    	void move(int x, int y) {}
    	void stimPack() {}
    }
    
    class Tank extends Unit{
    	void move(int x, int y) {}
    	void changeMode() {}
    }
    
    class Dropship extends Unit{
    	void move(int x, int y) {}
    	void load() {}
    	void unload() {}
    }

     

    [7-18] 다음과 같은 실행결과를 얻도록 코드를 완성하시오.

    [Hint] instanceof연산자를 사용해서 형변환한다.

    메서드명 : action

    기능 : 주어진 객체의 메서드를 호출한다.

    DanceRobot인 경우, dance() 호출

    SingRobot인 경우, sing() 호출

    DraeRobot인 경우, draw() 호출

    반환타입 :  없음

    매개변수 : Robot r - Robot 인스턴스 또는 Robot의 자손 인스턴스

    public class Ex7_18 {
    	public static void action(Robot r) {
    		if(r instanceof DanceRobot) {
    			((DanceRobot) r).dance();
    		} else if(r instanceof SingRobot) {
    			((SingRobot) r).sing();
    		} else if(r instanceof DrawRobot) {
    			((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]);
    		}
    	}
    }
    
    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("그림을 그립니다.");
    	}
    }

     

     

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

    1. 메서드명 : buy

    기능 : 지정된 물건을 구입, money에서 물건의 가격을 빼고, cart에 담는다. 만일 가진 돈이 물건의 가격보다 적으면 바로 종료

    반환타입 : 없음

    매개변수 : Product p

     

    2.  메서드명 : add

    기능 : 지정된 물건을 장바구니에 담는다

    반환타입 : 없음

    매개변수 : Product p

     

    3.  메서드명 : summary

    기능 : 구입한 물건의 목록과 사용금액, 남은 금액을 출력

    반환타입 : 없음

    매개변수 : 없음

    public class Ex7_19 {
    
    	public static void main(String[] args) {
    		Buyer b = new Buyer();
    		b.buy(new Tv1());
    		b.buy(new Computer());
    		b.buy(new Tv1());
    		b.buy(new Audio());
    		b.buy(new Computer());
    		b.buy(new Computer());
    		b.buy(new Computer());
    		
    		b.summary();
    	}
    }
    
    class Buyer {
    	int money = 1000;
    	Product1[] cart = new Product1[3]; // 구입 제품을 저장하기 위한 배열
    	int i = 0; // cart에 사용될 index
    	
    	// 메서드 작성
    	void buy(Product1 p) {
    		if(money < p.price) {
    			System.out.println("잔액이 부족하여 " + p.toString()+ "을/를 살수 없습니다.");
    			return;
    		}
    		money -= p.price;
    		add(p);
    	}
    	
    	void add(Product1 p) {
    		if(i >= cart.length) {
    			Product1[] tmp = new Product1[cart.length * 2]; // 더 긴 배열 생성
    			System.arraycopy(cart, 0, tmp, 0, cart.length); // 복사
    			cart = tmp; // 긴거로 변경
    		}
    		cart[i++] = p; // cart에 저장하고 i 1 증가
    	}
    	void summary() {
    		String items = "";
    		int total = 0;
    		
    		for(int j = 0; j < cart.length; j++) {
    			items += cart[j] + ", ";
    			total += cart[j].price;
    		}
    		
    		System.out.println("구입한 물건: " + items);
    		System.out.println("사용한 금액: " + total);
    		System.out.println("남은 금액: " + money);
    	}
    	
    }
    
    class Product1 {
    	int price;
    	
    	Product1(int price) {
    		this.price = price;
    	}
    }
    
    class Tv1 extends Product1{
    	Tv1() { super(100); }
    	
    	public String toString() { return "Tv"; }
    }
    
    class Computer extends Product1{
    	Computer() { super(200); }
    	
    	public String toString() { return "Computer"; }
    }
    
    class Audio extends Product1{
    	Audio() { super(50); }
    	
    	public String toString() { return "Audio"; }
    }

     

     

    [7-20] 다음의 코드를 실행한 결과를 적으시오.

    public class Ex7_20 {
    
    	public static void main(String[] args) {
    		Parent1 p = new Child1();
    		Child1 c = new Child1();
    		
    		System.out.println("p.x = " + p.x);
    		p.method();
    		System.out.println("c.x = " + c.x);
    		c.method();
    	}
    }
    
    class Parent1 {
    	int x = 100;
    	
    	void method() {
    		System.out.println("Parent method");
    	}
    }
    
    class Child1 extends Parent1 {
    	int x = 200;
    	
    	void method() {
    		System.out.println("Child method");
    	}
    }

     

    p.x = 100

    Child method

    c.x = 200

    Child method

    →조상 멤버변수와 같은 이름의 인스턴스 변수를 자손이 중복으로 정의했을 때(int x) 참조변수 타입이 조상이냐 자손이냐에 따라 값이 달라짐

    참조변수를 조상타입으로 사용하면 조상의 멤버변수가, 자손타입으로 사용하면 자손의 인스턴스 변수가 사용된다.

    메서드는 참조변수의 타입에 상관없이 실제 인스턴스 메서드(오버라이딩된 메서드)가 호출

     

    [7-21] 다음과 같이 attack메서드가 정의되어 있을 때, 이 메서드의 매개변수로 가능한 것 두 가지를 적으시오.

    interface Movable {
    	void move(int x, int y);
    }
    
    void attack(Movable f) {}

    Movable 인터페이스타입의 매개변수로 가능한 것 : null, Movable 인터페이스를 구현한 클래스나 그의 자손 인스턴스

     

    [7-22] 아래는 도형을 정의한 Shape클래스이다. 이 클래스를 조상으로 하는 Circle클래 스와 Rectangle클래스를 작성하시오. 이 때, 생성자도 각 클래스에 맞게 적절히 추가해야 한다.

    1. 클래스명 : Circle

    조상 클래스 : Shape

    멤버변수 : double r -반지름 

     

    2. 클래스명 : Ractangle

    조상 클래스 : Shape

    멤버변수 : double width - 폭

    double height - 높이

    메서드명 : isSquare

    기능 : 정사각형인지 아닌지를 알려준다.

    반환타입 : boolean

    매개변수 : 없음

     

    [7-24] 다음 중 인터페이스의 장점이 아닌 것은?

    a. 표준화를 가능하게 해줌 

    b. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.

    c. 독립적인 프로그래밍이 가능

    d. 다중상속을 가능하게 해줌

    e. 패키지간 연결을 도와준다

     

    [7-25] Outer클래스의 내부 클래스 Inner의 멤버변수 iv의 값을 출력하시오.

    class Outer {
    	class Inner{
    		int iv = 100;
    	}
    }
    public class Ex7_25 {
    
    	public static void main(String[] args) {
    		Outer o = new Outer();
    		Outer.Inner ic = o.new Inner();
    		System.out.println(ic.iv);
    	}
    }

     

    [7-26] Outer2클래스의 내부 클래스 Inner의 멤버변수 iv의 값을 출력하시오.

    class Outer2 {
    	static class Inner{
    		int iv = 200;
    	}
    }
    public class Ex7_26 {
    
    	public static void main(String[] args) {
    		Outer2.Inner ic = new Outer2.Inner();
    		System.out.println(ic.iv);
    	}
    }

     

     

    [7-27] 다음과 같은 실행결과를 얻도록 (1)~(4)의 코드를 완성하시오.

    class Outer3{
    	int value = 10;
    
    	class Inner {
    		int value = 20;
    		
    		void method() {
    			int value = 30;
    			System.out.println(/* 1 */ value);
    			System.out.println(/* 2 */ this.value);
    			System.out.println(/* 3 */ Outer.this.value);
    		}
    	}
    }
    
    public class Ex7_27 {
    
    	public static void main(String[] args) {
    		/* 4 */
    		Outer3 o = new Outer3();
    		Outer3.Inner inner = o.new Inner();
    		inner.method();
    	}
    }

     

    728x90
    댓글