archive
[OOP] Chap3. 자바와 객체지향 본문
스프링을 입문을 위한 자바 객체 지향의 원리와 이해 를 읽고 정리한 글입니다.
객체지향은 인간지향이다.
개발자를 좀 더 편하게 하고 현실 세계처럼 프로그래밍하기 위해 객체 지향의 개념이 탄생했다.
기존의 구조적 프로그래밍 언어는 코드를 논리적인 단위로 구분하고 분할해서 정복한다.
이런 논리적인 단위의 블록을 함수라고 한다.
객체 지향은 모든 것을 객체(Object)라 보고, 이를 분류(Class)하여 인지한다.
각 분류안의 객체들은 속성(Property)와 행위(Method)를 가지고 있다.
객체지향 이전에는 속성 따로 행위 따로 분리된 형태였다면, 객체 지향에서는 우리가 사고하는 방식대로 프로그래밍이 가능해졌다.
따라서 객체지향은 직관적이고 쉽고 인간적이다.
객체 지향의 4대 특성
캡 - 캡슐화(Encapsultaion) : 정보 은닉(Information hiding)
상 - 상속화(Inheritance) : 재사용
추 - 추상화(Abstraction) : 모델링
다 - 다형성(Polymorphism) : 사용 편의
클래스 vs 객체
저자는 기존의 붕어빵틀-붕어의 비유는 잘못됐다고 말한다.
클래스는 분류에 대한 개념이지 실체가 아니며, 객체는 실체다.
(ex) 클래스 : 객체 = 사람 : 김연아
추상화 : 모델링
추상화의 사전적 의미가 공통 특성/공통 속성 추출이라는 면에서 객체 지향의 추상화는 곧 모델링이다.
객체를 특성(속성+기능)에 따라 분류하니 객체를 통칭할 수 있는 집합적 개념인 클래스(분류)가 나오게 되는 것이다.
객체 : 세상에 존재하는 유일무이한 사물
클래스 : 같은 특성을 지닌 여러 객체를 총칭하는 집합의 개념
객체는 클래스의 인스턴스(instance)이다.
객체 지향 프로그래밍을 할 때 클래스를 먼저 설계하게 된다.
이 때, 객체들이 가진 공통된 특성을 찾는다.
이 때, 애플리케이션의 경계인 컨텍스트(Context)에 따라 클래스의 설계가 달라지게 된다.
컨텍스트는 애플리케이션에 어디에 활용될 것인지를 의미한다.
(ex) 병원 애플리케이션에서는 사람을 구체화하여 환자라는 클래스를, 은행에서는 고객이라는 클래스를 모델링할 것이다.
추상화를 다시 정의해보자면,
구체적인 것을 분해해서 애플리케이션 경계에 있는 특성만 가지고 재조합하는 것(=모델링)
이라고 할 수 있다.
모델링은 목적에 맞게 관심있는 특성만을 추출하여 표현하는 것이다. 즉, 추상화를 통해 실제 사물을 단순하게 묘사하는 것이다.
이러한 추상화는 객체 지향에서 클래스를 설계할 때, 데이터베이스의 테이블을 설계할 때 필요한 기법이다.
정리하자면, OOP의 추상화는 모델링이며 추상화의 결과는 클래스이다.
클래스 설계를 위해서는 애플리케이션 경계부터 정해야한다.
자바는 객체 지향의 추상화를 class 키워드를 통해 지원하고 있다.
클래스 객체_참조_변수 = new 클래스();
위 명령문은 클래스의 인스턴스, 즉 객체를 생성해 해당 객체의 주소값(포인터)를 참조 변수에 할당한다.
추상화와 T메모리
애니메이션의 쥐 캐릭터 관리 프로그램을 만든다고 했을 때,
애플리케이션의 경계를 설정하고 클래스 설계를 하여 UML클래스 다이어그램으로 나타낸 결과는 아래와 같다.
쥐 |
이름 나이 꼬리 수 |
울다() |
위의 논리적 설계를 개발환경에 맞춰 물리적 설계로 표현하면 아래와 같다.
Mouse |
+ name : String + age : int + countOfTail : int |
+ sing() : void |
이를 자바 코드로 표현해보면 아래와 같다
public class Mouse {
public String name;
public int age;
public int countOfTail;
public void sing() {
System.out.println(name + " 찍찍!!!");
}
}
이 때 Mouse클래스를 이용해 작성한 프로그램에서 T메모리의 변화를 알아보자.
main()메서드가 시작되면 모든 클래스가 스태틱 영역에 배치된다.
Mouse클래스도 마찬가지이지만 변수 저장 공간은 없고 이름만 존재한다.
위 세개의 속성은 클래스의 속성이 아닌 객체의 속성이므로
객체가 생성되야만 속성의 값을 저장하기 위한 메모리 공간이 스태틱 영역이 아닌 힙 영역에 할당된다.
(참고 : 클래스의 멤버와 객체의 멤버를 구분하는 자바 키워드는 static이다.)
Mouse mickey = new Mouse();
Mouse mickey 선언을 통해 객체 참조 변수 mickey가 main()함수의 스택 프레임 내에 생성된다.
new Mouse()를 통해 객체 생성자가 호출되면 생성된 객체는 T메모리의 힙 영역에 배치된다.
할당문을 통해 힙 영역에 저장된 Mouse클래스의 인스턴스의 시작 주소가 객체참조변수에 할당된다.
mickey.name = "미키";
객체 참조 변수 mickey와 참조 연산자를 이용해 힙 영역 상의 객체에 접근해 name 속성에 "미키"라는 문자열을 할당하고 있다.
mickey.sing();
mickey가 참조하는 객체의 sing() 메서드가 코드 실행 영역에서 실행될 것이다.
mickey = null;
mickey의 값에 null이 할당되어 더 이상 힙 영역의 Mouse객체를 참조하지 않는다.
참조되지 않는 객체는 가비지 컬렉터(Garbage Collector)에 의해 정리된다.
클래스 멤버 vs 객체 멤버 = static 멤버 vs 인스턴스 멤버
특정 속성에 대해 해당 클래스의 모든 객체가 같은 값을 가진다면 static 키워드를 통해 해당 속성을 클래스의 멤버로 저장할 수 있다.
static 키워드가 붙은 속성을 클래스 멤버 속성, 안 붙은 속성을 객체 멤버 속성이라고 한다.
클래스 멤버 = 정적 멤버 = 스태틱 멤버
객체 멤버 = 오브젝트 멤버 = 인스턴스 멤버
클래스 멤버 속성은 객체 멤버 속성과 달리, T메모리의 스태틱 영역에 클래스가 배치될 때 고유의 메모리 공간을 갖게 된다.
클래스 멤버 속성은 T메모리의 static영역에 상주하게 되므로 정적(static) 멤버라고도 하며, 객체 멤버 속성은 인스턴스 멤버라고도 한다.
이에 접근하기 위해서는 참조변수.속성, 클래스명.속성 두 가지 방법을 사용할 수 있다.
정적 메서드는 객체의 존재 여부와 관계없이 쓸 수 있는 메서드이다.
정적 메서드는 클래스에 속해있으며 클래스는 JVM구동 시 T메모리의 스태틱 영역에 배치되기 때문이다.
main() 메서드는 당연히 정적 메서드여야 한다.
그 밖에는 main() 메서드의 논리를 함수로 분할해서 사용하는 경우와 정적 변수에 대한 Getter, Setter로 사용하는 경우가 있다.
주로 클래스의 인스턴스를 만들지 않고 사용되는 유틸리티성 메서드를 주로 정적메서드로 구성한다. (ex. java.lang.Math 클래스)
UML 표기법에서 정적 멤버는 밑줄을 사용해 표시한다.
스택 영역에 생기는 지역 변수는 별도로 초기화하지 않으면 쓰레기값을 갖게 되는 반면에,
클래스 속성과 객체 속성은 초기화를 해주지 않아도 기본값을 갖는다. (ex. 0, 0.0, false, null)
멤버 변수는 공유 변수의 성격을 가지고 있기 때문이다.
(객체 변수는 객체 안의 다수의 메서드가 공유하는 변수이고 클래스 변수는 전역 변수로서 어디서든 접근 가능한 공유 변수이다.)
세 가지의 변수 유형을 표로 정리하면 아래와 같다.
이름 | 다른 이름 | T 메모리 내의 위치 |
static 변수 | 클래스(멤버)속성, 정적 변수, 정적 속성 | 스태틱 영역 |
인스턴스 변수 | 객체(멤버)속성, 객체 변수 | 힙 영역 |
local 변수 | 지역 변수 | 스택 영역 (스택 프레임 내부) |
상속 : 재사용 + 확장
객체지향의 상속은 상속의 사전적 의미보다는 재사용과 확장으로 이해하는 것이 적합하다.
따라서 상속도/계층도 보다는 분류도로 이해하는 것이 좋다.
(동물->포유류->고래)
객체 지향에서 상속은 상위 클래스의 특성을 하위 클래스에서 상속(특성 상속)하고, 거기에 필요한 속성을 추가(확장)해서 사용할 수 있다는 의미이다.
따라서 부모-자식 클래스 보다는 상위-하위클래스 또는 슈퍼-서브 클래스라고 표현하자.
상위 클래스로 갈수록 추상화, 일반화됐다고 말하며, 하위 클래스로 갈수록 구체화, 특수화됐다고 말한다.
상속 관계에서 "하위 클래스는 상위 클래스이다"라는 문장이 반드시 만족되어야 한다. (리스코프 치환 원칙)
(ex. 고래는 포유류다.)
이러한 흐름에 따라, 자바에서 inheritance라는 키워드는 존재하지 않으며
대신 상속관계를 나타낼 때에는 extends(확장)을 사용한다.
객체참조변수명은 유일무이한 객체스럽게, 클래스명은 분류답게 정하는 것이 좋다.
클래스 상속구조에서 최상위 클래스는 Object다.
정리하자면,
객체지향의 상속은 상위 클래스의 특성을 재사용/확장하는 것이며,
상속은 is a (kind of) 관계를 만족해야 한다.
자바는 다중 상속대신 인터페이스를 도입하였다.
인터페이스는 is able to 관계를 만족한다.
(ex. Serializable 인터페이스 : 직렬화할 수 있는)
즉, 인터페이스는 클래스가 '무엇을 할 수 있다'고 하는 기능을 구현하도록 강제한다.
인터페이스는 interface키워드로 작성하고, 이를 구현할 때는 implements키워드를 사용한다.
상위 클래스는 물려줄 특성이 풍성할 수록 좋고(LSP - 리스코프 치환 원칙)
인터페이스에는 메서드가 적을 수록 좋다(ISP - 인터페이스 분할 원칙)
내 생각 > 전자는 최대한 공통점을 많이 찾아서 상위 클래스에 넣어야 중복이 줄어들고, 하위 클래스에 사용 제약 등을 걸 수 있을 것 같음 && 필요한 기능이 있으면 새롭게 추가하는 것이 아니라 오버라이딩해서 사용하는 것이 다형성 측면에서 도움이 될 것 같음 / 인터페이스는 기능을 분할해서 한 인터페이스가 한가지의 기능만 담당하도록 하는 것이 객체지향적일 것 같다.
상속과 T메모리
하위클래스의 인스턴스가 생성될 때, 상위 클래스의 인스턴트도 함께 힙 영역에 생성된다.
모든 클래스의 최상위 클래스인 Object클래스의 인스턴스도 마찬가지로 생성된다.
상위 클래스 타입의 객체 참조 변수로 하위 클래스의 인스턴스를 생성한 경우, 하위 클래스에서 추가한 속성, 메서드를 사용할 수 없다.
다형성 : 사용편의성
객체지향에서 다형성은 오버라이딩과 오버로딩으로 구성된다.
오바라이딩 : 같은 메서드 이름, 같은 인자 목록으로 상위 클래스의 메서드를 재정의
오버로딩 : 같은 메서드 이름, 다른 인자 목록으로 다수의 메서드를 중복정의
주의할 점은 상위 클래스 타입의 객체 참조 변수를 사용하더라도 하위 클래스에서 오버라이딩한 메서드가 호출된다는 점이다.
오버로딩을 이용하면 함수명하나로 인자목록만 달리 해서 사용할 수 있고,
제네릭을 이용하면 하나의 함수만으로 다수의 함수를 구현한 효과를 낼 수 있다.
오버라이딩을 이용하면 하위클래스가 무엇인지 신경쓰지 않아도 자동으로 재정의된 메서드를 호출하므로 깔끔한 코드를 유지할 수 있다.
즉, 다형성은 프로그램을 작성할 때 사용편의성을 제공한다.
캡슐화 : 정보은닉
자바에서 정보은닉이라고 하면 접근 제어자(private, default, protected, public)와 접근자 및 설정자 메서드(Getter, Setter)를 떠올릴 수 있다.
private - 이 객체를 생성한 클래스에서만 접근 가능
default - 같은 클래스, 같은 패키지에 있는 클래스에서 접근 가능
protected - 상속관계에 있는 서브클래스, 같은 패키지에 있는 클래스에서 접근 가능
public - 해당 객체를 사용하는 프로그램 어디에서나 직접 접근가능
상속을 받지 않았다면 객체 멤버는 객체를 생성한 후 참조변수를 이용하여 접근해야 한다.
따라서 정적 멤버는 클래스명.정적멤버의 일관된형식으로 접근하는 것을 권장한다.
참고 : github.com/expert0226/oopinspring/tree/master/workspace_springjava/Chap03/src/encapsulation01
참조 변수의 복사
기본 자료형 변수 복사의 경우에는 Call By Value에 의해 단순히 값만 복사되며 두 변수는 서로 영향을 주지 않는다.
객체참조변수의 경우 변수의 값이 그대로 복사되는 것은 동일하지만, 저장하고 있는 값을 주소로 해석하기 때문에 Call By Reference(Call By Address)에 의해 주소값(포인터)가 복사된다.
'STUDY > Java' 카테고리의 다른 글
[OOP] Chap5. 객체 지향 설계 5원칙 - SOLID (0) | 2021.02.16 |
---|---|
[OOP] Chap4. 자바가 확장한 객체지향 (0) | 2021.02.04 |
[OOP] Chap2. 자바와 절차적/구조적 프로그래밍 (0) | 2021.01.27 |
[Java] 변수와 자료형 (0) | 2021.01.25 |
Java 애플리케이션 작동 원리 (0) | 2021.01.07 |