본문 바로가기

자바

[JAVA] String Literal & String Object

현업에서 시스템 장애를 진단 작업을 하면서 어떤 객체가 가장 많이 생성되는지 점검 하는 경우 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 메소드를 활용하는 것을 권장한다.