어제가 전반적인 개론이라면 오늘부터는 Java의 기본 문법으로 들어간다.
원래 프로그래밍 언어라는 게 기본 문법은 껍데기만 바꾸는 거라, 우리가 주목해야 되는 것은 ‘언어’가 컴퓨터의 입장에선 어떻게 동작하는지에 주목하여야 한다.
- 변수
- 자료형
- 형변환 :
Promotion, Casting
- 연산자
‘변수’라는 단어는 ‘고정되어 있지 않은, 변하는 수’라는 의미로 주로 사용된다.
그러나 프로그래밍의 입장에서 변수는 다음의 의미를 지닌다.
데이터 또는 자료를 저장하기 위한 메모리 공간
그게 뭔데?
이를 알기 위해선 컴퓨터의 목적을 살펴봐야 한다.
컴퓨터는 대단한 기계지만 그 목적은 오직 '사람 대신에 일을 처리해주는 것' 이다.
그래서 처음엔 계산만 맡겼지만 시간이 흐르면서 더욱 다양한 영역에서 인간이 할 일을 대신하게 되었다.
하지만 컴퓨터에게 일을 시키려면 그 목록이 필요한데 그것이 지난 시간에 알아본 '프로그램'이다.
그리고 프로그램 안에 들어가 있는 일에 대한 정보가 ‘데이터 또는 자료’이다.
컴퓨터는 입력(interface) → 기억(RAM, HDD) → 처리(CPU)
의 단계로 업무를 처리한다.
이는 사람의 업무 처리와 유사하다.
예를 들어 우리가 아주 맛있는 짜장면을 먹었다고 치자.
그럼 우리는 그 맛을 기억할 수 있을 것이다.
그러던 어느 날 지나가면서 중국집에서 짜장면을 먹는 사람을 봤는데 그 사람이 먹는 짜장면을 보고 우리가 먹었던 아주 맛있는 짜장면의 맛을 기억해낼 수 있을 것이다.
이처럼 인간 역시 외부로부터 정보를 받고 그거를 기억해서 처리한다.
컴퓨터도 키보드나 마우스로 통해 사용자로부터 정보를 입력받아 HDD와 RAM을 통해 기억한 것을 CPU가 처리한다.
이때, CPU는 HDD와 같은 보조 기억 장치에 직접 연결되지 않는다. 오직 주 기억 장치인 RAM 하고만 연결되어 데이터를 처리하게 된다.
따라서 프로그램을 처리하려면 들어오는 정보를 '저장' 또는 '기억'해둬야 되기 때문에, 정보를 저장할 수 있는 공간을 마련해야된다. 이때, 공간이 '메모리'에 존재하고 이를 '변수'라고 한다.
즉, 변수는 프로그램을 처리하기 위한 값을 컴퓨터에 기억시키기 위한 것으로 사용법은 다음과 같다.
DataType 변수명; // 선언
변수명 = 값; // 초기화
여기서 선언은 RAM
의 Stack
영역에 DataType
에 따라 공간을 하나 마련하고 입력한 변수명을 붙인다. 과거엔 이 변수명을 16진수 형태의 주소로 찍어줬다.
그런데 막상 다루는 변수는 기껏해야 1~8byte
공간인데 메모리는 GB
의 공간을 1byte
단위로 쪼개 놨다. 따라서 숫자 표현은 한계가 있어 현 변수명 형태가 만들어졌다.
하지만 우리가 알아야 할 것은 실제 컴퓨터는 숫자인 '주소' 로 인식하고 있다는 것이다.
앞서 변수를 선언할 때, 변수명 앞에 DataType
을 넣어줬는데, 이를 직역한게 자료형이다. 자료형은 말 그대로 우리가 컴퓨터에게 입력하는 데이터의 형식을 의미한다.
그렇다면 데이터의 형식은 왜 필요할까?
인간은 '1'을 작성하면 이를 '문자, 숫자'로 동식 인식하면서 구분까지 가능하다.
하지만 모든 데이터를 숫자로 처리하는 컴퓨터에겐 입력 받은 것이 문자인지, 숫자인지 구분해주지 않는다면 처리할 수 없다.
이는 자료의 형태에 따라서 메모리 상에 저장되는 영역이 다르고, 그 공간의 크기와 표현할 수 있는 범위가 다르게 설정되어 있기 때문이다.
이러한 자료형은 다음과 같이 정리할 수 있다.
구분 / 크기 | 1byte (8bit) | 2byte (16bit) | 4byte (32bit) | 8byte (64bit) |
---|---|---|---|---|
기본형 | byte, boolean | short, char | int, float | long, double |
참조형 | String |
정수형
byte
-128 ~ 127 (256개)
short
– 32768 ~ 32767 (65536개)
char 'A'
(ASCII 65) 0~65535 (65536개)
int 100032
-2^31 ~2^31-1
long
100000000L 20억 이상
실수형
float 3.14f
오차 없는 7자리
double 7.0879977
오차 없는 15자리
기본적으로 '1byte = 8bit' 이고, bit
는 0
과 1
로 수를 나타낸다. 따라서 n개의 bit가 표현할 수 있는 갯수는 2^n
개이다. 여기서 부호의 사용 여부에 따라 표현하는 범위가 반토막이 난다.
한 가지 짚고 넘어가야 할 건, 흔히 char
는 정수형이 아닌 문자형으로 구분하곤 한다.
하지만 char
가 나타내는 문자라는 개념 자체가 컴퓨터에게 없다.
즉, 컴퓨터는 하나의 약속으로서 문자모양을 코드로 변환하여 저장하는 것이고 그 규약을 ASCII code
라 한다.
추가로 실수형은 정수형과 달리 숫자의 개수에 관심이 없고, 표현할 수 있는 정밀도에 관심이 있다.
이는 실수형의 bit 분배 형식이 다르기 때문이다.
위 표를 보면 기본형들은 범위가 고정되어 그 안에서만 값을 저장할 수 있다.
또한 앞서 말했듯이 메모리 전체에서 8byte
는 굉장히 작은 값에 불과하다.
int a = 1;
int b = 2;
하지만 참조형은 사이즈가 고정되지 않고 얼마큼의 데이터를 넣는지에 따라 바뀐다.
예를 들어 그래서 매우 커질 가능성이 있다.
String a = "sdasd";
String b = "dasdasfwfdqwfqwf";
이런 기본형과 참조형를 컴퓨터의 관점에서 구분 짓기 위해선 운영체제(OS)를 알아야 한다.
흔히 운영체제의 핵심을 기본적으로 다음 두 가지로 인식한다.
인터페이스 제공
컴퓨터 부팅(펌웨어 ~ 운영체제)
하지만 진짜 핵심은 ‘자원관리’(Resource management)가 핵심이다
그렇다면 Resource management는 무엇인가?
CPU, RAM, HDD
를 운영체제가 관리하는 것으로 각 파트의 입장에서 다음과 같은 역할을 한다.
CPU
: 일처리 순서 배정 (운영체제의 스케쥴링 알고리즘)HDD
: 자료를 효율적으로 저장하여 가장 효율적인 loading을 구현한다. (File System-NTFS)RAM
: 가상 메모리 시스템
여기서 가상 메모리 시스템은 물리적인 형태의 메모리를 논리적으로 Stack, Date, Heap, Text으로 나누어 각각의 영역마다 다른 데이터를 저장한다.
Stack
: 지역 변수, 매개 변수Data
: Static 변수Heap
: 동적Text
: 기본 명령 코드, 입력 코드
기존 Win98까지 서버 컴퓨터에서만 돌아가던 시스템은 XP부터 가져오게 되면서 데이터의 충돌이 줄고, 입출력도 빨라졌다.
이처럼 운영체제는 가상 메모리 시스템을 통해서 메모리를 나누는데, 기본형 변수의 특징은 Stack
이라는 자료구조 영역에 적합한 반면, 참조형 변수는 Heap
에 저장되기 적합하다.
여기서 참조형 변수 공간은 Stack
에 생겨나지만 거기에 들어가 있는 것은 바로 Heap
에 있는 데이터의 주소이다.
Promotion, Casting
기본형 자료 안에는 대소 관계가 존재한다.
byte
→short
→int
→long
→float
→double
char
→
대소 관계라는 것은 특정 자료형이 다른 자료형을 전부 포함할 수 있을 때, '크다'고 한다. 이를 단순 자료의 크기 차이로 생각해선 안 된다. 컴퓨터는 안정적으로 연산을 하기 위해서 '형변환'을 일으킨다.
그리고 형변환은 관점의 차이에서 Promotion, Casting
로 구분된다.
Data type Promotion
먼저 Promotion
은 작은게 큰 걸로 바뀌는 건 자동적인 현상이다.
기본적으로 자바는 들어가는 데이터 값에 맞게 설계가 되어 있어 타입에 맞게 데이터를 넣어야 한다. 그러나 서로 다른 자료형이라고 무조건 오류가 나지 않는다.
이때, Promotion
은 “좁은 범위의 자료형이 넓은 범위의 자료형에 연산될 때” 일어난다.
구체적으로 컴퓨터가 연산이 되려면 피연산자의 데이터 타입이 맞아야 한다.
서로의
bit
가 맞아야 연산이 가능하다는 것이다.
따라서 작은 것이 큰 것에 들어가려면 서로 배정된 bit의 수가 안 맞기 때문에 모자란 비트를 자동으로 추가해주는 것이다.
Data type Casting
(강제 형변환 연산자)반대로 Casting
은 넓은 범위의 자료형이 좁은 범위의 자료형에 대입될 때 또는 성립이 불가능한 경우 char ↔ short
같은 경우에 사용된다.
이는 강제적으로 큰 값을 작은 값의 bit
에 맞추는 것으로 현상이 아닌 문법이다.
여기서 주의할 점은 범위가 수용가능하더라도 타입이 다르면 절대 되지 않는다는 것이다.
즉, 지금 가진 값이 아닌 데이터 타입이 더욱 중요하다.
또한, 작은 타입에 강제로 표현불가한 걸 오버플로우가 발생하기 때문에 데이터가 깨진다
연산자는 기본적인 상식을 벗어나지 않기 때문에 몇 가지 주의 사항만 넘어가고자 한다.
대입 연산자 : =
대입 연산자는 우항에서 좌항으로 대입이 된다.
int a = 10;
여기서 좌항은 값을 저장할 수 있는 공간이어야만 한다. 애초에 대입 자체가 공간에 저장
하는 것이다. 때문에 공간이 아닌 곳은 절대 대입 연산자의 좌항에 올 수 없다.
++
, --
++
: A = A+1
(1씩 증가)--
: A = A-1
(1씩 감소)막상 코드를 풀어서 보면 대입 연산자와 별반 다를 것이 없지만, 우리가 자주 헷갈리는 것은 '연산 타이밍'이 다르다는 것이다.
구체적으로 전위형과 후위형으로 나뉘는데 그 의미는 다음과 같다.
- 전위형 : 증가 후 참조
- 후위형 : 참조 후 증가
int A = 10;
System.out.print(A++); // 10, A+1
System.out.print(A); // 11
위 코드를 보게 되면 변수 A에 저장된 값은 10
이다. 그리고 출력문에 A++
을 했지만, 정작 나온 값은 10
이고 다음 출력문에서 1
이 더해진 11
이 나왔다.
이를 볼 때, 증감연산자의 타이밍이란 본인이 속한 라인에서 값이 증감하는 우선순위를 의미한다.