자바

[JAVA] String에 대하여

Unipiz 2022. 7. 12. 10:04

자바의 문자열

자바에서 문자열을 선언해 보자.

String str = "abc123가나다"

큰 생각 없이 썼었던 문자열이지만 과연 내부적으로 어떻게 돌아갈것인가?

C언어로 문자열을 만든다고 생각하면, 기본적으로 'a','b','c','1','2','3','가','나','다' 각각의 문자를 나열한 배열이다.

자바에서도 내부적으로는 각각의 배열일 것인가 ? 하는 물음에 시작되었다.

 

 

Primitive Type

자바에서 원시타입은 byte,short,int,long,float,double,char,boolean,void 가 있다.

 

 

String은 무엇인가?

일반적으로 객체를 생성할 때는 new 키워드를 사용하는데 특이하게 String은 new 연산자가 아니라

바로 값을 할당할 수 있는데 이를 문자열 리터럴 이라 부른다.

String str = "abc123가나다";
String str2 = new String("abc123가나다2");

이 둘의 차이점을 메모리 관점에서 접근해보자.

 

new 연산자를 이용하면 보통의 객체들처럼 Heap영역에 할당되고,

리터럴 표현식은 String Constant Pool이라는 영역에 할당된다.

-> Java8 부터 Pool 자체가 Heap영역으로 옮겨지면서 GC의 대상으로 바꼈다.

 

String Constant Pool?

String str = "abc123가나다";
String str2 = new String("abc123가나다");
String str3 = "abc123가나다";
String str4 = new String("abc123가나다");
System.out.println(str==str2);
System.out.println(str==str3);
System.out.println(str==str4);
System.out.println("______________");
System.out.println(str.equals(str2));
System.out.println(str.equals(str3));
System.out.println(str.equals(str4));

== 연산자는 주소값을 비교하고

equals() 는 내부의 값을 비교한다.

그래서 출력은 false,true,false // true,true,true 를 반환한다.

리터럴 방식의 선언은 String C Pool 에서 관리하여 같은 값이라면 메모리상 같은 주소를 바라보게 관리하고 있다느짐과

new 연산자는 또 다른 메모리에 할당 시킨다는 것을 알 수 있다.

리터럴 선언한 경우 String 내부적으로 intern() 메서드를 호출한다.

 

String 클래스 최하단에 intern() 메서드를 확인 할 수 있는데,

이미 존재할 경우 주소값을 반환하고 없다면 새로 객체를 생성한 후 주소값을 반환한다고한다.

 

 

그렇다면 +연산자를 사용한 경우엔 어떨까 ?

String str = "abc123";
String str2 = "abc";
String str3 = "123";
String str4 = str2+str3;
String str5 = str2+"123";
String str6 = "abc123";
System.out.println(str==str4);
System.out.println(str==str5);
System.out.println(str==str6);
System.out.println(str4.equals(str)

false, false, true, true 반환한다.

즉 '+' 연산자 사용 시 새 주소 값을 할당 하고 있다.

 

>> 자바 문자열은 생성법에 따라 성능적인 차이가 날 수 있단것을 알게되었다.

     리터럴 문자열은 상수로서 불변한다. 즉 실제 구동 시에 JVM은 String C Pool을 확인하고 재사용하거나 만든다.

     불변하기에 thread-safe 하다.

     내부적으로 어떻게 동작하는지 이해하고 new 연산자보단 String C Pool을 이용하는 리터럴 방식을 사용하자.