[TIL-260204] 자바 기초: 메모리 영역과 문자열 데이터 타입

2026. 2. 16. 19:28·Java
이 글은 2026년 2월 4일에 작성된 글입니다.

데이터 타입

자바의 데이터 타입은 크게 기본타입(원시타입 - primitive type)과 참조 타입(reference type)으로 분류된다.

  • 기본타입
    • byte, char, short, int, long, float, double, boolean
    • 실제 값을 변수에 저장함
  • 참조타입
    • 배열, 열거, 클래스, 인터페이스
    • 메모리의 주소를 값으로 저장함 (메모리 주소를 통해 객체를 참조)

 


메모리 영역

JVM 메모리 구조는 다음과 같다.

모든 구조를 정확히 알면 좋겠지만, 가장 중요하다고 느낀 JVM stack, Heap만 정리해보았다.

 

JVM Stack

  • 쓰레드마다 존재하며, 쓰레드 생성 시 같이 생성된다.
  • Stack Frame: 함수가 호출될 때, 그 함수만의 스택 영역을 구분하기 위하여 생성되는 공간. 함수 호출 시 할당되며 함수 종료되면 소멸함.
  • Stack Frame 내부에는 로컬 변수 스택이 있어 여기에 변수가 push되거나 pop됨.
  • 기본 타입 변수 - 스택 영역에 직접 값 가지고 있음
  • 참조 타입 변수 - 힙 영역이나 메소드 영역의 객체 주소를 가지고 있음

Heap

  • 클래스의 인스턴스 (객체)와 배열이 할당되는 영역
  • 힙 영역에서 생성된 객체와 배열은 스택 영역의 변수나 다른 객체의 필드에서 참조함 (위 사진 참고).
  • Heap과 Method area는 전체 쓰레드가 공유하는 데이터 영역이며 JVM이 시작될 때 생성되고 종료되면 해제된다.

 


String

String은 기본타입이 아닌 String 클래스의 객체이기 때문에 참조타입이다. 따라서 String을 이용해 문자열을 선언하는 경우, 그 변수에 값이 직접 저장되는 게 아니라 String 객체의 주소가 저장된다.

String 변수 선언 방법

1. 문자열 리터럴
자바는 문자열 리터럴이 동일하면 String 객체를 공유한다. 즉 같은 주소를 사용하는 것.

 public class StringCompare {
     public static void main(String[] args) {
         String name1 = "kim";
         String name2 = "kim";
         if (name1 == name2) // 주소를 비교함
             System.out.println("name1과 name2는 참조가 같다"); // 출력됨
         else
             System.out.println("name1과 name2는 참조가 다르다");

         if (name1.equals(name2)) // equals는 단순 문자열 비교
             System.out.println("name1과 name2는 문자열이 같다");
         }
 }

 

2. new 연산자 사용
new 연산자를 사용하여 직접 String 객체를 사용하면 새로운 객체를 생성하게 된다.

public class StringCompare {
 	public static void main(String[] args) {
 		String name3 = new String("kim");
 		String name4 = new String("kim");
 		if (name3 == name4)
 			System.out.println("name3과 name4는 참조가 같다");
 		else
 			System.out.println("name3과 name4는 참조가 다르다"); // 출력됨
 		
 		if (name3.equals(name4))
 			System.out.println("name3과 name4는 문자열이 같다");
 	}
 
 }

 

불변(immutable) 자료형

기본적으로 자바에서 String 객체의 값은 변경할 수 없다.

String name = "hello";
name = name + " java";

위 코드의 경우 name 변수의 값을 업데이트 한 것처럼 보이지만, 실제로 메모리에는 새로 “hello java” 값을 저장한 영역을 따로 만들어서 담고 새로 참조하는 형태로 변경된다.

 


StringBuffer와 StringBuilder

그래서 문자열을 수정하고 싶을 때 쓰는 것이 바로 StringBuffer, StringBuilder 클래스다.

문자열 데이터를 다룬다는 점에서 String 객체와 같지만, 객체의 공간이 부족해지는 경우 버퍼의 크기를 유연하게 늘려주어 가변적이라는 차이점이 있다.

append(), delete() 등을 이용해 문자열 크기를 변경할 수 있다. 따라서 문자열의 추가, 수정, 삭제가 빈번하게 발생하는 경우 String보다 StringBuffer 혹은 StringBuilder를 사용하는 것이 더 낫다.

StringBuffer

  • String과는 달리 문자열 수정 가능.
  • 빠르게 문자열 다룰 수 있음.
    (안드로이드 같은 메모리 자원이 부족한 모바일 환경에서 문자열을 자주 변경할 경우 많이 사용됨)
  • 멀티 스레드에 동기화 기능 제공.

StringBuilder

  • StringBuffer와 동일한 기능
  • 다른 점은 멀티 스레드에 동기화 기능 제공되지 않는다는 것.
  • 멀티 스레드 환경이 아니라면 StringBuilder를 사용하는 것이 좋음.

 


오늘의 문제 해결

🔴 문제

예제 문제를 풀어보던 중 실행은 잘 되지만 원하는 대로 결과가 안 나오는 문제가 생겼다.
평균 점수를 가지고 학점을 구하는 문제인데 Scanner로 input을 받아서 “keep” 이면 학점을 구하고 "quit" 이면 반복문을 종료하는 문제였다.

package basic;

import java.util.Scanner;

public class Ex12 {

    public static void main(String[] args) {
        double average = 40;
        Scanner input = new Scanner(System.in);

        for (;;) {
            String text = input.next();
            if (text=="keep"){
                System.out.println(average + "점의 학점을 구합니다.");
            } else if (text=="quit") {
                break;
            } else {
                System.out.println("keep, quit 중 하나를 입력하세요.");
                continue;
            }

            if (average >= 95) {
                System.out.println("학점은 A+입니다.");
            } else if (average >= 90) {
                System.out.println("학점은 A입니다.");
            } else if (average >= 85) {
                System.out.println("학점은 B+입니다.");
            } else if (average >= 80) {
                System.out.println("학점은 B입니다.");
            } else if (average >= 70) {
                System.out.println("학점은 C입니다.");
            } else if (average >= 60) {
                System.out.println("학점은 D입니다.");
            } else
                System.out.println("학점은 F입니다.");

            average += 20;
        }
        input.close();
    }
}

처음엔 이렇게 짰는데 아무리 keep이나 quit를 입력해도 계속 else문으로 넘어가서 keep, quit 중 하나를 입력하세요.가 출력되었다.

🔎 시도

사실 이 문제는 String에 대해 정확히 알지 못한 상태에서 풀었기 때문에 생긴 오류였다.

String은 new 생성자를 사용해 객체를 생성하면 힙 영역에 공간이 따로 할당하는 특징이 있었다. 그런데 Scanner를 사용해 문자열을 입력 받을 때도 마찬가지였다.

Scanner를 사용해 입력을 받을 때도 new 생성자로 생성한 객체와 같이 따로 힙 영역에 객체 공간을 할당하기 때문에 text=="keep" 이라는 코드에서 text가 뭐든 당연히 false 가 나올 수 밖에 없었던 것이다. 그냥 문자열 리터럴 “keep”과 new String(”keep”)은 다르기 때문이다.

✅ 해결

그래서 == 비교 연산이 아닌 문자열 자체를 비교하는 equals() 를 사용하기로 했다. 혹시나 대문자로 입력할 경우를 대비해서 equalsIgnoreCase() 메서드를 사용하였다.

import java.util.Scanner;

public class Ex12 {
    public static void main(String[] args) {
        double average = 40;
        Scanner input = new Scanner(System.in);

        for (;;) {
            String text = input.next();
            if (text.equalsIgnoreCase("keep")){
                System.out.println(average + "점의 학점을 구합니다.");
            } else if (text.equalsIgnoreCase("quit")) {
                break;
            } else {
                System.out.println("keep, quit 중 하나를 입력하세요.");
                continue;
            }

            if (average >= 95) {
                System.out.println("학점은 A+입니다.");
            } else if (average >= 90) {
                System.out.println("학점은 A입니다.");
            } else if (average >= 85) {
                System.out.println("학점은 B+입니다.");
            } else if (average >= 80) {
                System.out.println("학점은 B입니다.");
            } else if (average >= 70) {
                System.out.println("학점은 C입니다.");
            } else if (average >= 60) {
                System.out.println("학점은 D입니다.");
            } else
                System.out.println("학점은 F입니다.");

            average += 20;
        }
        input.close();
    }
}

💡 알게된 점

확실히 기본기가 중요하다는 걸 느꼈다. 대충 알고 있는 지식으로는 뜻하지 않는 부분에서 오류가 나기 마련이다. 코딩테스트 문제를 잘 풀기 위해서 자바 기초부터 차근차근 공부해야겠다고 생각했다.

'Java' 카테고리의 다른 글

[TIL-260209] 자바 기초: static, 상속, 추상 클래스  (0) 2026.02.17
[TIL-260206] 자바 기초: 메소드와 생성자  (0) 2026.02.17
[TIL 260205] 자바 기초: 배열과 ArrayList, Map, 그리고 클래스  (0) 2026.02.17
[TIL-260203] 자바 기초: 조건문과 반복문  (0) 2026.02.16
[TIL-260202] 클린 코드와 리팩토링  (0) 2026.02.16
'Java' 카테고리의 다른 글
  • [TIL-260206] 자바 기초: 메소드와 생성자
  • [TIL 260205] 자바 기초: 배열과 ArrayList, Map, 그리고 클래스
  • [TIL-260203] 자바 기초: 조건문과 반복문
  • [TIL-260202] 클린 코드와 리팩토링
hee-on
hee-on
작은 기록을 모아 꾸준히 성장해 나가는 개발 기록 공간입니다💻
  • hee-on
    희온의 dev log
    hee-on
  • 전체
    오늘
    어제
    • 전체 글 (46)
      • About (2)
      • Java (15)
      • Spring (4)
      • Spring Boot (2)
      • Front-end (6)
      • 알고리즘 (6)
        • Do it 알고리즘 코딩테스트 (자바편) (4)
      • DB (7)
      • Git (1)
      • 개발 지식 (2)
      • 일상 || 잡담 (1)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    Servlet
    SpringBoot
    til
    소개
    백준
    JSP
    취준
    MVC
    코테
    알고리즘
    개발자
    SQL
    Spring
    깃허브 코파일럿
    Java
    백엔드
    react
    JavaScript
    db
    안티그래비티
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
hee-on
[TIL-260204] 자바 기초: 메모리 영역과 문자열 데이터 타입
상단으로

티스토리툴바