Chapter7. 객체지향 프로그래밍2

개발빼-엠·2023년 1월 30일

Java

목록 보기
7/8
post-thumbnail

1.상속(inheritance)

상속이란?

기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다.

적은 양의 코드로 새로운 클래스를 작성할 수 있고 코드를 공통적으로 관리할 수 잇기 때문에 코드의 추가 및 변경이 매우 용이하다.

코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 크게 기여한다.

  • 기존의 클래스를 재사용해서 새로운 클래스를 작성하는 것
  • 두 클래스를 조상과 자손으로 관계를 맺어주는 것
  • 자손은 조상의 모든 멤버를 상속받는다.(생성자, 초기화블럭 제외)
  • 자손의 멤버개수는 조상보다 적을 수 없다.(같거나 많다.)
class 자손클래스 extends 조상클래스 {
	// ...
}

조상 클래스: 부모(parent)클래스, 상위(super)클래스, 기반(base)클래스

자손 클래스: 자식(child)클래스, 하위(sub)클래스, 파생된(derived)클래스

클래스간의 관계

  1. 상속관계(inheritance)

    공통부분은 조상에서 관리하고 개별부분은 자손에서 관리한다.

    조상의 변경은 자손에 영향을 미치지만, 자손의 변경은 조상에 아무런 영향을 미치지 않는다.

  2. 포함관계(composite)

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

    작은 단위의 클래스를 먼저 만들고, 이 들을 조합해서 하나의 커다란 클래스를 만든다.

클래스간의 관계결정하기 - 상속 vs 포함

  • 가능한 한 많은 관계를 맺어주어 재사용성을 높이고 관리하기 쉽게 한다.
  • ‘is-a’와 ‘has-a’를 가지고 문장을 만들어 본다.
    • 상속관계 - ‘~은 ~이다.(is-a)’
    • 포함관계 - ‘~은 ~을 가지고 있다.(has-a)’

단일상속(single inheritance)

  • Java는 단일상속만을 허용한다.
  • 비중이 높은 클래스 하나만 상속관계로, 나머지는 포함관계로 한다.

Object클래스 - 모든 클래스의 최고조상

  • 조상이 없는 클래슨느 자동적으로 Object클래스를 상속받게 된다.
  • 상속계층도의 최상위에는 Object클래스가 위치한다.
  • 모든 클래스는 Object클래스에 정의된 11개의 메서드를 상속받는다.

다른 클래스로부터 상속 받지 않는 모든 클래스들은 자동적으로 Object클래스로부터 상속받게 한다.

CaptionTv → Tv → Object

class Tv {}

class captionTv extends Tv {}
class Tv extends Object {}

class captionTv extends Tv {}

2.오버라이딩(over riding)

오버라이딩이란?

조상클래스로부터 상속받은 메서드의 내용을 상속받는 클래스에 맞게 변경하는 것

오버라이딩의 조건

  1. 선언부가 같아야 한다.(이름, 매개변수, 리턴타입)

  2. 접근제어자를 좁은 범위로 변경할 수 없다.

    조상의 메서드가 protected라면, 범위가 같거나 넓은 protected나 public으로만 변경할 수 없다.

  3. 조상클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.

오버로딩(over loading) VS 오버라이딩(over riding)

  • 오버로딩 - 기존에 없는 새로운 메서드를 정의하는 것(new)
    • 매개변수(parameter) 여러개 지원. 매개변수가 변경 될 수 있다.
  • 오버라이딩 - 상속받은 메서드의 내용을 변경하는 것(change, modify)
    • 매개변수는 변경할 수 없고, 메서드 내용만 변경이 가능하다.

super - 참조변수

  • this - 인스턴스 자신을 가리키는 참조변수. 인스턴스의 주소가 저장되어있음. 모든 인스턴스 메서드에 지역변수로 숨겨진 채로 존재
  • super
    • this와 같음
    • 조상의 멤버와 자신의 멤버를 구별하는 데 사용
    • this와 마찬가지로 super역시 static메서드에서는 사용할 수 없고 인스턴스 메서드에서만 사용할 수 있다.

super - 조상의 생성자

  • 자손클래스의 인스턴스를 생성하면, 자손의 멤버와 조상의 멤버가 합쳐진 하나의 인스턴스가 생성된다.
  • 조상의 멤버들도 초기화되어야 하기 때문에 자손의 생성자의 첫 문장에서 조상의 생성자를 호출해야 한다.

📙 Obeject클래스를 제외한 모든 클래스의 생성자 첫 줄에 생성자, this() 또는 super()를 호출해야 한다.
그렇지 않으면 컴파일러가 자동적으로 ‘super();’를 생성자의 첫줄에 삽입한다.

왼쪽의 코드와 같이 작성했을 경우 컴파일러는 오른쪽 코드와 같이 실행한다.

class Point {
	int x;
	int y;

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

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

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

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

3. package와 import

패키지

  • 서로 연관된 클래스와 인터페이스의 묶음
  • 클래스가 물리적으로 클래스파일(*.class)인 것처럼, 패키지는 물리적으로 폴더이다. 패키지는 서브패키지를 가질 수 있으며, ‘.’으로 구분한다.
  • 클래스의 실제 이름(full name)은 패키지명이 포함된 것이다.(String클래스의 full name은 java.lang.String → lang이 패키지이름. String이 클래스이름)
  • rt.jar는 Java API의 기본 클래스들을 압축한 파일(JDK설치경로 jre lib에 위치) *rt: runtime의 약자

패키지의 선언

package 패키지명;

  • 대소문자 모두 허용하지만, 클래스명과 쉽게 구분하기 위해 소문자로 하는 것을 원칙으로 하고 있다.
  • 패키지는 소스파일에 첫 번째 문장(주석 제외)으로 단 한번 선언한다.
  • 하나의 소스파일에 둘 이상의 클래스가 포함된 경우, 모두 같은 패키지에 속하게 된다.(하나의 소스파일에 단 하나의 public클래스만 허용한다.)
  • 자바 컴파일시 -d옵션(javac -d Test.java)을 사용하면 소스파일에 선언된 패키지 구조를 지정된 경로에 자동적으로 생성해준다.
  • 모든 클래스는 하나의 패키지에 속하며, 패키지가 선언되지 않은 클래스는 자동적으로 이름없는(unnamed)패키지에 속하게 된다.

클래스패스(classpath) 설정

클래스패스는 컴파일러나 JVM등이 클래스의 위치를 찾는데 사용되는 경로이다.

  • 클래스패스는 클래스파일(.class)을 찾는 경로. 구분자는 ‘;’
  • 클래스패스에 패키지가 포함된 폴더나 jar.파일을 나열한다.

[참고] java.exe의 ‘cp’옵션을 이용해서 일시적으로 클래스패스를 지정해 줄 수도 있다.

  • 클래스패스로 자동 포함된 폴더 for 클래스파일: 수동생성 해야함. → 설치경로 jre classes
  • 클래스패스로 자동 포함된 폴더 for jar파일: JDK설치시 자동생성됨. → 설치경로 jre lib ext

import문

  • 사용할 클래스가 속한 패키지를 지정하는데 사용.
  • import문을 사용하면 클래스를 사용할 때 패키지명을 생략할 수 있다.
  • java.lang패키지의 클래스는 import하지 않고도 사용할 수 있다. String, Objectt, System, Tread등등

import문의 선언

  • import문은 패키지문과 클래스선언의 사이에 선언한다. ⓵package문 ②import문 ③클래스 선언
  • import문을 선언하는 방법은 다음과 같다.
    import 패키지명.클래스명;
    // or
    import 패키지명.*;
  • import문은 컴파일 시에 처리되므로 프로그램의 성능에 아무런 영향을 미치지 않는다.
  • 이름이 같은 클래스가 속한 두 패키지를 import할 때는 클래스 앞에 패키지명을 붙여줘야 한다.

static import문

import문을 사용하면 클래스의 패키지명을 생략할 수 있는 것과 같이 static import문을 사용하면 static멤버를 호출할 때 클래스 이름을 생략할 수 있다.

import static java.lang.Integer.*;
import static java.lang.Math.random;
import static java.lang.System.out;

// 위와같이 import문을 선언하였다면
System.out.println(Math.random());

// 위의코드를 다음과 같이 간략히 할 수 있다.
out.println(random());

4.제어자(modifier)

제어자

  • 클래스, 변수, 메서드의 선언부에 사용되어 부가적인 의미를 부여한다.
  • 제어자는 크게 접근제어자와 그 외의 제어자로 나뉜다.
    • 제어자
      • 접근 제어자 - public, protected, default, private
      • 그 외 - static, final, abstract, native, transient, synchronized, volatile, strictfp
  • 하나의 대상에 여러 개의 제어자를 조합해서 사용할 수 있으나, 접근제어자는 단 하나만 사용할 수 있다.

static - 클래스의, 공통적인

static이 사용될 수 있는 곳은 멤버변수, 메서드, 초기화 블럭이다.

제어자대상의미
static멤버변수모든 인스턴스에 공통적으로 사용되는 클래스변수가 된다.

클래스변수는 인스턴스를 생성하지 않고도 사용 가능하다.
클래스가 메모리에 로드될 때 생성된다. |
| | 메서드 | 인스턴스를 생성하지 않고도 호출이 가능한 static메서드가 된다.
static메서드 내에서는 인스턴스멤버들을 직접 사용할 수 없다. |

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

final이 사용될 수 있는 곳은 클래스, 메서드, 멤버변수, 지역변수이다.

제어자대상의미
final클래스변경될 수 없는 클래스, 확장될 수 없는 클래스가 된다.
그래서 final로 지정된 클래스는 다른 클래스의 조상이 될 수 없다.
메서드변경될 수 없는 메서드, final로 지정된 메서드는 오버라이딩을 통해 재정의 될 수 없다.
멤버변수, 지역변수변수 앞에 final이 붙으면, 값을 변경할 수 없는 상수가 된다.

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

final이 붙은 변수는 상수이므로 보통은 선언과 초기화를 동시에 하지만, 인스턴스변수의 경우 생성자에서 초기화 할 수 있다.

abstract - 추상의, 미완성의

abstract가 사용될 수 있는 곳은 클래스, 메서드이다.

제어자대상의미
abstract클래스클래스 내에 추상메서드가 선언되어 있음을 의미한다.
메서드선언부만 작성하고 구현부는 작성하지 않은 추상메서드임을 알린다.

[참고] 추상메서드가 없는 클래스도 abstract를 붙여서 추상클래스로 선언하는 것이 가능하나 그렇게 해야 할 이유는 없다.

접근제어자(access modifier)

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

멤버 또는 클래스에 사용되어, 외부로부터의 접근을 제한한다.

제어자같은 클래스같은 패키지자손클래스전체
public
protected
default
private

클래스에는 public과 default만 사용할 수 있고, 멤버변수와 메서드에는 접근제어자 4개를 모두 사용할 수 있다.

default제어자는 따로 사용하지 않는다. 어떤 것도 사용하지 않으면 default이기 때문이다.

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

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

  • 외부로부터 데이터를 보호하기 위해서
  • 외부에는 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해서
대상사용가능한 접근 제어자
클래스public, (default)
메서드public, protected, default, private
멤버변수public, protected, default, private
지역변수없음

보통 멤버변수의 값을 읽는 메서드의 이름을 ‘get멤버변수이름’으로 하고, 멤버변수의 값을 변경하는 메서드의 이름을 ‘set멤버변수이름'으로 한다. (암묵적인 규칙)

get으로 시작하는 메서드를 ‘겟터(getter)’, set으로 시작하는 메서드를 ‘셋터(setter)’라고 부른다.

[참고] 하나의 소스파일(.java)에는 public클래스가 단 하나만 존재할 수 있으며, 소스파일의 이름은 반드시 public클래스의 이름과 같아야 한다.

캡슐화란?

접근제어자를 이용해서 멤버변수들은 privite로 내부에 감추고 메서드들은 public으로 외부에 노출시킴으로써 같은 클래스에 정의된 멤버들끼리는 서로 자유롭게 접근하고, 외부에서는 메서드를 통해서만 멤버변수에 접근할 수 있는 구조로 만드는 것.

직접 객체의 멤버변수에 접근할 수 없으며 메서드를 통해서만 멤버변수에 접근할 수 있다.

외부에서는 객체의 노출된 public 메서드만 호출할 수 있을 뿐 실제 내부가 어떻게 되어있는지 알 수없다.

상속을 통해 확장될 것이 예상되는 클래스라면 멤버에 접근 제한을 주되 자손클래스에서 접근하는 것이 가능하도록 하기 위해 private대신 protected를 사용한다.

생성자의 접근 제어자

  • 일반적으로 생성자의 접근 제어자는 클래스의 접근 제어자와 일치한다.
  • 생성자에 접근 제어자를 사용함으로써 인스턴스의 생성을 제한할 수 있다.

제어자의 조합

대상사용가능한 제어자
클래스public,(default), final, abstract
메서드모든 접근 제어자, final, abstract, static
멤버변수모든 접근 제어자, final, static
지역변수final
  1. 메서드에 static과 abstract를 함께 사용할 수 없다.

    static메서드는 몸통(구현부)이 있는 메서드에만 사용할 수 있기 때문이다.

  2. 클래스에 abstract와 final을 동시에 사용할 수 없다.

    클래스에 사용되는 final은 클래스를 확장할 수 없다는 의미이고, abstract는 상속을 통해서 완성되어야 한다는 의미이므로 서로 모둔되기 때문이다.

  3. abstract메서드의 접근제어자가 private일 수 없다.

    abstract메서드는 자손클래스에서 구현해주어야 하는데 접근 제어자가 private이면, 자손클래스에서 접근할 수 없기 때문이다.

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

    접근 제어자가 private인 메서드는 오버라이딩될 수 없기 때문이다. 이 둘 중 하나만 사용해도 의미가 충분하다.

5.다형성(polymorphism)

⭐️다형성은 중요하니 잘 이해하고 넘어가야한다.

다형성이란?

  • 여러 가지 형태를 가질 수 있는 능력
  • 하나의 참조변수로 여러 타입의 객체를 참조할 수 있는 것
    • 즉, 조상타입의 참조변수로 자손타입의 객체를 다룰 수 있는 것.(외우기ㅜ)

클래스가 서로 상속관계에 있을 경우, 조상클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조하도록 하는 것도 가능하다.

조상타입의 참조변수로 자손타입의 인스턴스를 참조할 수 있다.
반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.

인스턴스를 같은 타입의 참조변수로 참조하는것과 조상타입의 참조변수로 참조하는 것의 차이

조상타입의 참조변수로 참조하면 인스턴스 중에서 조상클래스의 멤버들만 사용할 수 있으며, 조상클래스에 정의 되지 않는 멤버는 사용이 불가능하다.

즉, 둘 다 같은 타입의 인스턴스지만 참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.

반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조하는 것은 불가능하다.

실제 인스턴스의 멤버 개수보다 자손타입의 인스턴스의 멤버개수가 더 많기 때문이다.

참조변수의 형변환

자손타입 → 조상타입 (Up-casting): 형변환 생략가능
조상타입 → 자손타입 (Down-casting): 형변환 생략불가

  • 서로 상속관계에 있는 타입간의 형변환만 가능하다.
  • 자손 타입에서 조상타입으로 형변환하는 경우. 형변환 생략가능하다.

참조변수의 형변환은 참조할 수 있는 멤버의 갯수가 달라질 뿐 다른 변화는 없다.

0개의 댓글