변수(variables) : 단 하나의 값을 저장할 수 있는 기억 공간 (읽고 쓰는 게 가능)
int a; // 선언
int b = 10; // 초기화(처음으로 값 저장)
b = 20; // 쓰기
int c = a + 1; // 변수 a 읽기| 변수 유형 | 설명 | 선언 위치 | 메모리 할당 시점 | 접근 제한 |
|---|---|---|---|---|
| 클래스 변수 (static 변수) | 클래스에 속하며 모든 인스턴스가 공유 | 클래스 내부, static 키워드 사용 | 클래스가 메모리에 로드될 때 | public, private, protected, default |
| 인스턴스 변수 | 객체(인스턴스)마다 개별적으로 존재 | 클래스 내부 | 객체 생성 시 | public, private, protected, default |
| 지역 변수 | 특정 메서드나 블록 내에서만 유효 | 메서드 또는 블록 내부 | 변수 선언 시 | 없음 (해당 블록 내에서만 접근 가능) |
| 매개변수 | 메서드 호출 시 전달되는 변수 | 메서드의 매개변수 목록 | 메서드 호출 시 | 없음 (메서드 내부에서만 사용 가능) |
상수 (final 변수) | 한 번 할당되면 변경 불가 | 클래스 내부 또는 메서드 내부 | 선언 시 또는 생성자에서 할당 | public, private, protected, default |
리터럴(literal) : 값 자체 (상수) → 타입이 존재함
e.g. 100, 200 'a', ...
문자열 리터럴은 클래스 로드 시에 메모리에 저장된다
String s = "ABC";
String t = "ABC"; // "ABC" 라는 문자열 리터럴이 이미 메모리에 할당되어 있기 때문에 참조해서 사용한다
String u = new String("ABC");
String v = new String("ABC"); // 새로운 메모리 공간에 할당하기 때문에 좋지 않다
primitive type : 실제 값 저장 → 원소
reference type : 메모리 주소값 저장(객체의 주소값) → 집합
(32bit 운영체제 - 4byte, 64bit 운영체제 - 8byte)
primitive 타입별 크기 및 범위
| 타입 | 크기 | 최소값 | 최대값 | 정밀도 |
|---|---|---|---|---|
byte | 1바이트 | -128 | 127 | 8비트 정수 |
short | 2바이트 | -32,768 | 32,767 | 16비트 정수 |
int | 4바이트 | -2,147,483,648 | 2,147,483,647 | 32비트 정수 |
long | 8바이트 | -9,223,372,036,854,775,808 | 9,223,372,036,854,775,807 | 64비트 정수 |
float | 4바이트 | ±1.4E-45 | ±3.4E38 | 약 7자리 소수 정밀도 |
double | 8바이트 | ±4.9E-324 | ±1.8E308 | 약 15자리 소수 정밀도 |
char | 2바이트 | '\u0000' (0) | '\uffff' (65,535) | 유니코드 문자 |
boolean | 1바이트※ | true 또는 false | true 또는 false | 1비트 (이론상)※※ |
※ boolean의 크기는 JVM 구현에 따라 다를 수 있음
※※ boolean은 1비트만 필요하지만, 실제 메모리 사용량은 JVM이 최적화하여 결정함
- 지수 표기법 : E-8 = 10^(-8), E+02 = 10^2
할당 시 타입의 범위가 변수 >= 리터럴 인 경우에만 할당 가능
char의 인코딩과 디코딩
인코딩(encoding) : 'A'(문자) → 65(ASCII 코드)
디코딩(decoding) : 65(ASCII 코드) → 'A'(문자)
| 십진수 값 | 32비트 이진수 표현 | 설명 |
|---|---|---|
| 2,147,483,646 | 0111 1111 1111 1111 1111 1111 1111 1110 | 정상 범위 |
| 2,147,483,647 | 0111 1111 1111 1111 1111 1111 1111 1111 | 32비트 정수(int)의 최댓값 |
| 오버플로우 발생 | 1000 0000 0000 0000 0000 0000 0000 0000 | 2,147,483,647 + 1 → -2,147,483,648로 순환 |
| -2,147,483,648 | 1000 0000 0000 0000 0000 0000 0000 0000 | 32비트 정수(int)의 최솟값 |
| -2,147,483,647 | 1000 0000 0000 0000 0000 0000 0000 0001 | 정상 범위 |
| 언더플로우 발생 | 0111 1111 1111 1111 1111 1111 1111 1111 | -2,147,483,648 - 1 → 2,147,483,647로 순환 |
형변환(casting) : 변수 또는 상수의 타입을 다른 타입으로 변환하는 것
float a = 3.14 // 리터럴의 타입은 double(8byte), 변수의 타입은 float(4byte) 이므로 불가
float f = (float) 3.14 // 명시적으로 형변환하면 할당 가능
float g = 3.14F // 리터럴 타입을 float하여 타입을 일치시키면 가능
int b = 'a' // 리터럴의 타입은 char(2byte), 변수의 타입은 int(4byte) 이므로 자동 형변환실수의 저장 방식
| 타입 | 부호 비트 (Sign) | 지수부 (Exponent) | 가수부 (Mantissa) | 총 비트 수 |
|---|---|---|---|---|
float | 1비트 | 8비트 | 23비트 | 32비트 |
double | 1비트 | 11비트 | 52비트 | 64비트 |
값 = (-1)^S * M * 2^E
(M은 1.XXXX의 이진수로 표현되고 XXXX 부분을 가수부에 저장)
부동 소수점에 오차가 발생하는 경우
정수는 오차가 없으나 범위가 좁고 실수는 오차가 있으나 범위가 넓음 → 오차가 없어야만 하는 상황이 존재하므로 잘 선택해서 사용
CPU가 싱글 코어에서 멀티 코어로 변하면서 변수에서 공유 자원에 대한 접근 문제가 생겨 변수 대신 상수를 선호 → 상수는 필요할 때마다 새로 메모리를 할당해야 함 → 메모리 용량도 늘어나서 감당 가능 (메모리를 해제하는 비용은 존재)
값을 직접 쓰지 않고 변수나 상수에 할당해서 사용해야 함
왜? 같은 값을 코드에서 일일이 수정해야 하는 상황 발생
가독성을 위해 정수형 값에 _(언더바) 사용 가능
int a = 100_000;