✔객체지향 프로그래밍(OOP)
0. OOP의 특징
◾ OOP is A P.I.E
1. Abstraction(추상화) : 현실의 객체를 추상화하여 클래스 구성.
2. Polymorphism(다형성)
: 하나의 객체를 여러 가지 타입(형)으로 참조.
3. Inheritance(상속)
: 부모 클래스의 자산을 물려받아 자식을 정의함으로 코드의 재사용 가능.
4. Encapsulation(데이터 은닉과 보호, 캡슐화)
: 데이터를 외부에 직접 노출시키지 않고 메서드를 이용해 보호.
1. 상속(Inheritance)
- 상속(Inheritance) : 기존 클래스의 자산(멤버, 필드)을 자식 클래스에 물려주어 재사용하는 기능.(생성자, 초기화 블록 제외)
- is-a 관계 : (ex) 아파트는 건물이다.
- UML Class Diagram : 실선 삼각형 (자식 -▷ 조상)
- 적용 : extends 키워드 사용.
class Parent {}
class Child extends Parent {}
- 단일 상속(Single Inheritance) : 자바는 단일 상속만 지원.
- 다중 상속의 경우 여러 클래스의 기능을 물려받을 수 있지만 관계가 복잡해진다.
- interface와 포함 관계(has-a)로 다중 상속처럼 활용 가능하다.
- 포함 관계(has-a) : 상속 이외의 클래스 재활용 방법.
- 2개 이상의 클래스에서 특성을 가져올 때 하나는 상속. 나머지는 멤버 변수(필드)로 처리.
- UML Class Diagram : 실선(-)
class Parent1 {}
class Parent2 {}
class Child extends Parent1 {
Parent2 p;
}
- 상속의 장점 : 부모 코드의 변경으로 모든 자식들에게 적용.
- Object 클래스 : 모든 클래스의 조상 클래스.
- 별도의 extends 선언이 없는 클래스들은 extends Object 생략.
- 즉, Object의 멤버는 모든 클래스의 멤버.
- Object의 hashCode(), equals(), toStirng() 등은 자주 재정의하여 사용.
- notify(), wait() 등 멀티 스레드 프로그래밍에 사용하는 메서드도 존재.
- 메서드 재정의(Overriding) : 조상 클래스에 정의된 메서드를 자식 클래스에서 적합하게 수정하는 것.
- 오버라이딩의 조건.
- 메서드 이름이 같아야 한다.
- 매개 변수의 개수, 타입, 순서가 같아야 한다.
- 리턴 타입이 같아야 한다.
- 접근 제한자는 부모보다 범위가 넓거나 같아야 한다.
- 조상보다 더 큰 예외를 던질 수 없다.
- Annotation : 컴파일러, JVM, 프레임워크 등이 보는 주석
- 소크 코드에 메타 데이터를 삽입하는 형태(소스 코드의 구조 변경, 환경 설정 정보 추가 등의 작업 담당.)
- @Deprecated : 컴파일러에게 해당 메서드가 deprecated 되었다고 알려줌.
- @Override : 컴파일러에게 해당 메서드는 override한 메서드임을 알려줌. 반드시 Super Class에 선언되어있는 메서드 여야 한다.
- @SuppressWarnings : 경고를 생략하기 위해 사용.
- super : 조상 클래스 객체를 의미.
- 변수의 scope : 사용된 위치에서 점점 확장해가며 처음 만난 선언부에 연결.
- method 내부 -> 해당 클래스 멤버 변수 -> 조상 클래스 멤버 변수.
- super() : this()와 동일하게 조상 클래스의 생성자를 의미.
- 조상 클래스에 선언된 멤버들은 조상 클래스의 생성자에서 초기화가 이뤄지므로 이를 재활용.
- 자식 클래스에 선언된 멤버들만 자식 클래스 생성자에서 초기화.
- 자식 클래스 생성자의 맨 첫 줄에서만 호출 가능.
- 명시적으로 this() 또는 super()를 호출하지 않는 경우 컴파일러가 super() 삽입.
- 따라서, 맨 상위의 Object까지 객체가 만들어지는 구조.
- toString() : 객체를 문자열로 변경하는 메서드.
- equals() : 두 객체가 같은지 비교하는 메서드.
- 등가비교 연산자(==)로 객체 연산시 두 객체의 주소값 비교.
- 자바는 연산자 재정의가 불가능하다.
- 따라서, equals를 재정의하여 객체 비교에 사용한다.
- 리터럴(Literal)들은 Class 메모리 영역의 Constant Pool에 저장된다.
- Literal : 프로그램에서 사용하는 변하지 않는 모든 값.(숫자, 문자, 논리값 등)
- 문자열도 리터럴.(단, new String()을 사용하면 리터럴이 아니다!!!!)
- Constant Pool에 [정수 - int, 실수 - double]로 저장.
- 하나의 값을 참조하는 형태이므로 비교 연산자로 비교 가능.
- hashCode() : 객체의 해시 코드. 시스템에서 객체를 구별하기 위해 사용하는 정수값.
- HashSet, HashMap 등에서 객체의 동일성을 확인하기 위해 사용한다.
- equals 메서드 재정의할 때 반드시 hashCode도 재정의해야 한다.(String, Number 등에서 재정의된 hashCode 활용 권장)
2. 캡슐화(데이터 은닉과 보호)
제한자(Modifier)
: 클래스, 변수, 메서드 선언부와 함께 사용되어 부가적인 의미 부여.
- 하나의 대상에 여러 제한자 가능. 접근 제한자는 하나만 사용 가능.
- 순서는 무관(일반적으로 접근 제한자를 맨 앞으로)
- 접근 제한자 : public, protected, (default=package), private
- 그 외 제한자
- static : 클래스 레벨의 요소 설정.
- final : 요소를 더 이상 수정할 수 없게 함.
- final class : 더 이상 확장 할 수 없음. 상속 금지 -> 오버라이딩(overriding) 방지(이미 완벽한 클래스들).
- final method : 더 이상 재정의 할 수 없음. 오버라이딩(overriding) 금지.
- final variable : 더 이상 값을 바꿀 수 없음. 상수화.
- abstract : 추상 메서드 및 루상 클래스 작성.
- synchronized : 멀티 스레드에서의 동기화 처리.
접근 제한자(Access Modifier)
: 멤버 등에 사용되며 해당 요소를 외부에서 사용할 수 있는지 설정.
- 데이터 은닉과 보호(Encapsulation)
- 외부에서 변수에 직접 접근하면 정보(데이터) 보호되지 않는다.
- private을 통해 외부 접근 차단
- 공개되는(public) 메서드를 통한 접근 통로 마련. 메서드에 정보 보호 로직 구성.(setter/getter)
- 객체의 생성 제어와 Singletone 디자인 패턴
- 객체의 생성을 제한해야하는 경우 사용.
- 여러 개의 객체가 필요없는 경우(Stateless한 객체) : 객체를 구별할 필요가 없는 경우(수정 가능한 멤버 변수가 없고 기능만 있는 경우)
- 객체를 계속 생성/삭제 하는데 많은 비용이 들어 재사용이 유리한 경우.
- Singleton 디자인 패턴
- 외부에서 생성자 접근 금지 -> 생성자 private 설정.
- 내부에서 private 접근 가능 -> 직접 객체 생성.(멤버 변수 private 설정.)
- 외부에서 private 멤버에 접근 가능한 getter 생성 -> setter 불필요.
- 객체없이 외부에서 접근할 수 있또록 getter와 객체 변수에 static 추가.
- 외부에서 getter로 언제나 접근 가능. -> 하나의 객체 재사용.
class Singletone{
private Singletone instance;
private Singletone(){}
public Singletone getInstance(){
if (this.instance == null){
this.instance = new Singletone();
}
return this.instance
}
}
3. 다형성(Polymorphism)
- 다형성(Polymorphism) : 하나의 객체가 많은 형(타입)을 가질 수 있는 성질(Heterogeneous, 헤테로 지니어스, 이질화)
- 상속 관계에 있을 때 조상 클래스의 타입으로 자식 클래스 객체를 참조(Reference)할 수 있다.
- 다형성의 활용
- 다른 타입의 객체를 다루는 배열
- 배열은 같은 타입의 데이터를 묶음으로 관리(Homogeneous, 호모지니어스, 동질화)
- 다형성으로 다른 타입의 데이터를 하나의 배열로 관리.
- Object는 모든 클래스의 조상 : 어떤 타입의 '객체'라도 저장할 수 있음.
- 기본적으로 기본형은 담을 수 없다. 참조형이 아니므로.
- 하지만, autoboxing을 통해 Wrapping되어 저장 가능.
- 이를 활용해 Collection API 등장.
- 매개 변수의 다형성
- 조상을 파라미터로 처리한다면 객체의 타입에 따라 메서드를 만들 필요가 없어진다.
- API에서 파라미터를 Obejct를 받는다는 것은 모든 객체를 처리한다는 의미.
- 필요하다면 하위 클래스에서 오버라이딩 필요.
- 다형성과 참조형 객체의 형 변환
- 메모리에 있는 것과 사용할 수 있는 것의 차이.
- 메모리에 있더라도 참조하는 변수의 타입에 따라 접근할 수 있는 내용 제한.
- 참조형 객체의 형 변환
- child(작은 집) -> super(큰 집) : 묵시적 형변환(캐스팅). 조상의 모든 내용이 자식에 있기 떄문에 가능.
- super -> child : 명시적 형변환(캐스팅). 형변환 생략 불가.
- 조상 클래스가 모든 자식 클래스에 변환될 수 없다. 확인 필요.
- instanceof 연산자 : 실제 메모리에 있는 객체가 특정 클래스 타입인지 boolean으로 리턴.
- 참조 변수의 레벨에 따른 객체의 멤버 연결
- 상속 관계에서 객체의 멤버 변수가 중복될 때 : 참조 변수 타입에 따라 연결이 달라짐.
- 상속 관계에서 객체의 메서드가 중복될 때(Override 되었을 때)
- 무조건 자식 클래스의 메서드가 호출됨. (Virtual Method Invocation)
- 최대한 메모리에 생성된 실제 객체에 최적화된 메서드가 동작.
- Java API처럼 공통 기능인 경우 Object를 파라미터로 쓰겠지만 많은 경우 비즈니스 로직상 최상위 객체 사용 권장.