자바의 데이터 타입은 크게 기본 타입(primitive type)과 참조 타입(reference type)으로 분류된다.
기본 타입은 byte, char, short, int, long, float, double, boolean 총 8개로 구성돼있다.
| 타입 | 메모리 크기 | 저장되는 값의 허용 범위 |
|---|---|---|
| byte | 1byte(8bit) | ~ ( (-128~127) |
| short | 2byte(16bit) | ~ ( (-32,786~32,767) |
| char | 2byte(16bit) | ~ ( (0~65535) |
| int | 4byte(32bit) | ~ ( (-2,147,483,648~2,147,483,647) |
| long | 8byte(64bit) | ~ ( (생략) |
char 타입은 문자 타입이기도 하다.
세계 각국의 문자를 0~65535 숫자로 매핑한 국제 표준 규약인 유니코드를 저장할 수 있다.
char c = 65; // 10진수 65와 매핑되는 문자:'A'
char c = 'A'; // 'A' 문자와 매핑되는 숫자: 65
| 타입 | 메모리 크기 | 저장되는 값의 허용 범위 | 유효 소수 이하 자리 |
|---|---|---|---|
| float | 4byte(32bit) | ~ | 7자리 |
| double | 8byte(64bit) | ~ | 15자리 |
boolean은 true, false 두 값을 저장한다.
값의 허용 범위가 작은 타입이 혀용 범위가 큰 타입으로 대입될 때 발생한다.
long longValue = 50000000L;
float floatValue = longValue; // 자동 타입 변환
값의 허용 범위가 큰 타입이 허용 범위가 작은 타입으로 쪼개어서 저장하는 것을 강제 타입 변환이라고 한다.
double doubleValue = 3.15;
int intValue = (int) doubleValue; // 강제 타입 변환
참조 타입은 객체의 번지를 참조하는 타입으로 배열, 열거, 클래스, 인터페이스 타입이 있다.
객체란? 데이터(필드) + 메서드
여기서는 간단하게 짚고 넘어가고, 자세한건 JVM 글에서 다루겠다.
바이트코드 파일을 읽은 내용이 저장되는 영역으로 클래스별로 상수, 정적 필드, 메서드 코드, 생성자 코드 등이 저장된다.
객체가 생성되는 영역이다. 객체의 번지는 메서드 영역과 스택 영역의 상수와 변수에서 참조할 수 있다.
메서드를 호출할 때마다 생성되는 프레임이 저장되는 영역이다. 메서드 호출이 끝나면 프레임은 자동 제거된다.
프레임 내부에는 로컬 변수 스택이 있다. 여기서 기본 타입 변수와 참조 타입 변수가 생성되고 제거된다.
참조 타입 변수는 객체를 참조한다. 따라서 참조하는 객체가 다르면 객체가 저장하고 있는 값이 같더라도 다르다.
String s1 = new String("문자열1");
String s2 = s1;
String s3 = new String("문자열1");
System.out.println(s1 == s2); // s1과 s2는 같은 객체를 참조하고 있으므로 true
System.out.println(s1 == s3); // s1과 s3는 모두 "문자열1" 저장하고 있지만 참조하는 객체가 다르므로 false
System.out.println(s1.equals(s3)); // 객체와 상관없이 내부 문자열만 비교하므로 true
String s1 = "문자열1"; // String 타입 변수 s1 선언 후 문자열 대입
String s2 = "문자열1";
System.out.println(s1 == s2); // s1과 s2는 동일한 문자열 리터럴로 생성된 객체를 참조하기 때문에 true
결론부터 말하면 메모리 할당 방식과 객체 생성 방식의 차이이다.
String s1 = new String("String");
new 연산자로 생성하면 Heap 영역에 새로운 String 객체를 생성한다.
문자열 상수 풀에 있는 문자열 리터럴을 복사해서 Heap 영역에 별도의 객체를 만든다.
String s1 = "String";
문자열 리터럴로 생성하면 String Constant Pool(문자열 상수 풀)에 저장된다.
문자열 리터럴이 이미 문자열 상수 풀에 존재하면 기존의 값을 재사용하기 때문에 동일한 리터럴 값을 가지는 다른 String 변수들이 있으면, 같은 메모리 주소를 공유한다.
new String()을 사용하면 Heap 영역에 새로운 객체가 생성되므로 더 이상 참조되지 않으면 GC의 대상이 되어 삭제될 수 있다.
문자열 상수 풀에 저장된 문자열 리터럴은 JVM이 종료될 때까지 유지된다.
String str1 = "str1";
String str2 = "str2";
// 비교
str1.equals(str2); // false
// 추출
```java
str1.charAt(0); // s
// 길이
```java
str1.length(); // 4
// 대체
```java
str1.replace("str", "String"); // String1
// 잘라내기
```java
str1.substring(1); // tr1
str1.substring(0, 3); // str
// 찾기
```java
str1.contains("st"); // true
// 분리
```java
str1.split("t"); // s, r1
| 구분 | 기본 타입 (Primitive Type) | 참조 타입 (Reference Type) |
|---|---|---|
| 저장 방식 | 값 자체를 저장 | 객체의 주소(참조값)를 저장 |
| 저장 위치 | Stack 메모리 | Heap 메모리 (객체), Stack 메모리 (참조값) |
| 메모리 관리 | 직접 관리 | GC(Garbage Collector)가 관리 |
| null 가능 여부 | 불가능 | 가능 |
| 값 비교 방식 | ==로 값 비교 가능 | ==는 주소 비교, equals()는 값 비교 |