참조형 변수 String
대부분의 프로그래밍 언어에서 문자열이라는 데이터 타입을 저장하기 위해서 string이라는 타입을 사용하지만, Java에서는 String라는 class를 사용한다. 그렇기 때문에 특별한 자료형으로 취급되어 여러가지 유의해야 할 사항들이 존재한다. 단순히 문자열을 다루지만, 이를 어떻게 다루느냐에 따라 어플리케이션의 성능이 차이가 날 수 있다.
String 특징
String은 문자열을 담은 객체로 생성이 된다.
Java 프로그래밍에서 String은 다른 기본형 자료형과는 다른 참조형 변수이다. 즉 stact영역이 아닌 heap영역에 객체와 같이 문자열 데이터가 생성된다.
int age = 25;
char str = 'a';
String name = "철수";
String은 변하지 않는다.
기본적으로 Java에서 String으로 생성된 문자열의 값은 변경할 수 없다.
아래의 예제코드를 보면 String으로 만든 str1변수에 Hello 라는 값을 넣어 생성한 뒤 str2에 str1과 같은 문자열을 가르키고 str1에만 World라는 단어를 추가하고 출력을 하면 출력값이 서로 다르다는 것을 알 수 있다. 이는 String의 불변의 특성에 의한 현상이다. 즉 str1은 새로운 문자열을 만들어 Hello World를 가르키고 str2는 str1과 같은 Hello를 가르키지만 str1에 World가 붙어있지않는다. str2는 Hello라는 문자열을 계속 가르키고 있기 때문이다.
public class ImmutableExample {
public static void main(String[] args) {
String str1 = "Hello";
String str2 = str1; // str2가 str1과 같은 문자열을 가리킴
System.out.println("str1: " + str1);
System.out.println("str2: " + str2);
str1 = str1 + " World"; // 문자열을 변경하려는 시도
System.out.println("str1 (after modification): " + str1);// str1 출력값은 Helloworld
System.out.println("str2 (after modification): " + str2);// str2는 변경되지 않음
}
}
왜 String은 불변일까
Java에서 String을 불변으로 설정한 이유는 크게 3가지가 있다고 한다.
- 첫번째는 JVM(Java Virtual Machine)에서는 따로 String Constant Pool 이라는 독립적인 영역을 만들어 다른 변수 혹은 객체들과 공유를 하게 되는데, 이 과정에서 데이터 캐싱이 일어나면서 그 만큼 성능적 이득을 취할 수 있기때문이다.
- 두번째는 데이터가 불변하다면 다중 스레드 환경에서 동기화 문제가 발생하지 않기 때문이다.
- 세번째는 보안적인 측면이다. 예를 들어 DB에서 사용자 이름, 암호는 DB 연결을 수신하기 위해 문자열로 전달되는데 만약 이 값이 변경이 가능하다면 참조 값을 변경하여 보안문제가 일어날 수 있기 때문이다.
데이터 캐싱 : 데이터를 임시로 저장하고 빠르게 검색할 수 있도록 하는 메모리나 저장장치의 기술 또는 프로세스를 가리킵니다. 이는 데이터의 접근 속도를 높이고 시스템 성능을 향상시키는 데 사용
Java String 주소 할당 방식
Java에서 String 변수를 선언하는 방식은 두 가지 방식이 있다.
- 리터럴을 이용하는 방식
- new 연산자를 이용하는 방식
String str1 = "Hello";
String str2 = "Hello";
----------------------
String str3 = new String("Hello");
String str4 = new String("Hello");
이 두 가지 방법은 Hello라는 문자열을 저장하는 점에서는 차이가 없지만 JVM(Java Virtual Machine)메모리 측면에서는 엄청난 차이가 있다. 그리고 이러한 차이 때문에 Java에서 String을 다룰때 의문스러운 점이 나타나게 된다.
String Constant Pool
문자열 데이터를 어떠한 방식으로 저장 함에 따라 메모리에서 적재되는 형태가 다르게 된다.
예를 들어 String을 이용해 문자열 리터럴을 변수에 저장하면 String Constant Pool이라는 영역에 존재하게 되고 new를 통해 객체를 생성하듯이 생성하면 이 값은 Heap 영역에 존재한다.
문자열 리터럴 값으로 할당한 두 변수 str1, str2 가 같은 메모리 주소를 가리킨다는 점이다.
위에서 String은 불변(immutable) 하다는 특징에서 언급했듯이, String 은 한번 사용이 되면 또다시 재사용될 확률이 높기 때문에, 이에 대한 적절한 대처 방법으로 Heap 영역 내에 문자열 상수의 Pool 을 유지하고 해당 Pool 로 사용자가 정의한 변수가 가지고 있는 value 들을 담고 같은 주소를 참조하도록 연결해주었기 때문이다.
그래서 자바를 프로그래밍 할때 new String() 방식을 안쓰고 곧바로 문자열 리터럴값을 할당하는 이유가 바로 메모리를 절약할 수 있다는 특징 때문에 지금까지 그렇게 코딩 해왔던 것이다.
문자열 비교 ==, equals() 의 차이점
Java에서는 일반적인 데이터 타입의 비교는 == 연산자를 사용하여 비교한다. 하지만 String처럼 객체의 값을 비교할 때는 ==이 아닌 equals() 라는 메소드를 사용하여 비교해야한다. 왜냐하면 == 로 문자열을 비교한다면 문자열의 고유의 값이 아닌 문자열의 메모리 주소값을 비교하고 equals() 는 문자열의 고유의 값을 비교하기 때문이다.
String str1 = "Hello"; // 문자열 리터럴을 이용한 방식
String str2 = "Hello";
String str3 = new String("Hello"); // new 연산자를 이용한 방식
String str4 = new String("Hello");
// 리터럴 문자열 비교
System.out.println(str1 == str2); // true
// 객체 문자열 비교
System.out.println(str3 == str4); // false
System.out.println(str3.equals(str4)); // true
// 리터럴과 객체 문자열 비교
System.out.println(str1 == str3); // false
System.out.println(str3.equals(str1)); // true
'Language > Java' 카테고리의 다른 글
[Java] 반복문 (0) | 2023.09.12 |
---|---|
[Java] 조건문 (0) | 2023.09.11 |
[Java] 연산자 (0) | 2023.09.01 |
[Java] 형 변환 (0) | 2023.08.22 |
[Java] 변수와 변수의 기본형 타입과 참조형 타입 (0) | 2023.08.22 |