자바

[JAVA] String Literal & String Object

화한 생명 2024. 5. 14. 17:16

현업에서 시스템 장애를 진단 작업을 하면서 어떤 객체가 가장 많이 생성되는지 점검 하는 경우 String 관련 객체는 몇백개 객체 중에서 상위 5개 안에 항상 포함된다고 한다. String 클래스에 대해 잘 알아야 하는 이유가 되겠다.

String 은 어떻게 생겼을까?

package java.lang;

public final class String // (1) 자식클래스 양산 불가
    implements java.io.Serializable, Comparable<String>, CharSequence,
               Constable, ConstantDesc  // (2)
  1. 자식클래스 양산 불가
    • 누구나 사용할 수 있는 클래스
    • 클래스가 final 로 선언되어 있으므로 더이상 이 클래스를 확장할 수 없다.
  2. 구현하는 인터페이스 목록
    • 인터페이스를 implements 한 클래스는 선언되어 있는 메소드의 몸통을 구현해야만 한다.

Serializable

  • 인터페이스는 구현해야 하는 메소드가 없는 특이한 인터페이스다.

⇒ 해당 객체를 파일로 저장하거나 다른 서버에 전송 가능한 상태가 된다.

Comparable

  • compareTo() 메소드 하나만 선언되어있다.
  • 매개변수로 넘어가는 객체와 현재 객체가 같은지 비교
  • 리턴 타입 int
    • 0: 같은 값일 때
    • -1 : 순서상으로 앞에 있을 때
    • 1: 순서 상으로 뒤에 있을 때
  • 객체의 순서 처리할 때 유용

CharSequence

  • 해당 클래스가 문자열을 다루기 위한 클래스임을 명시적으로 나타냄
  • StringBuilder, StringBuffer 도 CharSequence 를 다룬다.

String 선언하기

String 을 선언하는 방식은 크게 두가지가 있다.

하나는 기본 자료형을 선언 하듯이 초기화하는 방법, 다른 하나는 객체를 생성 하듯이 초기화 하는 방법이다.

String str="literal";
String str2 = new String("str object");

두 방식에 대해 자바는 구분하고 있다.

아래와 같은 코드를 실행한다고 생각해보자

    public void checkCompare(){
        String textA = "Check value";
        String textB = "Check value";
        if (textA == textB){
            System.out.println("textA == textB"); //출력
        } else {
		        System.out.println("textA == textB result is different.");
        }
        
        if(textA.equals("Check value")){
            System.out.println("textA.equals(textB) result is same"); //출력
        }
    }
    --------------------
[실행결과]
textA == textB
textA.equals(textB) result is same

String literal 로 선언한 두 변수는 각각 같은 값을 갖고 있어 서로 같은 것으로 인지된다.

그렇다면 아래와 같은 경우에도 변함이 없을까?

    public void checkCompare(){
        String textA = "Check value";
        String textC = new String("Check value");
        if (textA == textC){
            System.out.println("textA == textC");
        } else {
            System.out.println("textA == textC result is different.");
        }

        if(textC.equals("Check value")){
            System.out.println("textC.equals(textA) result is same");
        }
    }
    ---------------------
[실행결과]
textA == textC result is different.
textC.equals(textC) result is same

보기에는 같은 값을 갖고 있어서 결과는 달라지지 않을 것이라 생각하기 쉽다.

하지만 우리의 예상과는 다르게 ‘==’를 이용한 연산에서는 다르게 인식하고 있고, equals 연산에서는 같은 것으로 인식하고 있다.

그 이유는 간단하다.

자바에는 Constant Pool 이 존재하기 때문이다.

Constant Pool

  • 객체 재사용을 위한 공간
  • String 의 경우 Constant Pool 내부에 동일한 값을 갖는 객체가 있다면 해당 객체를 재사용한다.
  • new 를 활용해 String 을 선언하면 새로운 객체를 생성

각 선언 방식에 따른 저장 방식을 표현하면 아래와 같다.

따라서 첫번째 코드에서 literal로 선언한 textA와 textB 객체는 실제로 같은 객체이다.

반면 두번째 코드에서 literal과 객체로 생성한 textA 와 textC 는 서로 다른 객체이다.

String 객체를 생성하면 값이 같은 String 객체를 생성한다고 하더라도 Constant Pool 을 사용하는 것이 아닌 별도의 객체를 생성한다.

이를 통해 또 알 수 있는 점이 있다.

== 로 값을 비교하는 것은 변수의 저장된 주소를 기준으로 비교를 하는 것이고

equals 로 값을 비교하는 것은 해당 object 가 가진 값(대표하는 값)을 기준으로 비교하는 것이다.

그래서 문자열을 비교할 때 equals 메소드를 활용하는 것을 권장한다.