기존의 구조적 프로그래밍에서 가장 중요하게 여겨지는 것은 함수이다.
- 함수는 코드를 논리적 단위로 구분하고 분할해서 정복하다는 것이다.
- Divide & Conquer 전략이다.
- 함수는 논리적 단위의 블록이라고 할 수 있다.
객체 지향을 이해하기 위해서 다음의 사항을 살펴보자.
- 세상에 존재하는 모든 것은 사물인 객체이다.
- 각각의 사물은 고유하다.
- 사물은 속성을 갖는다.
- 사물은 행위를 한다.
그리고 우리는 이러한 사물을 분류(class)해서 이해한다.
- 김연아(Object)는 사람이라는 분류(class)에 속한다.
- 김연아(Object)는 나이, 몸무게, 키와 같은 속성(property)를 가진다.
- 김연아(Object)는 먹다, 자다, 걷다와 같은 행위(method)를 가진다.
위를 통해서 알 수 있는 점은 객체 지향은 직관적이고, 인간 지향이라는 것을 알 수 있다. (인간의 인지 및 사고 방식을 프로그래밍에 접목)
+) 클래스와 객체를 구분하는 법
클래스와 객체를 구분하는 법은 간단하다.
예시를 보자.
- 사람은 클래스인가? 객체인가?
- 김연아는 클래스인가? 객체인가?
- 뽀로로는 클래스인가? 객체인가?
- 펭귄은 클래스인가? 객체인가?
답을 구하는 방법은 간단하다. 나이(속성)를 물어보자.
사람 - 클래스, 김연아 - 객체, 뽀로로 - 객체, 펭귄 - 클래스
이제, 객체 지향의 4대 특성에 대해 알아보자. (책에서는 캡! 상추다🥬🥬 라고 표현한다.)
- 캡 : 캡슐화(Encapsulation) - 정보 은닉
- 상 : 상속(Inheritance) - 재사용
- 추 : 추상화(Abstraction) - 모델링
- 다 : 다형성(Polymorphism) - 사용 편의
추상화
추상화는 여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용이다.
여기서 중요한 포인트는 공통 특성과 공통 속성 추출이다.
위에서 객체는 고유하다고 했다. 그리고 고유한 객체들을 모아서 속성과 기능에 따라 분류(class)를 해보니 객체를 통칭할 수 있는 집합적 개념인 클래스가 나온다.
하나 더 살펴보면 같은 사람이라고 해도 어떤 맥락(Context)에 있는 지에 따라 기능/행위(Method)가 다르다.
- 은행이라면 고객과 은행원
- 병원이라면 환자와 의사
위와 같은 내용을 프로그래밍적 관점으로 본다면 애플리케이션 경계(Context)에 따라 클래스의 설계가 달라진다.
즉, 동일한 객체라도 Context에 따라 Class가 정의되고 역할이 변화한다. (이 말은 클래스 설계를 위해서는 애플리케이션 경계부터 정해야 한다는 의미이기도 하다.)
추상화란, 구체적인 것을 분해해서 관심 영역(애플리케이션 경계)에 있는 특성만 가지고 재조합(모델링)하는 것
추상화는 상속, 인터페이스, 다형성을 사용하여 구현될 수 있다.
- 그리고, 자바에서는 class를 통해 추상화가 지원된다.
클래스와 T메모리
지난번에 main() 메서드가 실행되면 T메모리에 일어나는 일에 대해서 알아보았다.
이때, JVM이 부팅되고 제일 먼저하는 일 중 하나가 개발자가 작성한 모든 Class들을 static 영역에 놓는 것이다.
- 그런데, 여기서 static 영역에 로드된 클래스 내부의 변수들은 메모리에 저장 공간이 즉시 할당되지 않는다.
- 저장공간은 객체가 생성되어야만 속성 값을 저장하기 위해 heap 영역에 할당된다.
- 생성된 객체는 객체 참조 변수(mickey)와 참조 연산자(.)를 사용해 실제 힙 상의 객체에 접근할 수 있다.
class Mouse {
private String name;
private int age;
private int countOfTail;
public String sing() {
return "My name is " + name + " 찍찍!";
}
}
public class Main {
public static void main(String[] args) {
Mouse mickey = new Mouse();
mickey.name = "미키";
mickey.age = 85;
mickey.countOfTail = 1;
mickey.sing();
}
}
하나 더 알아보면, main() 메서드의 중괄호가 닫히면, stack에 있는 메서드 지역 변수들이 사라지게 되며 더 이상 heap 영역에 존재하는 객체를 참조하지 않게 된다.
그리고 이렇게 되면, heap 영역에 있는 객체는 GC에 의해 참조되지 않는 객체를 쓰레기로 여겨지고 수거된다.
클래스 멤버 vs 인스턴스 멤버
위의 Mouse 예시를 볼 때, 한 가지 생각이 들 수 있다.
- 쥐 (Class) - 꼬리 1개
- 미키 (Object) - 꼬리 1개
쥐의 꼬리(속성)는 몇 개인가?라는 질문에 1개라는 답을 할 수 있다.
일반적으로 클래스는 설계도일 뿐이고, 객체만이 속성을 가지기 때문에, 클래스 자체의 속성에 대해서는 답을 할 수 없다.
하지만, 답을 할 수 있었던 것은 쥐(Class)에서 파생된 Object(미키, 제리, ...)들이 모두 같은 값을 가지기 때문이다.
그럼, 이를 객체의 heap 공간에 하나씩 생성하는 것이 아니라 static 영역에 공간을 할당하는 것이 좋을 것이다.
- 따라서, 이러한 것을 클래스 *멤버 (static 멤버, 정적 멤버)라고 한다. (클래스명.속성명 으로 접근 가능 - Mouse.countOfTail)
- 그리고, 객체의 멤버들은 인스턴스 멤버라고도 한다.
*멤버 : Java에서 멤버란 클래스에 속하는 구성 요소를 의미하며, 크게 필드(Field)와 메서드(Method)를 포함한다.
상속
객체 지향에서의 상속은 재사용과 확장이다. (가계도 같은 부모 - 자식 간의 관계가 아닌, 포유류 - 고래와 같은 분류도의 개념)
- 자바에서 상속을 사용할 때 inheritance가 아닌 extends이다.
상속을 만족하기 위해서는 반드시 이것을 만족해야 한다.
- 하위 클래스는 상위 클래스이다. (SOLID의 LSP(리스코프 치환)를 나타내는 말)
- 포유류는 동물이다.
- 고래는 포유류다.
또한, 상속은 is a kind of 관계이다.
- 펭귄 is a kind of 동물
자바는 다중상속 대신 인터페이스를 취했다. 인터페이스는 be able to 관계이다. (객체가 특정 기능을 수행할 수 있음을 정의)
상속과 T메모리
상속을 한 클래스가 객체를 생성하면 신기한 일이 일어난다.
만약, Animal을 상속한 Penguin이 객체를 생성하면, heap 영역에 Penguin과 Animal 인스턴스 2개가 생성된다.
더 알아보자.
Penguin pororo = new Penguin();
Animal pingu = new Penguin();
위의 2가지 코드를 보면, 비슷하면서도 다르다.
- 첫번째 코드는 객체 참조 변수인 pororo의 참조 변수 타입이 Penguin이다.
- 두번째 코드는 객체 참조 변수인 pingu의 참조 변수 타입이 Animal이다.
이는 암묵적 형변환과 연관되어서 두번째 pingu는 Penguin의 메서드를 사용 불가하다.
다형성
객체 지향에서의 다형성은 하나의 인터페이스나 메서드가 다양한 형태로 동작할 수 있는 것으로 오버라이딩과 오버로딩으로 구현된다.
오버라이딩과 오버로딩의 차이는 다음과 같다. (참고해보기)
다형성과 T메모리
상위 클래스 타입의 객체 참조 변수를 사용하더라도 하위 클래스에서 재정의한 메서드가 호출된다.
class Animal {
void speak() {
System.out.println("뭐라고 할까?");
}
}
class Dog extends Animal {
@Override
void speak() {
System.out.println("멍멍");
}
}
class Cat extends Animal {
@Override
void speak() {
System.out.println("야옹");
}
}
public class Main {
public static void main(String[] args) {
Animal myAnimal;
myAnimal = new Dog();
myAnimal.speak(); // "멍멍" 출력
myAnimal = new Cat();
myAnimal.speak(); // "야옹" 출력
}
}
캡슐화
캡슐화는 데이터를 보호하고 객체의 내부 구현을 감추는 기법이다. 정보은닉이라고도 한다.
자바에서 정보은닉은 접근 제어자로 구현한다.
정보 은닉은 주로 다음의 두 가지로 구현된다. :
- 필드를 private으로 선언하여 외부에서 직접 접근하지 못하도록 제한.
- Getter와 Setter 메서드를 통해 간접적으로 필드에 접근하거나 수정하도록 허용.
'📖 책책책 책을 읽읍시다. 📖 > 자바 객체 지향의 원리와 이해' 카테고리의 다른 글
Chapter 6. 스프링이 사랑한 디자인 패턴 (1) | 2024.12.03 |
---|---|
Chapter 5. 객체 지향 설계 5원칙 - SOLID (0) | 2024.11.26 |
Chapter 4. 자바가 확장한 객체 지향 (0) | 2024.11.26 |
Chapter 2. 자바와 절차적 / 구조적 프로그래밍 (2) | 2024.11.24 |