Programming/Java \ Spring

🔥자바스터디🔥 자바의 정석 CH7 객체지향 프로그래밍(상속, 오버라이딩, 제어자, 다형성, 추상클래스, 인터페이스, 내부클래스)

1. 상속

 

      - 상속 : 기존의 클래스를 재사용하여 새로운 클래스르 작성하는 것

                 코드를 공통적으로 관리할 수 있기 때문에 유지보수면에서 매우 용이하다.

      - 구현 : 새로 작성하는 클래스 이름 뒤에 상속 받고자 하는 클래스의 이름을 키워드 extends 와 함께 적어준다.

                  ex) class Child extends Parent{}

      - 자식클래스는 부모클래스의 모든 멤버를 상속 받는다. (포함한다.)

      - 생성자와 초기화 블럭은 상속되지 않는다. 멤버(메소드, 필드)만 상속된다.

      - 자식 클래스의 멤버 개수는 부모 클래스보다 항상 같거나 많다.

       

** 접근제어자가 private 또는 default인 멤버들은 상속되지 않는다기보다 상속은 받지만 자식 클래스로부터의 접근이 제한되는 것이다.

** 같은 내용의 코드를 한곳에서 관리함으로써 코드의 중복이 줄어든다

 

1.2 클래스간의 관계 - 포함관계

 

      - 상속이외의 클래스를 재사용하는 방법,

        한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것

class Circle {
	int x;   // 원점의 x좌표
    int y;   // 원점의 y좌표
    int r;   // 반지름
}

class Point {
	int x;   // x좌표
    int y;   // y좌표
}

// 포함관계
class Circle{
	Point c = new Point(); // 원점
    int r;
}

1.3 클래스간의 관계 결정하기

 

       상속관계 : '~은 ~이다. (is-a)'

       포함관계 : '~은 ~을 가지고 있다. (has-a)'

 

**  toString()은 Object 클래스에 정의된 것으로 어떤 객체에 대해서도 toString()을 호출하는 것이 가능하다.

**  toString을 직접 호출하지 않아도 어떤 객체를 System.out.print로 호출하면 자동으로 toString이 호출되도록 약속되어 있다.

 

1.4 단일상속 (다중상속 비허용)

 

1.5 Object클래스 - 모든 클래스의 조상

 

2. 오버라이딩

2.1 오버라이딩이란?

      

      - 부모클래스로부터 상속받은 메소드의 내용을 변경하는 것

 

2.2 오버라이딩의 조건

 

       자식클래스에서 오버라이딩하는 메소드는 부모클래스의 메소드와

                 - 이름이 같아야한다.

                 - 매개변수가 같아야 한다.

                 - 반환타입이 같아야 한다.

       (선언부가 일치해야한다.)

     

   1) 접근 제어자는 부모 클래스의 메소드보다 좁은 범위로 변경 할 수 없다.

         ** 접근제어자(넓은 범위 >> 좁은 범위)

              public >> protected >> (default) >> private

 

   2) 부모 클래스의 메소드보다 많은 수의 예외를 선언할 수 없다.

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

 

2.3 오버로딩 vs 오버라이딩

 

        - 오버로딩 : 기존에 없는 새로운 메소드를 정의하는 것

        - 오버라이딩 : 상속받은 메소드의 내용을 변경하는 것

 

2.4 super

 

        - 자식 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수(구별하는데 사용)

 

2.5 super() - 조상 클래스의 생성자

       

         - Object클래스를 제외한 모든 클래스의 생성자 첫 줄에 생성자, this(), super()를 호출해야한다.

           호출하지 않으면 컴파일러가 자동적으로 super();를 생성자의 첫줄에 삽입한다.

         - 생성자가 정의되어 있는 클래스에는 컴파일러가 기본 생성자를 자동으로 추가하지 않으므로 명시해야한다.

 

3. package와 import

3.1 패키지(package)

 

        - 하나의 소스파일에는 첫번째 문장으로 단 한 번의 패키지 선언만을 허용한다

        - 모든 클래스는 반드시 하나의 패키지에 속해야한다.

        - 다른 패키지를 포함할 수 있으며, 점(.)으로 구분한다. (계층구조로 구성 가능)

        - 패키지는 물리적으로 클래스 파일(.class)을 포함하는 하나의 디렉토리다.

 

3.2 패키지의 선언 / import문의 선언

// 패키지 선언
package 패키지명;

// import 선언
import 패키지명.클래스명;
import 패키지명.*;

 * 패키지를 기재하지 않아도 자바에서 '이름없는 패키지(unnamed package)'를 제공하기 때문에 컴파일되지만 

   미리 패키지를 구성하여 적용하는게 좋다.

 

3.3 import문

 

        - 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보를 제공하는 것

        - import문으로 사용하고자 하는 클래스의 패키지를 명시해주면 클래스사용시 패키지경로를 생략할수 있다.

 

3.4 import문의 선언

 

        - import 패키지명.* 사용시 하위 패키지의 클래스까지 포함하는 것은 아니다.

        - 모든 소스파일에는 묵시적으로 import java.lang.*; 이 선언되어있다.

 

4. 제어자(modifier)

4.1 제어자란?

 

        - 클래스, 변수, 메소드 선언부에 사용되며, 접근제어자와 그외 제어자로 나뉜다.

        - 하나의 대상에 여러개의 제어자를 조합하여 사용 가능하나 접근제어자는 한개만 사용할수 있다.

접근제어자
    public, protected, default, private
그 외
    static, final, abstract, native, transient, synchronized, volatile, strictfp

4.2 static - 클래스의, 공통적인

 

        - 인스턴스가 아닌 클래스에 관계되었기 때문에 인스턴스를 생성하지 않고 사용 가능하다.

         (멤버변수, 메소드, 초기화 블럭)

 

4.3 final - 마지막의, 변경될 수 없는

4.4 생성자를 이용한 final 멤버변수 초기화

 

        - 클래스 내에 매개변수를 갖는 생성자를 선언하여,

          인스턴스를 생성할 때 final이 붙은 멤버변수를 초기화하는데 필요한 값을 생성자의 매개변수로부터 제공 받는다.

          (각 인스턴스마다 final 필드를 다른 값으로 초기화 할수 있다.)

 

4.5 abstract - 추상의, 미완성의

 

        - 메소드의 선언부만 작성하고 실행내용을 구현하지 않은 추상메소드를 선언하는데 사용된다.

          클래스에 사용되면 클래스 내에 추상메소드가 선언되어있음을 의미한다.

          (추상클래스 - 추상메소드가 포함된 클래스, 추상메소드 - 구현부가 없는 메소드)

 

4.6 접근 제어자

 

        - public : 접근 제한이 없음
        - protected : 같은 패키지 안이거나, 다른패키지의 자식 클래스일때 접근이 가능하다
        - default : 같은패키지 내에서만 접근이 가능하다.
        - private : 같은 클래스 내에서만 접근이 가능하다.

 

  ** 접근범위가 넓은 쪽에서 좁은 쪽으로 나열하면

      public > protected > default > private

 

4.7 접근 제어자를 이용한 캡슐화

 

         <접근제어자를 사용하는 이유>

          - 외부로부터 데이터를 보호하기 위해

           - 외부에는 불필요한, 내부적으로만 사용되는 부분을 클래스 내부에 감추기 위해 (복잡성을 줄일수있다)

 

4.8 생성자의 접근 제어자

 

        - 생성자의 접근 제어자를 private로 지정하면 외부에서 인스턴스를 생성할수 없다. (클래스 내부에서는 가능)

          (때문에 다른 클래스의 부모클래스가 될수없다. / 클래스 앞에 final을 추가하여 상속불가를 알리는 것이 좋다.)

        - 따라서 인스턴스를 생성해 반환해주는 public 메소드를 제공함으로 외부에서 사용 가능하도록 한다.

          (public and static)

       - ex) singleton

 

4.9 제어자의 조합

 

<제어자를 조합해서 사용할 때 주의해야 할 점>

 

1. 메서드에  static 과  abstract 를 함께 사용할 수 없다.
    static 메서드는 몸통이 있는 메서드에만 사용할 수 있다.

2. 클래스에  abstract 와  final 을 동시에 사용할 수 없다.
    abstract 메서드는 상속되어서 완성되어야 하는데 final이 붙은 클래스는 상속받을 수 없기 때문이다.
3.
 abstract 메서드의 접근 제어자가  private 일 수 없다.
   마찬가지로 abstract메서드는 자식 클래스에서 구현해주어야 하는데 private이면 자식 클래스에서 접근할 수 없기 때문이다.

4. 메서드에  private 와  final 을 같이 사용할 필요는 없다.

   접근제어자가 private인 메서드는 오버라이딩 될 수 없기 때문이다. 하나만 써도 충분하다.

 

5. 다형성

5.1 다형성이란?

 

        - 여러가지 형태를 가질 수 있는 능력

          조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하였다.

 // 부모클래스 TV, 자식클래스 CaptionTV 일때

CaptionTV ct = new CaptionTV(); 
TV t = new CaptionTV();         // 부모클래스에서 자식클래스 참조 가능
CaptionTV tv = new TV();        // 자식클래스에서 부모클래스 참조 불가

// t와 ct는 CaptinoTV 객체를 생성했으나 사용할수 있는 멤버는 부모클래스 t가 더 적다.
// 자식클래스에만 있는 독립적 멤버는 부모클래스에서 참조할수 없기 때문이다.

/* 부모클래스 타입으로 자식객체를 참조하는것이 좋은 이유는 상속으로 인한 확장성 때문인 것 같다.
   아무래도 세대가 많아지면 각각 상속받은 변수나 메소드 외에 독립적인 멤버(변수나 메소드)가 있을 것이고,
   아래와 같이 같은 부모를 상속받은 자식끼리의 형변환은 안되나 부모로 받으면 사용할수가 있다.   
*/
public class Management {
	
	public static void main(String[] args) {
			M2 m = new M2();
			Management ma = m;
			M3 m2 = (M3)ma;	       // 부모클래스 타입의 객체가 다른 자식클래스여도 형변환 가능
            m2 = (M3)m;            // 같은 부모클래스를 상속받았어도 형변환 불가
	}
}

public class M2 extends Management{/*블라블라*/}

public class M3 extends Management{/*블라블라*/}

5.2 참조변수의 형변환

     

       - 서로 상속관계에 있는 클래스 사이에서만 형변환이 가능하다.

        (자손타입 >> 조상타입 : 형변환 생략가능, 조상타입 >> 자손타입 : 형변환 생략불가)

      - 참조변수의 형변환은 인스턴스에 아무런 영향을 미치지 않는다.

        (사용할수 있는 멤버의 범위를 조절하는 것일 뿐이다.)

 

5.3 instanceof 연산자

Car c = new FireEngine();

if(c instanceof FireEngine){     // 왼쪽 비교할 참조변수, 오른쪽 피연산자
}

       - 참조변수가 참조하고 있는 객체의 타입을 확인할수 있다. 

       - 값이 null인 참조변수를 instanceof 연산하면 false가 반환된다.

        (조상타입으로 연산하면 true가 반환된다)

 

5.4 참조변수와 인스턴스의 연결

 

       - 중복된 멤버변수가 있을때 참조변수의 타입에 따라 결과값이 달라진다.

         인스턴스 메소드의 경우 객체에 정의된 메소드가 호출되지만 static 메소드나 멤버변수의 경우 참조변수의 타입에 영향을 받는다.

 

5.5 매개변수의 다형성

 

       - 메소드의 매개변수에 참조될 변수가 동일한 부모클래스를 가졌다면 부모클래스 타입의 매개변수로 선언할 수 있다.

          void buy(TV t), void buy(Audio a) 등으로 오버로딩하지 않고 void buy(Product p)로 선언할수 있다.

//PrintStream클래스에 정의되어있는 print(Object o)메소드

public void print(Object obj){
	write(String.valueOf(obj); // valueof()가 반환한 문자열을 출력한다.
}

public static String valueOf(Object obj){
	return (obj == null) ? "null" : obj.toString(); // 문자열을 반환한다.

5.6 여러종류의 객체를 배열로 다루기

 

       - 조상타입의 참조변수 배열을 사용하면, 공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어서 다룰수 있다.

       - 여러종류의 객체를 가변 크기로 다루고 싶을땐 Vector 클래스를 이용하면 된다. (내부적으로 Object 타입의 배열)

         (Vector의 기능을 개선한 것이 ArrayList이기 때문에 ArrayList<Object>를 이용하여 여러타입을 담는 것이 좋다.)

 

6. 추상클래스

6.1 추상클래스란?

 

       - 추상클래스는 구현부가 없는 메소드를 포함하고 있는 클래스를의미하며, 추상클래스로는 인스턴스를 생성할수 없다.

         (추상메소드를 가지고 있지 않아도(일반 클래스여도) abstract를 명시하여 추상클래스로 지정할수 있다.

       - 제어자 abstract를 클래스 선언부 앞부분에 명시한다.

 

6.2 추상메소드

 

       - 메소드의 구현부를 작성하지 않은 미완성의 메소드

        abstract 리턴타입 메소드명();

      - 상속받는 자손클래스는 오버라이딩을 통해 조상클래스의 추상메소드를 모두 구현해야한다.

 

6.3 추상클래스의 작성

 

      - 추상화 : 기존 클래스의 공통부분을 뽑아내 조상 클래스를 만드는 것 (공통의 조상을 만드는 작업)

      - 구체화 : 상속을 통해 클래스를 구현, 확장하는 작업

 

7. 인터페이스

7.1 인터페이스란?

 

      - 추상클래스보다 추상화 정도가 높아서 오직 추상메소드와 상수만을 멤버로 가질수 있다.

        (JDK1.8 부터 static메소드와 디폴트 메소드를 추가 허용한다.)

 

7.2 인터페이스의 작성

interface 인터페이스명 {
	public static final 타입 상수이름 = 값;
    public abstract 메소드명(매개변수);
}

//인터페이스 멤버의 제약사항
/*
	1. 모든 멤버변수는 public static final이어야 하며, 이를 생략할 수 있다.
    2. 모든 메소드는 public abstract이어야 하며, 이를 생략할 수 있다.
    단, static 메소드와 디폴트 메소드는 예외
*/

7.3 인터페이스의 상속

 

      - 인터페이스만 상속받을수 있으며, 클래스와 달리 다중상속이 가능하다.

 

7.4 인터페이스의 구현

 

      - 인터페이스 자체로 인스턴스를 생성할 수 없으며, 추상클래스처럼 자신에 정의된 추상메소드를 구현할 클래스를 작성해야한다.

      - 인터페이스 구현시 extends 대신 implements를 사용한다.

      - 상속과 구현을 동시에 할수 있다.

 

7.5 인터페이스를 이용한 다중상속

 

      - 인터페이스는 static상수만 정의할 수 있으므로 조상클래스의 멤버변수와 충돌 가능성이 거의 없다.

      - 추상메소드는 구현 내용이 없으므로 조상메소드를 상속받으면 된다. 

>> 다중 상속의 이점이 없어지므로, 비중이 높은 쪽을 선택해 상속받고 그 외 멤버는 내부에 포함시키는 방식으로 처리한다.

 

7.6 인터페이스를 이용한 다형성

 

      - 인터페이스를 구현한 자손클래스 또한 인터페이스 타입의 참조변수로 참조할수 있다.(형변환도 가능하다)

      - 메소드의 매개변수, 리턴타입으로 인터페이스 타입을 지정 가능하다.

        (리턴타입의 인터페이스를 구현한 클래스의 인스턴스를 반환, 매개변수로 사용하는 것을 의미한다.)

 

7.7 인터페이스의 장점

 

      - 개발시간을 단축시킬 수 있다.

      - 표준화가 가능하다.

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

      - 독립적인 프로그래밍이 가능하다.

 

7.8 인터페이스의 이해

 

< 인터페이스를 이해하기 위해 꼭 기억해두어야할 것 >

 

      - 클래스를 사용하는 쪽(user)과 클래스를 제공하는 쪽(Provider)이 있다.

      - 메소드를 사용(호출)하는 쪽(user)에서는 사용하려는 메소드(Provider)의 선언부만 알면 된다. (내용은 몰라도 된다)

 

** 사용하는 쪽과 제공하는 쪽 둘의 직접적인 관계를 간접적으로 바꿔서 변경 등의 영향을 사용하는 쪽에 덜받게 하기 위해 인터페이스를 구현한다.

//직접적인 관계(user a - provider b)

class A{
	public void methodA(B b){
    	b.methodB();
    }
}

class B{
	public void methodB(){
    	System.out.print("methodB()");
    }
}

//간접적인 관계(user a - interface i - provider b)

class A{
	public void methodA(I i){
    	i.methodB();
    }
}
 
interface I{
	public void methodB();
}

class B implements I{
	public void methodB(){
    	System.out.print("methodB in B class");
    }
}
 

** methodA에서 B클래스 인스턴스를 사용하였는데 interface 인스턴스를 호출함으로써 interface I를 구현한 객체를 불러옴을 알수 있다.

    최종적으로 활용되는 B클래스 인스턴스의 존재, 변경 등등의 어떠한 이벤트에도 A는 영향을 받지 않음을 알수 있다.

 

7.9 디폴트 메소드와 static 메소드

 

      - 디폴트 메소드 : 추상 메소드의 기본적인 구현을 제공하는 메소드

                             추상 메소드가 아니기 때문에 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.

                             접근제어자는 public(생략가능), 리턴타입 앞에 default 표기, 추상메소드와 다르게 구현부({}) 표시를 해야한다.

                             default void newMethod(){};

 

       ** 디폴트 메소드와 기존 메소드의 이름이 중복되면 충돌이 발생할수 있다.

       ** 충돌 해결하는 규칙

               1) 여러 인터페이스의 디폴트 메소드 간의 충돌

                   - 인터페이스를 구현한 클래스에서 디폴트 메소드를 오버라이딩해야 한다.

               2) 디폴트 메소드와 조상 클래스의 메소드 간의 충돌

                   - 조상 클래스의 메소드가 상속되고, 디폴트 메소드는 무시된다.

 

8. 내부클래스

8.1 내부클래스란?

    

      - 클래스 내에 선언된 클래스

      - 장점 : 1) 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.

                 2) 코드의 복잡성을 줄일 수 있다.(캡슐화)

      - 내부 클래스는 자신을 감싸고 있는 외부클래스 외 타클래스에서 사용이 잦지 않아야한다.

 

8.2 내부클래스의 종류와 특징

 

      - 변수의 선언위치에 따른 종류와 동일하다.

 

8.3 내부 클래스의 선언

     

      - 변수의 선언위치와 동일하고, 동일한 유효범위(scope)와 접근성을 갖는다.

 

8.4 내부 클래스의 제어자와 접근성

      - 내부클래스 중에서 static class 만 static 멤버를 가질수 있다.

        단, final과 static이 동시에 붙은 변수는 상수이므로 모든 내부 클래스에서 정의가 가능하다.

      - static 멤버는 인스턴스 멤버(내부클래스 포함)에 직접 접근할수 없다. 접근시 외부클래스를 먼저 생성해야만 접근할수 있다.

      - 내부 클래스에서는 외부 클래스의 private멤버도 접근 가능하다.

      - 외부클래스의 지역변수는 final이 붙은 상수만 접근 가능하다.

        (메소드 수행 후 지역변수 소명시점에도, 지역클래스의 인스턴스가 소멸된 지역변수를 참조하려는 경우가 발생할수 있기 때문)

 

8.5 익명 클래스

 

      - 클래스의 선언과 객체의 생성을 동시에 하기 때문에 단 한번만 사용될 수 있고 오직 하나의 객체만을 생성할 수 있는 일회용 클래스이다.

      - 이름이 없어서 생성자를 가질수도 없고, 하나의 클래스를 상속받거나 하나의 인터페이스만을 구현한다.

        new Object(){};