[자바 무료 강의] String 객체와 문자열 상수 - 코드라떼
Lesson List button
코스자바로 배우는 프로그래밍
hamburger button
강의String 객체와 문자열 상수최종수정일 2022-07-13
아이콘약 5분

우리는 아무렇지 않게 문자열을 선언하고 사용해왔습니다. 그러나 String 객체와 문자열 상수라는 것에 대해 자세히 배우지 않으면 프로그래밍시 원하지 않는 결과를 겪을 수 있습니다. 이번 강의는 String 객체와 문자열 상수에 대해 깊게 알아보는 시간입니다.

노트 강의

이 강의는 String 객체에 대해서 좀 더 깊게 다가가보는 시간입니다.


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에 저장됩니다.

그림1

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

그림2

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

그림3

  • 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 인스턴스를 통해 생성했습니다.

이전에 “현실 세계에서 존재하는 것을 객체로 만들다” 강의에서 new 키워드를 이용하여 만든 객체는 각각 별도의 메모리 공간에 저장된다고 했었습니다.

즉 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 객체의 메서드를 사용할 수 있다는 점입니다. 그 이유는 코드 작성자의 편의를 위해 가능하도록 만들어졌습니다. 이러한 예외적인 부분은 컴파일시에 처리해주고 있습니다.

일단 다양한 예시를 들어 설명드렸는데요. 실습도구를 통해 연구해보시고 실험해보세요. 실험하는 만큼 더 확신을 가지게 됩니다.

도전자 질문
아이콘yongryang(2022-07-06 09:21 작성됨)
좋은 강의 제공해주셔서 감사드립니다!

1)
강의 노트 중 `2. 같은 문자열인지 어떻게 비교할 수 있을까?`
에서 사용하시는 `equal()`은 혹시 `equals()`가 아닌가요~?

2)
case4에 이레귤러는 무엇을 뜻하는지 여쭤볼 수 있을까요?
아이콘코드라떼(2022-07-13 04:15 작성됨)
안녕하세요. 코드라떼입니다 :)

1)
말씀하신것처럼 equal이 아닌 equals입니다. 오타는 수정하겠습니다 :)

2)
일반적으로 메서드를 사용하려는 경우 인스턴스 생성 후 (new 키워드를 사용하여 인스턴스 생성) 참조 자료형 변수에 인스턴스의 참조 값을 저장하여 메서드를 호출하나 문자열 상수에서 바로 메서드를 호출할 수 있는 부분이 변칙적인 부분이라서 '이레귤러'라는 단어를 사용했습니다.

감사합니다 :)
아이콘resCogitans(2021-06-28 07:46 작성됨)
case3의 테스트 코드에서

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

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

System.out.println(text3 == text2);
로 비교해야 하지 않나요?
아이콘코드라떼(2021-06-28 14:35 작성됨)
resCogitans께서 말씀하신 것이 맞습니다. 강의 노트를 꼼꼼히 확인하지 못하여 혼동 드린 점 죄송하다는 말씀을 드리며 수정하겠습니다. 

감사합니다^^
이용약관|개인정보취급방침
알유티씨클래스|대표, 개인정보보호책임자 : 이병록
이메일 : cs@codelatte.io
사업자등록번호 : 824-06-01921
통신판매업신고 : 2021-성남분당C-0740
주소 : 경기도 성남시 분당구 대왕판교로645번길 12, 9층 24호
파일
파일파일
Root
파일

Output
root$
Lesson List button
코스자바로 배우는 프로그래밍
hamburger button
강의String 객체와 문자열 상수최종수정일 2022-07-13
아이콘약 5분

우리는 아무렇지 않게 문자열을 선언하고 사용해왔습니다. 그러나 String 객체와 문자열 상수라는 것에 대해 자세히 배우지 않으면 프로그래밍시 원하지 않는 결과를 겪을 수 있습니다. 이번 강의는 String 객체와 문자열 상수에 대해 깊게 알아보는 시간입니다.

노트 강의

이 강의는 String 객체에 대해서 좀 더 깊게 다가가보는 시간입니다.


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에 저장됩니다.

그림1

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

그림2

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

그림3

  • 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 인스턴스를 통해 생성했습니다.

이전에 “현실 세계에서 존재하는 것을 객체로 만들다” 강의에서 new 키워드를 이용하여 만든 객체는 각각 별도의 메모리 공간에 저장된다고 했었습니다.

즉 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 객체의 메서드를 사용할 수 있다는 점입니다. 그 이유는 코드 작성자의 편의를 위해 가능하도록 만들어졌습니다. 이러한 예외적인 부분은 컴파일시에 처리해주고 있습니다.

일단 다양한 예시를 들어 설명드렸는데요. 실습도구를 통해 연구해보시고 실험해보세요. 실험하는 만큼 더 확신을 가지게 됩니다.

도전자 질문
아이콘yongryang(2022-07-06 09:21 작성됨)
좋은 강의 제공해주셔서 감사드립니다!

1)
강의 노트 중 `2. 같은 문자열인지 어떻게 비교할 수 있을까?`
에서 사용하시는 `equal()`은 혹시 `equals()`가 아닌가요~?

2)
case4에 이레귤러는 무엇을 뜻하는지 여쭤볼 수 있을까요?
아이콘코드라떼(2022-07-13 04:15 작성됨)
안녕하세요. 코드라떼입니다 :)

1)
말씀하신것처럼 equal이 아닌 equals입니다. 오타는 수정하겠습니다 :)

2)
일반적으로 메서드를 사용하려는 경우 인스턴스 생성 후 (new 키워드를 사용하여 인스턴스 생성) 참조 자료형 변수에 인스턴스의 참조 값을 저장하여 메서드를 호출하나 문자열 상수에서 바로 메서드를 호출할 수 있는 부분이 변칙적인 부분이라서 '이레귤러'라는 단어를 사용했습니다.

감사합니다 :)
아이콘resCogitans(2021-06-28 07:46 작성됨)
case3의 테스트 코드에서

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

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

System.out.println(text3 == text2);
로 비교해야 하지 않나요?
아이콘코드라떼(2021-06-28 14:35 작성됨)
resCogitans께서 말씀하신 것이 맞습니다. 강의 노트를 꼼꼼히 확인하지 못하여 혼동 드린 점 죄송하다는 말씀을 드리며 수정하겠습니다. 

감사합니다^^
이용약관|개인정보취급방침
알유티씨클래스|대표, 개인정보보호책임자 : 이병록
이메일 : cs@codelatte.io|운영시간 09:00 - 18:00(평일)
사업자등록번호 : 824-06-01921|통신판매업신고 : 2021-성남분당C-0740
주소 : 경기도 성남시 분당구 대왕판교로645번길 12, 9층 24호(경기창조혁신센터)
파일
파일파일
Root
파일

Output
root$