참조 자료형의 가장 대표적인 형태는 '클래스'다. 이번에는 자바가 제공하는 클래스 중 문자열을 저장하는 String 클래스를 알아보자. 이미 앞에서도 몇 가지 형태의 문자열을 써봤겠지만, 자바에서 가장 많이 쓰는 자료형 중 하나다.
문자열의 표현과 객체 생성 문자열은 반드시 큰따옴표(String a = "문자열")안에 표기해야 한다. 큰따옴표 안에는 String a = ""와 같이 아무런 문자열이 오지 않아도 상관없다. 하지만 큰따옴표는 절대 생략할 수 없다. String 클래스의 객체를 생성하는 데는 크게 2가지 방법이 있다. 첫 번째 방법은new 키워드를 사용하는 방법으로 생성자의 입력매개변수로 저장할 문자열을 입력한다.
🐧String 클래스의 객체 생성 방법 1
아직 클래스를 배우기 전이므로 이런 모양이 다소 어색해 보일 수 있지만, 대부분의 클래스가 이 방법으로 객체를 생성한다. 생성자의 개념은 아직 배우지 않았지만, 클래스명과 동일하면서 뒤에 소괄호가 있는 형태다. new 키워드는 배열에서 배웠으므로 실제 데이터가 힙 메모리에 위치한다는 것은 예측할 수 있을 것이다. 두 번째 방법은 간단히 문자열 리터럴, 즉 문자열 값만 입력하는 방법이다. 이제까지 여러 번 사용해봤을 것이다.
🐧String class의 객채 생성 방법 2
String 참조 변수명 = "문자열"
예
String str = "안녕";
첫 번째 방법을 사용하든 두 번째 방법을 사용하든 메모리에 저장되는 방식은 동일하다. String은 참조 자료형이므로 밑의 그림과 같이 실제 데이터인 String 객체는 힙 메모리에 위치하고 참조 변수는 힙 메모리의 실제 객체 위치를 가리키게 될 것이다. 하지만 이 두 가지 방법 사이에는 결정적인 차이가 1개 있다. 그 차이는 곧 설명하기로 하고, 여기서는 2가지의 String 객체를 생성하는 방법만 기억하자.
String 클래스도 당연히 클래스이므로 다른 클래스들의 특징을 모두 지니고 있다. 하지만 워낙 자주 사용되는 클래스이다 보니 다른 클래스에는 없는 2개의 특징이 있다.첫 번째 특징은 한 번 정의된 문자열은 변경할 수 없다는 것이다. 만일 문자열의 내용을 변경하면 자바 가상 머신은 기존의 문자열을 수정하는 것이 아니라 새로운 문자열을 포함하고 있는 객체를 생성해 사용하고 기존의 객체는 버린다.두 번째 특징은 문자열 리터럴을 바로 입력해 객체를 생성할 때 같은 문자열끼리 객체를 공유한다는 것이다.이는 메모리의 효율성 때문이다. 그럼 각각의 특징을 자세히 살펴보자.
특징 1. 객체 안의 값을 변경하면 새로운 객체를 생성 String 객체는 내부에 포함된 문자열을 변경할 수 없다. 다음 예를 살펴 보자.
먼저 코드를 String str1 = new String("안녕")과 같은 형태로 작성하면 메모리의 클래스 영역에는 String 클래스의 바이트 코드가 로딩되고 스택에는 참조 변수 str1의 공간이 생긴다. 실제 데이터인 "안녕"은 힙 메모리에 생성되며, 생성된 실제 데이터의 위칫값이 스택 메모리의 str1 공간에 저장될 것이다. 이 상황에서 String str2 = str1, 즉 참조 자료형의 값을 복사하면 스택 메모리의 값이 복사되므로 str1과 str2 는 이제 모두 동일한 객체를 가리키고 있을 것이다. 이제 str1 = "안녕하세요"와 같이 수정하면 자바 가상 머신은 기존의 문자열을 수정하는 것이 아닌 "안녕하세요"라는 문자열을 포함하고있는 새로운 String 객체를 생성하고 이 위치를 str1의 공간에 저장한다. 이는 일반적으로 참조 자료형에서 2개의 참조 변수가 1개의 객체를 가리킬 때 하나의 참조 변수에 접근해 객체의 값을 변경하면 다른 참조 변수가 가리키는 값도 함께 변하는 것과느니 구분되는 특징이다. 이상의 과정을 메모리 구조로 살펴보면 다음과 같다.
+) String 클래스의 특징과 배열의 특징 비교 값을 변경할 때 새로운 객체를 생성하는 String 클래스의 특징을 앞에서 배운 배열의 특징과 비교해보자. 배열은 객체의 값 자체가 수정되므로 참조 변수 복사 이후 하나의 변수에서 수정하면 나머지 변수에도 적용된다.
특징 2. 리터럴을 바로 입력한 데이터는 문자열이 같을 때 하나의 객체를 공유 두 번째 방법인 문자열 리터럴만 입력해 String 객체를 생성하면 하나의 문자열을 여러 객체가 공유할 수 있다. 이는 다른 클래스에 없는 특징으로 특정 문자열의 객체를 여러 개 만들어 사용할 때 메모리 효율성을 증가시키기 위한 것이다. 다음처럼 4개의 String 객체를 생성해 보자.
String str1 = new String("안녕");
String str2 = "안녕";
String str3 = "안녕";
String str4 = new String("안녕");
모두 동일한 문자열을 포함하고 있으며 첫 번째와 네 번째는 new 키워드 나머지 2개는 문자열 리터럴을 사용했다. 이제 메모리를 살펴 보자.
String str1 = new String("안녕")이 실행되면 힙 메모리에는 "안녕"이라는 객체 하나가 생성될 것이다. 두 번째는 문자열 리터럴로 입력했는데, 이때는 힙 메모리에 이미 "안녕"이 있어도 새롭게 안녕이라는 개체를 추가한다. 세 번째도 문자열 리터럴로 생성했는데 이때 이미 앞에서 문자열 리터럴로 생성한 "안녕"이라는 객체가 있으므로 새롭게 객체를 생성하는 것이 아니라 기존에 있는 이 객체를 공유한다. 마지막에는 다시 new로 객체를 생성했으며 이때는 새롭게 객체를 생성한다. 정리하면new로 생성할 때는 동일한 문자열 객체가 힙 메모리에 있든 없든 무조건 새롭게 객체를 생성한다. 문자열 리터럴로 생성할 때는 힙 메모리에 리터럴로 생성된 동일 문자열을 포함하고 있는 객체가 있으면 그 객체를 공유한다.
String 클래스의 주요 메서드
length() : 문자열의 길이를 리턴한다 charAt() : 문자열에서 특정 인덱스에 위치해 있는 문자를 알아 낸다. indexOf() : 문자열에서 특정 문자나 특정 문자열을 앞에서부터 찾아 위칫값을 알아낸다. lastindexOf() : 문자열에서 특정 문자나 특정 문자열을 뒤에서부터 찾아 위칫값을 알아낸다. String.valueOf() : 기본 자료형을 문자열로 바꾸는 정적 메서드다. concat(): 2개의 문자열을 연결한다. + 연산자와 동일한 기능을 수행한다. getBytes(): 문자열을 byte 배열로 변환한다. 자바 입출력 과정에서 주로 사용한다. toCharArray() : 문자열을 char 배열로 변환한다. 자바 입출력 과정에서 주로 사용한다.