본문으로 건너뛰기
개발 머꼬
개발 노트Java
hohyeon.dev32

Java Integer를 ==로 비교했더니 결과가 오락가락하는 이유

  • #Autoboxing
  • #Common Pitfall
  • #Engineering Note
  • #Java

문제 발생

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

Integer c = 200;
Integer d = 200;
System.out.println(c == d); // false

같은 방식으로 만든 Integer인데 100은 ==가 true, 200은 false가 나와 당황하는 경우가 흔합니다.

원인 분석

Integer a = 100;처럼 primitive int를 Integer에 대입하면 오토박싱(autoboxing)이 일어나며, 내부적으로 Integer.valueOf(100)이 호출됩니다. Integer.valueOf()는 -128부터 127까지의 값을 미리 캐싱해두고, 이 범위 안의 값은 항상 같은 캐시된 객체를 반환합니다. 그래서 100은 같은 객체를 참조해 ==가 true가 됩니다.

하지만 200은 캐시 범위를 벗어나므로 valueOf()가 호출될 때마다 새 Integer 객체를 생성합니다. 겉보기엔 값이 같아도 서로 다른 객체이므로 ==(참조 비교)는 false입니다.

==는 객체의 참조를 비교하고, equals()는 객체가 정의한 값을 비교합니다. Integer.equals()는 값을 비교하도록 오버라이드되어 있으므로 항상 올바른 결과를 줍니다.

해결 방안

  1. 박싱된 타입(Integer, Long, Character 등)을 비교할 때는 항상 .equals()를 씁니다.
System.out.println(c.equals(d)); // true
  1. Objects.equals()를 쓰면 둘 중 하나가 null이어도 NullPointerException 없이 안전하게 비교할 수 있습니다.
System.out.println(Objects.equals(c, d));
  1. 가능하면 박싱 타입 대신 primitive 타입(int, long)을 쓰는 것이 이런 함정 자체를 피하는 가장 확실한 방법입니다. primitive는 ==가 항상 값 비교입니다.
  2. 캐시 범위(-128~127)에 의존하는 코드를 짜지 않습니다 — 이 범위는 JVM 구현에 따라 늘어날 수 있지만(-XX:AutoBoxCacheMax 같은 옵션), 그 범위 자체에 의존하는 로직은 언제든 깨질 수 있는 우연한 동작입니다.

공식 문서

마지막 수정

좋아요북마크

댓글0

아직 댓글이 없어요. 첫 의견을 편하게 남겨 보세요.