[Java] String 클래스

2025. 2. 7. 00:23·TIL/Java
  • 기본형을 통해서는 연속된 문자인 문자열을 어떻게 다루어야 할까?
  • char는 문자 하나 밖에 저장하지 못하므로 문자열을 저장하기 위해서는 char[]를 선언해서 연달아 저장해주어야 한다
public class StringMain{

    public static void main(String[] args) {
    	char[] charArr = {'H','e','l','l','o'};
        System.out.println(charArr);
    }   
}

// 실행결과
// Hello
  • 그렇지만 개발할 때 문자열은 굉장히 많이 사용되는 자료형이며 매번 배열을 통해서 문자열을 다루기란 쉽지 않다.
  • 따라서 자바에서는 String 클래스를 제공해서 문자열을 쉽게 다루도록 돕고 있다

 

String을 통해 문자열을 생성하는 방법

  • 쌍따옴표 사용 :  String str = "Hello";
  • 객체 생성 : String str = new String("Hello");
  • String은 클래스이므로 객체를 생성해서 저장해주어야 하지만 편의상 쌍따옴표로 문자열을 감싸주면 자바에서 자동으로 new String("Hello")와 같은 역할을 해준다 ( 실제로는 인스턴스를 바로 생성하지는 않고 성능 최적화를 위해 문자열 풀을 사용하긴 한다 ) 

 

String 클래스 구조

  • String 클래스는 대략 다음과 같이 생겼다
public final class String {
	
    // private final char[] value; // java 9버전 이전
	private final byte[] value;
	
	public String concat(String str) {...}
	...
}

속성 ( 필드 )

  • String 클래스의 필드인 value에는 문자열 값이 저장된다. 문자열은 사실 byte[]에 저장되는 것을 알 수 있다 (  java 9 버전 이전으로는 char[]를 사용했다가 9버전부터 메모리 최적화를 위해 byte를 사용한다 )
  • 즉 String 클래스는 char[]를 내부에 감추어 사용자가 문자열을 편리하게 사용할 수 있도록 만들어진 클래스이다

기능 ( 메서드 )

  • 따라서 String 클래스는 문자열과 관련한 다양한 메서드를 제공한다
  • 문자열 정보 조회, 문자열 비교, 문자열 검색, 문자열 조작 및 변환, 문자열 분할 및 조합, 기타 유틸리티에 활용할 수 있는 정말 많은 메서드가 존재한다
  • 양이 너무 많기 때문에 따로 정리해보도록 하겠다

 

String의 사용

String의 연산

  • String 은 클래스이므로 참조형이다
  • 따라서 원칙 상 String 변수끼리 + 연산이 불가능하지만 편의상 String 타입 변수끼리 + 연산을 실행하면 String.concat() 메서드를 사용하는 것과 같은 효과를 준다

String의 비교

  • String 클래스를 비교할 때는 == 을 통한 동일성 비교가 아니라 equals()를 통해서 동등성을 비교해야 한다
  • 물론 어느 객체든 == 비교보다는 equals() 를 활용해야하긴 하지만..
  • String 클래스에서 이미 Object.equals()를 오버라이딩 해두었기 때문에 바로 String 객체에서 equals() 메서드를 활용하게 되면 객체 내부의 문자열을 비교해서 동등성 비교 결과를 반환한다

문자열 리터럴, 문자열 풀

public class StringMain2 {

    public static void main(String[] args) {
        String str1 = new String("hello");
        String str2 = new String("hello");

        System.out.println("new String() == 비교 : " + (str1 == str2));
        System.out.println("new String() equals 비교 : " + (str1.equals(str2)));

        String str3 = "hello";
        String str4 = "hello";
        System.out.println("리터럴 == 비교 : " + (str3 == str4));
        System.out.println("리터럴 == equals 비교 : " + (str3.equals(str4)));

    }
실행 결과

new String() == 비교 : false
new String() equals 비교 : true
리터럴 == 비교 : true
리터럴 == equals 비교 : true
  • new 생성자를 통해서 String 객체를 생성하게 되면 당연히 새로운 객체가 생겨나므로 동일성 비교에서 false를 반환한다.
  • 그런데 " " 을 통해서 String 객체를 대입하게 되면 자바가 자동으로 new 생성자를 통해 매번 새로 인스턴스를 생성해주는 줄 알았는데 str3과 str4의 참조값이 같다. 왜 그럴까?
  • 리터럴을 통한 String 사용시 자바는 메모리 효율성과 성능 최적화를 위해서 "문자열 풀" 이라는 것을 사용한다
  • 자바가 처음 실행되는 시점에 문자열 리터럴을 활용한다면 문자열 풀에 해당 리터럴 문자열 인스턴스를 만들어서 미리 넣어둔다. 이 때 동일한 문자열에 대해서 인스턴스는 하나만 생성한다
  • 런타임 시점에 문자열 리터럴이 등장하면 문자열 풀에 이미 생성되어 있는 인스턴스의 참조값을 가져와서 변수에 대입한다

  • 따라서 사실은 리터럴이 나오면 new 생성자를 활용해 String 객체를 생성하는 것이 아니고, 문자열 풀에서 해당 문자열 인스턴스를 찾아 참조값을 가져오는 것이다
  • 그렇기 때문에 str3, str4는 모두 문자열 풀에 존재하는 인스턴스의 참조값을 반환받아서 공유 참조를 하고 있던 것이다
  • 문자열 풀 덕분에 동일한 리터럴을 사용하는 경우 메모리 사용을 줄이고, 매번 인스턴스를 생성하는 시간 또한 단축하여 성능 최적화를 할 수 있다

 

String 클래스 - 불변 객체

  • String은 왜 불변 객체로 생성되었을까?
  • 방금 자바는 문자열 풀을 통해서 메모리 최적화를 한다는 사실을 알게 되었다
  • 그런데 만약 String이 불변 객체가 아니라면 공유 참조하고 있는 변수 중 하나에서 문자열을 수정하게 된다면? 해당 문자열 객체를 참조하고 있던 모든 변수에 영향을 미치게 될 것이다
  • 따라서 String 클래스를 불변 객체로 만들게 된 것
  • String.concat() 메서드를 활용해서 두 문자열을 이어붙일 때에도 기존 두 문자열은 그대로 존재하고, 새로운 문자열 객체를 만들어서 반환하는 것이다

 

StringBuilder - 가변 String

  • 만약 1000만번 반복하며 String에 a를 한 번씩 이어붙여나간다면 어떨까?
  • 각 반복마다 새로운 문자열 객체를 생성해야할테고, 점점 길어지는 문자열 객체를 저장하며 메모리는 메모리대로 잡아먹고, 시간 또한 많이 소요될 것이다
  • 문자열을 자주 생성하거나 변경할 때 성능이 매우 떨어지게 된다
  • 따라서 자바는 String을 가변으로 활용할 수 있는 클래스인 StringBuilder를 제공한다

StringBuilder

  • String과 유사하지만 byte[]가 final로 선언되어 있지 않기 때문에 문자열을 수정하기에 용이하다
  • append () 메서드로 현재 문자열의 뒤에 새로운 문자열을 덧붙일 수 있다
  • insert ()  메서드로 특정 위치에 문자열을 삽입할 수 있는 등 여러가지 문자열과 관련한 메서드를 제공한다
  • StringBuilder를 활용해서 1000만번 반복하여 a를 하나씩 이어붙여나간다면 1000만번 반복 이후 단 한 번만 String 객체를 생성하면 되기 때문에 메모리적으로 성능적으로 최적화가 가능하다

StringBuilder 활용

  • String이 아니라 StringBuilder를 활용해서 문자열을 처리해야 하는 경우는 다음과 같다
  • 반복분에서 반복해서 문자열을 수정할 때
  • 조건문을 통해 동적으로 문자열을 조합할 때
  • 복잡한 문자열의 특정 부분을 변경해야 할 때
  • 매우 긴 대용량 문자열을 다룰 때

 

String 최적화

문자열 리터럴 최적화

  • 자바 컴파일러는 다음과 같이 문자열 리터럴을 더하는 부분을 자동으로 합쳐준다

문자열 리터럴 최적화

  • 컴파일 전
  • String helloWorld = “Hello” + “ World”;
  • 컴파일 후
  • String helloWorld = “Hello World”;
  • 따라서 런타임에 별도의 문자열 결합 연산을 수행하지 않아도 된다

String  변수 최적화

  • 문자열 변수의 경우 그 안에 어떤 값이 들어있는지 컴파일 시점에는 알 수 없기 때문에 단순히 합칠 수 없다
  • 이런 경우 StringBuilder를 활용해서 더해 최적화를 수행한다
    • 사실 간단한 문자열의 경우에는 위와 같이 최적화를 해주기 때문에 StringBuilder를 사용하지 않아도 괜찮다

 

'TIL > Java' 카테고리의 다른 글

[Java] 열거형 - enum  (0) 2025.02.09
[Java] 래퍼 클래스  (1) 2025.02.09
[Java] 불변 객체  (0) 2025.02.06
[Java] equals()  (0) 2025.02.05
[Java] toString()  (0) 2025.02.04
'TIL/Java' 카테고리의 다른 글
  • [Java] 열거형 - enum
  • [Java] 래퍼 클래스
  • [Java] 불변 객체
  • [Java] equals()
Charlie.han
Charlie.han
백엔드 개발자가 되기 위해 공부한 것들(Java, Spring, SQL, 네트워크, AWS 등)을 기록하고 공유하는 블로그입니다.
  • Charlie.han
    Chalie's Devlog
    Charlie.han
  • 전체
    오늘
    어제
    • 분류 전체보기 (32)
      • TIL (32)
        • Java (20)
        • Spring (2)
        • SQL (5)
        • 알고리즘 (4)
        • 트러블슈팅 (1)
      • WIL (0)
        • 카카오테크 부트캠프 (0)
  • 태그

    프로그래머스 베스트앨범 자바
    프로그래머스 소수찾기 자바
    불변객체
    데이터베이스
    인텔리제이 한글 깨짐
    프로그래머스
    jdk란?
    unsupported characters for the charset 'iso-8859-1'
    다형성
    인터페이스
    object
    묵시적형변환
    sql limit
    컬렉션프레임워크
    인텔리제이한글
    인스턴스
    SQL
    default메서드
    static메서드
    sql where
    제네릭 클래스
    string
    static 필드
    타입안전열거형
    프로그래머스 베스트앨범
    참조공유
    자바
    Java
    super()
    프로그래머스 소수찾기
  • 최근 글

  • hELLO· Designed By정상우.v4.10.3
Charlie.han
[Java] String 클래스
상단으로

티스토리툴바