Ch 2.2 변수의 타입 (Variable Types)
자바는 어떤 형태의 데이터를 저장할 것인지 미리 지정해 두어야 하는 강타입(Strongly Typed) 언어 입니다. 데이터의 종류에 따라 값의 저장과 해석에 사용되는 변수의 타입이 결정됩니다.
1. 기본형(Primitive Type) vs 참조형(Reference Type)
자바의 자료형은 크게 두 가지로 나뉩니다.
| 구분 | 기본형 (Primitive Type) | 참조형 (Reference Type) |
|---|---|---|
| 저장 내용 | 실제 값 을 직접 저장 | 객체가 저장된 메모리 주소 를 저장 |
| 메모리 위치 | 스택(Stack) | 힙(Heap) |
| 종류 | 8가지 (int, double 등) | 무한 (String, 배열, 클래스 등) |
| 기본값 | 타입별로 정해진 값 (0, false 등) | null |
| 크기 | 타입마다 고정 (1~8 bytes) | JVM에 따라 다름 (4 or 8 bytes 주소) |
기본형 변수 (스택) 참조형 변수 (스택 → 힙)
┌──────────────┐ ┌──────────────┐ ┌─────────────────┐
│ int age=25 │ │ String name │──────▶│ "Alice" (힙) │
│ age: [25] │ │ name: [주소]│ │ (실제 객체) │
└──────────────┘ └──────────────┘ └─────────────────┘
노트
기본형은 값 자체가 스택에 저장되어 빠르게 접근할 수 있습니다. 참조형은 힙 메모리의 객체를 가리키는 주소를 스택에 저장합니다.
2. 8가지 기본형 완전 정리
기본형은 총 8가지이며, 논리형·문자형·정수형·실수형으로 구분됩니다.
| 분류 | 타입명 | 크기 | 범위 | 기본값 | 래퍼클래스 |
|---|---|---|---|---|---|
| 논리형 | boolean | 1 bit (JVM 의존) | true / false | false | Boolean |
| 문자형 | char | 2 bytes | '\u0000' ~ '\uFFFF' (0 ~ 65,535) | '\u0000' | Character |
| 정수형 | byte | 1 byte | -128 ~ 127 | 0 | Byte |
| 정수형 | short | 2 bytes | -32,768 ~ 32,767 | 0 | Short |
| 정수형 | int | 4 bytes | -2,147,483,648 ~ 2,147,483,647 | 0 | Integer |
| 정수형 | long | 8 bytes | -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807 | 0L | Long |
| 실수형 | float | 4 bytes | ±3.4 × 10^38 (소수 7자리) | 0.0f | Float |
| 실수형 | double | 8 bytes | ±1.8 × 10^308 (소수 15자리) | 0.0d | Double |
public class AllPrimitiveTypes {
public static void main(String[] args) {
boolean isJavaFun = true; // 논리형
char letter = 'J'; // 문자형
byte tinyNum = 100; // 정수형 (작은 범위)
short smallNum = 30000; // 정수형 (중간 범위)
int normalNum = 2000000; // 정수형 (기본, 가장 많이 사용)
long bigNum = 9_000_000_000L; // 정수형 (큰 범위, L 접미사 필수)
float floatNum = 3.14f; // 실수형 (f 접미사 필수)
double doubleNum = 3.141592653; // 실수형 (기본, 정밀도 높음)
System.out.println("boolean: " + isJavaFun);
System.out.println("char: " + letter);
System.out.println("byte: " + tinyNum);
System.out.println("short: " + smallNum);
System.out.println("int: " + normalNum);
System.out.println("long: " + bigNum);
System.out.println("float: " + floatNum);
System.out.println("double: " + doubleNum);
}
}
3. 리터럴(Literal)과 표기법
리터럴은 코드에 직접 적힌 고정된 값을 말합니다. 타입에 따라 표기법이 다릅니다.
정수형 리터럴
int decimal = 100; // 10진수 (기본)
int binary = 0b01100100; // 2진수 (0b 접두사)
int octal = 0144; // 8진수 (0 접두사)
int hex = 0x64; // 16진수 (0x 접두사)
long bigLong = 100L; // long 리터럴 (L 또는 l 접미사, 대문자 권장)
long billion = 1_000_000_000L; // 언더바로 가독성 향상 (Java 7+)
// 위 4개는 모두 같은 값(100)
System.out.println(decimal == binary); // true
System.out.println(binary == octal); // true
System.out.println(octal == hex); // true
실수형 리터럴
double d1 = 3.14; // double (기본, 접미사 없음)
double d2 = 3.14d; // double (d 접미사 명시)
double d3 = 3.14e2; // 지수 표기법 = 314.0
float f1 = 3.14f; // float (f 접미사 필수!)
float f2 = 3.14F; // float (F도 가능)
// 3.14는 기본적으로 double이므로 float에는 f를 붙여야 함
// float f3 = 3.14; // 에러! possible lossy conversion from double to float
문자형 리터럴
char c1 = 'A'; // 문자 리터럴 (작은따옴표)
char c2 = 65; // 정수값으로 문자 지정 (A = 65)
char c3 = '\u0041'; // 유니코드 직접 표기 (A)
char c4 = '\n'; // 특수 문자 (줄바꿈)
char c5 = '\t'; // 특수 문자 (탭)
// 이스케이프 시퀀스
// \' 작은따옴표
// \" 큰따옴표
// \\ 백슬래시
// \n 줄바꿈
// \t 탭
// \r 캐리지리턴
boolean 리터럴
boolean b1 = true;
boolean b2 = false;
boolean b3 = (10 > 5); // 조건식도 boolean 리터럴처럼 사용 가능
4. 참조형 타입: String, 배열, 클래스, 인터페이스
참조형은 기본형 8가지를 제외한 모든 타입입니다.
// String (문자열) - 가장 많이 사용되는 참조형
String greeting = "Hello, Java!";
String empty = ""; // 빈 문자열
String nullStr = null; // 아무 객체도 참조하지 않음
// 배열
int[] scores = {95, 88, 72, 100};
String[] names = {"Alice", "Bob", "Charlie"};
// 클래스 (사용자 정의)
// Student student = new Student("Alice", 20);
5. String의 특별한 취급
String은 클래스이지만 기본형처럼 new 없이 리터럴로 생성할 수 있습니다. 자바는 자주 쓰이는 String을 위해 문자열 상수 풀(String Constant Pool) 이라는 특별한 메모리 공간을 운영합니다.
public class StringComparison {
public static void main(String[] args) {
// 리터럴 방식: String Pool 사용 (같은 내용이면 같은 객체 공유)
String s1 = "hello";
String s2 = "hello";
// new 방식: 매번 새 객체 생성 (힙에 별도 저장)
String s3 = new String("hello");
String s4 = new String("hello");
// == : 주소(참조) 비교
System.out.println(s1 == s2); // true (같은 Pool 객체)
System.out.println(s1 == s3); // false (다른 객체)
System.out.println(s3 == s4); // false (다른 new 객체)
// .equals() : 내용(값) 비교 ← 문자열 비교는 항상 이걸 사용!
System.out.println(s1.equals(s2)); // true
System.out.println(s1.equals(s3)); // true
System.out.println(s3.equals(s4)); // true
}
}
경고
String 비교 시 반드시 .equals()를 사용하세요!==은 주소를 비교하므로 예상치 못한 결과가 나올 수 있습니다.
6. null과 NullPointerException
null은 참조형 변수가 아무 객체도 가리키지 않는 상태입니다.
public class NullExample {
public static void main(String[] args) {
String name = null; // name은 아무것도 가리키지 않음
System.out.println(name); // null (출력은 가능)
// null인 변수의 메서드를 호출하면 NullPointerException 발생!
try {
int length = name.length(); // 에러!
} catch (NullPointerException e) {
System.out.println("NullPointerException 발생!");
System.out.println("null인 변수의 메서드를 호출할 수 없습니다.");
}
// null 체크 방법
if (name != null) {
System.out.println("길이: " + name.length());
} else {
System.out.println("name이 null입니다.");
}
}
}
팁
Java 14+부터 NullPointerException 메시지가 개선되어 어떤 변수가 null인지 명확히 알려줍니다.
7. 래퍼 클래스 (Wrapper Class)
기본형에는 각각 대응하는 래퍼 클래스가 있습니다. 기본형을 객체로 다뤄야 할 때(컬렉션, 제네릭 등) 사용합니다.
public class WrapperExample {
public static void main(String[] args) {
// 기본형 → 래퍼 클래스 (Boxing)
int primitiveInt = 42;
Integer wrappedInt = Integer.valueOf(primitiveInt); // 명시적 Boxing
Integer autoBoxed = primitiveInt; // 자동 Boxing (Java 5+)
// 래퍼 클래스 → 기본형 (Unboxing)
int unboxed = wrappedInt.intValue(); // 명시적 Unboxing
int autoUnboxed = wrappedInt; // 자동 Unboxing
// 래퍼 클래스의 유용한 메서드
System.out.println(Integer.MAX_VALUE); // 2147483647
System.out.println(Integer.MIN_VALUE); // -2147483648
System.out.println(Integer.toBinaryString(255)); // 11111111
System.out.println(Integer.parseInt("100")); // 100 (String → int)
System.out.println(Double.parseDouble("3.14")); // 3.14 (String → double)
}
}
8. Object 클래스 - 모든 클래스의 최상위 부모
자바의 모든 클래스는 Object 클래스를 자동으로 상속합니다. 즉 Object는 자바 클래스 계층 구조의 최상위입니다.
Object
├── String
├── Integer
├── ArrayList
├── Scanner
└── (모든 사용자 정의 클래스)
public class ObjectExample {
public static void main(String[] args) {
// Object 타입으로 모든 참조형 변수를 받을 수 있음
Object obj1 = "Hello"; // String을 Object로
Object obj2 = 42; // Integer를 Object로 (autoboxing)
Object obj3 = new int[]{1, 2, 3}; // 배열도 Object
// toString() - 모든 객체에서 사용 가능 (Object에서 상속)
System.out.println(obj1.toString()); // Hello
System.out.println(obj2.toString()); // 42
}
}
9. 상수 선언과 숫자 리터럴 가독성
public class LiteralReadability {
// 상수는 클래스 수준에서 static final로 선언하는 것이 일반적
static final double PI = 3.14159265358979;
static final int SECONDS_PER_DAY = 24 * 60 * 60; // 86400
public static void main(String[] args) {
// Java 7+ 숫자 언더바(_) 표기법 - 가독성 향상
long population = 8_000_000_000L; // 80억
int creditCardNum = 1234_5678_9012_3456; // 카드번호
double avogadro = 6.022_140_76e23; // 아보가드로 수
System.out.println("지구 인구: " + population);
System.out.println("하루 초: " + SECONDS_PER_DAY);
System.out.println("원주율: " + PI);
}
}
10. 실전 예제: 다양한 타입 변수 선언 및 출력
public class TypesDemo {
public static void main(String[] args) {
// 개인 정보
String name = "이자바";
int age = 28;
char gender = '남';
boolean isMarried = false;
// 신체 정보
double height = 175.5;
float weight = 68.3f;
// 금융 정보
long salary = 3_500_000L; // 월급 350만원
double taxRate = 0.033; // 세율 3.3%
// 계산
long tax = (long)(salary * taxRate);
long netSalary = salary - tax;
// 출력
System.out.println("===== 직원 정보 =====");
System.out.printf("이름: %s%n", name);
System.out.printf("나이: %d세%n", age);
System.out.printf("성별: %c%n", gender);
System.out.printf("결혼여부: %b%n", isMarried);
System.out.printf("키: %.1fcm%n", height);
System.out.printf("몸무게: %.1fkg%n", weight);
System.out.printf("월급: %,dL원%n", salary);
System.out.printf("세금 (%.1f%%): %,d원%n", taxRate * 100, tax);
System.out.printf("실수령액: %,d원%n", netSalary);
}
}
출력 결과:
===== 직원 정보 =====
이름: 이자바
나이: 28세
성별: 남
결혼여부: false
키: 175.5cm
몸무게: 68.3kg
월급: 3,500,000원
세금 (3.3%): 115,500원
실수령액: 3,384,500원
정리
- 자바 자료형: 기본형 8가지+ 참조형(무한)
- 기본형: 값을 직접 스택에 저장, 8가지 (boolean, char, byte, short, int, long, float, double)
- 참조형: 힙의 객체 주소를 스택에 저장 (String, 배열, 클래스 등)
- String 비교는 반드시
.equals()사용(==는 주소 비교) null: 참조형 변수의 초기 상태, 메서드 호출 시 NPE 발생- 래퍼 클래스: 기본형의 객체 버전 (Integer, Double 등)
- 모든 클래스는
Object를 상속