String클래스
다른 언어에서는 문자열을 char형의 배열로 다루었으나 자바에서는 문자열을 위한 클래스를 제공한다. 그것이 바로 String클래스인데, String클래스는 문자열을 저장하고 이를 다루는데 필요한 메서드를 함께 제공한다.
-변경 불가능한(immutable클래스)
String클래스에서는 문자열을 저장하기 위해서 문자열 참조변수(char[]) value를 인스턴스 변수로 정의해놓고 있다. 인스턴스 생성 시 생성자의 매개변수로 입력받는 문자열은 이 인스턴스변수(value)에 문자형 배열(char[])로 저장되는 것이다.
public final class String implements java.io.Serializable, Comparable[
private char[] value;
//...생략
한 번 생성된 String인스턴스가 갖고 있는 문자열은 읽어 올 수만 있고, 변경할 수는 없다.
덧셈연산자 '+'를 사용해서 문자열을 결합가는 것은 매 연산 시 마다 새로운 문자열을 가진 String인스턴스를 생성하는 것이다. 따라서 메모리 공간을 차지하게 되므로 가능한 결합횟수를 줄이는 것이 좋다. 이와 같은 이유로 문자열간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우에는 StringBuffer클래스를 사용하는 것이 좋다.
-문자열 리터럴과 문자열의 비교
문자열을 만들 때는 두 가지 방법, 문자열 리터럴을 지정하는 방법과 String클래스의 생성자를 사용해서 만드는 방법이 있다.
String클래스의 생성자를 이용한 경우에는 new연산자에 의해서 메모리할당이 이루어지기 때문에 항상 새로운 String인스턴스가 생성된다. 그러나 문자열 리터럴은 이미 존재하는 것을 재사용하는 것이다.
자바 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다. 이 때 같은 내용의 문자열 리터럴은 한번만 저장된다. 문자열 리터럴도 String인스턴스이고, 한번 생성하면 내용을 변경할 수 없으니 하나의 인스턴스를 공유하면 되기 때문이다.
클래스 파일에는 소스파일에 포함된 모든 리터럴의 목록이 있다. 해당 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때, 이 리터럴의 목록에 있는 리터럴들이 JVM내에 있는 '상수 저장소(constant pool)'에 저장된다.
String str1 == "abc"; //문자열 리터럴로 재사용
String str2 == "abc"; //문자열 리터럴로 재사용
String str3 == new String("abc"); //새로운 String 인스턴스 생성
String str4 == new String("abc"); //새로운 String 인스턴스 생성
//str1 == str2 //true
//str3 == str4 //false
//str1.equals(str2) //true
//str3.equals(str3) //ture
-빈 문자열(empty string)
길이가 0인 배열이 존재할 수 있다는 것은 앞서 배웠다. 따라서 char형 배열도 길이가 0인 배열을 생성할 수 있고, 이 배열을 내부적으로 가지고 있는 문자열이 바로 빈 문자열이다.
그러나 String s = ""; 과 같은 표현이 가능하다고 해서 char c = ''; 와 같은 표현도 가능한 것은 아니다. char형 변수에는 반드시 하나의 문자를 지정해야한다.
일반적으로 변수를 선언할 때, 각 타입의 기본값으로 초기화 하지만 String은 참조형 타입의 기본값인 null보다는 빈 문자열로, char형은 유니코드의 첫 번째 문자로 기본값인 '\u0000'보다는 공백으로 초기화 하는 것이 보통이다.
-String클래스의 생성자와 주요 메서드
위 표를 보면 매개변수 타입이 char인 것들이 있고, int인 것들도 있다. 그 이유는 확장된 유니코드를 다루기 위해서이다.
유니코드는 원래 2byte, 즉 16bit타입인데 이걸로도 모자라서 20bit로 확장하게 되었다. 그래서 하나의 문자를 char타입으로 다루지 못하고, int타입으로 다룰 수밖에 없다.
확장에 의해 새로 추가된 문자들을 '보충문자(supplementary characters)'라고 하는데, String클래스의 메서드 중에서는 보충 문자를 지원하는 것이 있고 지원하지 않는 것도 있다.
매개변수가 'int ch'인 것들은 보충문자를 지원하는 것이고, 'char ch'인 것들은 지원하지 않는 것들이다.
-기타 활용 메서드
join()
join()은 여러 문자열 사이에 구분자를 넣어서 결합한다. 구분자로 문자열을 자르는 split()과 반대의 작업을 한다고 생각하면 이해하기 쉽다.
String animal = "dog,cat,bear";
String[] arr = animal.split(",");
String str = String.join("-", arr);
System.out.println(str); //dog-cat-bear
getBytes(String charsetName)
이를 사용하면, 문자열의 문자 인코딩을 다른 인코딩으로 변경할 수 있다. 자바가 UTF-16을 사용하지만, 문자열 리터럴에 포함되는 문자들은 OS의 인코딩을 사용한다. 한글 윈도우즈의 경우 문자 인코딩으로 CP949를 사용하며, UTF-8로 변경하려면, 아래와 같이 한다.
byte[] utf8_str = "가".getBytes("UTF-8); //문자열을 UTF-8로 변환
String str = new String(utf8_str, "UTF-8"); //byte배열을 문자열로 변환
서로 다른 문자 인코딩을 사용하는 컴퓨터 간에 데이터를 주고받을 때는 적절한 뮨자 인코딩이 필요하다.
UTF-8은 한글 한 글자를 3byte로 표현하고, CP949는 2byte로 표현한다.
String.format()
format()은 형식화된 문자열을 만들어내는 간단한 방법이다. printf()와 사용법이 완전히 똑같다.
String str = String.format("%d 더하기 %d는 %d입니다.", 3, 5, 3+5);
System.out.println(str); //3 더하기 5는 8입니다.
-String과 기본형간의 변환
기본형 값을 String으로 변환
숫자로 이루어진 문자열을 숫자로, 또는 그 반대로 변환하는 경우가 자주 있다. 그 방법은 숫자에 빈 문자열""을 더해주기만 하면 된다. 이 외에도 valueOf()를 사용하는 방법도 있다.
성능면에서는 valueOf()가 더 좋지만, 빈 문자열을 더하는 방법이 간단하고 편하기 때문에 성능향상이 필요한 경우에만 valuOf()를 사용하면 된다.
int i = 100;
String str1 = i + ""; //100을 "100"으로 변환
String str2 = String.valueOf(i); //100을 "100"으로 변환
String을 기본형 값으로 변환
반대로 String을 기본형으로 변환하는 방법도 간단하다. valueOf를 쓰거나 앞서 배운 parseValueType()를 사용하면 된다.
int i = Integer.parseInt("100"); //"100"을 100으로 변환
int i2 = Integer.valueOf("100"); //"100"을 100으로 변환
예전에는 parseInt()와 같은 메서드를 많이 썼는데, 메서드의 이름을 통일하기 위해 valueOf()가 나중에 추가되었다. valueOf(String s)는 메서드 내부에서 그저 parseInt(String s)를 호출할 뿐이므로, 두 메서드는 반환 타입만 다르지, 같은 메서드이다.
parseValueType()메서드는 2(NumberFormatException)가 발생할 수 있으므로 주의해야 한다. 그래서 문자열 양끝의 공백을 제거해주는 trim()을 습관적으로 같이 사용하기도 한다.
그러나 부호를 의미하는 '+'나 소수점을 의미하는 '.'와 float형 값을 뜻하는 f등과 같이 자료형 접미사는 허용된다.
StringBuffer클래스
String클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없지만 StringBuffer클래스는 변경이 가능하다. 내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며, StringBuffer인스턴스를 생성할 때 그 크기를 지정할 수 있다.
이 때, 편집할 문자열의 길이를 고려하여 버퍼의 크기를 충분히 잡아주어 불필요한 작업을 방지해야한다.
StringBuffer클래스는 String클래스와 유사한 점이 많다. 문자열을 저장하기 위한 char형 배열의 참조변수를 인스턴스변수로 선언해 놓고 있다. StringBuffer인스턴스가 생성될 때, char형 배열이 생성되며 이 때 생성된 char형 배열을 인스턴스변수 value가 참조하게된다.
public final class StringBuffer implements java.io.Serializable{
private char[] value;
//...생략
-StringBuffer의 생성자
StringBuffer클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고, 이 배열은 문자열을 저장하고 편집하기 위한 공간(buffer)으로 사용된다.
StringBuffer인스턴스를 생성할 때는 생성자 StringBuffer(int length)를 사용해서 저장될 문자의 길이를 고려하여 충분히 여유있는 크기로 지정하는 것이 좋다. 문자열을 다루는 작업을 할 때, 버퍼의 크기가 작업하려는 문자열의 길이보다 작을 때는 내부적으로 버퍼의 크기를 증가시키는 작업이 수행된다. 배열의 길이는 변경될 수 없으므로 새로운 길이의 배열을 생성한 후에 이전 배열의 값을 복사하는 것이다. 때문에 성능이 저하되는 것이다.
버퍼의 길이를 따로 지정해주지 않으면 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다.
public StringBuffer(int length){
value = new char[length];
shared = false;
}
public StringBuffer(int length){
this(16);
}
public StringBuffer(int length){
this(str.length() + 16);
append(str);
}
-StringBuffer의 변경
append()는 반환타입이 StringBuffer인데 자신의 주소를 반환한다. 그래서 아래와 같은 문장이 수행되면, sb에 새로운 문자열이 추가되고 sb자신의 주소를 반환하여 sb2에는 sb의 주소가 저장된다.
StringBuffer sb = new StringBuffer("abc");
sb.append("123");
StringBuffer sb2 = sb.append("zz");
System.out.println(sb); //abc123zz
System.out.println(sb2); //abc123zz
sb와 sb2가 모두 같은 StringBuffer인스턴스를 가리키고 있으므로 같은 내용이 출력된다. 그래서 하나의 stringBuffer인스턴스에 대해 연속적으로 append()를 호출하는 것도 가능하다.
StringBuffer sb = new StringBuffer("abc");
sb.append("123").append("zz");
-StringBuffer의 비교
String클래스에서는 equals메서드를 오버라이딩해서 문자열의 내용을 비교하도록 구현되어 있지만, StringBuffer클래스는 equals메서드를 오버라이딩하지 않아서 StringBuffer클래스의 equals메서드를 사용해도 등가비교연산자(==)로 비교한 것과 같은 결과를 얻는다.
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
System.out.println(sb == sb2); //false
System.out.println(sb.equalse(sb2)); //false
반면에 toString()은 오버라이딩되어 있어서 StringBuffer인스턴스에 toString()을 호출하면, 담고있는 문자열을 String으로 반환한다.
그래서 StringBuffer인스턴스에 담긴 문자열을 비교하기 위해서는 StringBuffer인스턴스에 toString()을 호출해서 String인스턴스를 얻은 다음, 여기에 equals()메서드를 사용해서 비교해야 한다.
StringBuffer sb = new StringBuffer("abc");
StringBuffer sb2 = new StringBuffer("abc");
String s = sb.toString();
String s2 = sb2.toString();
System.out.println(s.equals(s2)); //true
StringBuffer클래스의 생성자와 메서드
StringBuffer클래스 역시 문자열을 다루기 위한 것이기 때문에 String클래스와 유사한 메서드를 많이 가지고 있다. 그리고 추가,변경,삭제와 같이 저장된 내용을 변경할 수 있는 메서드들이 추가로 제공된다.
'Java > java.lang패키지와 유용한 클래스' 카테고리의 다른 글
[Java] java.lang패키지(wrapper 클래스) - 코딩밥상 (0) | 2023.02.16 |
---|---|
[Java] java.lang패키지(Object 클래스) - 코딩밥상 (0) | 2023.02.12 |