본문 바로가기

Java Live Study

Live Study 6주차 : 상속

목표

자바의 상속에 대해 학습하기

 

학습할 것

  • 자바 상속의 특징
  • super 키워드
  • 메소드 오버라이딩
  • 다이나믹 메소드 디스패치 (Dynamic Method Dispatch)
  • 추상 클래스
  • final 키워드
  • Object 클래스

 

과제 마감

2020.12.26 1PM

 

 


 

자바 상속의 특징

 

객체 지향에서 상속을 통해 얻을 수 있는 효과 3가지

1) 클래스 검색이 쉬움

상속을 통하여 클래스를 체계화할 수 있다. 다수 클래스의 공통적인 성질을 한 개의 상위 클래스에 표현함으로써 상위 클래스에 하위 클래스를 분류하는 관점이 표현될 수 있다. 또한 하위 클래스에는 상위 클래스에 대하여 추가된 속성과 기능이 표현되기 때문에 하위 클래스의 특징을 이해하기 쉽다. 이러한 효과는 체계적인 클래스 계층 구조를 제공하기 때문에 클래스를 쉽게 검색할 수 있다.

 

2) 클래스 확장이 쉬움

상속을 통하여 새로운 클래스를 생성하기가 용이하다. 상속을 활용하여 상위 클래스의 속성과 기능을 그대로 이어받고 그것을 확장하는 형태로 새로운 클래스를 정의할 수 있기 때문에 새로운 클래스의 정의를 쉽게 할 수 있다.

 

3) 클래스 변경이 쉬움

상속을 통하여 기존 클래스의 기능을 쉽게 변경할 수 있다. 상속을 이용하면 기존 클래스의 확장은 물론, 기존 클래스의 코드를 변경하여 사용할 수 있다. 상위 클래스의 기능을 하위 클래스에서 재정의함으로써 기존 코드를 변경할 수 있다. 코드를 변경해도 기존 코드와 변경된 코드가 명확하게 분리되어 있어 사용이 편리하다.

 

 

 

자바에서의 상속에 대한 특징 2가지

4) 다중 상속을 허용하지 않음

자바에서 클래스는 여러 개의 하위 클래스는 가질 수 있지만 상위 클래스는 오직 하나만 가질 수 있다.

5) 자바의 모든 클래스는 Object클래스의 하위 클래스이다.

자바는 거대한 라이브러리 클래스를 가진 언어이며, 이 라이브러리 클래스들은 상속 관계의 계층 구조를 이루고 있으며, 계층 구조의 최상위 클래스는 Object 클래스이다. 또한 우리가 프로그램을 작성할 때 상위 클래스를 지정하지 않아도 묵시적으로 Object 클래스가 상위 클래스로 지정된다. 결론적으로 자바의 모든 클래스는 Object 클래스의 하위 클래스이며, Object 클래스의 모든 속성과 기능을 상속받아 사용할 수 있다.

 

 

 

또한, 자바에서는 클래스 선언 시 상위 클래스를 지정하기 위해 확장을 의미하는 extends라는 예약어를 사용한다.

상위 클래스의 모든 요소를 상속받고 추가 요소를 더 가지는 확장의 개념이다.

 

class '클래스 이름' extends '상위 클래스 이름' '이름' {

... 추가되는 멤버 변수

... 생성자 (생성자는 상속되지 않음)

... 추가되는 메소드

}

 

 

 


 

 

super 키워드

예약어 super는 두 가지 형태로 사용된다.

 

1) 하위 클래스에 가려진 상위클래스의 멤버 변수나 메소드에 접근 할 때 사용

super.'멤버 변수'

super.'메소드 이름(매개 변수)

 

class SB1 {
    public int x = 500;
    public int y = 1000;
}

class SB2 extends SB1 {
    public String x = "Hello";
    public String xx = x + super.x; // super를 이용하여 상위 클래스의 변수에 접근
    public String yy = "" + y + super.y; // super를 이용하여 상위 클래스의 변수에 접근
}

public class SuperTest{
    public static void main(String args[]){
        SB2 sb2 = new SB2();
        System.out.println("객체 sb2의 x,y : " + sb2.x + sb2.y);
        System.out.println("객체 sb2의 xx : " + sb2.xx);
        System.out.println("객체 sb2의 yy : " + sb2.yy);
    }
}

/* output
객체 sb2의 x,y : Hello1000
객체 sb2의 xx : Hello500
객체 sb2의 yy : 10001000
*/

 

 

 

2) 상위 클래스의 명시적 생성자를 호출하기 위해 사용

super()는 하위 클래스의 생성자에서만 사용이 가능하며, 사용 시 첫 번째 라인에 위치해야 한다. 이것은 상위 클래스의 생성자가 항상 하위 클래스 생성자보다 먼저 수행되어야 함을 의미한다.

 

class SD1{
    public int i1;
    public double d1;
    public SD1(int i1){
    	System.out.println("SD1(int i1)생성자 수행");
        thie.i1 = i1 * i1;
        System.out.println(i1 + "의 제곱은 : " + this.i1);
    }
    public SD1(double d1){
    	System.out.println("SD1(double d1)생성자 수행");
        thie.i1 = d1 * d1;
        System.out.println(d1 + "의 제곱은 : " + this.d1);
    }
}

class Sub1 extends SD1{
	public Sub1(int i1){
    	super(i1); // 상위 클래스의 생성자 호출
        System.out.println("Sub1(int i1)생성자 수행");
        this.i1 = this.i1 * i1;
        System.out.println(i1 + "의 세제곱은 : " + this.i1);
    }
    
    public Sub1(double d1){
    	super(d1); // 상위 클래스의 생성자 호출
        System.out.println("Sub1(int d1)생성자 수행");
        this.d1 = this.d1 * d1;
        System.out.println(d1 + "의 세제곱은 : " + this.d1);
    }
}

public class SuperTest{
	public static void main(String args[]){
    	Sub1 sub1 = new Sub1(10); // 생성자에 의해 결과 출력
        Sub1 sub2 = new Sub1(10.5);
    }
}

/* output
SD1(int i1)생성자 수행
10의 2제곱은 : 100
Sub1(int i1)생성자 수행
10의 3제곱은 : 1000
SD1(double d1)생성자 수행
10.5의 2제곱은 : 110.25
Sub1(double d1)생성자 수행
10.5의 3제곱은 : 1157.625
*/

 

 

 


 

메소드 오버라이딩

 

오버로딩(overloading) : 같은 클래스 내에서 같은 이름의 생성자나 메소드를 사용하는 경우

오버라이딩(overriding) : 상속 관계에서 상위 클래스에 있는 메소드를 하위 클래스에 다시 선언하는 것

 

오버라이딩이 성립하기 위해서는 상위 클래스에 선언된 메소드와 반환값의 타입메소드 이름, 매개 변수의 형과 개수가 일치해야한다.

 

 

동일한 클래스 내에서 이루어지는 오버로딩을 중첩으로 표현한다면, : 같은 메소드 여러 개 존재

오버라이딩은 치환으로 표현할 수 있다. : 상위 클래스의 메소드를 하위 클래스의 메소드로 교체

 

상속과 오버라이딩의 개념을 이용하여 상위 클래스의 모든 속성과 기능을 상속받고, 변경이 필요한 특정 메소드를 오버라이딩하여 기존의 클래스 기능을 쉽게 변경하여 사용할 수 있다. 오버라이딩은 다형성의 기반이 되는 개념이다.

class Ramyon {
    protected String boiled_pot;
    protected String boilwater(String elecpot, String water){
    	System.out.println(elecpot + "에 " + water + "을 넣어 끓인다.(Ramyon클래스)");
        return "끓은 물";
    }
    
    public void cookRamyon(String ramyon, String vegetable, int time){
        boild_pot = boilwater("전기 냄비", "물");
        System.out.println(boild_pot + "에 " + ramyon + "과 " + vegetable + "를 넣고 " + time + "분간 끓인다.");
        System.out.println("일반 라면 요리 완료(Ramyon클래스)");
    }
}

class MixedRamyon extends Ramyon1 {
    public void cookRamyon(String ramyon, String vegetable, int time){ // 메소드 오버라이딩
        boiled_pot = boilwater("전기 냄비", "물");
        System.out.println(boiled_pot + "에 " + ramyon + "과 " + vegetable + "을 넣고 " + time + "분간 끓인다.");
        System.out.println("물을 버리고 양념장을 넣고 비빈다.(MixedRamyon클래스)");
        System.out.println("비빔라면 요리 완료(MixedRamyon클래스)");
    }
}

public class OverridingTest{
	public static void main(String args[]){
    	int s;
        Ramyon r = new Ramyon();
        r.cookRamyon("신라면", "파", 5); // 상위 클래스의 메소드 호출
        MixedRamyon mr = new MixedRamyon();
        mr.cookRamyon("비빔면", "버섯", 6); // 하위 클래스의 메소드 호출
	}
}

/* output
전기냄비에 물을 넣어 끓인다.(Ramyon클래스)
끓은 물에 신라면과 파를 넣고 5분간 끓인다.(Ramyon클래스)
일반 라면 요리 완료(Ramyon클래스)
전기냄비에 물을 넣어 끓인다.(Ramyon클래스)
끓은 물에 비빔면과 버섯을 넣고 6분간 끓인다.(Ramyon클래스)
물을 버리고 양념장을 넣고 비빈다.(MixedRamyon클래스)
비빔라면 요리 완료(MixedRamyon클래스)
*/

 

 

 

메소드 오버라이딩과 한정자

메소드를 오버라이딩하는 경우 상위 클래스의 메소드 한정자보다 허용 범위가 넓은 경우에만 허용되고, 그 반대의 경우는 허용되지 않는다. 예를 들어 상위 클래스에서 public으로 사용된 메소드를 하위 클래스에서 한정자 없이 사용하거나 protected로 메소드를 오버라이딩 할 수 없다.

한정자의 허용 범위는 public → protected → 한정자 사용 안함

private는 상속되지 않으므로 대상 아님.

 

class AA{
	public void aa(int a, int b){
    	System.out.println(a+b);
    }
}

class BB extends AA{
	protected void aa(int a, int b){ // protected → public 허용 안됨! error!
    	System.out.println(a*b);
    }
}

 


 

다이나믹 메소드 디스패치 (Dynamic Method Dispatch)

 

: 오늘 라이브 후에 채우겠습니다!

 

 


추상 클래스

추상화는 복잡한 문제들 중에 공통적인 부분을 추출하여 추상 클래스로 제공하고, 상속을 이용하여 나머지 클래스들을 하위 클래스로 제공하는 기법이다.

추상 클래스는 하위 클래스에서 구현되는 추상적인 기능만을 정의하는 클래스이다. 추상 클래스는 기능이 무엇인지만 정의하고 어떻게 구현되는지는 정의하지 않는다. 추상 클래스에서 정의된 추상적인 기능은 하위 클래스에서 구현된다. 그러므로 하나의 추상 클래스에 정의된 기능을 여러 개의하위 클래스에서 서로 다른 형태로 구현하여 사용할 수 있다.

 

추상 메소드는 추상 클래스 내에 정의되는 메소드로서 선언 부분만 있고 구현 부분이 없는 메소드이다. 추상 클래스를 상위 클래스로 하는 하위 클래스에서는 상위 클래스의 추상 메소드를 서로 다른 방법으로 구현하여 사용할 수 있다. 즉 하위 클래스에서는 상위 클래스에서 추상 메소드로 정의된 메소드를 오버라이딩하여 사용한다.

 

추상 메소드의 사용은 객체 지향 언어에서의 다형성을 지원할 수 있는 효율적인 요소이다. 추상 메소드로 정의된 메소드 이름은 하위 클래스에서 공통적으로 사용될 수 있으며, 또한 서로 다르게 구현될 수 있기 때문이다. 즉 하나의 메소드 이름을 사용하지만 서로 다른 구현 방법을 가질 수 있다는 의미이다.

 

추상 클래스로부터는 직접 객체가 생성될 수 없다. 추상 메소드는 구현 부분이 없는 메소드이기 때문이다.

 

abstract class '클래스 이름' {

   ...

   클래스에 기술할 수 있는 일반적인 멤버 변수와 메소드

   abstrct void '추상 메소드 이름'();

}

 

abstract class Shape{ // 추상 클래스 선언
    ...
    abstract void computeArea(); // 추상 메소드 선언
}

public class Circle extends Shpe{ // 추상 클래스로부터 상속받음
    ...
    void computeArea(){ // 추상 메소드 오버라이딩
        // 실제 원의 면적을 계산하는 기능
    }
}

public class Triangle extends Shpae{ // 추상 클래스로부터 상속받음
    ...
    void computeArea(){ // 추상 메소드 오버라이딩
        // 실제 삼각형의 면적을 계산하는 기능
    }
}

 

final 키워드

자바에서 final은 3가지 형태로 사용되며, 모두 변하는 것을 방지하기 위해 사용된다.

보안과 설계 부분을 명확하게 하기 위해 사용한다.

보안; 기존의 클래스를 이용하여 하위 클래스를 작성한 다음 하위 클래스를 상위 클래스로 대치 시켜 시스템에 침입할 수 있다.

설계; 클래스가 개념적으로 완벽하게 구현되어 있다면 그 클래스는 더 이상 하위 클래스를 가질 필요가 없기 때문에 final로 선언한다.

 

1) 메소드 지역 변수나 객체 변수에 final을 붙여 상수로 사용

class AA{
    public final int MAX = 100;
    
    public void tmp(){
    	final int maxx = 50;
        maxx++; // error! final로 지정된 지역 변수 값 변경 불가
    }
}

class BB{
    AA aa = new AA();
    aa.MAX = 200; // error! final로 지정된 객체 변수의 값 변경 불가
}

 

2) 메소드에 final을 붙여 선언. 하위 클래스에서 이 메소드를 오버라이딩 할 수 없음.

final로 선언된 메소드는 상속은 허용하지만, 변환해서 사용하는 것은 허용하지 않는 다는 것을 의미함.

 

class AA{
    final void calc(){}
}
class BB extneds AA{
    void calc(){} // error! fianl로 선언된 메소드 오버라이딩 불가
}

 

3) 클래스에 final을 붙여 선언. 상속을 허용하지 않음

하위 클래스를 허용하지 않겠다는 것을 의미함.

 

final class AAA{}
class BBB extends AAA{} // error! final로 선언된 클래스 상속 불가

 

 


 

Object 클래스

Object클래스는 java.lang 패키지에 속해 있는 라이브러리 클래스이다.

앞서 자바의 특징으로 자바의 모든 클래스는 Object 클래스의 하위 클래스라고 하였다. 즉 명시적으로 상위 클래스를 지정하는 것과 상관없이 모든 자바 클래스의 최상위 클래스는 Object 클래스이다.

명시적으로 상위 클래스가 지정된 경우에도, 그 상위 클래스의 상위클래스가 최종적으로 Object 클래스가 된다.

 

반대로 설명하면, Object 클래스에서 선언된 모든 속성과 기능은 모든 자바 클래스에 상속되기 때문에 자유롭게 사용할 수 있다.

 

자바에서 제공되는 대부분의 라이브러리 클래스는 속성을 직접 사용하는 것을 허용하지 않는다. 대부분의 라이브러리 클래스는 기능을 메소드로 제공하고 있다. 즉 우리가 라이브러리 클래스를 사용한다는 의미는 그 클래스에서 제공되는 메소드를 사용한다는 의미이다.

 

메소드 설명
protected Object clone() 객체의 복사본을 만들어 Object 형의 객체로 반환
public boolean equals(Object object) 현재의 객체와 Object로 지정된 객체가 같으면 true, 다르면 false를 반환
protected void finalize() 자바에서는 객체가 더 이상 사용되지 않으면 자동적으로 쓰레기 수집 기능을 수행한다. finalize() 메소드는 쓰레기 수집 기능이 수행 되기 전에 호출되며 객체가 점유하고 있던 자원들을 해제하는 데 사용된다.
public Class<?> getClass() 객체명의 클래스명을 Class형의 객체로 반환
public int hashCode() 호출한 객체와 연관된 해쉬 hash 코드를 얻는다. 해쉬 코드는 메모리에 저장된 객체에 16진수 주소를 의미한다.
public String toString() 현재 객체의 문자열 표현을 반환
public void notify() 대기 중인 스레드 중 하나의 스레드를 다시 시작시킨다
public void notifyAll() 대기 중인 모든 스레드를 다시 시작시킨다
public void wait() 스레드의 실행을 중지하고 대기 상태로 간다
public void wait(long millisec) 스레드의 실행을 중지하고 milisec 밀리초 동안 대기한 다음 다시 시작한다
public void wait(long millisec, int nanosec) 스레드의 실행을 중지하고 millesec 밀리초 + nanosec 나노초 동안 대기한 다음 다시 시작한다

 

 

 

 


참고

  • 처음 시작하는 JAVA 프로그래밍, 김충석 저

'Java Live Study' 카테고리의 다른 글