본문 바로가기

프로그래밍 언어/JAVA

JAVA 입문 - String 클래스

지난 글: [프로그래밍 언어/JAVA] - JAVA 입문- Object 클래스

 

String을 선언하는 두 가지 방법

자바는 문자열을 사용할 수 있도록 String 클래스를 제공한다. 문자열은 프로그램을 구현할 때 많이 활용된다고 한다. String을 사용할 때 문자열을 생성자의 매개변수로 하여 생성하는 방식과 이미 생성된 문자열 상수를 가리키는 방식이 있다.

String str1 = new String("abc");	//생성자의 매개변수로 문자열 생성
String str2 = "test";				//문자열 상수를 가리키는 방식

언뜻 비슷해 보이지만, 내부적으로 두 방식은 큰 차이가 있다. new 예약어를 사용하여 객체를 생성하는 경우는 "abc" 문자열을 위한 메모리가 할당되고 새로운 객체가 생성된다. 하지만 str2 = "test";와 같이 생성자를 이용하지 않고 바로 문자열 상수를 가리키는 경우는 str2가 기존에 만들어져 있던 "test"라는 문자열 상수의 메모리 주소를 가리키게 된다. 즉 String str3 = "test"; 코드를 작성하면 str2와 str3는 주소 값이 같게 된다. 다음 예제는 String을 생성자로 생성했을 때의 주소 값과 문자열 상수를 바로 가리킬 때의 주소 값을 비교한다.

출력 결과

문자열 상수를 바로 가리키는 경우는 주소 값이 같음을 알 수 있다.

 

String 클래스의 final char[ ] 변수

다른 프로그래밍 언어는 문자열을 구현할 때 일반적으로 char[ ] 배열을 사용한다. 자바는 String 클래스를 제공해 char[ ] 배열을 직접 구현하지 않고도 편리하게 문자열을 사용할 수 있다. String 클래스의 구현 내용을 보면 private final char value[ ]라고 선언된 char형 배열이 있다. 프로그램에서 String s = new String("abc")라고 쓰면 abc는 String 클래스의 value 변수에 저장된다. 헌데 이 변수는 private으로 선언되어 있다. final은 문자열을 변경할 수 없다는 뜻이기에 한 번 생성된 문자열은 변경되지 않는다. 이런 문자열의 특징을 '문자열은 불변(immutable)한다'라고 한다. 그럼 프로그램에서 두 개의 문자열을 연결하면 어떻게 될까? 이런 경우 둘 중 하나의 문자열이 변경되는 것이 아니라 두 문자열이 연결된 새로운 문자열이 생성된다.

 

아래 예제는 String으로 두 개의 문자열("java", "android")을 생성하고 concat( ) 메서드로 두 문자열을 연결한다. 원래 문자열의 주소와 연결된 문자열의 주소 값을 System.identity.HashCode( ) 메서드를 사용해 비교해보자.

출력 결과

예제에서 두 개의 문자열을 생성하고, 10행에서 두 문자열을 연결하는 concat( ) 메서드를 호출했다. 12행의 javaStr 변수 출력 결과를 보면 "javaandroid"로 연결되어 잘 출력되었다. 이는 "java" 문자열에 "android" 문자열이 연결된 것 같지만, 위 설명처럼 문자열은 불변하므로 javaStr 변수 값 자체가 변한게 아니라 새로운 문자열이 생긴 것이다.

 

StringBuffer와 StringBuilder 클래스 활용

프로그램을 만들다 보면 문자열을 변경하거나 연결해야 할 때가 많다고 한다. 그런데 String 클래스는 한 번 생성되면 그 내부의 문자열이 변경되지 않기에 String 클래스를 사용하여 문자열을 계속 연결하거나 변경하는 프로그램을 작성하면 메모리가 많이 낭비된다. 이 문제를 해결하는 것이 StringBuffer와 StringBuilder 클래스이다.

 

StringBuffer와 StringBuilder는 내부에 변경 가능한(final이 아닌) char[ ] 변수를 가지고 있다. 이 두 클래스를 사용하여 문자열을 연결하면 기존에 사용하던 char[ ] 배열이 확장되므로 추가 메모리를 사용하지 않는다. 따라서 문자열을 연결하거나 변경할 경우 두 클래스 중 하나를 사용하면 된다. 두 클래스의 차이는 여러 작업(스레드)이 동시에 문자열을 변경하려 할 때 문자열이 안전하게 변경되도록 보장해 주는가 아닌가의 차이다.

 

StringBuffer 클래스는 문자열이 안전하게 변경되도록 보장하지만, StringBuilder 클래스는 아니다. 프로그램에서 따로 스레드를 생성하는 멀티스레드 프로그램이 아니라면 StringBuilder를 사용하는 것이 실행 속도가 더 빠르다.

 

아래 예제를 통해 StringBuilder 사용법을 알아보자.

출력 결과

5행에서 생성한 "java" 문자열에 여러 문자열을 추가해야 하는 경우에 8행처럼 일단 StringBuilder 클래스를 생성하고 여기에 문자열을 추가(append)한다. 그러면 append( ) 메서드가 실행될 때마다 메모리가 새로 생성되는 것이 아니라, 하나의 메모리에 계속 연결되는 것을 해시 코드  값을 통해 알 수 있다. 연산 전 메모리 주소와 연산 후 메모리 주소가 같기 때문이다. 문자열을 변경한 후에 buffer에 toString( ) 메서드를 호출하면 다시 문자열로 반환할 수 있다.

 

참고 서적: 자바 프로그래밍 입문 - 박은종

'프로그래밍 언어 > JAVA' 카테고리의 다른 글

JAVA 입문 - Class 클래스  (0) 2022.06.02
JAVA 입문 - wrapper 클래스  (0) 2022.06.01
JAVA 입문- Object 클래스  (0) 2022.05.30
JAVA 입문 - 인터페이스 활용  (0) 2022.05.29
JAVA 입문 - 인터페이스 요소  (0) 2022.05.28