CH 7. 객체지향 프로그래밍 II
1. 상속
1.1. 상속의 정의와 장점
-상속: 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것
-코드의 재사용성 증가, 코드의 중복 제거, 프로그램의 생산성과 유지보수 기여
class Child extends Parent {
//…
}
● 조상 클래스: 부모 클래스, 상위 클래스, 기반 클래스
● 자손 클래스: 자식 클래스, 하위 클래스, 파생된 클래스
-상속계층도 만들면 쉽게 이해 가능
-자손 클래스가 변경되는 것은 조상 클래스에 아무런 영향을 주지 못한다.
-자손 클래스는 항상 조상 클래스보다 같거나 많은 멤버를 갖는다.
–> 조상 클래스를 확장한다는 의미로도 해석 가능
-같은 내용의 코드를 하나 이상의 클래스에 중복으로 추가해야 하는 경우 상속 관계 이용해 중복 최소화해야 한다.
-자손 클래스의 인스턴스를 생성하면 조상 클래스의 멤버도 함께 생성됨. 따로 조상 클래스의 인스턴스를 생성하지 않고도 조상 클래스의 멤버 사용 가능.
1.2. 클래스간의 관계 – 포함관계
-상속 외에 클래스를 재사용하는 방법 –> 클래스 간의 포함관계를 맺어주는 것.
-한 클래스의 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것.
class Circle {
Point p = new Point();
}
-간결하고 손쉽고 클래스 작성 가능, 다른 클래스 작성하는데 재사용 가능.
1.3. 클래스간의 관계 결정하기
-상속관계를 맺어줄 것인가 포함관계를 맺어줄 것인가?
-상속관계; ~은 ~다
-포함관계: ~은 ~을 가지고 있다
1.4. 단일상속
-자바에서는 단일상속만을 허용한다.
-장점
● 복합적인 기능을 가진 클래스를 쉽게 작성할 수 있다.
-단점
● 클래스간의 관계가 복잡해진다.
● 서로 다른 클래스로부터 상속받은 멤버간의 이름이 같은 경우 구별할 수 있는 방법이 없다.
–> 자바는 다중상속의 장점을 포기하고 단일상속만을 허용
1.5. Object 클래스 – 모든 클래스의 조상
-모든 클래스 상속계층도의 최상위에 있는 조상클래스
-다른 클래스로부터 상속받지 않는 모든 클래스는 자동적으로 Object 클래스로부터 상속받게 함.
class Tv extends Object { //컴파일러가 자동적으로 추가
//…
}
2. 오버라이딩
2.1. 오버라이딩이란?
-조상클래스로부터 상속받은 메서드의 내용을 변경하는 것
2.2. 오버라이딩의 조건
-내용만을 새로 작성하는 것
-자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와
● 이름이 같아야 한다.
● 매개변수가 같아야 한다.
● 반환타입이 같아야 한다.
–> 선언부가 서로 일치해야 한다.
1. 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
2. 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
3. 인스턴스 메서드를 static 메서드로, 또는 그 반대로 변경할 수 없다.
2.3. 오버로딩 vs. 오버라이딩
-오버로딩: 기존에 없는 새로운 메서드를 정의하는 것
-오버라이딩: 상속받은 메서드의 내용을 변경하는 것
2.4. super
-자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조 변수
-this 사용할 수 있으나 조상 클래스 멤버와 자손 클래스 멤버 구별하기 위해 super 사용함.
-super 역시 static 메서드에서는 사용할 수 없고 인스턴스 메서드에서만 사용할 수 있다.
-메서드 역시 super 써서 호출할 수 있다. –> 조상 클래스의 메서드의 내용에 추가적으로 작업을 덧붙이는 경우에 좋다.
2.5. super() – 조상 클래스의 생성자
-super() 역시 생성자.
-자손 클래스의 인스턴스가 생성될 때 조상 클래스 멤버의 초기화 작업이 수행되어야 하기 때문에 자손 클래스의 생성자에서 조상 클래스의 생성자가 호출되어야 한다.
-자식 클래스 생성자의 첫 줄에서 조상 클래스 생성자 호출해야 한다.
-상속관계는 거슬러 올라가서 Object 클래스의 생성자인 Object()까지 가야 끝이 난다.
-없으면 컴파일러가 자동으로 super() 추가해 준다.
Point3D(int x, int y, int z){
super(x, y); //조상 클래스의 생성자 Point(int x, int y)를 호출한다.
this.z = z;
}
3. package와 import
3.1. 패키지
-클래스, 인터페이스의 묶음
-서로 관련된 클래스들끼리 그룹 단위로 묶어놓음으로써 효율적 관리 가능
-클래스의 실제 이름은 패키지명을 포함한 것
I.e. String 클래스의 패키지명 포함한 이름은 java.lang.String
-패키지는 물리적으로 하나의 디렉토리
-패키지도 다른 패키지를 포함할 수 있으며 점‘.’으로 구분한다.
-모든 클래스는 반드시 하나의 패키지에 속해야 한다.
-하나의 소스파일에는 첫 번째 문장으로 단 한번의 패키지 선언만을 허용한다.
3.2. 패키지의 선언
package com.javachobo.book;
-패키지 선언문은 반드시 소스파일에서 주석과 공백을 제외한 첫 번째 문장이어야 한다.
-하나의 소스파일에 단 한번만 선언되며, 해당 소스파일에 포함된 모든 클래스나 인터페이스는 선언된 패키지에 속한다.
-소문자로 하는 것이 원칙
-패키지 지정되지 않은 클래스는 자동적으로 ‘이름 없는 클래스’에 속하게 된다.
3.3. import 문
-다른 패키지의 클래스를 사용하려면 패키지명이 포함된 클래스 이름을 사용해야 한다.
-import문 사용시 클래스 이름에서 패키지명은 생략할 수 있다.
3.4. import 문의 선언
-import문은 package문 다음에, 클래스 선언문 이전에 위치해야 한다.
-한 소스파일에 여러번 선언 가능
import 패키지명.클래스명;
or
import 패키지명.*;
-지정된 패키지에 속하는 모든 클래스를 패지키명 없이 사용할 수도 있음.
-* 사용한다 해도 하위 패키지의 클래스까지 포함하는 것은 아니다.
-같은 패키지 내의 클래스들은 패키지명을 생략할 수 있다.
-모든 소스파일에는 묵시적으로 다음과 같은 import문이 선언되어 있다
import java.lang.*;
–> 매우 빈번히 사용되는 중요한 클래스들이 속한 패키지
3.5. static import문
-static import문 사용하면 static 멤버를 호출할 때 클래스 이름을 생략할 수 있다.
import static java.lang.Math.random;
System.out.println(random());
4. 제어자
4.1. 제어자란?
-제어자: 선언부에 함께 사용되어 부가적인 의미를 부여한다.
● 접근 제어자: public, protected, default, private
● 그 외: static, final, abstract, native, transient, synchronized, volatile, stictfp
-하나의 대상에 대해서 여러 제어자를 조합하여 사용하는 것 가능.
-접근 제어자는 한번에 하나만 선택해서 사용 가능.
4.2. static – 클래스의, 공통적인
-클래스 변수는 인스턴스에 관계없이 같은 값을 갖는다.
-인스턴스 메서드와 static 메서드의 차이는 메서드 내에서 인스턴스 멤버를 사용하는가 여부.
-static이 사용될 수 있는 곳: 멤버변수, 메서드, 초기화 블록
-인스턴스 멤버 사용하지 않는 곳에서는 static 사용하는 것이 더 편리하고 속도도 빠르다.
4.3. final – 마지막의, 변경될 수 없는
-거의 모든 대상에 사용될 수 있다.
-final이 사용될 수 있는 곳: 클래스, 메서드, 멤버변수, 지역변수
● 클래스: 변경될 수 없는 클래스, 다른 클래스의 조상이 될 수 없다.
● 메서드: 오버라이딩 통해 재정의될 수 없다.
● 멤버변수, 지역변수: 값을 변경할 수 없는 상수가 된다.
-인스턴스 변수의 경우 생성자에서 초기화할 수 있다. –> 각 인스턴스마다 서로 다른 상수 설정 가능
4.4. abstract – 추상의, 미완성의
-미완성의 의미
-메서드의 선언부만 작성하고 실제 수행내용은 구현하지 않은 추상메서드 선언에 사용.
-abstract가 사용될 수 있는 곳: 클래스, 메서드
-인스턴스 생성 불가능
-완성된 클래스에 abstract 붙이는 경우도 있다. –> 인스턴스 생생할 필요 없을 때
4.5. 접근 제어자
-해당하는 멤버 또는 클래스를 외부에서 접근하지 못하도록 제한하는 역할
-접근 제어자가 사용될 수 있는 곳: 클래스, 멤버변수, 메서드, 생성자
제어자 | 같은 클래스 | 같은 패키지 | 자손 클래스 | 전체
public | O O O O
protected | O O O
(default) | O O
private | O
-대상에 따라 사용할 수 있는 접근 제어자
● 클래스: public, (default)
● 메서드, 멤버변수: public, protected, (default), private
● 지역변수: 없음
-클래스의 내부에 선언된 데이터를 보호하기 위함
-데이터 감추기 –> 캡슐화
-외부에는 불필요한, 내부적으로만 사용되는 부분을 감추기 위해서
-멤버변수를 private이나 protected로 제한하고 public 메서드로 통해서 변경하게 함
–> getter, setter
-하나의 소스파일에는 단 하나의 public 클래스만 허용됨.
-생성자의 접근 제어자
● private 지정할 경우 클래스 내부에서만 인스턴스 생성 가능.
● public static 메서드 있어야 한다.
● 다른 클래스의 조상이 될 수 없다. –> 명시적으로 나타내기 위해 final 추가
public final class Math {
private Math(){}
//…
}
4.6. 제어자의 조합
-대상에 따라 사용할 수 있는 제어자
● 클래스: public, (default), final, abstract
● 메서드: 모든 접근 제어자, final, abstract, static
● 멤버변수: 모든 접근 제어자, final, static
● 지역변수: final
-주의해야 할 사항
● 메서드에 static과 abstract 함께 사용 불가
● 클래스에 abstract와 final 동시 사용 불가
● abstract 메서드의 접근 제어자가 private일 수 없다.
● 메서드에 private과 final을 같이 사용할 필요는 없다.
5. 다형성
5.1. 다형성이란?
-한 타입의 참조변수로 여러 타입의 객체를 참조할 수 있도록 함
-조상 클래스 타입의 참조변수로 자손 클래스의 인스턴스를 참조할 수 있도록 함.
Tv t = new CaptionTv();
CaptionTv() c = new CaptionTv();
-참조변수 t로는 CaptionTv 인스턴스의 모든 멤버를 사용할 수 없다.
-참조변수의 타입에 따라 사용할 수 있는 멤버의 개수가 달라진다.
-반대의 경우 컴파일 에러 발생한다.
5.2. 참조변수의 형변환
-서로 상속관계에 있는 클래스 사이에서만 형변환 가능
-조상의 조상으로도 형변환이 가능하다.
-자손 타입 –> 조상 타입 // 형변환 생략 가능
-형제 관계는 자바에서 존재하지 않는다.
Car car = null;
FireEngine fe = new FireEngine();
FireEngine fe2 = null;
car = fe; //업캐스팅
fe2 = (FireEngine)car; //다운캐스팅
-업캐스팅의 경우 참조변수가 다룰 수 있는 멤버의 개수가 실제 인스턴스가 갖고있는 멤버의 개수보다 적을 것이 분명 –> 형변환 생략 가능
-다운캐스팅의 경우 형변환 생략 불가능, instanceof 연산자 사용해서 실제 인스턴스 타입 확인하는 것이 안전.
-형변환은 인스턴스에 영향 미치지 않음. 인스턴스에서 사용할 수 있는 멤버의 범위 조절하는 것일 뿐.
Tv t = new CaptionTv(); == Tv t = (Tv)new CaptionTv();
–> 생략된 형태
Car car = new Car();
FireEngine fe = null;
fe = (FireEngine)car; // 컴파일은 되지만 실행 단계에서 에러 발생
-조상타입의 인스턴스를 자손타입의 참조변수로 참조하는 것은 허용되지 않는다.
(car 참조변수가 참조하고 있는 인스턴스가 Car 타입의 인스턴스이기 때문)
5.3. instanceof연산자
-참조변수가 참조하고 있는 인스턴스의 실제 타입을 알아보기 위한 연산자
-true 결과가 나온다는 것은 참조변수가 검사한 타입으로 형변환이 가능하다는 것
-참조변수.getClass().getName() –> 인스턴스의 클래스 이름을 문자열로 반환
5.4. 참조변수와 인스턴스의 연결
-조상 클래스에 선언된 멤버변수와 같은 이름의 인스턴스 변수를 자손 클래스에 중복으로 정의했을 때, 조상 타입의 참조변수로 자손 인스턴스를 참조하는 경우와 자손 타입의 참조변수로 자손 인스턴스를 참조하는 경우가 다르다.
● 조상 타입의 참조 변수 사용: 조상 클래스에 선언된 멤버 변수 사용
● 자손 타입의 참조 변수 사용: 자손 클래스에 선언된 멤버 변수 사용
-메서드는 참조 변수의 타입에 관계없이 항상 실제 인스턴스 타입에 정의된 메서드가 호출.
-super.x는 조상 클래스 인스턴스 변수를, this.x나 그냥 x가 자손 클래스 인스턴스 변수 지칭.
5.5. 매개변수의 다형성
void buy(Product p){}
–> Product 클래스의 자손 타입의 참조변수면 어느 것이나 매개변수로 받아들일 수 있다는 뜻
5.6. 여러 종류의 객체를 배열로 다루기
Product[] p = new Product[3];
p[0] = new Tv();
p[1] = new Computer();
p[2] = new Audio();
-조상 타입의 참조변수 배열 사용시 공통의 조상을 가진 서로 다른 종류의 객체를 배열로 묶어 다룰 수 있다.
-Vector 클래스: 동적으로 크기가 관리되는 객체배열
● Vector(): 10개 객체 저장하는 Vector 인스턴스 생성, 추가시 크기 자동 증가
● boolean add(Object o): 객체 추가 후 성공시 true 반환
● boolean remove(Object o)
● boolean isEmpty(): Vector 비어있는지 검사
● Object get(int index): 지정된 위치의 객체 반환, 반환 타입이 Object 타입
● int size(): 객체의 개수 반환
6. 추상 클래스
6.1. 추상클래스란?
-추상클래스는 미완성 설계도와 같음
-추상클래스로 인스턴스 생성할 수 없음.
-상속을 통해서 자손 클래스에 의해서만 완성 가능.
-완전하지는 않지만 어느 정도 틀을 갖춘 상태에서 시작하는 것.
abstract class 클래스이름{
//…
}
-추상 클래스 역시 생성자를 가지며, 멤버변수와 메서드도 가질 수 있다.
-추상 메서드가 없는 완성된 클래스라 할지라도 추상클래스로 지정되면 클래스의 인스턴스를 생성할 수 없다.
6.2. 추상 메서드
-선언부만 작성하고 구현부는 작성하지 않은 메서드
-괄호 {} 대신 문자의 끝을 알리는 ; 붙여준다.
-오버라이딩 통해 조상의 추상 클래스의 추상 메서드 모두 구현해 주어야 한다.
-상속받은 추상 메서드 중 하나라도 구현하지 않는다면 자손 클래스 역시 추상 클래스로 지정해야 한다.
abstract class Player{
abstract void play(int pos);
abstract void stop();
}
class AudioPlayer extends Player{
void play(int pos){ … }
void stop(){ … }
}
abstract class AbstractPlayer{
void play(int pos){ … }
}
6.3. 추상 클래스의 작성
-추상화: 기존의 클래스의 공통부분을 뽑아내서 조상 클래스를 만드는 것
-메서드에 ; 대신 아무 내용 없는 {} 붙이면 추상 메서드가 아닌 일반 메서드로 간주
-사실 abstract 안 쓰고 자손 클래스에서 오버라이딩 시켜도 되긴 함
–> but 자손 클래스에서의 구현을 강요하기 위해서 추상 메서드 사용함.
7. 인터페이스
7.1. 인터페이스란?
-일종의 추상클래스
-추상 클래스보다 추상화 정도가 높아서 일반 메서드 또는 멤버 변수를 가질 수 없다.
-추상 메서드와 상수만을 가질 수 있다.
-기본 설계도라 할 수 있다.
7.2. 인터페이스의 작성
-키워드로 class 대신 interface 사용
interface 인터페이스{
public static final 타입 상수이름 = 값;
public abstract 메서드 이름(매개변수 목록);
}
-모든 멤버변수는 public static final이어야 하며, 생략 가능
-모든 메서드는 public abstract여야 하며, 생략 가능
-단, static 메서드와 디폴트 메서드는 예외
-편의상 생략하는 경우가 많다.
7.3. 인터페이스의 상속
-인터페이스는 인터페이스로만 상속받을 수 있다.
-다중상속을 허용한다.
-최고 조상같은 거 없음.
7.4. 인터페이스의 구현
class Fighter implements Fightable{
public void move(int x, int y){ … };
}
-구현하는 인터페이스의 메서드 중 일부만 구현한다면 추상 클래스로 선언해야 한다.
-인터페이스 속 추상 메서드를 구현하는 클래스의 메서드는 접근 제어자가 public으로 되어야 한다.
7.5. 인터페이스를 이용한 다중상속
-인터페이스로 다중상속을 구현하는 경우는 거의 없다.
7.6. 인터페이스를 이용한 다형성
-해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있다.
-인터페이스 타입으로의 형변환도 가능하다.
-메서드의 매개변수의 타입으로 사용될 수 있다.
-리턴 타입으로 인터페이스의 타입을 지정하는 것 역시 가능하다. –> 메서드가 해당 인터페이스를 구현한 클래스의 인스턴스를 반환한다는 것을 의미한다.
-분산환경 프로그래밍에서 위력을 발휘한다.
7.7. 인터페이스의 장점
1. 개발 시간을 단축시킬 수 있다.
2. 표준화가 가능하다.
3. 서로 관계없는 클래스들에게 관계를 맺어줄 수 있다.
4. 독립적인 프로그래밍이 가능하다.
7.8. 인터페이스의 이해
-인터페이스의 본질적인 측면
● 클래스를 사용하는 쪽과 제공하는 쪽이 있다.
● 메서드를 사용하는 쪽에서는 메서드의 선언부만 알면 된다.
-인터페이스를 이용해 클래스 제공하는 쪽의 선언과 구현을 분리할 수 있다.
7.9. 디폴트 메서드와 static 메서드
-JDK 1.8부터 디폴트 메서드와 static 메서드도 추가할 수 있게 되었다.
-static 메서드의 접근 제어자 역시 항상 public이며 생략 가능.
-디폴트 메서드
● 예전에는 인터페이스의 메서드를 추가하면 인터페이스를 구현한 기존의 모든 클래스들이 새로 추가된 메서드를 구현해야 했다.
● 키워드 default를 붙이고 몸통{}이 있어야 한다.
● 디폴트 메서드와 조상 클래스의 메서드가 충돌할 경우 디폴트 메서드는 무시된다.
8. 내부 클래스
8.1. 내부 클래스란?
-클래스 내에 선언된 클래스
-장점
● 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다.
● 코드의 복잡성을 줄일 수 있다(캡슐화).
-내부 클래스는 외부 클래스 제외하고 다른 클래스에서 잘 쓰이지 않아야 한다.
8.2. 내부 클래스의 종류와 특징
-변수의 선언 위치에 따른 종류와 같다.
-인스턴스 클래스, 스태틱 클래스, 지역 클래스, 익명 클래스가 있다.
8.3. 내부 클래스의 선언
-같은 선언위치의 변수와 동일한 유효범위와 접근성을 갖는다.
8.4. 내부 클래스의 제어자와 접근성
-final과 static이 동시에 붙은 변수는 상수이므로 모든 내부 클래스에서 정의가 가능하다.
8.5. 익명 클래스
-단 한번만 사용 가능, 오직 하나의 객체만을 생성할 수 있는 일회용 클래스
-생성자도 가질 수 없으며 단 하나의 클래스 상속받거나 인터페이스만을 구현할 수 있다.