자바 String Literal, String Object? immutable?

2023. 11. 5. 18:23java

해당 글을 참고하여 작성했습니다.

 

[Java]String 리터럴(Literal), String 객체(Object)

Java에서 문자열을 생성하는 과정은 2가지 방법이 있습니다. 1. 문자열 리터럴(Literal)을 사용 public class Main { public static void main(String[] args) { String strLiteral1 = "TEST"; String strLiteral3 = "TEST"; String strLitera

developer-talk.tistory.com

 

Literal과 객체는 어디에 저장되고 관리되는가? 에 대한 답이 되었다.

new 연산자를 이용하여 String Object를 생성할 경우 메모리의 Heap 영역,

"" 로 Literal로 선언할 경우 String Contant Pool에 할당된다. (String Contant Pool 도 Heap 영역에 있다.)

equals vs ==

== 는 객체의 주소값을 비교하는 연산

equals는 문자열의 내용이 일치하는지 확인하는 연산

    public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        return (anObject instanceof String aString)
                && (!COMPACT_STRINGS || this.coder == aString.coder)
                && StringLatin1.equals(value, aString.value);
    }

자세히 살펴보면 주소값이 같은지 우선 확인하고 아니라면 3가지 조건이 모두 참인지 확인하여 두 문자열의 내용이 일치하는지 확인한다.

 

 

 

 

 

check point

1. 자바 string 더하기 연산은 어떻게 되는걸까? String을 조작하는 연산은 메모리를 사용할까?

public class Main {
    public static void main(String[] args) {
        String s1 = "hihi";
        String s2 = "hi" + "hi";
        if(s1==s2) {
            System.out.println("성공");
        }
    }
}

실행 결과로 "성공" 이 출력 된다.

주소값이 같은걸 보아 리터럴 + 리터럴  = 리터럴로 생성됨을 알 수 있다.

 

public class Main {
    public static void main(String[] args) {
        String s1 = "hihi";
        String s2 = "hi" + "hi";
        String s3 = new String("hi") + "hi";
        if(s1==s3) {
            System.out.println("성공");
        }
    }
}

실행 결과로 "성공" 이 출력되지 않는다.

따라서 리터럴 + String Object = Object로 새롭게 객체를 생성함을 알 수 있다. 

 

기존 String Object에서 더하기 연산을 하면 기존 객체에 문자만 추가할까? 아니면 계속해서 새로운 객체를 만들어 낼까?

public class Main {
    public static void main(String[] args) {
        String s1 = "hihi";
        String s2 = "hi" + "hi";
        String s3 = new String("hi");
        System.out.println(System.identityHashCode(s3));
        s3 = s3 + "hi";
        System.out.println(System.identityHashCode(s3));

    }
}

결과로 계속해서 새로운 객체를 만들어 낸다는 것을 알 수 있다.

이러한 동작 과정들은  immutable과 관련있다.

 

연산에 대한 추가적인 정보는 아래의 링크에서 얻을 수 있었다.

 

자바 String, StringBuilder 그리고 StringBuffer 성능 차이 비교

자바에서 String과 StringBuilder 그리고 StringBuffer의 차이는 무엇일까? 그리고 제일 빠른 연산 속도는 어떤 것일까?

madplay.github.io

 

StringBuilder와 StringBuffer는 최초에 문자 객체를 만들고 기존 객체에서 문자를 추가하는 형태로 동작된다.

따라서 기존의 연산보다 더 빠르다. 또한 StringBuilder와 StringBuffer의 차이는 동기화 여부에 있다고 한다.

StringBuilder 동기화 보장 x

StringBuffer 동기화 보장 o

동기화 여부는 멀티스레드 환경과 연관이 있는 키워드로 해당 게시글에선 더 자세히 말하지 않겠다. (궁금하면 race condition 알아보기)

위의 게시글에서 성능 비교 그래프를 확인할 수 있었는데 너무 유익한 게시글이었다.

 

또 해당 코드에서 기존 공간을 늘리기 위한 새로운 공간을 할당하는 과정에서 수행속도가 매우 큰 차이가 나게된다는 소개도 인상 깊었다.

 

 

 

2. 문자열 리터럴의 경우 어떻게 비교되는가?

Java Language Specification 문서를 보면 내부적으로 String의 intern 메소드를 호출함을 알 수 있다.

String 에서 intern은 위처럼 선언되어있고 native로 선언되어 내부에서 동작함을 알 수 있다.

 

 

literal은 immutable 이라는 특성을 가지고 있다.

예를 들어서

int a = 3;

a = 5; 

위의 코드를 작성한다면 literal 내부가 3에서 5로 바뀌는게 아니라 참조 주소를 5를 가리키도록 변경시킨다.

이 문제로 얕은 복사와 깊은 복사 문제가 생긴다.

객체들은 immutable 하지 않기 때문에 데이터가 바뀌면 그 속의 내용도 바뀌어버린다.

int a = 3;

a = 5; 

에서 a가 불변하지않다면 참조하는 주소가 바뀌는게 아니라 주소가 같은채로 3이 5로 바뀌어버리는 것과 같은 문제이다.

따라서 immutable하지 않는 객체들에 관해 얕은 복사와 깊은 복사 문제를 구분해야한다.

 

이러한 내용들은 Cloneable 인터페이스에 clone 추상 메소드가 깊은 복사를 담당하고 있고 Cloneable 인터페이스를 상속받는다면 깊은 복사 기능이 있다. (Object clone과 구분해야한다. 또 만약 Cloneable 를 상속받으면 오버라이딩하고 직접 하나하나 구현해야한다. 그렇지않으면 Object clone의 얕은 복사와 다를바없다.)

 

예로 ArrayList도 Cloneable를 상속받아 clone함수가 있고 clone을 통해 깊은 복사를 수행할 수 있다.

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

여기서 return type이 object라 형변환이 필요하다.

public class Main {
    public static void main(String[] args) {
        String[] fruitsName = {"apple", "banana"};
        ArrayList<String> fruits = new ArrayList<>(Arrays.asList(fruitsName));
        ArrayList<String> clone = (ArrayList<String>) fruits.clone();
    }
}

해당 방식으로 깊은 복사를 수행할 수 있다.

'java' 카테고리의 다른 글

함수형 프로그래밍 , 람다식  (0) 2023.11.05
자바 Arrays.asList  (0) 2023.11.05
자바 심화 체크리스트  (0) 2023.11.05
자바 collection 개념  (0) 2023.11.05
우테코 3회차 정리  (0) 2023.11.03