개발 학습 실습실

이 사이트의 실습은 자바스크립트를 켜야 움직입니다. 아래는 같은 내용을 글로 적은 것입니다.

공부 › Java › 자주 쓰는 클래스

==와 equals

같은 값인데 ==는 왜 false가 나오나

==는 두 변수가 같은 객체를 가리키는지를, equals는 내용이 같은지를 비교합니다. 문자열과 Integer는 만드는 방법에 따라 같은 객체를 재사용하기도 하고 새로 만들기도 해서, 값이 같아도 ==의 결과가 달라집니다.

실행으로 대조 · JDK 21에서 모든 조합을 실행해 대조

이 실습에 나오는 개념

동일성과 동등성
동일성은 두 참조가 메모리상 같은 객체를 가리키는지를 뜻하며 == 연산자로 비교한다. 동등성은 두 객체가 논리적으로 같은 값을 가지는지를 뜻하며 equals() 메서드로 비교한다. 문자열이나 값 객체를 비교할 때 ==를 쓰면 내용이 같아도 false가 나올 수 있다.
String Pool(문자열 상수 풀)
JVM이 문자열 리터럴을 저장해 두고 같은 내용의 리터럴이 다시 나오면 기존 객체를 재사용하는 저장소다. 같은 문자열을 여러 번 만들지 않아 메모리를 아낀다. 이런 공유가 가능한 것은 String이 불변이기 때문이다. new String("a")처럼 생성하면 풀과 별개의 새 객체가 만들어진다.
박싱과 언박싱
기본형(int 등)을 래퍼 객체(Integer 등)로 바꾸는 것이 박싱, 그 반대가 언박싱이다. 컬렉션은 객체만 담을 수 있어 기본형을 넣으면 자동으로 박싱된다. 객체 생성과 변환 비용이 들고, null인 래퍼를 언박싱하면 NullPointerException이 발생한다.
불변 객체
생성된 뒤에는 상태가 바뀌지 않는 객체다. 값이 바뀌지 않으므로 여러 스레드가 동시에 사용해도 안전하고, 다른 곳에 넘겨도 변경될 걱정이 없으며, 캐싱하거나 해시 컬렉션의 키로 쓰기에 적합하다. 필드를 private final로 선언하고 상태를 바꾸는 메서드를 두지 않으며, 클래스를 final로 해 하위 클래스가 동작을 바꾸지 못하게 한다. 필드가 가변 객체(List, Date 등)를 가리키면 생성자와 getter에서 복사본을 주고받아(방어적 복사) 밖에서 내부를 바꾸지 못하게 한다.

이렇게 바꿔 보세요

처음 설정으로 실행하면

  1. String a = "hello": 문자열 리터럴은 String Pool에 하나만 만들어 둡니다. a는 풀에 있는 "hello" 객체를 가리킵니다.
  2. String b = new String("hello"): new는 무조건 새 객체를 만듭니다. 풀의 "hello"와 내용은 같지만 힙의 다른 자리에 있는 별개의 객체입니다.
  3. a == b: 두 변수가 가리키는 대상이 같은지 비교합니다. a는 객체 1, b는 객체 2를 가리키므로 false입니다.
  4. a.equals(b): 객체 안의 값을 비교합니다. 내용이 같으므로 true입니다.

실행 결과

코드

String a = "hello";
String b = new String("hello");

System.out.println(a == b);
System.out.println(a.equals(b));

이 모형의 전제

모든 조합의 == 결과는 이 PC의 JDK 21에서 같은 코드를 실행한 결과와 대조했습니다. Integer 캐시 범위 -128 ~ 127은 자바 언어 명세가 보장하는 최소 범위이며, JVM 옵션으로 상한을 늘릴 수 있습니다. String Pool의 위치는 JDK 7부터 힙 안이지만, 그림에서는 구분을 위해 따로 표시했습니다.

앞 실습: 전략 패턴 · 다음 실습: equals와 hashCode

이 실습에 나오는 개념

이 실습에서 보는 것

설정

코드


      
    

그림

실행 비교

이렇게 바꿔 보세요