Java 변수와 타입 | 기본 타입, 참조 타입, 형변환

Java 변수와 타입 | 기본 타입, 참조 타입, 형변환

이 글의 핵심

Java 기본·참조 타입과 형변환, String·배열 사용 시 흔한 함정을 짚습니다. 리터럴 접미사와 동등 비교(== vs equals)까지 실무 관점으로 정리했습니다.

들어가며

Java는 정적 타입 언어(변수마다 타입을 미리 적어 두고, 소스를 바이트코드로 바꿀 때 검사하는 방식)이므로, 변수에는 컴파일러가 검사할 수 있는 타입(명찰)을 붙입니다. 기본형과 참조형을 구분해 두면 이후 컬렉션·객체와 연결하기 쉽습니다.


1. 기본 타입 (Primitive Types)

정수 타입

// byte: -128 ~ 127
byte b = 127;

// short: -32,768 ~ 32,767
short s = 32767;

// int: -2,147,483,648 ~ 2,147,483,647 (기본)
int i = 2147483647;

// long: -9,223,372,036,854,775,808 ~ 9,223,372,036,854,775,807
long l = 9223372036854775807L;  // L 접미사 필수

// 언더스코어 사용 가능 (가독성)
int million = 1_000_000;
long billion = 1_000_000_000L;

정수 타입 선택 가이드: 일반 비즈니스 로직에서는 int가 기본입니다. 파일 크기·타임스탬프처럼 범위가 큰 값은 long, 메모리가 극도로 제한된 바이너리 포맷이나 대량 배열에서는 byte/short를 고려합니다. L 접미사를 빼먹으면 리터럴이 int로 처리되어 범위를 넘을 때 컴파일 에러가 납니다.

실수 타입

// float: 32비트 부동소수점
float f = 3.14f;  // f 접미사 필수

// double: 64비트 부동소수점 (기본)
double d = 3.14159;
double scientific = 1.23e-4;  // 0.000123

부동소수점 주의: floatdouble은 이진 부동소수점이라 0.1 + 0.2 같은 식이 기대와 다르게 보일 수 있습니다. 그래도 지리·그래픽·통계 전처리 등에서는 여전히 널리 쓰이며, 정확한 십진 연산이 필요하면 BigDecimal로 전환하는 것이 안전합니다.

문자 타입

char c = 'A';
char korean = '가';
char unicode = '\u0041';  // 'A'

// char는 16비트 유니코드

불리언 타입

boolean flag = true;
boolean isActive = false;

// 조건식 결과
boolean isAdult = age >= 18;

boolean과 널: 기본 타입 booleannull을 가질 수 없지만, 래퍼 Booleannull이 가능해 NPE(NullPointerException) 위험이 생깁니다. API 설계 시 “세 가지 상태(참/거짓/미정)”가 필요하면 Optional<Boolean>·별도 enum을 검토하세요.


2. 참조 타입 (Reference Types)

String

// 리터럴 (String Pool)
String name1 = "홍길동";
String name2 = "홍길동";
System.out.println(name1 == name2);  // true (같은 객체)

// new 키워드
String name3 = new String("홍길동");
System.out.println(name1 == name3);  // false (다른 객체)
System.out.println(name1.equals(name3));  // true (값 비교)

== 와 equals: 리터럴로 만든 동일 문자열은 풀에서 재사용될 수 있어 ==가 true로 나올 수 있지만, **일반적인 비교는 항상 equals**가 안전합니다. 특히 사용자 입력·DB에서 읽은 문자열은 ==에 의존하면 버그로 이어지기 쉽습니다.

배열

// 선언 및 초기화
int[] numbers = {1, 2, 3, 4, 5};
int[] arr = new int[5];

// 접근
System.out.println(numbers[0]);  // 1
numbers[0] = 10;

// 길이
System.out.println(numbers.length);  // 5

// 다차원 배열
int[][] matrix = {
    {1, 2, 3},
    {4, 5, 6}
};
System.out.println(matrix[0][1]);  // 2

배열 실무 노트: Java 배열은 크기가 고정이라, 동적 증가가 잦은 경우 ArrayList 등 컬렉션이 낫습니다. 다차원 배열은 “배열의 배열”이라 행마다 길이가 달라도 되지만, 직사각형이 아닌 구조는 루프 작성 시 주의가 필요합니다.


3. 형변환 (Type Casting)

자동 형변환 (Widening)

// 작은 타입 → 큰 타입 (자동)
byte b = 10;
int i = b;      // OK
long l = i;     // OK
float f = l;    // OK
double d = f;   // OK

// 순서: byte → short → int → long → float → double

명시적 형변환 (Narrowing)

// 큰 타입 → 작은 타입 (명시적)
double d = 3.14;
int i = (int) d;  // 3 (소수점 버림)

long l = 1000L;
int i2 = (int) l;

// 주의: 데이터 손실 가능
int big = 130;
byte small = (byte) big;  // -126 (오버플로우)

4. 래퍼 클래스 (Wrapper Classes)

기본 타입 vs 래퍼 클래스

기본 타입래퍼 클래스
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

오토박싱/언박싱

Java 5부터 기본 타입과 래퍼 클래스 간 자동 변환이 지원됩니다:

// 오토박싱 (Auto-boxing): 기본 타입 → 래퍼 클래스
Integer obj = 10;
// 내부 동작: Integer obj = Integer.valueOf(10);
// 기본 타입 int를 자동으로 Integer 객체로 변환

// 언박싱 (Unboxing): 래퍼 클래스 → 기본 타입
int primitive = obj;
// 내부 동작: int primitive = obj.intValue();
// Integer 객체를 자동으로 int로 변환

// 자동 변환 예시
Integer a = 10;  // 오토박싱
Integer b = 20;  // 오토박싱
Integer sum = a + b;  // 언박싱 → 연산 → 오토박싱
// 내부 동작:
// 1. a.intValue() + b.intValue() (언박싱)
// 2. 10 + 20 = 30 (int 연산)
// 3. Integer.valueOf(30) (오토박싱)

// 컬렉션에서 자동 변환
List<Integer> numbers = new ArrayList<>();
numbers.add(10);  // 오토박싱: int → Integer
int first = numbers.get(0);  // 언박싱: Integer → int

// 주의: null 언박싱 시 NullPointerException
Integer nullValue = null;
// int x = nullValue;  // NullPointerException!
// null을 기본 타입으로 변환할 수 없음

// 안전한 처리
Integer value = getValue();  // null 가능성
int result = (value != null) ? value : 0;  // null 체크

오토박싱 성능 주의:

// ❌ 나쁜 예: 반복문에서 오토박싱
Integer sum = 0;
for (int i = 0; i < 1000; i++) {
    sum += i;  // 매 반복마다 언박싱 + 박싱 발생
}
// 1000번의 불필요한 객체 생성

// ✅ 좋은 예: 기본 타입 사용
int sum = 0;
for (int i = 0; i < 1000; i++) {
    sum += i;  // 기본 타입 연산 (빠름)
}
Integer result = sum;  // 마지막에 한 번만 박싱

래퍼 클래스 메서드

// 문자열 → 숫자
int num = Integer.parseInt("123");
double d = Double.parseDouble("3.14");

// 숫자 → 문자열
String str = Integer.toString(123);
String str2 = String.valueOf(123);

// 비교
Integer a = 100;
Integer b = 100;
System.out.println(a.equals(b));  // true

// 최대/최소값
System.out.println(Integer.MAX_VALUE);  // 2147483647
System.out.println(Integer.MIN_VALUE);  // -2147483648

5. var (Java 10+)

// 타입 추론
var name = "홍길동";        // String
var age = 25;              // int
var price = 19.99;         // double
var list = new ArrayList<String>();

// 주의: 초기화 필수
// var x;  // 에러!

6. 실전 예제

예제 1: 타입 변환

public class TypeConversion {
    public static void main(String[] args) {
        // 문자열 → 숫자
        String input = "123";
        int num = Integer.parseInt(input);
        
        // 안전한 변환
        try {
            int result = Integer.parseInt("abc");
        } catch (NumberFormatException e) {
            System.out.println("변환 실패");
        }
        
        // 숫자 → 문자열
        int value = 123;
        String str = String.valueOf(value);
        String str2 = Integer.toString(value);
    }
}

예제 2: 배열 처리

public class ArrayExample {
    public static void main(String[] args) {
        int[] scores = {85, 92, 78, 95, 88};
        
        // 합계
        int sum = 0;
        for (int score : scores) {
            sum += score;
        }
        
        // 평균
        double average = (double) sum / scores.length;
        System.out.println("평균: " + average);
        
        // 최대값
        int max = scores[0];
        for (int score : scores) {
            if (score > max) {
                max = score;
            }
        }
        System.out.println("최대값: " + max);
    }
}

정리

핵심 요약

  1. 기본 타입: byte, short, int, long, float, double, char, boolean
  2. 참조 타입: String, 배열, 객체
  3. 형변환: 자동 (Widening), 명시적 (Narrowing)
  4. 래퍼 클래스: Integer, Double 등
  5. 오토박싱: 자동 변환

다음 단계

  • Java 클래스와 객체
  • Java 컬렉션
  • Java Stream API

관련 글

  • C++ 변수와 자료형 | int, double, string 완벽 정리 [초보자용]
  • Kotlin 변수와 타입 | val, var, 기본 타입 완벽 정리
  • Swift 변수와 타입 | var, let, 옵셔널
  • C++ numeric_limits |
  • C++ 템플릿 |