자바의정석 ch7

soso·2023년 2월 15일
0
post-thumbnail

Chapter7 객체지향 프로그래밍 Ⅱ

상속

기존의 클래스로 새로운 클래스를 작성하는것이다.(코드의 재사용 용의)
두 클래스를 부모(조상)와 자식(자손)으로 관계를 맺어주는것이다

상속받고자하는 클래스의 이름을 키워드'extends' 와 함께 서주면된다.

class Prenrt{ }
class Child extends Prent(){
} //  Prent는 부모(조상)클래스, Child는 자식(자손)클래스이다
  • 자손 클래스는 조상클래스의 모든 멤버를 상속받는다.
    (단, 생성자와 초기화 블럭은 상속되지 않는다.)
  • 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.

포함관계

포함(composite)이란?
클래스의 멤버로 참조변수를 선언하는것이다.
작은 단위의 클래스를 만들고, 이 들을 조합해서 클래스를 만든다.

클래스간의 관계 - 상속관계(inheritance)

공통부분은 조상에서 관리하고 개별부분은 자손에서 관리한다.
조상의 변경은 자손에 영향을 미치지만, 자손의 변경은 조상에 아무런 영향을 미치지 않는다.

클래스간의 관계 - 포함관계(composite)

포함이란?
한 클래스에서 멤버변수로 다른 클래스를 선언한 것이다.
작은 단위의 클래슬르 먼저 만들고, 이 들을 조합해서 하나의 클래스를 만든다.

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

상속관계 '~은~이다.(is-a)'
포함관계 '~은~을 가지고있다.(has-a)'

클래스를 가지고 문장을 만들었을 때
'~은 ~이다.'라는 문장이 성립한다면, 서로 상속관계를 맺어 주고,
'~은 ~을 가지고 있다.'는 문장이 성립한다면 포함관계를 맺어 주면 된다.

단일 상속(Single Inheritance)

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

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

-Object클래스는 모든 클래스 상속계층도의 최상위에 있는 조상 클래스이다.
-부모가 없는 클래스는 자동적으로 Object클래스를 상속받게 된다.
-모든 클래스는 Object클래스에 정의된 메서드를 상속받는다.
주요 메서드는 toString(), equals(Object o), hashCode()등이 있다.

오버라이딩(overriding)

조상으로부터 받은 메서드의 내용을 변경하는 것을 오버라이딩이라고 한다.
상속받은 메서드를 그대로 사용하기도 하지만, 자손 클래스 자신에게 맞게 변경해야하는 경우가 많다. 이럴 때 조상클래스를 오버라이딩한다.

오버라이딩의 조건

선언부(메서드 이름, 매개변수, 반환타입)는 조상 클래스와 일치해야 한다.
접근제어자(access modifier)와 예외(exception)는 제한된 조건 하에서만 다르게 변경할 수 있다

접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경 할 수 없다.
만일 조상 클래스에 정의된 메서드의 접근 제어자 protected라면, 이를 오버라이딩하는 자손 클래스의 메서드는 접근 제어자가 protected나 public이여야 한다.
대부분의 경우 같은 범위의 접근 제어자를 사용한다.
접근제어자의 접근 범위를 넓은 것에서 좁은 순으로 나열하면
public, protected, (default), private이다

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

오버라이딩할때 지켜야 하는 조건
1. 선언부가 조상 클래스의 메서드와 일치해야 한다.
2. 접근 제어자를 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
3. 예외는 조상 클래스의 메서드보다 많이 선언할 수 없다.

오버로딩 vs. 오버라이딩

오버로딩은 기존에 없는 새로운 메서드를 추가하는것이고,
오버라이딩은 조상으로부터 상속받은 메서드의 내용을 변경하는 것이다.

오버로딩(overloading) 기본에 없는 새로운 메서드를 정의하는것(new)
오버라이딩(overriding) 상속받은 메서드의 내용을 변경하는것(change, modify)

참조변수 super

객체 자신을 가리키는 참조변수. 인스턴스 메서드(생성자)내에만 존재한다.
조상의 멤버를 자신의 멤버와 구별할 때 사용한다.

super()- 조상의 생성자

조상의 생성자를 호출할 때 사용한다.
조상의 멤버는 조상의 생성자를 호출해서 초기화 한다.

추가조건
생성자의 첫줄에 반드시 생성자를 호출해야 한다.
그렇지 않으면 컴파일러가 생성자의 첫 줄에 super();를 삽입한다

패키지(package)

서로 관련된 클래스의 묶음이다.
클래스는 클래스파일(*.class), 패키지는 폴더.하위 패키지는 하위 폴더이다
클래스의 실제 이름(full name)은 패기지명을 포함한다.
클래스가 물리적으로 하나의 클래스파일(.class)인것과 같이 패키지는 물리적으로 하나의 디렉토리이다.
디렉토리가 하위 디렉토리를 가질수 있는것처럼, 패키지도 다른 패키지를 호함할 수 있으며 점'.'으로 구분한다.

패키지의 선언

package 패키지명;

패키지는 소스파일의 첫 번째 문장으로 단 한번 선언한다.
같은 소스파일의 클래스들은 모두 같은 패키지에 속하게 된다.
패키지 선언이 없으면 '이름없는패키지(nuname package)에 속하게 된다.(default package)

패키지 패스(classpath)

클래스 파일(*.class)의 위치를 알려주는 경로(path)이다.
환경변수 classpath로 관리하며, 경로간의 구분자는','를 사용한다.
classpath(환경변수)에 패키지의 루트를 등록해줘야 한다.

import문

클래스를 사용할때 패키지이름을 생략할 수 있다.
컴파일러에게 클래스가 속한 패키지를 알려준다.

컴파일 시에 컴파일러는 import문을 통해 소스파일에 사용된 클래스들의 패키지 를 알아 낸 다음, 모든 클래스이름 앞에 패키지명을 붙여 준다.
이클립스는 단축키 'otrl +shift+o'를 누르면, 자동으로 import문을 추가해주는 기능을 제공한다.

import 패키지명.클래스명;
또는
import 패키지명.*; (지정된 패키지에 속하는 모든 클래스)

static import문

static멤버를 사용할 때 클래스 이름을 생략할 수 있게 해준다.

import static java.lang.Integer.*; // Integer클래스의 모든 static 메서드
import static java.lang.Math.random; // Math.random()만. 괄호() 안붙임
import static java.lang.System.out; //	System.out을 out만 참조가능

System.out.println(Math.random()); → out.println(random());

제어자(modifier)

클래스와 클래스의 멤버(멤버 변수, 메서드)에 부가적인 의미 부여한다.
하나의 대상에 여러 제어자를 같이 사용가능하다. (접근제어자는 하나만 가능)

제어자의 종류는 크게 접근제어자그 외의 제어자로 나눌 수 있다.

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

static - 클래스의, 공통적인

인스턴스 변수는 하나의 클래스로 부터 생성되었더라도 각기 다른 값을 유지하지만, 클래스 변수(static멤버변수)는 하나의 변수를 모든 인스턴스가 공유하기 때문에 인스턴스에 관계없이 같은 값을 갖는다.

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

class StaticTest{
	static int width = 200;   // 클래스 변수(static변수)
    static int height = 120; //  클래스 변수(static변수)
    
    static{	// 클래스 초기화 블럭
    	//	static변수의 복잡한 초기화 수행
    }
     static int max(int a, int b){	클래스 변수(static변수)
    	return a > b ? a: b;
    }
 }

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

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

class FinalTest{
	final int MAX_SIZE = 10;
    
    final void getMaxSize(){
    	final int LV = MAX_SIZE; // 오버라이딩할 수 없는 메서드(변경불가)
        return MAX_SIZE;
     }
  }    

abstract - 추상의, 미완성의

abstract가 사용될 수 있는 곳 - 클래스, 메서드

abstract class AbstractTest{ // 추상 클래스(추상 메서들를 포함한 클래스)
	abstract void move();   // 추상 메서드(구현부가 없는 메서드)

접근제어자(access modifier)

접근 제어자가 사용될 수 있는곳 - 클래스, 멤버변수, 메서드, 생성자
private 같은 클래스 내에서만 접근이 가능하다.
(default) 같은 패키지 내에서만 접근이 가능하다.
protected 같은 패키지 내에서, 그리고 다른 패키지의 자손클래스에서 접근가능하다.
public 접근 제한이 전혀 없다.


접근 범위가 넓은 쪽에서 좁은 순서

캡슐화와 접근 제어자

접근제어자를 사용하는 이유
외부로 부터 데이터를 보호하기 위해서 사용한다.
외부러부터 불필요한, 내부적으로만 사용되는, 부분을 감추기 위해 사용한다.

다형성

여러가지 형태를 가질수 있는 능력
하나의 참조변수로 여러 타입의 객체를 참조할 수 있는것
즉, 조상타입의 참조변수로 자손타입의 객체를 다룰 수 있는것이 다형성이다.

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

참조변수의 형변환

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

서로 상속관계에 있는 타입간의 형변환은 양방향으로 자유롭게 수행될 수 있으나, 참조 변수가 가리키는 인스턴스의 자손타입으로 형변환은 허용되지 않는다.
그래서 참조변수가 가리키는 인스턴스의 타입이 무엇인지 먼저 확인하는 것이 중요하다.

instanceof 연산자

참조변수가 참조하는 인스턴스의 실제 타입을 체크하는데 사용한다.
이항연산자이며 피연산자는 참조형 변수와 타입. 연산결과는 true, false.
instanceof의 연산결과가 true이면, 해당 타입으로 형변환이 가능하다.

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

  • 멤버변수가 중복정의된 경우, 참조변수의 타입에 따라 연결되는 멤버변수가 달 라진다. (참조변수타입에 영향받음)
  • 메서드가 중복정의된 경우, 참조변수의 타입에 관계없이 항상 실제 인스턴스의 타입에 정의된 메서드가 호출된다.(참조변수타입에 영향받지 않음)

매개변수의 다형성

참조형 매개변수는 메서드 호출시,
자신과 같은 타입 또는 자손타입
의 인스턴스를 넘겨줄수있다.

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

조상타입의 배열에 자손들의 객체를 담을수 있다

추상 클래스(abstract class)

추상 클래스(abstract class)란?

  • 클래스가 설계도라면 추상클래스는 '미완성 설계도'이다.
  • 추상메서드(미완성 메서드)를 포함하고 있는 클래스이다.
    *추상메서드 : 선언부만 있고 구현부(몸통, body)가 없는 메서드
  • 일반메서드가 추상메서드를 호출할 수 있다.(호출할때 필요한 건 선언부)
  • 완성된 설계도가 아니므로 인스턴스를 생성할 수 없다.
  • 다른 클래스를 작성하는 데 도움을 줄 목적으로 작성된다.

추상클래스
미완성 설계도, 인스턴스 생성불가.
미완성 메서드(추상 메서드)를 포함하고 있는 클래스.

추상 메서드(abstract method)란?

  • 선언부만 있고 구현부(몸통, body)가 없는 메서드
  • 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우에 사용
  • 추상클래스를 상속받는 자손클래스에서 추상메서드의 구현부를 완성해야
    한다.

/* 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명한다.*/
abstract 리턴타입 메서드이름();

abstract class Player{	// 추상클래스
	abstract void play(int pos);	// 추상메서드
	abstract void stop;	            // 추상메서드
}

class AudioPlayer extends Player{
	 void play(int pos){ /* 내용 생략*/}	// 추상메서드를 구현
     void stop(){ /* 내용 생략*/}		// 추상메서드를 구현
     
 abstract class AbstractPlayer extends Player{
 	 void play(int pos){ /* 내용 생략*/} // 추상메서드 구현
 }

추상클래스의 작성

여러 클래스에 공통적으로 사용될 수 있는 클래스를 바로 작성하기도 하고, 기존의 클래스의 공통적인 부분을 뽑아서 추상클래스로 만들어 상속하도록 하는 경우도 있다.

상속이 자손 클래스를 만드는데 조상 클래스를 사용하는 것이라면, 이와 반대로 추상화는 기 존의 클래스의 공통부분을 뽑아내서 조상 클래스를 만드는 것이라고 할 수 있다.

인터페이스(interface)

인터페이스(interface)란?

  • 일종의 추상클래스. 추상클래스(미완성 설계도)보다 추상화 정도가 높다.
  • 실제 구현된 것이 전혀 없는 기본 설계도.(알맹이 없는 껍데기)
  • 추상메서드와 상수만을 멤버로 가질 수 있다.
  • 인스턴스를 생성할 수 없고, 클래스 작성에 도움을 줄 목적으로 사용된다.
  • 미리 정해진 규칙에 맞게 구현하도록 표준을 제시하는 데 사용된다.
interface 인터페이스 이름{
	public static fianl 타입 상수이름 = 값;
    public abstract 메서드아름(매개변수목록);

일반적인 클래스와 달리 인터페이스의 멤버들은 다음과 같은 제약사항이 있다.

  • 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
  • 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
    단, static메서드와 디폴트 메서드는 예외(JDK1.8부터)

인터페이스의 상속

인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와는 달리 다중상속,즉, 여러개의 인터페이스로부터 상속을 받는 것이 가능하다.

interface Movable {
	/**지정된 위치(X, y)로 이동하는 기능의 메서드 */
	void move(int x, int y);
}
interface Attackable {
	/**지정된 대상(u)을 공격하는 기능의 메서드 */
	void attack(Unit u);
}
interface Fightable extends Movable, Attackable { }

클래스의 상속과 마찬가지로 자손 인터페이스(Fightabile)는 조상 인터페이스(Norable.Atackable)에 정의된 멤버를 모두 상속받는다.
그래서 Fightable자체에는 정의된 멤버가 하나도 없지만 조상 인터페이스로부터 상속받은 두 개의 추상에서드, move(int x, int y)와 attack(Unit u)을 멤버로 갖게 된다.

인터페이스의 구현

  • 인터페이스를 구현하는 것은 클래스를 상속받는 것과 같다.
    다만, 'extends' 대신 implements'를 사용한다.
  • 인터페이스에 정의된 추상메서드를 완성해야한다.
  • 상속과 구현이 동시에 가능하다.
class 클래스이름 implements 인터페이스이름{
	// 인터페이스에 정의된 추상메서드를 모두 구현해야 한다.
 }
 
class Fighter implements Fightable {
	public void move(int x, int y) {/* 내용 생략 */}
    public void attack(Unit u) {/* 내용 생략 */}
 }

만일 구현하는인터페이스의 메서드중 일부만 구현한다면, abstract를 붙여서 추상클래스로 선언해야한다.

abstract class Fighter implements Fightable{
	public void move(int x, int y) {/* 내용 생략 */}
 }

다음과 같이 상속과 구현을 동시에 할수있다.

class Fighter extends Unit implements Fightable{
	public void move(int x, int y) {/* 내용 생략 */}
    public void attack(Unit u) {/* 내용 생략 */}
 }

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

  • 인터페이스 타입의 변수로 인터페이스를 구현한 클래스의 인스턴스를 참조할 수 있다.
  • 인터페이스를 메서드의 매개변수 타입으로 지정할 수 있다.
  • 인터페이스를 메서드의 리턴타입으로 지정할 수 있다.

인터페이스의 장점

  • 개발시간을 단축시킬 수 있다.
  • 표준화가 가능하다.
  • 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
  • 독립적인 프로그래밍이 가능하다.

1. 개발시간을 단축시킬 수 있다.
일단 인터페이스가 작성되면, 이를 사용해서 프로그램을 작성하는 것이 가능하다.
메서드를 호출하는 쪽에서는 메서드의 내용에 관계없이 선언부만 알면 되기 때문이다.

그리고 동시에 다른 한 쪽에서는 인터페이스를 구현하는 클래스를 작성하게 하면, 인터페 이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도 양쪽에서 동시에 개발을 진행할 수 있다.

2. 표준화가 가능하다.
프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음, 개발자들에게 인터페이스를 구 현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능 하다.

3. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
서로 상속관계에 있지도 않고, 같은 조상클래스를 가지고 있지 않은 서로 아무런 관계도 없 는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어 줄 수 있다.

4. 독립적인 프로그래밍이 가능하다.
인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제구현에 독립 적인 프로그램을 작성하는 것이 가능하다. 클래스와 클래스간의 직접적인 관계를 인터페이 스를 이용해서 간접적인 관계로 변경하면, 한 클래스의 변경이 관련된 다른 클래스에 영향 을 미치지 않는 독립적인 프로그래밍이 가능하다.

인터페이스의 이해
▶인터페이스는...
-두 대상(객체) 간의 '연결, 대화, 소통'을 돕는 '중간 역할을 한다.
-선언(설계)와 구현을 분리시키는 것을 가능하게 한다.

▶인터페이스를 이해하려면 먼저 두 가지를 기억하자.
-클래스를 사용하는 쪽(User)과 클래스를 제공하는 쪽(Provider)이 있다.
-메서드를 사용(호출)하는 쪽(User)에서는 사용하려는 메서드(Provider)의선언부만 알면 된다.

A(User) → B(Provider)

디폴트 메서드와 static메서드

  • 인터페이스에 디폴트 메서드, static메서드를 추가 가능하게 바뀌었다.(DK1.8 부터)
  • 클래스와 달리 인터페이스에 새로운 메서드(추상메서드)를 추가하기 어러움이 있다.
    (해당 인터페이스를 구현한 클래스가 추가된 메서드를 구현하도록 변경필요)
  • 이러한 문제점을 해결하기 위해 디폴트 메서드(default method)를 고안했다.
  • 디폴트 메서드는 인터페이스에 추가된 일반 메서드(인터페이스 원칙 위반)

디폴트 메서드가 기존의 메서드와 충돌하는 경우 아래와 같이 해결한다.
1.여러 인터페이스릐 디폴트 메서드 간의 충돌
-인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
2.디폴트 메서드와 조상 클래스의 메서드 간의 충돌
-조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시한다.

내부 클래스(inner class)

내부 클래스(inner class)란?

클래스 안에 선언된 클래스이다.
특정 클래스 내에서만 주로 사용되는 클래슬르 내부 클래스로 선언한다.
GUI어플리케이션(AWT, Swing)의 이벤트처리에 주로 사용된다.

내부클래스의 장점
내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
코드의 복잡성을 줄일 수 있다.(캡슐화)

내부 클래스의 종류와 특징

내부 클래스의 종류는 변수의 선언위치에 따른 종류와 동일하다.
유효범위와 성질도 변수와 유사하므로 비교해보면 이해된다.

내부 클래스의 선언

변수가 선언된 위치에 따라 인스턴스 변수, 클래스 변수(static 변수), 지역변수로 나뉘듯이 내부 클래스도 이와 마찬가지로 선언된 위치에 따라 나뉜다.
그리고, 각 내부 클래스의 선언 위치에 따라 같은 선언위치의 변수와 동일한 유효범위(scope)와 접근성(accessibility)을 갖는다.

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

-내부 클래스의 접근제어자는 변수에 사용할 수 있는 접근제어자와 동일하다.
-static클래스만 static맴버를 정의할 수 있다.
-내부 클래스도 외부 클래스의 멤버로 간주되며, 동일한 접근성을 갖는다.
-외부 클래스의 지역변수인 final이 붙은 변수(상수)만 접근 가능하다.
지역 클래스의 인스턴스가 소멸된 지역변수를 참조할 수 있기 때문이다.

익명클래스(anonymous class)

  • 이름없는 일회용 클래스이다.
  • 단 하나의 객체만 생성할 수있다.

이름이 없기 때문에 생성자도 가질 수 없으며, 조상클래스의 이름이나 구현하고자 하는 인터 페이스의 이름을 사용해서 정의하기 때문에 하나의 클래스로 상속받는 동시에 인터페이스를 구현하거나 둘 이상의 인터페이스를 구현오로지 단 하나의 클래스를 상속받거나 단 하나의 인터페이스만 구현할 수 있다.

new 조상클래스이름(){
	// 멤버 선언
 }
 		또는
 
 new 구현인터페이스이름(){
	// 멤버 선언
}
profile
오늘의 기록

0개의 댓글