기본형(Primitive)처럼 사용되는 String클래스에 대해서 알아보자.
1. String클래스 기본형 vs 참조형
int num = 10;
String str = new String("This is String");
`String 클래스`는 기본형(Primitive type)인 int
, double
, char
와 다르게 참조형이다(Reference type).
하지만 워낙 선언 자체를 리터럴을 이용한 방식을 많이 사용하기 때문에 기본형으로 헷갈리기 십상이다.
첫 글자가 소문자인 자료형은 기본형 타입밖에 없다. 고로 String은 참조 자료형이다.
String클래스의 참조변수는 직접 값을 저장하고 스택영역에 존재하는 기본형과는 다르게 객체와 같이 메모리 상의 힙 영역에 저장되어 객체주소가 변수에 저장된다.
2. String클래스 선언 방법
String 클래스의 변수 선언 방법은 2가지가 있다.
- 리터럴 방식
new
연산자를 통한 방식
String str = "This is String";
String str = new String("This is String");
참조형 타입의 경우 new 연산자를 통해서만 참조변수 생성이 가능하지만 java에서 특별히 많이 사용되는 String 클래스에 한해서 리터럴 방식도 허용한다고 한다.
두 방식은 메모리에 저장될 때 큰 차이점을 보인다.
String 리터럴로 생성하면 해당 String 값은 Heap 영역 내 String Constant Pool에 저장되어 재사용되지만, new 연산자로 생성하면 같은 내용이라도 여러 개의 객체가 각각 Heap 영역을
차지하게 된다.
String hello_1 = "hello";
String hello_2 = "hello";
String hello_3 = new String("hello");
String hello_4 = new String("hello");
리터럴 방식
- String 리터럴로 생성한 객체는 String Pool에 들어간다.
- String 리터럴로 생성한 객체의 값이 이미 String Pool에 존재하면 해당 객체는 String Pool의 주소를 참조한다.
new 연산자 방식
- new 연산자로 생성한 String 객체는 같은 값이 String Pool에 이미 존재하더라도 Heap 영역 내 별도의 객체를 가리키게 된다.
System.identityHashCode();
identityHashCode()는 실제로 저장된 메모리의 16진수 주소를 10진수로 나타내는 메서드이다.
String hello_1 = "hello";
String hello_2 = "hello";
String hello_3 = new String("hello");
String hello_4 = new String("hello");
System.out.println(System.identityHashCode(hello_1)); // 777874839
System.out.println(System.identityHashCode(hello_2)); // 777874839
System.out.println(System.identityHashCode(hello_3)); // 596512129
System.out.println(System.identityHashCode(hello_4)); // 824318946
코드 결과를 보면 알다시피 리터럴을 통한 생성은 같은 주소를 참조하고 new 연산자를 통해 생성한 객체는 각각 다른 주소를 참조한다.
3. String intern() 메서드
String을 리터럴로 선언할 경우 내부적으로 String의 intern()
메서드가 호출되는데,
intern() 메서드는 주어진 문자열이 string constant pool에 존재하는지 equal()
메서드로 검색한다.
만약 pool 내에 같은 문자열이 존재한다면, 그 주소값을 반환하고 없다면 string constant pool에 넣고 새로운
주소값을 반환하게 된다.
그래서 문자열을 리터럴로 선언할 경우 equal()
을 사용하지 않고 ==
연산자를 통해서 비교해도 상관없다.
이미 선언할 때부터 내부적으로 equals를 통해서 string pool을 검색하기 때문이다.
4. 문자열 비교 ==, equals()
문자열을 비교할 때 ==
과 String의 equals()
의 차이점은 뭘까?
==
연산자는 비교 대상의 주소값을 비교하는 반면 equals()
는 비교 대상의 value 즉 값
을 비교한다.
String hello_1 = "hello";
String hello_2 = "hello";
String hello_3 = new String("hello");
String hello_4 = new String("hello");
System.out.println(hello_1 == hello_2); // true
System.out.println(hello_1 == hello_3); // false
System.out.println(hello_3 == hello_4); // false
System.out.println(hello_1.equals(hello_2)); // true
System.out.println(hello_1.equals(hello_3)); // true
System.out.println(hello_1.equals(hello_4)); // true
==
- hello_1, hello_2 비교 시 string pool의 같은 주소를 가리키기 때문에 true 반환
- hello_1, hello_3 비교시 hello_3은 힙 영역의 다른 주소를 가리키기 때문에 false 반환
- hello_3, hello_4 비교시 값은 같지만 서로 다른 영역의 주소를 가리키기 때문에 false 반환
equals()
- 모든 참조변수의 값, value 자체를 비교하는 메서드이기 때문에 전부 true 반환
'Java > String' 카테고리의 다른 글
[Java] String +연산과 StringBuffer, StringBuilder (2) | 2024.01.18 |
---|