어제 2장에서 T메모리에 대한 설명과 간단한 코드에서 T메모리에 어떻게 할당되는지 배웠는데요. 오늘은 쪼~금 더 나아가 코드가 좀 더 많아지고 분기가 일어났을 때 어떤 변화가 발생하는지부터 시작해보도록 하겠습니다.
02. 자바와 절차적/구조적 프로그래밍
이번에 살펴볼 클래스는 Start3
이구요. 코드는 아래와 같습니다.
public class Start3 {
public static void main(String[] args) {
int i = 10;
int k = 20;
if (i == 10) {
int m = k + 5;
k = m;
} else {
int p = k + 10;
k = p;
}
System.out.println("completed");
// k = m + p;
}
}
T메모리에선 어떠한 변화가 일어날까요?
간략하게 정리하면 다음과 같습니다.
if~else
문에서if
문이true
이므로 Stack영역에 쌓입니다. 그리고 밖에 있던 k의 값을 가져와 5를 증가시킨 후 쓰레기값이 있던 m을 초기화합니다. 다시 밖에 있던 k을 m의 값으로 대입합니다.else
문은 실행하지 않습니다. 그래서 Stack영역에 쌓이지도 않습니다.아 그리고 어제 말씀 못 드린 부분이 있는데요.
System.out.println()
은 별도의 코드 실행 영역1 에서 GPU(그래픽 처리 장치, Graphic Process Unit)에 의뢰를 하여 처리되기 때문에 T메모리에 아무런 변화를 주지 않습니다.if
문 중괄호가 닫히는 9번째 줄에서 if 블록 스택 프레임이 Stack영역에서 사라집니다. 이 부분은 14줄에 Breakpoint을 두고 관찰하면 볼 수 있는데요. 아래와 같습니다.- 스크린샷을 보시면
if
문 안에 있던 지역변수m
이 14줄에선 없어진 것을 볼 수 있습니다! - 따라서 주석처리한 15줄에서 변수
m
이 없고,else
문도 실행을 안했으니 변수p
도 찾을 수 없기 때문에 오류가 발생할 것입니다2.
- 스크린샷을 보시면
변수는 어느 T메모리의 영역에 존재할까요?? 답은 세 군대 모두 존재할 수 있습니다. 하지만 각 영역 안에 있는 각 변수는 그 목적이 다르고 그렇기 때문에 이름도 다르다고 합니다.
이번에는 static 메서드가 있는 Start4
클래스를 살펴보도록 하겠습니다.
public class Start4 {
public static void main(String[] args) {
int k = 5;
int m;
m = square(k);
}
private static int square(int k) {
int result;
k = 25;
result = k;
return result;
}
}
이를 T메모리로 나타내면 다음과 같습니다.
여기서도 몇 가지 살펴볼 만한 내용을 정리하겠습니다.
- static 메서드로 정의하였기 때문에 Static 영역에
square()
가 등록이 됩니다 - Stack영역에서
square()
스택 프레임은 여느 메서드와 같이 중괄호가 닫히는 순간 없어집니다. 여기에서 k는 main에 있는 k와 다른 메모리인 거 아시죠? 매개변수로 전달했으니 Call By Value(값에 의한 전달)입니다. - 이전
Start3
에서main()
안에 있던if
문과 달리main()
과square()
가 별개의 스택 프레임에 있습니다. 따라서 서로 접근이 불가합니다. main()
에서square()
에 접근 못하는 것은square()
가 나중에 생겼다가 진행 흐름이 다시main()
으로 돌아올때쯤square()
스택 프레임이 없어졌기 때문이다는 것은 이해하지만 반대로square()
에서main()
에 있는 변수를 참조하는건 가능할 수도 있지 않냐고 궁금해할 수도 있는데요. 그건 자바를 만든 사람들이 그렇게 정했기 때문입니다5.- 물론 전역변수를 사용하여 Static영역의
Start4
클래스의 두번째 줄에static
변수를 넣어 모든 메서드에서 공유해서 사용할 수도 있습니다. 하지만 이는 모든 스택 프레임에서 전역 변수에 접근하게 함으로 값 변경에 대한 예측을 어렵게 한다는 점에서 가능한 피해야 합니다. 다만 원주율과 같이 읽기 전용으로 값을 공유해 전역 상수로 사용하는 것은 권장한다고 합니다. 이는 공유함으로써 얻는 이득이 훨씬 크기 때문이죠.
멀티 스레드 / 멀티 프로세스의 이해
조금 전에 전역 변수의 부작용에 대해서 설명을 했었는데요. 멀티 스레드를 사용할 때 전역 변수의 부작용이 좀 더 뚜렷하게 나타난다고 합니다. 우선 멀티 스레드와 멀티 프로세스에 대해 알아보죠.
우선 위 그림은 '멀티 스레드'입니다. 멀티 스레드는 T메모리에서 스택 영역을 여러 개로 쪼개어 사용하는 구조입니다. 따라서 스택 영역에서 각 스레드 간 간섭은 없지만 스태틱 영역, 힙 영역에서는 같이 공유하여 사용합니다. '멀티 프로세스'의 경우는 스택 영역을 쪼개는 것이 아니라 T메모리가 여러 개로 분할 된 구조입니다. 메모리를 많이 잡아먹는 단점은 있지만 스태틱 영역과 힙 영역을 공유하지 않기 때문에 그 부작용이 적겠죠!
public class Start6 extends Thread {
static int share;
public static void main(String[] args) {
Start6 t1 = new Start6();
Start6 t2 = new Start6();
t1.start();
t2.start();
}
public void run() {
for (int count = 0; count < 10; count++) {
System.out.println(share++);
try { sleep(1000); }
catch (InterruptedException e) {}
}
}
}
위 코드는 '멀티 스레드'가 적용된 모습인데요. 보시다시피 2줄에서 share
라는 전역 변수를 같이 t1
, t2
객체가 공유하고 있습니다. 이 경우 이러한 결과가 나타납니다.
각 객체에서 하나씩 증가된 값을 보여주는 것을 기대했지만 다른 메서드에서 공유하는 전역변수의 값을 변경하면서 이렇게 예측하지 못할 결과값을 출력하게 되는 것이죠.
지금까지 2장에서 '절차적/구조적 프로그래밍'에 대해 배웠습니다. '그럼 이게 자바의 객체 지향과 무슨 상관이 있느냐?'하고 의문을 가질 수 있는데요. 저자는 자바의 메서드가 절차적/구조적 프로그래밍은 계승한 것이며 여기서의 지혜를 배울 것을 말하고 있습니다.
구조적 프로그래밍의 핵심은 결국 '함수' 일텐데요. 함수로 쪼개는 이유는 중복 제거도 있겠지만 논리적으로 작은 단위로 쪼개어 하나씩 해결하다보면 결국 해결할 수 있다는 때문입니다. 자바의 메서드에서도 이러한 지혜가 그대로 적용될 수 있습니다. 그리고 이 장에서 중점적으로 다루었던 T메모리가 객체 지향을 이해하는데 큰 도움이 된다고 하네요. 이 부분은 뒤에서 좀 더 살펴보겠습니다!
자바와 객체 지향
객체 지향의 4대 특성은 뭘까요? 자바 기본서를 한번이라도 본 적이 있으시다면 적어도 한 두개 정도는 바로 말씀하실 수 있으실텐데요. 음.. 저자는 이렇게 왜 외우길 추천합니다. 저도 가물가물 했는데 이 방식으로 외우면 도움이 될 것 같아요. "캡! 상추다"
캡 - 캡슐화(Encapsulation): 정보 은닉(Information hiding)
상 - 상속
(Inheritance)6: 재사용추 - 추상화(Abstraction): 모델링
다 - 다형성(Polymorphism): 사용 편의
클래스 vs 객체
클래스는 무엇이고, 객체는 무엇일까요? 이건 수업 시간에 Pobi가 한 질문이기도 한데요. 그때 여러가지 대답들이 나왔지만 가장 먼저 나온 대답이 '클래스는 붕어빵틀, 객체는 붕어빵'이었습니다. 정말 유명한 비유이죠.. 하지만 저자는 이러한 비유가 클래스와 객체를 적절하게 설명하지 못한다고 합니다. 이 비유의 논리는 붕어빵틀이 붕어빵은 만들어낸다는 논리이죠. 그렇다면 만약 붕어빵틀을 만드는 금형기계와 붕어빵틀의 관계는 클래스와 객체 관계라고 할 수 있을까요? 이러한 논리적 모순 때문에 저자는 개념과 실체로써 구분할 것을 말하고 있습니다. 따라서 다음과 같은 비유가 될 수 있습니다.
클래스 : 객체 = 펭귄 : 뽀로로 = 사람 : 김연아
객체 : 세상에 존재하는 유일무이한 사물
클래스: 분류, 집합 같은 속성과 기능을 가진 객체를 총칭하는 개념
펭귄이라는 개념의 실체는 뽀로로, 사람이라는 개념의 실체는 김연아가 될 수 있는 것이죠. 클래스를 이용해 object를 만들었다는 것을 강조하기 위해 객체를 클래스의 인스턴스(instance)라고도 합니다. 이제는 이러한 개념을 가지고 객체 지향의 4대 특성을 하나씩 살펴보도록 하겠습니다.
추상화
추상화라는 개념은 미술사에서 들어볼 수 있죠. 사람을 추상적인 그림들이 실제랑 같지 않고 어쩔 때는 사람의 형상조차 갖지 못하는 이유는 눈에 보이는 그대로가 아닌 화가 마음 속에서 느꼈던 특징을 풀어내었기 때문입니다. 추상은 사전적 의미로 다음과 같습니다.
추상 : 여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용
이 사전적 의미에서 주목해야할 점은 '공통되는 특성이나 속성'입니다. 즉 추상화는 관찰자의 시점에서 관심있는 특성만 골라 재조합한 것이라 말할 수 있습니다.
사람 클래스를 만들기 위해 추상화에 기초하여 공통되는 특성이나 속성을 꺼집어내면 다음과 같이 나올 수 있습니다.
사람 |
---|
시력, 몸무게, 혈액형, 나이, 키, 성별, 직업, 연봉 |
먹다(), 자다(), 공부하다(), 타자치다(), 계산하다(), 치료하다(), 이체하다(), 운동하다() |
이렇게 사람을 추상화할 수 있는데요. 사실 사람의 속성과 기능은 이것보다 훨씬 더 많죠. 그래서 하나의 개념이 있는데요. 애플리케이션 경계인 '컨텍스트(Context)'라는 것입니다. 즉, '어디까지 나타낼 것이냐'이죠. 좀 더 세련되게 바꾸면 이렇게 말할 수 있습니다.
"내가 만들고하자 하는 애플리케이션은 어디에서 사용될 것인가?"7
컨텍스트에 따라 조금 전 설계했던 사람 클래스가 아래와 같이 달라질 것입니다.
병원 애플리케이션 | 은행 애플리케이션 |
---|---|
시력, 몸무게, 혈액형, 나이, 키, | |
먹다(), 자다(), |
이를 토대로 추상화를 정의하면 다음과 같습니다.
추상화란 구체적인 것을 분해해서 관심 영역(애플리케이션 경계, Application Boundary)에 있는 특성만 가지고 재조합하는 것 = 모델링8
이러한 점에서 추상화를 '모델링'이라고 합니다. 자바는 이러한 객체 지향의 추상화를 class
라는 키워드를 통해 지원하고 있습니다!! 클래스와 객체 관계를 자바에선 이렇게 표현합니다.
클래스 | 객체 참조변수 | = | new | 클래스 | () |
---|---|---|---|---|---|
객체참조변수의 자료형(type) | 생성된 객체를 참조할 수 있는 변수 | 할당문 | 새로운 | 만들고자 하는 객체의 분류 | 메서드 |
그리고 이러한 과정을 '새로운 객체를 하나 생성해 그 객체의 주소값(포인터)을 객체 참조 변수에 할당'한다고 말합니다.
오늘은 여기까지만 정리하려고 합니다. 기존과 다른 관점에서 클래스와 객체, 그리고 추상화에 대해 배울 수 있는 기회였습니다. 제가 최근에 배우고 있는 객체와 클래스 개념에 대해 가장 정확하게 표현하고 있는 것 같습니다. 이러한 책을 만나서 너무 기쁘고 뒷부분에는 어떤 내용이 나오게 될지 기대됩니다!!
if
문 안에서는 변수k
가 사용 가능했지만 15줄에서 변수m
을 못 사용한다는 것을 T메모리 상의 변화를 통해 알 수 있습니다.↩'Book > programming' 카테고리의 다른 글
스프링 입문을 위한 자바 객체지향의 원리와 이해6 (2) | 2018.10.27 |
---|---|
스프링 입문을 위한 자바 객체지향의 원리와 이해5 (0) | 2018.10.21 |
스프링 입문을 위한 자바 객체지향의 원리와 이해4 (0) | 2018.10.18 |
스프링 입문을 위한 자바 객체지향의 원리와 이해3 (0) | 2018.10.18 |
스프링 입문을 위한 자바 객체지향의 원리와 이해1 (4) | 2018.10.14 |