JAVA

[JAVA]String 객체와 문자열 상수

당고개 2023. 9. 20. 23:04

1. String 객체와 문자열 상수

우리는 익숙하게 String 객체와 문자열을 사용했었습니다.

String name = "포도";

해당 코드는 세 부분으로 나뉘어져 있습니다.
String 참조 자료형, 변수명, 문자열로 구성되어있습니다.
String 참조 자료형을 사용할 수 있는 이유는 Java 언어 스펙에 이미 String 클래스가 정의되어 있기 때문입니다. 그리고 쌍따옴표로 감싸진 "포도"문자열 상수(String constant) 라고 부릅니다.

문자열 생성

문자열을 생성할 때 여러 방법으로 생성할 수 있습니다.

// 문자열 상수
String name = "포도";

// String 객체를 이용하여 생성
String name = new String ("포도");

// String 객체를 이용하여 생성
String name = String.format("%s도", "포");

// String 객체를 이용하여 생성
String name = String.join("%s%s", new String[] {"포", "도"});

// StringBuilder 객체를 이용하여 생성
String name = new StringBuilder().append("포").append("도").toString();

// StringBuffer 객체를 이용하여 생성
String name = new StringBuffer().append("포").append("도").toString();

// 문자열 상수를 더하여 생성
String name = "포"+"도";

// 변수에 저장된 문자열 상수와 문자열 상수를 더하여 생성
String text = "포";
String name =  text+"도";

문자열을 생성하는 방법은 다양합니다. 그러나 생성 방법에 따라 Java 언어를 실행하는 가상머신이 문자열을 관리하는 방법이 달라지며 그에 따라 문자열을 비교하는 방법도 달라집니다.

문자열 비교(중요)

문자열 상수String 인스턴스로 저장된 문자열서로 다른 관리 방법을 가지고 있는데요, 리터럴은 별도의 메모리에 일시적으로 저장된다고 했었습니다. 문자열 상수(리터럴)는 자바 가상머신이 관리하는 String constant pool에 저장됩니다.

그리고 문자열 상수가 아닌 다른 방법으로 만들어진 String 객체는 자바 가상머신이 관리하는 Heap이라는 공간에 저장됩니다.

이러한 다른 관리 기법으로 인해 == 연산자를 이용하여 같은 문자열인지 비교할 수가 없습니다.

  • java8에서 == 연산자는 기본 자료형에 저장된 값은 상수이므로 값을 기준으로 비교하나, 참조 자료형에 저장된 값은 메모리 주소값이기 때문에 메모리 주소값을 비교한다.

case 1

String text1 = "포도";
String text2 = "포도";

// 문자열 상수끼리 비교하기 때문에 같다.
System.out.println(text1 == text2);

문자열 상수는 String constant pool한 번 저장되면 동일한 문자열 상수를 다른 메모리 공간안에 저장하지 않고 이미 저장된 문자열 상수를 사용합니다, 즉 같은 문자열 상수는 저장된 메모리 공간의 주소가 같습니다.

case 2

String text1 = “포도”;
String text2 = “포”+“도”;

// 문자열 상수끼리 비교하기 때문에 같다.
System.out.println(text1 == text2);

변수와 문자열 상수를 + 연산자를 통해 더하는 코드를 발견하셨을 수 있습니다. 문자열과 함께 사용되는 + 연산자는 변수나 리터럴 값을 문자열로 변환하고 합하여 새로운 문자열을 만들어 내는 연산과정을 거칩니다.

"포"+"도" 라는 연산은 문자열 상수와 문자열 상수를 합하여 새로운 문자열 상수를 만들어 냅니다. 다만 이 과정은 Runtime(프로그램 실행) 환경에서 연산하는 것이 아니라, 컴파일 시에 문자열 상수를 만들어 냅니다. 그러므로 동일한 String constant pool 메모리 공간에 저장되므로 저장된 메모리 공간의 주소가 동일합니다.

결론적으로 같은 문자열 상수끼리 비교하기 때문에 같습니다.

case 3

String text1 = "포";
String text2 = "포도";
String text3 = text1+"도";

// StringBuilder 인스턴스와 문자열 상수끼리 비교하기 때문에 동일하지 않다.
System.out.println(text2 == text3);

case 2와 비슷한 방법입니다. 그러나 text1 변수와 "도" 문자열 상수를 + 연산자를 사용하여 이용하면 내부적으로 StringBuilder 인스턴스를 생성하고 연산하여 String 객체를 반환합니다.

즉 문자열 상수가 아니라 String 객체로 암시적 형변환을 한다고 생각하시면 쉽습니다.

그 이유는 text1 변수라는 것은 Runtime(프로그램 실행) 환경에서는 언제든지 값이 변할 수 있기 때문입니다. 그러므로 상수로 판단하지 않고, String 인스턴스로 최종 변환합니다.

결론적으로 인스턴스와 문자열 상수끼리 비교하기 때문에 동일하지 않습니다.

case 4

String text1 = new String("포도");
String text2 = new String("포도");

// 동일하지 않다 (인스턴스 끼리 비교)
System.out.println(text1 == text2);

이번엔 문자열을 String 인스턴스를 통해 생성했습니다.

객체는 각각 별도의 메모리 공간에 저장 됩니다. 즉 text1 변수와 text2 변수에 저장된 메모리 주소값은 서로 다릅니다. 그러므로 동일하지 않습니다.

case 5

String text1 = new StringBuilder("포").append("도").toString();
String text2 = "포도";

// 동일하지 않다 (인스턴스와 상수끼리 비교)
System.out.println(text1 == text2);

text1은 new 키워드를 이용하여 StringBuilder라는 인스턴스를 이용하여 문자열을 생성했고 text2는 문자열 상수를 저장하고 있습니다. 그러므로 위에서 설명했듯이 인스턴스와 상수를 비교했을 때 동일하지 않습니다.

2. 같은 문자열인지 어떻게 비교할 수 있을까?

Java8 언어에서 == 연산자를 이용한 문자열 비교에 대한 문제가 존재했습니다.
그렇다면 같은 문자열인지 어떻게 비교할 수 있을까요? String 클래스의 equals 메서드를 통해 동일한 문자열인지 비교할 수 있습니다.

public boolean equals(Object object);

case 1

String text1 = new String("포도");
String text2 = "포도";

// 인스턴스와 문자열 상수와 비교이나 문자열은 동일하다.
System.out.println(text1.equals(text2));

equals 메서드를 사용하면 메모리 주소를 비교하는 것이 아니라 저장된 문자열을 토큰화합니다. 그래서 문자들이 서로 동일한지 문자를 하나씩 비교하여 동일한지 확인합니다. 그러므로 메모리 주소가 아닌 문자열을 비교하므로 동일한 문자열이라는 것을 확인할 수 있습니다.

case 2

String text1 = "포도";
String text2 = "포도";

// 문자열 상수와 문자열 상수 비교, 문자열을 비교하므로 동일하다.
System.out.println(text1.equals(text2));

문자열 상수와 문자열 상수의 문자열을 비교합니다. 그러므로 더 이상 설명 필요없이 동일합니다.

case 3

String text1 = String.format("%s도", "포");
String text2 = new StringBuilder("포").append("도").toString();

// 인스턴스와 인스턴스 비교, 문자열을 비교하므로 동일하다.
System.out.println(text1.equals(text2));

String.format 메서드와 StringBuilder 인스턴스로 생성된 문자열을 비교합니다. 결론적으로 동일한 문자열을 비교하므로 동일합니다.

 case 4

String text1 = "포도";

// 문자열 상수도 String 클래스가 가지고 있는 메서드를 사용할 수 있다.
System.out.println("포도".equals(text1));

이런 코드가 가능한가요?

가능합니다.

Java 언어에서 가장 이레귤러 같은 부분이 문자열 상수에서 String 객체의 메서드를 사용할 수 있다는 점입니다. 그 이유는 코드 작성자의 편의를 위해 가능하도록 만들어졌습니다. 이러한 예외적인 부분은 컴파일시에 처리해주고 있습니다.

 

 

 

출처 : https://www.codelatte.io/courses/java_programming_basic/PP95VV3NZJM71V14

'JAVA' 카테고리의 다른 글

[JAVA]상속  (0) 2023.09.21
[JAVA]String 메서드  (0) 2023.09.21
[JAVA]캡슐화  (0) 2023.09.12
[JAVA]객체의 합성  (0) 2023.09.12
[JAVA]메서드 오버로딩  (0) 2023.09.08