자바생
article thumbnail
728x90

String을 비교할 때 왜 .equals()를 사용할까에 대해 궁금해하면서 공부를 하게됐다.

새로운 줄에서 + 연산

public class Main {
    public static void main(String[] args) throws IOException {
        String str1 = "a";

        str1 += "bc";

        String str2 = "abc";

        System.out.println(str1 == str2); //false

        System.out.println("str1 = " + str1); //abc
        System.out.println("str2 = " + str2); //abc

        String str3 = "a" + "bc";

        System.out.println(str2 == str3); //true
        System.out.println(str1 == str3); //false
        
        System.out.println("str1.getClass() = " + str1.getClass()); //String
        System.out.println("str2.getClass() = " + str2.getClass()); //String
        System.out.println("str3.getClass() = " + str3.getClass()); //String

    }
}
  • str1, str2, str3은 모두 "abc"이면서, String class이다.
  • 하지만 str2과 str3은 str1과 == 비교했을 때 false가 나온다.
  • 왜 str1만 다르다고 나올까?

String의 불변성

String을 공부하면서 한 번 생성된 String 객체는 변할 수 없다고 들었다. 

String str1 = "a";
System.out.println(str1); //a
str1 += "bc";
System.out.println(str1); //abc
  • 위와 같이 코드를 작성하면 str1이 변경이 된다
  • 변경이 되는데 왜 String을 불변이라 하는가?
  •  
  • 문자열이 바뀐 것이 아니라 "a"에 "bc"가 연결된 새로운 문자열이 추가로 생긴 것
  • 왜 새로운 문자열이 추가로 생긴 것인가??
  • "String constant pool"

String constant pool

String을 리터럴로 선언하게 된다면 string constant pool에 저장된다. 만약에 아래와 같은 코드를 짤 경우를 생각해보자.

String str1 = "a";
String str2 = "a";
System.out.print(str1 == str2); //true
  • str1는 new 를 이용하지 않고 리터럴 문자열로 선언
  • "a"는 String constant pool에 저장
  • str2 은 "a"를 리터럴 문자열로 선언
  • String constant pool에 "a"가 존재하는지 "intern()"메서드를 사용하여 찾음
    • intern 메서드란 리터럴 문자열이 저장되는 constant pool에 해당 문자열이 있는지 없는지 찾아주는 메서드!
  • "a"가 존재하기 때문에 str2는 str1이 참조하는 주소값을 참조
  • 따라서 == 비교시 true 반환

 

  • string constant pool 은 Heap에 위치
  • java7 이전 String constant pool은 Perm 영역에 존재했지만 7 이후엔 Heap 영역에 존재
  • 따라서 GC의 대상(어렵쓰,, "일반적"으론 아니라고 한다") 

Java 1.5 

String str1 = "a";

str1 += "bc";
  • JDK 1.5 버전 이후로 여러 줄로 String '+' 연산을 할 경우에는 매 줄마다(연산마다) 새로운 StringBuilder가 선언
  • append 메소드가 호출되어 문자열을 연결시킴
  • 새로운 객체를 반환해 String pool에 저장이 안 되고 heap에 저장
  • str2과 비교하면 false가 나온다. 

이와 마찬가지 경우로 str1.concat("bc")도 새로운 객체를 생성함. 따라서 비교 시 false가 나온다.


java 9

이번 주제에 대해 다시 궁금해져서 블로그 글을 읽으면서 코드를 작성하는 중에 궁금한 점이 생겼다.

아직도 StringBuilder에 의해 연산이 실행되고 있는지말이다.

다른 블로그를 통해 StringBuilder를 사용하지 않는다는 것을 보았고,

이번엔 내가 직접 눈으로 보고자, intellij에서 .class를 decompile하여 바이트 코드를 보았다.

단일문, for-loop에서의 연산일 경우로 나눴다.

 

String 연산 bytecode(단일문)

원래 코드

public class Main {
    public static void main(String[] args) throws IOException {
        String str1 = "a";
        str1 += "bc";
        String str2 = "a" + "bc";
        String str3 = "a".concat("bc");
    }
}
  • 결론부터 말하자면 str1, str2, str3을 각각 == 연산 실행시 모두 false가 나옴

바이트 코드를 통한 분석

  • str1
    • 바이트 코드를 보면 StringConcatFactory.makeConcatWithconstants를 이용하여 연산을 실행하는 것을 알 수 있음

  • str2
    • String constant pool 에 저장
  • str3
    • String의 concat() 을 통해 연산이 실행

 

  • str3은 concat 을 통해 연산을 실행하는데 왜 str2에 대해 == 연산 시 false가 나올까?
    • concat 코드를 보면 new String을 return 하므로 새로운 객체가 생성되어 false가 나오게 됨!!

  • 아래의 코드를 보면 str2와 str4가 같은 주소값을 참조하며, str3은 다른 주소를 참조한다!!
String str2 = "a" + "bc";
String str3 = "a".concat("bc");
String str4 = "abc";

System.out.println("string constant pool str2 = " + System.identityHashCode(str2));
System.out.println("concat str3 = " + System.identityHashCode(str3));
System.out.println("string constant pool str4 = " + System.identityHashCode(str4));
string constant pool str2 = 960604060
concat str3 = 186370029
string constant pool str4 = 960604060

 

String 연산 bytecode(for-loop)

원래 코드

String a = "a";

for (int i = 0; i < 10; i++) {
    a += "b";
}

System.out.println("a = " + a);

 

바이트 코드를 통한 분석

Code:
       0: ldc           #2                  // String a
       2: astore_1
       3: iconst_0
       4: istore_2
       5: iload_2
       6: bipush        10
       8: if_icmpge     24
      11: aload_1
      12: invokedynamic #3,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
      17: astore_1
      18: iinc          2, 1
      21: goto          5
      24: return

 

단일문과 같이 StringConcatFactory.makeConcatWithconstants 를 사용한다!

 

 


REFERENCES

java 1.5, 8 의 + 연산

where is String constant pool?

JAVA9에서의 String + 연산 총 정리

 

 

728x90

'Java' 카테고리의 다른 글

스트림  (0) 2022.01.04
==와 equals의 차이  (0) 2021.05.25
Object 메소드  (0) 2021.05.24
(TYPE) type -> String , String -> type(TYPE)  (0) 2021.05.24
Comparator & Comparable  (0) 2021.04.05
profile

자바생

@자바생

틀린 부분이 있다면 댓글 부탁드립니다~😀

검색 태그