자바의 정석 Chapter07 객체지향 프로그래밍2

Eunkyung·2021년 10월 17일
0

Java

목록 보기
7/21
post-thumbnail

1. 상속

1.1 상속의 정의와 장점

상속이란, 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것
코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여

class Parent {} // 상위 클래스
class Child extends Parent {} // 상위 클래스를 상속받은 하위 클래스

TV클래스로부터 상속받아 CaptionTv클래스 작성

class TV {
    boolean power;
    int channel;
    
    void power() {
        power = !power;
    } 
    void channelUp() {
        ++channel;
    }
    void channelDown() {
        --channel;
    }
}
class CaptionTv extends TV {
    boolean caption;

    void displayCaption(String text) {
        if (caption) {
            System.out.println(text);
        }
    }
}

class CaptionTvTest{
    public static void main(String[] args) {
        CaptionTv ctv = new CaptionTv();
        ctv.channel = 10;
        ctv.channelUP();
        System.out.println(ctv.channel);
        ctv.displayCaption("Hello, World");
        ctv.caption = true;
        ctv.displayCaption("Hello, World");
    }
}


자식 클래스의 인스턴스 생성 시 부모 클래스의 멤버와 자식 클래스의 멤버가 합쳐진 하나의 인스턴스가 생성된다.
그림을 보면 Tv클래스를 상속받은 CaptionTv는 TV클래스에 정의된 멤버변수를 사용할 수 있다.
부모 클래스가 변경되면 자식 클래스는 자동적으로 영향을 받지만 (TV클래스의 멤버 변경 시 CaptionTv도 변경) 자식 클래스가 변경되는 것은 부모 클래스에 아무런 영향을 주지 못한다.

  • 생성자와 초기화 블럭은 상속되지 않으며, 멤버만 상속된다.
  • 자식 클래스의 멤버 개수는 부모 클래스보다 항상 같거나 많다.

1.2 단일 상속

하나 이상의 클래스로부터 상속받을 수 없다.
다중상속을 허용하지 않기 때문에 클래스의 포함관계 활용 가능

1.3 Object클래스

Object클래스는 모든 클래스의 부모로 다른 클래스로부터 상속 받지 않는 모든 클래스들은 컴파일러가 자동적으로 Object클래스로부터 상속받도록 한다.

2. 오버라이딩

2.1 오버라이딩

부모 클래스로부터 상속받은 메서드의 내용을 자식 클래스에서 변경하는 것

2.2 오버라이딩 조건

부모 클래스의 메서드 선언부(이름, 매개변수, 반환타입)가 일치해야함

2.3 super

자식 클래스에서 부모 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다.
멤버변수와 지역변수의 이름이 같을 때 this를 붙여서 구별했듯이 부모 클래스와 자식 클래스의 멤버를 구별하기 위해 사용하며, 인스턴스메서드에서만 사용가능하다.

2.3 super()

부모 클래스의 생성자 호출 시 사용하며 반드시 자식 클래스의 생성자 첫 줄에서 호출해야한다.

3. 제어자

3.1 static

'클래스의', '공통적인'의미를 가지고 있으며 JVM의 메서드 영역에 저장되며 인스턴스를 생성하지 않아도 사용할 수 있다. 인스턴스변수는 하나의 클래스로부터 생성되었더라도 각기 다른 값을 유지하지만, 클래스변수(static멤버변수)는 인스턴스에 관계없이 같은 값을 갖는다.

3.2 fianl

변수에 사용되면 값을 변경할 수 없는 상수가 되며, 메서드에 사용되면 오버라이딩 불가, 클래스에 사용되면 상속이 불가능하다.

3.3 abstract

  1. 추상 메서드 : 선언부만 작성하고 구현부는 작성하지 않은 메서드
  2. 추상 클래스 : 클래스 내에 추상 메서드 선언
abstract class AbstractTest { // 추상 클래스(추상 메서드를 포함한 클래스)
	abstract void move(); // 추상 메서드(구현부가 없는 메서드
}

추상 클래스는 아직 완성되지 않은 메서드가 존재하기 때문에 인스턴스를 생성할 수 없다.

3.4 접근 제어자

해당 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하여 데이터를 보호할 수 있다.

접근 제어자가 사용될 수 있는 곳 - 클래스, 멤버변수, 메서드, 생성자

private : 같은 클래스 내에서만 접근 가능
default : 같은 패키지 내에서만 접근 가능
protected : 같은 패키지, 다른 패키지의 자식 클래스에서 접근 가능
public : 접근 제한 없음

접근 제어자 예제

public class TimeTest {
    public static void main(String[] args) {
        Time t = new Time(12, 25, 30);
        System.out.println(t);
        t.setHour(t.getHour() + 1); // t.shour = 13처럼 멤버변수에 직접 접근 불가
        System.out.println(t);
    }
}

class Time {
    private int hour;

    @Override
    public String toString() {
        return hour + ":" + minute + ":" + second;
    }

    private int minute;
    private int second;

    Time(int hour, int minute, int second) {
        setHour(hour);
        setMinute(minute);
        setSecond(second);
    }
    
    public int getHour() {
        return hour;
    }

    public void setHour(int hour) {
        if (hour < 0 || hour > 23) {
            return;
        }
        this.hour = hour;
    }

    public int getMinute() {
        return minute;
    }

    public void setMinute(int minute) {
        if (minute < 0 || minute > 59) {
            return;
        }
        this.minute = minute;
    }

    public int getSecond() {
        return second;
    }

    public void setSecond(int second) {
        this.second = second;
    }

}

hour는 0보다 같거나 크고 24보다는 작은 범위를 가져야하고 minute는 0보다 같거나 크고 59보다 작은 범위를 가져야한다. private 접근제어자를 통해 멤버변수의 직접적인 접근을 막고 getter, setter 메서드를 이용하여 간접적으로 멤버변수의 값을 읽고 변경할 수 있다.

싱글톤 패턴 예제

public class Printer {
    	private static Printer printer = null; // 중복 객체 생성을 막기 위해 null로 초기화
    
    	private Printer(){} // 생성자의 접근 제어자를 private으로 지정하면 외부에서 인스턴스 생성 불가능
    
    	public static Printer getInstance() { // getInstance()메서드를 통해서만 생성 가능
    		if(printer == null) { // 객체가 이미 생성되었는지 판단
    			printer = new Printer();
    		}
    		return printer;
    	}
    
    	public void print(String input) {
    		System.out.println(input);
    	}
    }

4. 다형성

4.1 다형성

부모 클래스 타입의 참조변수로 자식 클래스의 인스턴스 참조 가능

TV t = new TV();, CaptionTv c = new CaptionTv(); 처럼 인스턴스 타입과 일치하는 참조변수 사용했지만 서로 상속관계에 있을 경우 부모 클래스의 타입의 참조변수로 자식 클래스의 인스턴스 참조가 가능하다.
Tv t = new CaptionTv(); 이 경우 Tv클래스의 멤버들만 사용가능하며, CaptionTv에만 정의되어 있는 caption과 display Caption()은 참조변수 t로 사용이 불가능하다.
둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

그렇다면 반대로 CaptionTv c = new Tv(); 처럼 자식 타입의 참조변수로 부모 클래스 타입의 인스터를 생성하는 것은 가능할까?
불가능하다. Tv의 멤버 개수보다 참조변수 c가 사용할 수 있는 멤버 개수가 더 많기 때문이다.

4.2 참조변수의 형변환

참조변수의 형반환은 인스턴스 주소값은 그대로, 사용할 수 있는 멤버의 개수만 조절한다.

자식타입 -> 부모타입(Up-casting) : 형변환 생략가능

자식타입 <- 부모타입(Down-casting) : 형변환 생략불가

형변환 예제

public class CastingTest1 {
    public static void main(String[] args) {
        Car car = null;
        FireEngine fe = new FireEngine();
        FireEngine fe2 = null;
        
        fe.water();
        car = fe; // car = (Car) fe; 형변환 생략 가능
//        car.water(); 자식 클래스에 정의된 메서드 호출 불가
        fe2 = (FireEngine) car; // 자식 타입 <- 부모 타입 형변환 생략 불가능
        fe2.water(); // 자식 클래스에 정의된 메서드 호출 가능
    }
    
}
class Car{
    String color;
    int door;
    
    void drive() {
        System.out.println("drive");
    }
    void stop() {
        System.out.println("stop");
    }
}

class FireEngine extends Car { // Car 클래스 상속
    void water() {
        System.out.println("water");
    }
}

출처

  • 자바의 정석 - 남궁성 지음
profile
꾸준히 하자

0개의 댓글