-
Java java.lang - 2, String 클래스Java 2024. 2. 25. 15:43728x90반응형
String 클래스
변경 불가능한(immutable) 클래스
String 클래스에는 문자열을 저장하기 위해 문자형 배열 참조변수(char[]) value를 인스턴스 변수로 정의해놓았다.
한 번 생성된 String 인스턴스가 갖고 있는 문자열은 읽어올 수만 있고, 변경할 수는 없다.
예를 들어 + 연산자를 이용해서 문자열을 결합하는 경우,
인스턴스 내의 문자열이 바뀌는 것이 아니라 새로운 문자열이 담긴 String 인스턴스가 생성된다.
+ 연산자를 사용해서 문자열을 결합하는 것은,
매 연산 시마다 새로운 문자열을 가진 String 인스턴스가 생성되어 메모리 공간을 차지하게 되므로,
가능한 한 결합횟수를 줄이는 것이 좋다.
문자열 간의 결합이나 추출 등 문자열을 다루는 작업이 많이 필요한 경우에는,
String 대신 StringBuffer 클래스를 사용하는 것이 좋다.
StringBuffer 인스턴스에 저장된 문자열은 변경이 가능하므로,
하나의 StringBuffer 인스턴스만으로도 문자열을 다루는 것이 가능하다.
참고 String 클래스는 앞에 final이 붙어 있으므로 다른 클래스의 조상이 될 수 없다.
문자열의 비교
문자열을 만들 때는 문자열을 리터럴을 지정하는 방법과, String 클래스의 생성자를 사용해서 만드는 방법이 있다.
String s1 = "abc"; // 문자열 리터럴 "abc"의 주소가 s1에 저장됨 String s2 = "abc"; // 문자열 리터럴 "abc"의 주소가 s2에 저장됨 String s3 = new String("abc"); // 새로운 String 인스턴스를 생성 String s4 = new String("abc"); // 새로운 String 인스턴스를 생성
String의 equals()는 두 문자열의 내용("abc")를 비교하기 때문에 두 경우 모두 true가 나온다.
하지만 등가비교연산자 '=='로 비교했을 때는 결과가 다르다s1 == s2 >> true s3 == s4 >> false
String 클래스의 생성자를 이용한 경우는,
new 연산자에 의해 메모리할당이 이루어지기 때문에 항상 새로운 String 인스턴스가 생성된다.
그러나 문자열 리터럴은 이미 존재하는 것을 재사용하는 것이다.
참고 문자열 리터럴은 클래스가 메모리에 로드될 때 자동적으로 미리 생성된다.문자열 리터럴
자바 소스파일에 포함된 모든 문자열 리터럴은 컴파일 시에 클래스 파일에 저장된다.
이 때 같은 내용의 문자열 리터럴은 한 번만 저장된다.
문자열 리터럴도 String 인스턴스이고,
한번 생성하면 내용을 변경할 수 없으니 하나의 인스턴스를 공유하면 되기 때문이다.
클래스 파일에는 소스파일에 포함된 모든 리터럴의 목록이 있다.
해당 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때,
이 리터럴 목록에 있는 리터럴들이 JVM 내에 있는 '상수 저장소(constant pool)'에 저장된다.
빈 문자열(empty string)
길이가 0인 배열이 존재할 수 있을까? 답은 'Yes'
char형 배열도 길이가 0인 배열을 생성할 수 있고,
이 배열을 내부적으로 가지고 있는 문자열이 바로 빈 문자열이다.String s = "";
참조변수 s가 참조하는 String 인스턴스는 내부에 new char[0]과 같이 길이가 0인 char형 배열을 저장하고 있는 것이다.
참고 C언어는 길이가 0인 배열을 선언할 수 없다.
그러나 String s = "";이 가능하다고 해서 char = "";도 허용하는 것은 아니다.
char형 변수는 반드시 하나의 문자를 지정해야 한다.
참고 C언어는 문자열의 끝에 널 문자가 항상 붙지만, 자바에서는 널 문자를 사용하지 않는다. 대신 문자열의 길이정보를 따로 저장한다.
일반적으로 변수를 선언할 때, 각 타입의 기본값으로 초기화하지만,
String은 참조형 타입의 기본값인 null보다는 빈 문자열로,
char형은 기본값인 '\u0000'대신 공백으로 초기화하는 것이 보통이다.참고 \u0000은 유니코드의 첫 번째 문자로써 아무런 문자도 지정되지 않은 빈 문자이다.
join()과 StringJoiner
join()은 여러 문자열 사이에 구분자를 넣어서 결합
구분자로 문자열을 자르는 split()과 반대의 작업jdk1.8부터 추가
String animals = "dog,cat,bear"; String[] arr = animals.split(","); String s = String.join("-", arr); sysout(s); // dog-cat-bear StringJoiner sj = new StringJoiner(",", "[", "]"); String[] sArr = {"aa", "bb", "cc"}; for(String s : sArr) { sj.add(s.toUpperCase()); } sysout(sj.toString()); // [AA,BB,CC]
유니코드의 보충 문자
유니코드는 원래 2byte, 즉 16비트 문자체계인데, 이거로 모자라서 20비트로 확장하였다.
그래서 하나의 문자를 char타입으로 다루지 못하고, int타입으로 다룰 수밖에 없다.
확장에 의해 새로 추가된 문자들을 '보충 문자'라고 한다.
매개변수가 'int ch'인 메서드는 보충 문자를 지원하고, 'cchar ch'면 지원하지 않는다.
보충문자를 사용할 일은 거의 없다.
참고 jdk1.5부터 확장된 유니코드(20bit)가 적용되었다.
문자 인코딩 변환
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으로 변환
숫자에 빈 문자열 ""을 더한다.
성능은 valueOf()가 더 좋지만, 빈 문자열이 더 편하기 때문에,
성능이 중요할 때만 valueOf를 사용한다.int i = 1; String s1 = i + ""; String s2 = String.valueOf(i);
참고 참조변수에 String을 더하면, 참조변수가 가리키는 인스턴스의 toString()을 호출하여 String을 얻은 다음 결합한다.
String을 기본형 값으로 변환
valueOf를 사용하거나 parseInt()를 쓴다.
int i = Integer.parseInt("1"); int i2 = Integer.valueOf("1");
valueOf는 원래 반환 타입이 int가 아니라 Integer인데,
오토박싱(auto-boxing)에 의해 Integer가 int로 자동 변환된다.Integer i2 = Integer.valueOf("1"); // 원래 반환 타입이 Integer
예전에는 parseInt같은 메서드를 많이 썼는데, 메서드 이름을 통일하기 위해 valueOf가 나중에 추가되었다.
valueOf(String s)는 메서드 내부에서 그저 parseInt(String s)를 호출할 뿐이라,
두 메서드는 반환 타입만 다르고 같은 메서드다.public static Integer valueOf(String s) throws NumberFormatException{ return Integer.valueOf(parseInt(s, 10)); // 10은 10진수를 의미 }
참고 parseInt(s, 10)은 parseInt(s)와 같다.
문자열 "A"를 문자 'A'로 변환하는건 char ch = "A".charAt(0);
Boolean, Byte같이 기본형 타입의 이름의 첫 글자가 대문자인 것은 래퍼 클래스(wrapper class)이다.
기본형 값을 감싸고 있는 클래스라는 뜻에서 붙여진 이름으로 기본형을 클래스로 표현한 것이다.
parseInt나 parseFloat같은 메서드는 문자열에 공백 또는 문자가 포함된 경우,
변환 시 예외(NumberFormatException)가 발생할 수 있으므로 주의.
문자열 양 끝의 공백을 제거하는 trim()을 습관적으로 같이 사용하기도 함int i = Integer.parseInt(" 123 ").trim();
부호를 의미하는 '+'나 소수점을 의미하는 '.'이랑 float형 값을 뜻하는 'f'같은 자료형 접미사는 허용
단, 자료형에 알맞은 변환을 하는 경우만 허용
참고 '+'가 포함된 문자열이 parseInt로 변환가능해진 것은 jdk1.7부터다.
참고 Integer 클래스의 static int parseInt(String s, int radix)를 사용하면,
16진수 값으로 표현된 문자열도 변환할 수 있기 때문에 대소문자 구분 없이 a, b, c, d, e, f도 사용할 수 있다.
int result = Integer.parseInt("a", 16);의 경우 정수 10이 저장된다.
16진수 a는 10진수로 10을 뜻한다.StringBuffer 클래스와 StringBuilder 클래스
String 클래스는 인스턴스를 생성할 때 지정된 문자열을 변경할 수 없지만,
StringBuffer 클래스는 변경이 가능하다.
내부적으로 문자열 편집을 위한 버퍼(buffer)를 가지고 있으며,
StringBuffer 인스턴스를 생성할 때 그 크기를 지정할 수 있다.
이 때, 편집할 문자열의 길이를 고려하여 버퍼의 길이를 충분히 잡아주는 것이 좋다.
편집 중인 문자열이 버퍼의 길이를 넘어서게 되면,
버퍼의 길이를 늘려주는 작업이 추가로 수행되어야하기 때문에 작업 효율이 떨어진다.
StringBuffer 클래스는 String 클래스와 같이 문자열을 저장하기 위한 char형 배열의 참조변수를 인스턴스변수로 선언해놓았다.
StringBuffer 인스턴스가 생성될 때, char형 배열이 생성되며 이 때 생선된 char형 배열을 인스턴스 변수 value가 참조하게 된다.
StringBuffer의 생성자
StringBuffer 클래스의 인스턴스를 생성할 때, 적절한 길이의 char형 배열이 생성되고,
이 배열은 문자열을 저장하고 편집하기 위한 공간으로 사용된다.
StringBuffer 인스턴스를 생성할 때는 생성자 StringBuffer(int length)를 사용해서,
StringBuffer 인스턴스에 저장될 문자열의 길이를 고려하여 충분히 여유있는 크기로 지정하는 것이 좋다.
버퍼의 크기를 지정해주지 않으면 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성한다.public StringBuffer(int length){ value = new char[length]; shared = false; } public StringBuffer(){ this(16); } public StringBuffer(String str){ this(str.length() + 16); append(str); }
StringBuffer의 변경
append()는 반환타입이 StringBuffer인데 자신의 주소를 반환한다.
그래서 append()를 여러번 호출할 수 있다.
만약 append의 반환타입이 void라면 이렇게 할 수 없었을 것이다.
StringBuffer의 비교
StringBuffer 인스턴스에 toString을 호출하면, 담고 있는 문자열을 String으로 반환한다.
String 인스턴스를 얻은 다음, 여기에 equals메서드를 사용해서 비교해야 한다.
StringBuffer는 멀티쓰레드에 안전(thread safe)하도록 동기화되어 있다.
멀티쓰레드로 작성된 프로그램이 아닌 경우,
StringBuffer의 동기화는 불필요하게 StringBuffer의 성능을 떨어뜨린다.
그래서 StringBuffer에서 쓰레드의 동기화만 뺀 StringBuilder가 새로 추가되었다.
StringBuilder는 StringBuffer와 완전 똑같은 기능으로 작성되어 있어서,
소스코드에서 StringBuffer 대신 StringBuilder를 사용하도록 바꾸기만 하면 된다.
즉, StringBuffer타입의 참조변수를 선언한 부분과 StringBuilder의 생성자만 바꾸면 된다.StringBuffer sb; sb = new StringBuffer(); sb.append("abc"); >> StringBuilder sb; sb = new StringBuilder(); sb.append("abc");
StringBuffer도 충분히 성능이 좋기 때문에 성능 향상이 반드시 필요한 경우를 제외하고는,
기존에 작성한 코드에서 StringBuffer를 StringBuilder로 굳이 바꿀 필요는 없다.728x90반응형'Java' 카테고리의 다른 글
Java 클래스 (0) 2024.02.26 Java java.lang - 3, Math 클래스 (1) 2024.02.25 Java java.lang - 1, Object 클래스 (1) 2024.02.25 Java 예외 처리 (1) 2024.02.25 Java 인터페이스 (1) 2024.02.25