부트캠프에서 칠판에 설명하는 그룹스터디를 하고 있다.
이번주 발표 주제 소개!
String은 왜 대문자일까?”int, double과 같은 기본형 타입은 모두 소문자다.
하지만 String은 유일하게 대문자로 시작한다. 왜 그럴까?
오늘은 이 작은 호기심으로부터 시작된, Java에서의 문자열(String)에 대해 다뤄보겠다.
자바에서 데이터 타입은 크게 두 가지로 나뉜다.
int, double, char, boolean 등 → 값이 스택 메모리에 직접 저장된다.String, 배열, 사용자 정의 클래스 등 → 변수에는 주소값(참조값) 이 저장되고, 실제 데이터는 힙 메모리에 저장된다.class Dog {
String name;
Dog(String name) {
this.name = name;
}
}
public class Main {
public static void main(String[] args) {
int age = 3;
Dog d = new Dog("달래");
}
}
코드를 실행하면 자바는 메모리를 다음처럼 구분해서 사용한다:
Dog, Main, 그 안의 메서드들)가 저장Dog 클래스 정의, Main 클래스 정의Dog(String name) 생성자 메서드, main() 메서드 정보main() 메서드의 스택 프레임이 생김 → int age = 3 → 스택에 3 저장 → Dog d = ... → 참조값(주소)이 저장됨 (실제 객체는 힙에 있음)new 키워드로 생성한 객체의 실제 데이터(값) 가 저장되는 공간new Dog("달래") → Dog 객체가 힙에 생성"달래"도 문자열 리터럴이므로 String Pool에 들어가고, 그 주소가 Dog 객체의 name 필드에 저장StringString s = "현정";
이 코드는 "현정"이라는 문자열 객체가 힙 메모리에 생성되고, 변수 s는 그 주소를 갖게 된다.
String은 참조형 타입이기 때문에 값이 아닌 주소를 변수에 저장한다.
문자열은 자바에서 가장 많이 쓰이는 타입 중 하나다.
매번 새로운 객체를 만들면 메모리 낭비가 발생하기 때문에
자바는 효율적인 메모리 관리를 위해 힙에 ✨String Pool✨이라는 공간을 제공한다.
String name = "달래";
String sameName = "달래";
name == sameName은 true를 반환한다.new String()은 왜 다를까?String name1 = new String("현정");
String name2 = new String("현정");
new 키워드로 인해 힙 메모리에 별도로 생성된 두 개의 문자열 객체가 생긴다.name1 == name2는 false다. 값은 같아도 주소가 다르기 때문이다.String name = "달래";
name = "현정"
문자열이 수정된 것처럼 보이지만 실제로는 "현정"이라는 새로운 객체가 생성되고, name이 그 객체를 참조하게 된다.
기존 "달래"는 변경되지 않고 그대로 남아있다. name만 "현정"을 바라보게 방향을 바꾼 것이다.
이처럼 String은 불변(immutable) 성질을 가지며, 한 번 만들어진 문자열 객체는 절대 수정되지 않는다.
== vs .equals()String a = "달래";
a += "바보";
String b = "달래바보";
System.out.println(a == b); // false
System.out.println(a.equals(b)); // true
==는 주소값(참조값) 을 비교한다..equals()는 값 자체를 비교한다.a + " there"는 런타임에 새 객체를 만들어 힙에 저장하니까, a == b는 false가 된다.if (input.equals("exit")) { ... }
위 조건문을 보면 "exit"이라는 문자열이 변수에 할당되지 않았는데도 비교되고 있다.
그런데 이 "exit"은 실제로 String Pool에 저장된 리터럴이다.
리터럴은 변수에 저장되지 않아도 코드에 등장하는 순간 컴파일 타임에 String Pool에 자동 등록된다.
즉 아래 두 가지 모두 "exit"을 String Pool에 등록시킨다:
String s = "exit"; // ✅ 당연히 등록
if (input.equals("exit")) { ... } // ✅ 변수에 저장 안 해도 등장만 하면 등록됨
.equals() 써야 하는 이유다음과 같이 사용자 입력을 받아 비교하는 경우를 생각해보자:
아래 코드는 예상과 달리 실행되지 않을 수 있다.
입력값이 같아 보여도 "exit"은 String Pool에 들어있는 리터럴, input은 사용자 입력으로 힙에서 새로 만들어진 객체이기 때문이다. 즉, 값이 같아 보여도 주소값이 다르다.
때문에 .equals()를 써야 실제 문자열 내용을 비교해서 원하는 결과를 얻을 수 있다.
Scanner sc = new Scanner(System.in);
String input = sc.nextLine();
// false
if (input == "exit") {
System.out.println("종료합니다.");
}
// true
if (input.equals("exit")) {
System.out.println("정상 종료");
}
==는 주소값 비교 (객체가 같은지).equals()는 값 비교 (내용이 같은지).equals()를 반드시 써야 한다String은 왜 대문자인가?String은 자바에서 제공하는 클래스이기 때문에 대문자로 시작한다.
자바에서 클래스, 인터페이스 같은 참조형 타입은 관례적으로 대문자로 표기한다.
String은 참조형 타입이다 → 대문자==보다는 .equals() 사용을 권장한다.