사람을 사랑한 기술
스프링을 비롯한 모든 신기술은 갑자기 하늘에서 뚝 떨어진 것이 아니다.
스프링은 과거로부터 현재까지 프로그래밍 발전사에서 그 정점에 있다고 해도 과언이 아니다.
기계어 - 0과 1의 행진 / 너무나 비인간적인 언어
컴퓨터 CPU 속도가 2GHz라면 컴퓨터는 1초당 대략 20억번 전구 스위치를 껐다 킬수 있다.
메모리가 1GB라면 당신의 컴퓨터는 대략 80억개의 전구를 관리한느 성능을 갖춘 것이다.
어셈블리어 - 0과 1의 행진을 벗어나 인간 지향으로 / 기계어 니모닉
기계어는 컴퓨터가 이해하는 유일한 언어지만, 인간은 눈높이를 그 수준까지 낮추기에는 나무 낮다.
"기계들의 명령어들을 일상 용어로 표현하고 이걸 기계가 알 수 있는 기계어로 번역하면 어떨까?"
그래서 만들어진 것이 기계어 명령어와 일상 용어를 일대일로 매칭하는 코드표였다.
니모닉과 기계어의 일대일 매칭 코드표를 만든 것이다. 그 매칭 코드표를 어셈블리어라고 불렀다. 그런데 CPU마다 기계어가 다르기 때문에 CPU 별로 각각의 어셈블리어도 달랐다. CPU마다 실행할 수 있는 기계어 세트가 달랐은 당연히 어셈블리어도 기계마다 달랐다.
어셈블리어를 기계어로 번역해주는 소프트웨어를 어셈블러라고 한다.
어셈블러가 처음 나왔을 때 0과 1의 ㅇ행렬에서 벗어나 영어이긴 하지만 일상 용어로 프로그램을 작성할 수 있다는 크나큰 변화의 축복이었을 것이ㄷ .
하지만 애드삭 어셈블리어와, 유니박 어셈블러가 호환이 되지 않았다,
C언어 강력한 이식성 : One Source Multi Object Use Anywhere
어셈블리어라면 같은 일을 하는 프로그램의 소스 팡리을 각 기계의 종류만큼 만들어야 했다. 멀티소스였다. 그런데 C언어의 등장으로 이제는 소스 파일 단 하나만 만들면 된다. 싱글 소스이다ㅣ.
하나의 소스 파일을 각 기계에 맞는 컴파일러로 컴파일만 하면 각 기계에 맞는 기계어 목적 파일이 만들어 진다.
- 하나의 C 소스 파일만 작성
- 기종마다 하나씩 기계어 목적 파일을 생성
- 모든 컴퓨터에서 실행 가능
운영체제가 하드웨어의 특성을 추상화 하고 컴파일러는 운영체제별로 만들어져 공급하게 되는데 그 운영체제들이 또 나름의 틍성이 있었기에 하나의 소스로 각 기종별로 컴파일하기 전에 그 기종에 맞게 소스를 변경하는 작업이 필요했다.
short는 int보다 크지 않은 정수
long은 int 보다 작지 않은 정수
어떤 운영체제는 Int를 2바이트로 인지하고, 다른 운영체제는 int를 4바이트로 인지하기 때문에 발생한다.
int는 정수를 의미하는 여엉 단어인 integer의 줄임말이지 그 크기가 몇 바이트인지 규정하는 말이 아니었다.
결국 운영체제 별로 소스 수정 과정이 필요했다. 그래도 어셈블리어를 하나의 기꼐옹에서 다른 기계용으로 작성한느 수고와 노력에비하면 간편해졌다.
C++ 정말 인간적인 프로그래밍 방법론, 객체지향
C++은 C의 객체지향 개념을 도입함으로써 역사에 한 획을 그은 언어이다.
자바 진정한 객체지향 언어
C++은 순수 객체 지향 개념에 가장 충실한 언어이긴 하지만 숨은 반전이 있었다.
C++는 객체 없는 프로그래밍도 가능하다는 것이다.
객체지향 지원 언어라고 부르는 것이 맞다고 보는 것이 좋다.
객체지향 언어에는 클래스가 있다. 자바에서는 클래스를 떠나 존재할 수 있는 것이 아무것도 없다.
심지어 시작점인 main()도 클래스 외부가 아닌 내부에 존재해야 한다.
그런데 c++은 main() 함수는 클래스와 별도로 존재할 수 있으며 prinf() 함수는 클래스나 객체와 관계없이 호출 가능함
C++은 객체지향 개념을 도입함으로써 개발자에게 사랑을 실천했다. 자바는 어떻게 사랑을 실천했을까 ?
자바와, C#은 JVM이다.
컴파일러를 기종별로 구매하지 않아도 된다.
심지어 자바 컴파일러는 무료이다.
C언어로 작성한 소스를 다른 기종의 컴퓨터에서 실행하려면 소스와 각 기종용 컴파일러를 준비해야만 했다.
그런데 자바로 구현한 소스는 다른 기종의 컴퓨터에서 실행하기 위해 목적 파일인 오브젝트 파일만 가져가면 된다.
다른 기존의 컴퓨터에 해당 기종용 JRE가 설치되어 있어야 한다.
JVM이 중재자로서 각 플랫폼에서 프로그램을 구동하는데 아무 문제가 없게끔 만들어 주는 것이다.
이 특징이 Write Once Run Anywhere라고 한다.
JDK ; 자바 개발 도구
JRE ; Java Runtime Environment 자바 실행환경
JVM ; Java Virual Machine 자바 가상 기계
객체지향 프로그램의 메모리 사용 방식
코드 실행 영역과, 데이터 저장 영역으로 나뉘고
데이터 저장 영역은 스태틱, 스택, 힙 영역으로 나뉜다.
자바에 존재하는 절차적/구조적 프로그래밍의 유산
객체 지향프로그래밍은 절차적/구조적 프로그램의 어깨를 딛고 있다.
절차적 프로그래밍을 한마디로 표현하자면 goto를 쓰면 안된다.
실행 순서가 인간이 이해하기에 너무 복잡해질 가능성이 있기 때문에 프로그램의 실행 순서를 이리저리 이동할 수 있게 된다.
그래서 원칙적으로 goto의 사용을 금지하는 것이다.
구조적 프로그램은 함수를 쓰라는 것이다.
함수를 쓰면 좋은 이유는 중복 코드를 한곳에 모아서 관리할 수 있고, 논리를 함수 단위로 분리해서 이해하기 쉬운 코드를 작성할 수 있기 때문이다.
여기에 더해 구조적 프로그래밍의 지침 중에는 공유 사용 시 문제가 발생하기 쉬운 전역 변수보다는 지역 변수를 쓰라는 것도 있다.
자바에서 절차적/구조적 프로그래밍의 유산은 메서드 안에서 확인할 수 있다.
객체지향 프로그래밍에서 제어문이 존재할 수있는 유일한 공간은 바로 메서드 내부이기 때문이다.
(여기서 한가지 의문은 함수/메서드는 무엇이 다르냐는 것이다 이다. 결론은 전혀 다르지 않다. 절차적/구조적 프로그래밍에서 함수라 불렸기 때문에 객체지향에서는 좀 다르게 불려야하지 않을까 해서 메서드 라고 불렸다고 한다.)
그래도 굳이 차이점을 찾는다면함수는 클래스나 객체와 아무 관계가 없지만 메서드는 반드시 클래스 정의 안에 존재해야 한다는것이다.
객체 지향 언어에서 클래스 외부에 존재할 수 있는 것은 없다.
다시 보는 main 메서드
메서드는 프래그램이 실행되는 시작점이다.
스태틱 - 클래스들의 놀이터
스택 - 메서드들의 놀이터
힙 - 객체들의 놀이터
JRE느 먼저 main 메서드가 있는지 확인한다.
JVM에 전원을 넣어 부팅한다.
부팅한 JVM은 목적 파일을 받아 그 목적 파일을 실행한다.
모든 자바 프로그램이 반드시 포함해야 하는 패키지가 있다. java.lang이다.
가장 먼저 lang 패키지는 메모리의 스태틱 영역에 가져다 놓는다.
main 메서드가 실행되기 전 JVM에서 실행하는 전처리 작업들
java.lang 패키지는 스태틱 영역에 배치
import 된 패키지는 스태틱 영역에 배치
프로그램 상의 모든 클래스를 메모리의 스태틱 영역에 배치함
메서드들의 놀이터는 스택이다.
메서드가 놀기 위해 스택 프레임이 스택 영역에 할당된다.
더 정확하게 얘기하면 중괄호를 만날때마다 스택 프레임이 한개씩 생긴다.
그 다음 메서드의 배개변수들의 변수 공간을 할당한다.
JRE는 눈에 보이지 않게 뒤에서 JVM을 부팅하고
JVM은 메모리 구조를 만들고 java.lang 패키지 로딩, 각종 클래스 로딩, main 메서드 스택 프레임 배치, 변수 공간 배치 등등의 일을 처리했다.
그 다음 메서드의 끝을 나타내는 닫는 중괄호를 만나면 스택 프레임이 소멸된다.
그러면 JRE는 JVM을 종료하고 JRE 자체도 운영체제 상의 메모리에서 사라진다.
if문이 있으면 스택 프레임에 if문이 쌓인다.
중괄호를 만나면 스택 프레임이 시작된다
if문의 닫는 중괄호를 만나면 스택 영역에서 사라진다.
main함수를 닫는 중괄호를 만나면 메모리 소멸, JVM 가동 중지, JRE 사용 했던 자원을 운영체제에 반납
메모리 상에 k가 존재하니 당연히 접근 가능하다.
외부 블록에서 내부 블록의 변수에는 접근할 수 없지만 내부 블록에서 외부블록에서는 접근 가능하다. 라는 의미는
제임스 고슬링이 일부러 그렇게 만든 것이 아니라, 시간의 흐름. 즉 코드의 진행에 따른 T메로리 변화를 보면 당연히 그럴 수 밖에 없다는 결론이 나온다.
스택 메모리 내의 스택 프레임 안의 변수를 지역 변수라고 한다. 그 지역에서만 사용할 수 있고 외부에서는 사용할 수 없기 때문이다
그 지역이 사라지면 메모리에서 함께 사라진다.
메서드를 블랙박스화 한다 -> 입력값과 반환값에 의해서만 메서드들 사이에서 값이 전달될 뿐 ㅅ로 내부의 지역 변수를 볼 수 없다.
사실 메모리의 스냅샷을 차근차근 그려보면
T메모리 상에 squre)0 메서드 스택 프레임은 아직 존재하지 않기 때문에 squre 메서드 내의 지역 변수도 아직 존재하지 않으며 존재하지 않기 때문에 당연히 접근이 불가능하고 squre 함수 호출이 끝나면 스택 프레임이 사라지기 때문에 어떤 실행문도 squre 메서드 지역 변수에는 접근이 불가능하다.
하지만 메서드 내에서 그 main 메서드의 지역변수를 참조할 수있을 것 같지만 자바 스펙을 만든사람이 금지 시켰다.
이유를 추측해보면
1. 그것이 이치에 맞기 때문이다. 메서드는 서로의 고유 공간인데 서로 침범하면 무단 침입으로 자바월드에 문제를 유발시킬 수 있다.
2. 포인터 문제 때문이다. squre)0 메서드에서 main 내부의 지역변수에 접근한다고 하면, 메모리 상의 위치를 정확히 알고 있어야 하는데, 그 위치를 알기 위해서는 바로 변수의 메모리 위치 즉 포인터라고 읽고 메모리 주소 값이라 이해해야 하는 그 값을 알아야 한다.
자바가 가장 환영받는 이유는 포인터가 없다는 이유 때문이다.
3. 또 메서드는 호출하면서 만들어지는 스택 구조는 항시 변화한다.
결국 메서드에서 하단의 메서드 스택 프레임들 중에서 한 곳에 있는 메서드 내부의 지역 변수를 참조하려면 포인터가 필요하다, 자바에서는 포인터를 사용할 수 없으므로 언어 스펙상으로도 메서드 스택 프레임 사이에 변수를 참조하는 것은 불가능 하다는결론에 도달.
메서드를 호출할 때마다 해당 메서드의 스택 프레임이 생긴다.
만약 위에서 squre 메서드를 여러번 호출하면 매번 squre 메서드 스택 프레임이 만들어졌다 사라진다.
메서드를 호출하면서 인자로 전달되는 것은 변수 자체가 아니라 변수가 저장한 값만을 복제해서전달 한다. 이런 전달 방식을 값에 의한 전달, Call By Value라고 한다.
전역 변수와 메모리
두 메서드 사이에 값을 전달하는 방법은 메서드를 호출할 때 메서드의 인자를이용하는 방법과 리턴값을 이용하는 방법이 있다.
그런데 메서드 사이에 값을 공유하는 방법이 하나 더 있다.
바로 전역 변수를 사용하는 것이다.
'개발 > Spring' 카테고리의 다른 글
자바 문자열 String (0) | 2021.03.18 |
---|---|
자바 컬렉션 Set (0) | 2021.03.15 |
스프링이란? (0) | 2021.03.14 |
스프링 입문을 위한 자바 객체지향 원리와 이해 - 프롤로그 (0) | 2021.03.13 |
자바 컬렉션 List (0) | 2021.03.07 |