[Java] 기초 - 참조자료형, 문자열

이지연·2025년 12월 10일

개요

아래의 내용은 java_grammer 레파지토리 C01Basic 디렉터리에 저장되어있는 내용을 정리하였다.


참조자료형

기본형 vs 참조형

자바의 데이터 타입은 크게 기본형(Primitive Type)참조형(Reference Type) 으로 나뉨.

구분예시저장 위치(개념적으로)비교 방식저장되는 것
기본형 (Primitive Type)int, char, boolean 등스택 프레임에 값 자체 저장== 로 값 비교실제 값
참조형 (Reference Type)String, 배열, 클래스 등스택에는 주소, 실제 데이터는 힙(Heap)equals() 로 값 비교객체(주소)
  • 기본형 변수에는 값이 직접 들어가고, 같은 값이면 == 비교 시 true가 나옴.
  • 참조형 변수에는 힙에 있는 객체의 주소 가 저장되고, == 로 비교하면 주소 비교, equals() 를 써야 내부 값 비교가 됨.
int a = 10;
int b = 10;
System.out.println(a == b); // true

String s1 = new String("hello");
String s2 = new String("hello");
System.out.println(s1 == s2);      // false
System.out.println(s1.equals(s2)); // true

실제 JVM 구현에서는 “무조건 기본형=스택, 참조형=힙”이라고 딱 잘라 말할 수는 없지만, 학습 단계에서는 위와 같은 그림으로 이해하고 가면 됨.


문자열

String과 String Pool

String 은 참조 자료형이고, 내부적으로는 불변(immutable) 객체 로 설계되어 있음.
문자열을 생성하는 방식은 크게 두 가지가 있음.

String st1 = new String("Hello world"); // new 사용
String st2 = new String("Hello world");

String st3 = "Hello world"; // 리터럴
String st4 = "Hello world";
  • new String("...") : 호출할 때마다 새로운 String 객체를 힙에 생성함.
  • "Hello world" 리터럴 : String Pool(상수 풀) 에 저장되고, 같은 리터럴을 다시 쓰면 기존 풀에 있는 객체를 재사용함

그래서:

System.out.println(st1 == st2); // false (서로 다른 객체)
System.out.println(st3 == st4); // true (String Pool의 같은 객체 참조)
System.out.println(st1 == st3); // false
System.out.println(st1.equals(st3)); // true (문자열 내용은 같음)

일반적으로는 new 보다는 리터럴 방식이 메모리 재사용 측면에서 더 권장되는 패턴임.


Wrapper 클래스와 오토박싱

Wrapper 클래스 는 기본형 타입을 객체 형태로 감싼 참조형 타입임.

기본형Wrapper
byteByte
shortShort
intInteger
longLong
floatFloat
doubleDouble
charCharacter
booleanBoolean

Wrapper를 쓰는 대표적인 이유는 다음과 같음.

  • 컬렉션(List, Set, Map) 에는 기본형을 직접 넣을 수 없고, 참조형만 저장 가능
  • 형 변환/파싱 관련 유틸 메서드 제공 (예: Integer.parseInt, Double.parseDouble 등)

오토박싱 / 오토언박싱

자바는 기본형과 Wrapper 사이의 변환을 자동으로 해주는 기능을 제공함

  • 오토박싱 : 기본형 → Wrapper 자동 변환
  • 오토언박싱 : Wrapper → 기본형 자동 변환
int i1 = 10;
Integer i2 = i1;              // 오토박싱
int i3 = i2;                  // 오토언박싱

String s = Integer.toString(i1); // int → String
int n = Integer.parseInt(s);     // String → int

컬렉션과 함께 쓰일 때 특히 자주 보게 됨.

List<Integer> list = new ArrayList<>();
list.add(10); // int가 자동으로 Integer로 박싱되어 저장됨

배열에는 예외적으로 기본형을 직접 넣을 수 있지만, List 같은 컬렉션에는 반드시 Wrapper 타입을 사용해야 함.[3]


문자열 주요 메서드 (개념)

String 은 불변 객체라, 한 번 만들어진 값이 바뀌지 않고, 수정처럼 보이는 연산은 항상 새로운 String 객체 를 만들어냄.
자주 사용하는 주요 메서드의 “개념적인 용도”만 정리하면 아래와 같음.

메서드역할
equals()문자열 내용이 같은지 비교
equalsIgnoreCase()대소문자 구분 없이 내용 비교
length()문자열 길이(문자 개수) 반환
charAt(index)특정 인덱스 위치의 문자 반환
toCharArray()문자열을 char[] 배열로 변환
indexOf(str)앞에서부터 str 이 처음 나타나는 위치 반환, 없으면 -1
lastIndexOf(str)뒤에서부터 찾았을 때 위치 반환
contains(str)해당 문자열 포함 여부 boolean 반환
substring(begin, end)[begin, end) 구간의 부분 문자열 반환
trim() / strip()양쪽 끝 공백 제거 (Java 11부터 strip 권장)
toUpperCase() / toLowerCase()대문자/소문자로 변환 (원본은 그대로)
replace(a, b)a 문자열을 b 문자열로 치환
replaceAll(regex, b)정규표현식으로 매칭되는 부분을 b로 치환
split(delimiter)구분자를 기준으로 문자열을 잘라 String[] 반환

정규식 패턴 ([가-힣], [A-Za-z], \\s+ 등) 은 문자열 필터링/검증에서 자주 쓰이므로, 개념만이라도 익혀두면 좋음.


null, 빈 문자열, 공백

입력값 검증에서 자주 헷갈리는 세 가지 개념임.

  • null : 아무 객체도 가리키지 않는 상태 (참조 자체가 없음)
  • "" : 길이가 0인 문자열 (빈 문자열)
  • " " : 공백 문자 하나를 포함한 문자열

null 인 참조에 대해 메서드를 호출하면 NullPointerException 이 발생하지만, 빈 문자열/공백 문자열은 정상적인 String 객체라 메서드 호출이 가능함.

String case1 = null; // null은 문자열 아님
String case2 = ""; // 빈 문자열은 문자열
String case3 = " "; // 공백
System.out.println("null과 빈문자열 비교 : " + case1 == case2); // false
System.out.println("null 체크 : " + case1 == null); // true
// System.out.println(case1.isEmpty()); // error : NullPointerException
System.out.println("빈 문자열의 빈문자열 체크 : " + case2.isEmpty()); // true
System.out.println("공백의 빈문자열 체크 : " + case3.isEmpty()); // false(공백이 차있는 것)
System.out.println("빈문자열의 빈문자열 체크 : " + case3.isBlank()); // true

문자열 조합: String, StringBuffer, StringBuilder

String 은 불변이라 += 로 여러 번 이어 붙이면 매번 새로운 객체가 만들어져 성능이 나빠질 수 있음.
이 문제를 해결하기 위해 가변(mutable)한 문자열 버퍼StringBuffer, StringBuilder 를 사용함.

  • StringBuffer
    • 멀티스레드 환경에서 안전하게 동작하도록 동기화를 제공
    • 그만큼 오버헤드가 있어 상대적으로 느릴 수 있음
  • StringBuilder
    • 동기화 없음, 단일 스레드 기준 가장 빠름
    • 대부분의 일반적인 상황에서 선호됨

공통적으로 다음과 같은 메서드를 제공함.

  • append() : 문자열/문자/숫자를 뒤에 이어 붙임
  • insert(index, str) : 특정 위치에 문자열 삽입
  • deleteCharAt(index) : 특정 위치 문자 삭제
  • substring() : String과 비슷한 방식으로 부분 문자열 추출
  • reverse() : 문자열을 뒤집음

실제로 콘솔에 여러 줄 출력하기보다, StringBuilderStringBuffer 에 문자열을 모아서 한 번에 출력하는 게 I/O 호출 횟수도 줄고 성능도 더 좋음.


문자열 비교: compareTo

StringComparable<String> 을 구현하고 있어서 compareTo() 로 사전순 비교가 가능함.

  • 결과가 0이면 두 문자열이 같다는 뜻
  • 양수면 기준 문자열이 인자로 받은 문자열보다 사전순으로 뒤
  • 음수면 기준 문자열이 사전순으로 앞

내부적으로는 각 문자의 유니코드 값을 순서대로 비교해서 차이를 계산하는 방식이라, 정렬 구현이나 사전순 정렬 시에 활용할 수 있음.

String st1 = "hello";
String st2 = "dello";
String st3 = "hello";
String st4 = "hfllo";

System.out.println("compareTo 1->2 : "+st1.compareTo(st2)); //4
System.out.println("compareTo 2->1 : "+st2.compareTo(st1)); //-4
System.out.println("compareTo 1->3 : "+st1.compareTo(st3)); //0
System.out.println("compareTo 4->3 : "+st4.compareTo(st3)); //1
        
비교다른 위치문자1문자2ASCII 차이결과
st1 vs st20h(104)d(100)104-100+4
st2 vs st10d(100)h(104)100-104-4
st1 vs st3전체 동일---0
st4 vs st31f(102)e(101)102-101+1
profile
Eazy하게

4개의 댓글

comment-user-thumbnail
2025년 12월 11일

참조자료형 = 클래스기반이다를 기억하자

답글 달기
comment-user-thumbnail
2025년 12월 11일

배열과 리스트의 차이점은? 배열의 크기는 동적으로 변동할 수 없으나, 리스트는 동적으로 변할 수 있다!

답글 달기
comment-user-thumbnail
2025년 12월 11일

마무리 정리

  • 기본형은 값 자체를, 참조형은 힙 객체의 주소를 다루며, 참조형 비교에는 equals() 사용이 기본
  • String 은 불변이고, 리터럴은 String Pool을 통해 재사용되며, 반복 결합 시에는 StringBuilder/StringBuffer 가 효율적임.
  • Wrapper 클래스와 오토박싱/언박싱 덕분에 기본형도 컬렉션에서 자연스럽게 쓸 수 있음.
답글 달기
comment-user-thumbnail
2025년 12월 11일

저는 지금 머리가 null 상태입니다.

답글 달기