String은 왜 대문자일까?

DALLAE·2025년 4월 13일

개발 지식

목록 보기
2/3

Immutable String in Java

부트캠프에서 칠판에 설명하는 그룹스터디를 하고 있다.
이번주 발표 주제 소개!

String은 왜 대문자일까?”

int, double과 같은 기본형 타입은 모두 소문자다.

하지만 String은 유일하게 대문자로 시작한다. 왜 그럴까?

오늘은 이 작은 호기심으로부터 시작된, Java에서의 문자열(String)에 대해 다뤄보겠다.


📦 1. 자바의 데이터 타입 분류

자바에서 데이터 타입은 크게 두 가지로 나뉜다.

  • 기본형 타입 (Primitive Types) 예: int, double, char, boolean 등 → 값이 스택 메모리에 직접 저장된다.
  • 참조형 타입 (Reference Types) 예: String, 배열, 사용자 정의 클래스 등 → 변수에는 주소값(참조값) 이 저장되고, 실제 데이터는 힙 메모리에 저장된다.

🛟 2. 자바의 메모리 구조

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("달래");
    }
}

코드를 실행하면 자바는 메모리를 다음처럼 구분해서 사용한다:

메서드 영역 (Method Area)

  • 클래스에 대한 정보(예: Dog, Main, 그 안의 메서드들)가 저장
  • 프로그램 실행 내내 유지되고 모든 곳에서 공유
  • Dog 클래스 정의, Main 클래스 정의
  • Dog(String name) 생성자 메서드, main() 메서드 정보

스택 (Stack)

  • 지역 변수, 매개변수, 참조값이 저장
  • 메서드가 호출될 때마다 생성되고 끝나면 사라짐 (Last In First Out 구조)
  • main() 메서드의 스택 프레임이 생김 → int age = 3 → 스택에 3 저장 → Dog d = ... → 참조값(주소)이 저장됨 (실제 객체는 힙에 있음)

힙 (Heap)

  • new 키워드로 생성한 객체의 실제 데이터(값) 가 저장되는 공간
  • 가비지 컬렉터가 사용하지 않는 객체를 자동으로 제거
  • new Dog("달래") → Dog 객체가 힙에 생성
  • "달래"도 문자열 리터럴이므로 String Pool에 들어가고, 그 주소가 Dog 객체의 name 필드에 저장

🧀 3. 참조형 String

String s = "현정";

이 코드는 "현정"이라는 문자열 객체가 힙 메모리에 생성되고, 변수 s는 그 주소를 갖게 된다.

String참조형 타입이기 때문에 값이 아닌 주소를 변수에 저장한다.


🌟 4. 특별한 공간, ⭐️String Pool⭐️

문자열은 자바에서 가장 많이 쓰이는 타입 중 하나다.

매번 새로운 객체를 만들면 메모리 낭비가 발생하기 때문에

자바는 효율적인 메모리 관리를 위해 힙에 ✨String Pool✨이라는 공간을 제공한다.

String name = "달래";
String sameName = "달래";
  • 문자열 리터럴은 String Pool에 저장되고 동일한 값은 공유된다.
  • 위 두 변수는 같은 문자열 리터럴을 참조하기 때문에 name == sameNametrue를 반환한다.

🤔 5. new String()은 왜 다를까?

String name1 = new String("현정");
String name2 = new String("현정");
  • new 키워드로 인해 힙 메모리에 별도로 생성된 두 개의 문자열 객체가 생긴다.
  • 그래서 name1 == name2false다. 값은 같아도 주소가 다르기 때문이다.

🤯 6. 문자열은 불변(immutable)이라더니?

String name = "달래";
name = "현정"

문자열이 수정된 것처럼 보이지만 실제로는 "현정"이라는 새로운 객체가 생성되고, name이 그 객체를 참조하게 된다.

기존 "달래"변경되지 않고 그대로 남아있다. name"현정"을 바라보게 방향을 바꾼 것이다.

이처럼 String불변(immutable) 성질을 가지며, 한 번 만들어진 문자열 객체는 절대 수정되지 않는다.


🍣 7. 문자열 비교, == 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가 된다.

7-1. 🔥 리터럴은 등장만 해도 String Pool에 들어간다?

if (input.equals("exit")) { ... }

위 조건문을 보면 "exit"이라는 문자열이 변수에 할당되지 않았는데도 비교되고 있다.

그런데 이 "exit"은 실제로 String Pool에 저장된 리터럴이다.

💡

리터럴은 변수에 저장되지 않아도 코드에 등장하는 순간 컴파일 타임에 String Pool에 자동 등록된다.

즉 아래 두 가지 모두 "exit"을 String Pool에 등록시킨다:

String s = "exit"; // ✅ 당연히 등록
if (input.equals("exit")) { ... } // ✅ 변수에 저장 안 해도 등장만 하면 등록됨

7-2. ‼️💀 실전에서는 무조건 .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()를 반드시 써야 한다

💡 8. 결론: String은 왜 대문자인가?

String은 자바에서 제공하는 클래스이기 때문에 대문자로 시작한다.

자바에서 클래스, 인터페이스 같은 참조형 타입은 관례적으로 대문자로 표기한다.


✏️ 9. 요약

  • String참조형 타입이다 → 대문자
  • 문자열은 불변이다 → 새로운 값이 들어오면 새 객체가 생성된다.
  • 문자열 리터럴은 코드에 등장하는 순간 String Pool에 자동 등록되어 공유된다.
  • 문자열 비교는 ==보다는 .equals() 사용을 권장한다.
profile
안녕!!

0개의 댓글