이 글은 "Java의 정석 (남궁 성 지음)"을 읽고 주관적으로 요약한 글입니다.
1. 상속 (inheritance)
1) 상속
- 정의
- 기존의 클래스를 재사용하여, 새로운 클래스를 작성하는 것
- 클래스의 이름 뒤에 상속받고자 하는 클래스 이름을 키워드 'extends'와 함께 사용
class Child extends Parent { }
- 장점
- 코드의 양을 줄임
- 적은 양의 코드로 새로운 클래스를 작성 가능 - 코드를 공통적으로 관리
- 코드의 추가/변경이 매우 용이 - 재사용성 / 중복 제거 / 생산성과 유지보수
- 코드의 양을 줄임
- 구성
- 조상 클래스
- 부모 클래스, 상위 클래스, 기반 클래스
- parent class, super class, base class - 자손 클래스
- 자식 클래스, 하위 클래스, 파생된 클래스
- child class, sub class, derived class - 형재 클래스는 없다
- 같은 부모 클래스로부터 상속받은 두 클래스는 아무 관계가 없음
- 두 클래스에 공통으로 추가될 내용이 있으면 부모 클래스에 추가
- 이러한 이유로 코드의 중복이 줄고 작업양이 줄어든다
- 조상 클래스
- 상속 관계
- 프로그램이 커질수록 클래스 간의 관계가 복잡해지므로, 관계를 쉽게 이해하기 위한 수단이 필요
- 상속 계층도(class hierarchy)
- 클래스 간의 상속 관계를 그림으로 표현한 것 - 다이어그램
- Child 클래스는 Parent 클래스의 멤버들을 포함
- Parent 클래스의 멤버가 Child 클래스에 자동으로 추가됨
- 상속 내용
- 멤버만 상속되고, 생성자와 초기화 블럭(즉, 초기화 관련내용)은 상속되지 않는다
- 자손 클래스의 멤버 수는 항상 조상 클래스보다 많거나 같다
- private 또는 default인 멤버들은 상속되지 않는다기보다, 상속 받지만 자손 클래스로부터 접근이 제한된 것이다
- Parent class로부터 상속받은 Child class를 상속한 GrandChild class 또한 Parent class 멤버를 상속받는다
- 즉, Parent class의 수정은 모든 자손 클래스에게 영향을 준다 - 자손 클래스의 인스턴스 생성은 조상 클래스 멤버 + 자손 클래스 멤버가 하나의 인스턴스로 생성되는 것이다
2) 포함 (Composite)
- 클래스의 재사용 방법
- 상속 (extends)
- 다른 클래스의 참조변수를 포함 (composite)
- 정의
- 멤버변수로 다른 클래스 타입의 참조변수를 선언하는 것
Circle c = new Circle(new Point(150, 150), 50);
- 클래스 재사용 방법 결정하기
- ~은 ~이다 (is-a)
- 상속 - ~은 ~을 가지고 있다 (has-a)
- 포함 - 항상 이 방식으로 구별이 가능하진 않으나 감은 잡을 수 있음
- ~은 ~이다 (is-a)
3) 단일상속
- 둘 이상의 클래스로부터 상속받을 수 없음
- C++에서는 여러 조상 클래스로부터 상속받을 수 있었음
- Java는 단일 상속만 허용
- 클래스 간의 관계가 복잡해지지 않음
- 복잡함 : A에게 상속 받은 power()와 B에게 상속 받은 power()중 어느 걸 사용해야하나?
- 클래스 간의 관계가 명확해지고 코드의 신뢰성을 높임
4) Object 클래스
- 모든 클래스 상속계층도의 최상위 조상 클래스
- 상속 받지 않는 모든 클래스는 자동으로 Object 클래스로부터 상속됨
- 컴파일러가 자동으로 'extends Object'를 추가
- 이로 인해 유용한 메서드 사용 가능
- toString(), equals 같은 메서드를 정의 없이 사용할 수 있는 이유
2. 오버라이딩 (overriding)
1) 오버라이딩
- 정의
- 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것
class Point {
int x;
int y;
String getLocation() {
return "x : " + x + ", y : " + y;
}
}
class Point3D extends Point {
int z;
String getLocation() { // 2D에서 3D로 변경되었으므로 오버라이딩
return "x : " + x + ", y : " + y + ", z : " + z;
}
}
- 제약
- 오버라이딩 조건 = 메서드 선언부가 일치해야 함
- 메서드 이름이 같아야 함
- 매개변수가 같아야 함
- 반환타입이 같아야 함 (단, 부모 클래스의 반환타입으로 타입변환이 가능하면 허용)
- 오버라이딩시 제약
- 접근 제어자를 더 좁은 범위로 변경할 수 없음
- 예외를 더 많이 선언할 수 없음 (조상 클래스에서 선언한 예외 범위 내)
- 인스턴스 메서드를 static으로, 또는 그 반대로 변경 불가능
- static 메서드는 각 클래스에 별개로 정의됨
- 오버라이딩 조건 = 메서드 선언부가 일치해야 함
- 오버로딩 vs 오버라이딩
- 오버로딩
- 메서드 추가 - 오버라이딩
- 메서드 변경
- 오버로딩
2) super
- 정의
- 참조변수
- 조상 클래스로부터 상속받은 멤버를 참조할 때 사용
- this
- this는 지역변수와 멤버변수를 구분할 때 사용
- 이처럼 super와 this로 상속받은 멤버와 자신의 멤버를 구분할 때 사용
- 주의
- super와 this는 근본적으로 같음 (메서드가 속한 인스턴스의 주소)
- 따라서 상속받은 멤버도 this로 접근이 가능
- 중복 정의되어 서로 구분이 필요할 경우만 super 사용
- 사용
- 인스턴스 메서드에서만 사용 가능
- 메서드/변수 모두 사용 가능
- super() - 조상 클래스의 생성자
- 조상 클래스의 생성자 호출시 사용
- 자손 클래스의 인스턴스 생성시 조상 클래스 멤버의 초기화 작업이 필요
- 따라서 자손 클래스 생성자에서 조상 클래스 생성자가 호출되어야함 - 생성자의 첫 줄에는 무조건 조상 클래스를 호출해야함
- 없을 경우 컴파일러가 자동으로 'super();'를 첫줄에 삽입
- 생성자가 정의되어 있으면 컴파일러가 삽입하지 않으므로, 정의해야함 - 즉, 조상 클래스의 멤버는 조상 클래스의 생성자로 초기화되어야함
- 조상 클래스의 생성자 호출시 사용
3. package와 import
1) package
- 정의
- 서로 관련된 클래스의 묶음
- 클래스 또는 인터페이스 포함 가능
- 물리적으로 하나의 디렉토리
- 클래스가 물리적으로 하나의 클래스 파일인 것과 같음
- 따라서 패키지에 속한 클래스는 해당 디렉토리에 존재하는 클래스 파일이어야 함
- 기능
- 관련 클래스를 묶어 효율적으로 관리
- 같은 이름의 클래스 구분 가능
- 다른 개발자가 개발한 라이브러리 이름과 충돌 방지
- 조건
- 소스파일 첫 번째 문장으로 단 한 번의 패키지 선언만 허용
- 모든 클래스는 반드시 하나의 패키지에 속해야함
- '.'을 구분자로 계층 구조 구성
- 물리적으로 클래스 파일을 포함하는 하나의 디렉토리
- 선언
- package 패키지명;
- 반드시 소스파일의 첫 번재 문장이어야함 (주석/공백 제외)
- 해당 소스파일에 포함된 모든 클래스/인터페이스가 모두 패키지에 소속됨
- 일반적으로 소문자
- 이름없는 패키지 (unnamed package)
- 패키지 미지정시 자동으로 '이름없는 패키지'에 속함
- 따라서, 패키지를 지정하지 않은 모든 클래스가 같은 패키지에 속함
- 클래스패스 (classpath)
- 컴파일러나 JVM 등이 클래스 위치를 찾는데 사용되는 경로
- 패키지의 루트 디렉토리를 클래스패스에 포함시켜야함
- CLASSPATH 환경변수
- 실행
- 실행시 패키지명을 모두 적어줘야함
- java com.codechobo.book.PackageTest
- 실행시 패키지명을 모두 적어줘야함
2) import
- 역할
- 다른 패키지의 클래스 사용시 매번 패키지명을 붙이기 불편함
- 같은 패키지의 클래스는 패키지명 생략 가능 - 컴파일러에게 소스파일에 사용된 클래스의 패키지에 대한 정보를 제공
- 따라서, 클래스 이름에서 패키지명을 생략하게 해줌
- 컴파일시 모든 클래스이름 앞에 패키지명을 붙여줌
- 프로그램 성능에 영향을 주지 않음
- 다른 패키지의 클래스 사용시 매번 패키지명을 붙이기 불편함
- 선언
- 일반적인 소스파일의 순서
- package문
- import문
- 클래스 선언 - 선언 방식
- import 패키지명.클래스명;
- import 패키지명.*;
- 일반적인 소스파일의 순서
- 주의사항
- import하는 패키지가 많으면 클래스의 소속 패키지를 구별하기 어려움
- '*' 사용은 하위 패키지의 클래스까지 포함을 의미하지 않음
- 즉, 아래 1과 2를 3으로 대체할 수 없음
- import java.util.*;
- import java.text.*;
- import java.*; -> 하위에 패키지들의 클래스를 포함하지 않는다
- 모든 소스파일은 java.lang.*;가 묵시적으로 선언됨
- static import
- static 멤버를 호출할 때 클래스 이름 생략 가능
// 1. static 멤버 호출
System.out.println(Math.random());
// 2. static import
import static java.lang.System.out;
import static java.lang.Math.random;
System.out.println(random();
4. 제어자 (modifier)
1) 제어자
- 정의
- 부가적인 의미 부여
- 클래스, 변수, 메서드의 선언부에 위치
- 제어자 종류
- 접근 제어자
- public, protected, default, private - 그 외 제어자
- static, final, abstract, native, transient, synchronized, volatile, strictfp
- 접근 제어자
- 제어자 조합
- 여러 제어자 조합 가능
- 접근 제어자는 4가지 중 1개만 사용 가능
- 일반적으로 접근 제어자를 가장 왼쪽에 위치 (제어자의 순서는 상관없음)
2) 다양한 제어자
- static - 클래스의, 공통적인
- 하나의 변수를 모둔 인스턴스가 공유
- static이 붙은 다음 3가지는 인스턴스 생성 없이 사용 가능
- 멤버변수
- 메서드
- 초기화 블럭
- static 메서드 vs 인스턴스 메서드
- 근본적인 차이는 인스턴스 멤버의 사용 여부
- static 메서드 내에서는 인스턴스 멤버들을 직접 사용할 수 없음
- 따라서, 인스턴스 멤버를 사용하지 않는 메서드는 static 선언을 고려
- static 메서드는 인스턴스 생성이 필요없어 편리하고 속도도 빠름
- final - 마지막의, 변경될 수 없는
- 사용 대상
- 변수에 붙으면 상수
- 메서드에 붙으면 오버라이딩 방지
- 클래스에 사용하면 상속 방지 - 대표적인 final 클래스
- Math, String - final 변수의 초기화
- 일반적으로 선언과 동시에 초기화
- 인스턴스 변수의 경우, 생성자를 통해 초기화
- 이를 통해 상수지만 인스턴스마다 다른 값을 만들 수 있음
- 사용 대상
- abstract - 추상의, 미완성의
- 추상 클래스 / 추상 메서드 선언에 사용
- 추상 클래스 : 추상 메서드를 포함하는 클래스
- 추상 메서드 : 메서드 선언부만 작성하고 구현하지 않은 메서드 - 추상 클래스는 추상 메서드의 존재로 인스턴스 생성이 불가
- 추상 메서드가 없는 추상 클래스
- 드물게 인스턴스 생성이 필요없는 클래스에 사용
- 다른 클래스가 상속받아 원하는 메서드만 오버라이딩 할 수 있는 장점
- 추상 클래스 / 추상 메서드 선언에 사용
3) 접근 제어자
- 정의
- 멤버/클래스에 사용
- 클래스는 public/(default) - 외부에서 접근하지 못하도록 제한하는 역할
- 제어자가 없으면 default를 의미
- 멤버/클래스에 사용
- 접근 범위
제어자 | 같은 클래스 | 같은 패키지 | 자손 클래스 | 전체 |
public | ------------- | ------------- | ------------- | -------------> |
protected | ------------- | ------------- | -------------> | |
(default) | ------------- | -------------> | ||
private | -------------> |
- 캡슐화
- 접근 제어자를 사용하는 이유 -> 캡슐화의 정의
- 외부로부터 데이터 보호
- 복잡성 제거
- 외부로부터 데이터 보호
- 데이터의 유효성 유지
- 비밀번호 같은 데이터 변경 방지 - 복잡성 제거
- 내부 작업을 위해 임시로 사용되는 멤버변수
- 부분작업 처리를 위한 메서드
- 메서드 변경 / 오류 테스트 등에서 확인해야할 범위를 좁힐 수 있음
- 접근 제어자를 사용하는 이유 -> 캡슐화의 정의
- 생성자의 접근 제어자
- 생성자의 접근 제어자로 인스턴스 생성을 제한할 수 있음
- 싱글톤
- private 생성자로 클래스 내부에서면 인스턴스 생성
- 인스턴스를 생성해서 반환해주는 public 메서드만 제공
- 이를 통해 사용할 수 있는 인스턴스 개수 제한 가능
- 생성자가 private이면 다른 클래스의 조상이 될 수 없으므로, 클래스에 final을 추가해주는 것이 좋음
class Singleton {
private static Singleton s = new Singleton();
private Singleton() {
// ...
}
public static Singleton getInstance() {
return s;
}
// ...
}
- 제어자의 조합
- 메서드에 static과 abstract는 함께 사용 불가
- 클래스에 abstract와 final을 동시에 사용 불가
- 메서드에 abstract와 private 함께 사용 불가
- 메서드에 private와 final을 함께 사용할 필요 없음
- private 메서드는 오버라이딩 될 수 없기 때문
'책읽기' 카테고리의 다른 글
[파이썬 알고리즘 인터뷰][비트연산] 싱글 넘버 (0) | 2021.08.16 |
---|---|
[파이썬 알고리즘 인터뷰] 19장 - 비트 조작 (0) | 2021.08.16 |
[파이썬 알고리즘 인터뷰][이진검색] 2D 행렬 검색2 (0) | 2021.08.13 |
[파이썬 알고리즘 인터뷰][이진검색] 두 수의 합2 (0) | 2021.08.13 |
[파이썬 알고리즘 인터뷰][이진검색] 두 배열의 교집합 (0) | 2021.08.13 |