도서/자바의 신

[도서/자바의 신] #15 String

yulee_to 2022. 12. 25. 03:27

자바의 신

✔️이 글은 [자바의 신 - 이상민 지음] 도서를 바탕으로 정리한 글입니다. 


자바에서 가장 많이 사용하는 String 클래스

객체에 더하기를 하면 toString() 메소드가 호출되고 그 결과가 더해지는 것이라 결론적으로는 String을 더하는 것이다. 

 

자바에서 많이 쓰이는 String 클래스에 대해 알아보자.

public final class String extends Object
    implements Serializable, Comparable<String>, CharSequence

public final로 선언되어 있어 누구나 사용할 수 있지만 확장할 수 없는 클래스이다. 

implements 뒤에 오는 인터페이스들을 구현해 놓은 것이 String 클래스이다. 

Serializable 인터페이스는 구현해야 하는 메소드가 하나도 없는 특별한 인터페이스로 이걸 implements한다는 건 해당 객체를 파일로 저장하거나, 다른 서버에 전송 가능한 상태가 된다. 나중에 27장에서 더 다뤄질 것이다.

Comparable에는 compareTo()라는 메소드 하나만 선언되어 있다. 이 메소드는 매개변수로 넘어가는 객체와 현재 객체가 같은지를 비교하는 데 사용된다. equals()와 비슷해보이지만 이 메소드의 리턴타입은 int로 같으면 0, 순서상으로 앞이면 -1, 뒤면 1을 리턴한다. 

CharSequence는 해당 클래스가 문자열을 다루기 위한 클래스라는 것을 명시적으로 나타내는 데 사용된다. 

String의 생성자에는 이런 것들이 있다

생성자를 알아보기 전에 캐릭터 셋은 문자의 집합으로 한글, 일본어와 같이 특정 나라의 글자를 의미한다. 

디코딩은 일반적으로 알아보기 힘든 값들을 알아보기 쉽게 변환하는 것이다.

생성자 설명
String() 비어있는 String 객체 생성. String name = null;이라고 선언하는 게 이것보다 더 효율적임
String(byte[] bytes)  현재 사용중인 플랫폼의 캐릭터 셋을 사용해 제공된 byte 배열을 디코딩한 String 객체를 생성
String(byte[] bytes, Charset charset)  지정된 캐릭터 셋을 사용하여 제공된 byte 배열을 디코딩한 String 객체를 생성
String(byte[] bytes, String charsetName)  지정한 이름을 갖는 캐릭터 셋을 사용하여 지정한 byte 배열을 디코딩한 String 객체를 생성
String(byte[] bytes, int offset, int length) 현재 사용중인 플랫폼의 기본 캐릭터 셋을 사용해 지정한 byte 배열의 일부를 디코딩한 String 객체를 생성
String(byte[] bytes, int offset, int length, Charset charset) 지정된 캐릭터 셋을 사용하여 byte 배열의 일부를 디코딩한 String 객체를 생성
String(bytes[] bytes, int offset, int length, String charsetName) 지정한 이름을 갖는 캐릭터 셋을 사용하여 byte 배열의 일부를 디코딩한 String 객체 생성
String(char[] value) char 배열의 내용들을 붙여 String 객체 생성
String(char[] value, int offset, int count) char 배열의 일부 내용들을 붙여 String 객체 생성
String(int[] codePoints, int offset int count) 유니코드 코드 위치(Unicode code point)로 구성되어 있는 배열의 일부를 새로운 String 객체를 생성
String(String original) 매개 변수로 넘어온 String과 동일한 값을 갖는 String 객체를 생성, 복제본 생성
String(StringBuffer buffer) 매개 변수로 넘어온 StringBuffer 클래스에 정의되어 있는 문자열의 값과 동일한 String 객체 생성
String(StringBuilder builder)  매개 변수로 넘어온 StringBuilder 클래스에 정의되어 있는 문자열의 값과 동일한 String 객체를 생성

정말 많은 생성자들이 있지만 주로 사용되는 건 String(byte[] bytes)와 String(byte[] bytes, String charsetName)정도이다. 

String 문자열을 byte로 변환하기

생성자의 매개변수로 넘어가는 byte 배열은 현재의 문자열 값을 byte 배열로 변환하는 getBytes()라는 메소드들로 만들어진다.

리턴 타입 메소드 이름 및 매개 변수  설명
byte[] getBytes() 기본 캐릭터 셋의 바이트 배열을 생성
byte[] getBytes(Charset charset) 지정한 캐릭터 셋 객체 타입으로 바이트 배열을 생성
byte[] getBytes(String charsetName) 지정한 이름의 캐릭터 셋을 갖는 바이트 배열을 생성

캐릭터 셋을 잘 알고 있거나 같은 프로그램 내에서 문자열을 변환할 땐 맨 위 메소드를 사용하고, 다른 시스템에서 전달 받은 문자열을 byte 배열로 변환할 때에는 두번째나 세번째를 사용하는 것이 좋다. 

 

한글은 기본적으로 알파벳이 아니기 때문에 고유의 캐릭터 셋을 가진다. 

java.nio.Charset 클래스 API에 표준 캐릭터 셋이 정해져 있다. 

캐릭터 셋 이름 설명
US-ASCII 7비트 아스키
ISO-8859-1 ISO 라틴 알파벳
UTF-8 8비트 UCS 변환 포맷
UTF-16BE 16비트 UCS 변환 포맷, big-endian 바이트 순서
UTF-16LE 16비트 UCS 변환 포맷, little-endian 바이트 순서
UTF-16 16비트 UCS 변환 포맷, 바이트 순서는 byte-order mark라는 것에 의해 정해짐
EUC-KR 8비트 문자 인코딩으로, EUC의 일종이며 대표적인 "한글 완성형" 인코딩
MS949 Microsoftt에서 만든 "한글 확장 완성형" 인코딩

캐릭터 셋에 따라 한글을 다루는 바이트크기가 달라진다. EUC-KR의 경우 한글 한 글자당 2bytes, UTF-16은 3bytes이다.

 

  • byte 배열과 String 타입의 캐릭터 셋을 받는 생성자
  • getBytes() 메소드 중에서 String 타입의 캐릭터 셋을 받는 메소드

이 두 경우에서 존재하지 않는 캐릭터 셋의 이름을 지정하면 UnsupportedEncodingException이 발생할 수 있기 때문에 try-catch로 감싸주거나 메소드 선언시 throws 구문을 추가해줘야 한다. 

객체의 널 체크는 반드시 필요하답니다

모든 객체를 처리할 때에는 null 체크를 반드시 해야 한다. 

객체가 null이라는 건 아무런 초기화가 되어 있지 않으며, 클래스에 선언 되어 있는 어떤 메소드도 사용할 수 없다는 것을 의미해 메소드들은 모두 예외를 발생시킨다.

객체가 null인지 아닌지는 실행시에만 확인할 수 있어 문제가 있는 코드여도 컴파일은 된다.

 

객체의 null 체크는 equals가 아닌 ==와 != 연산자를 통해서 체크할 수 있다. 

String의 내용을 비교하고 검색하는 메소드들도 있어요

문자열의 길이를 확인하는 메소드 : length()

리턴 타입은 int형이고, 공백도 길이에 포함해서 값이 리턴된다.

배열도 객체이지만 메소드가 없는 특수한 객체로 배열 크기를 확인할 때 괄호가 없는 length를 쓰지만 이 외의 모든 클래스 객체들은 괄호가 있는 length()를 써줘야 한다. 

 

문자열이 비어있는지 확인하는 메소드 : isEmpty()

리턴 타입은 boolean이고, 비어있는 문자열이면 true 비어있지 않으면 false를 리턴한다.

공백 하나로 되어 있는 문자열이라도 false를 리턴한다.

 

문자열이 같은지 비교하는 메소드

리턴 타입 메소드 이름 및 매개 변수
boolean  equals(Object anObject)
boolean equalsIgnoreCase(String anotherStr)
int compareTo(String anotherStr)
int compareToIgnoreCase(String str)
boolean contentEquals(CharSequence cs)
boolean contentEquals(StringBuffer sb)

매개변수로 넘어온 값과 String 객체가 같은지를 비교하기 위한 메소드이다. 

IgnoreCase가 붙은 메소드는 대소문자 구분을 안한다.

 

자바에서는 객체를 재사용하기 위해 Constant Pool이라는 것이 만들어져 있고, String의 경우 동일한 값을 갖는 객체가 있으면 이미 만든 객체를 재사용한다. 따라서 큰 따옴표로 같은 문자열로 초기화된 두개의 String 객체가 있다면 두 객체는 같은 객체이다. 

다만, new 예약어를 통해 객체를 생성하면 Constant Pool의 값을 재활용하지 않아 별도의 객체를 생성하게 되어 같은 문자열을 갖는 String 객체여도 ==으로 비교시 false가 나오고, equals()로는 true가 나온다.

 

compareTo() 메소드는 보통 정렬(sorting)할 때 사용해 비교하려는 매개 변수로 넘겨준 String 객체가 알파벳 순으로 앞에 있으면 양수를, 뒤에 있으면 음수를 반환한다. 그리고 알파벳 순서만큼 그 숫자가 커진다.

 

contentEquals()는 매개변수로 넘어오는 객체가 String 객체와 같으지 비교하는 데 사용된다. 자세한 사항은 마지막 절에서 참고.

 

특정 조건에 맞는 문자열이 있는지를 확인하는 메소드 

리턴 타입 메소드 이름 및 매개 변수
boolean  startsWith(String prefix)
booelan startsWith(String prefix, int toffset)
boolean endsWith(String suffix)
boolean continas(CharSequence s)
boolean matches(String regax)
boolean regionMatches(boolean ignoreCase, int toffset, String other,int ooffset, int len)
boolean  regionMatches(int toffset, String other, int ooffset, int len)

가장 많이 사용하는 메소드는 startsWith()로 매개 변수로 넘겨준 값으로 시작하는지를 확인한다. 

endsWith()는 매개변수로 넘겨준 값으로 끝나는지를 확인해준다.

contains()는 매개변수로 넘겨준 값이 문자열에 존재하는지를 확인한다.

 

regionMatches()는 문자열 중에서 특정 영역이 매개변수로 넘어온 문자열과 동일한지 확인하는 메소드이다.

매개변수를 살펴보면

매개변수 의미
ignoreCase true일 경우 대소문자 구분을 하지 않고, 값을 비교
toffset 비교 대상 문자열의 확인 시작 위치
other 존재하는지 확인할 문자열
ooffset other 객체의 확인 시작 위치
len 비교할 char의 개수

자바에서 String의 char 위치는 배열과 마찬가지로 0부터 시작하며 공백도 하나의 char로 여긴다. 

toffset이나 ooffset이 음수이거나, toffset+len이나 ooffset+len이 대상의 길이보다 길 때는 무조건 결과가 false가 된다. 

String 내에서 위치를 찾아내는 방법은 여러 가지에요 

String 클래스에서 indexOf라는 단어가 포함되어 있는 메소드로 해당 객체의 특정 문자열이나 char가 있는 위치를 알 수 있다.

찾고자 하는 문자열이나 char가 없으면 이 메소드는 -1을 반환한다.

리턴 타입 메소드 이름 및 매개 변수
int indexOf(int ch)
indexOf(int ch, int fromIndex)
indexOf(String str)
indexOf(String str, int fromIndex)
lastIndexOf(int ch)
lastIndexOf(int ch, int fromIndex)
lastIndexOf(String str)
lastIndexOf(String str, int fromIndex)

indexOf는 앞에서부터(가장 왼쪽) 문자열이나 char를 찾으며, lastIndexOf는 뒤에서부터(가장 오른쪽) 문자열이나 char를 찾는다. 

int형 매개변수를 받는 메소드는 char형을 넘겨주어도 같은 정수형이라 자동으로 형변환이 되어 수행된다.

indexOf와 lastIndexOf의 시작위치인 fromIndex는 둘다 맨 왼쪽에서부터의 위치를 말하며 그 위치에서부터 각각 오른쪽으로, 왼쪽으로 값을 찾는다.

String의 값의 일부를 추출하기 위한 메소드들은 얘네들이다

정규표현식(Regular Expression)은 이메일을 점검하거나, 웹페이지의 URL을 점검하는 등의 작업을 쉽게 하기 위해서 공식에 따라 만든 식을 의미한다. JDK1.4부터 제공하며 java.util.regex 패키지의 Pattern 클래스 API에서 정보를 확인할 수 있다. 

 

특정 값을 추출하기 위한 메소드들에 대해 알아보자.

 

char 단위의 값을 추출하는 메소드

리턴 타입 메소드 이름 및 매개변수 설명
char charAt(int index) 특정 위치의 char 값을 리턴
void getChars(int srcBegin, int srcEnd, char[] dst, int dstBegin) 매개변수로 넘어온 dst라는 char 배열 내에 srcBegin에서 srcEnd에 있는 char를 저장. dst 배열의 시작위치는 dstBegin
int  codePointAt(int index) 특정 위치의 유니코드 값을 리턴
int codePointBefore(int index) 특정 위치 앞에 있는 char의 유니코드 값을 리턴
int  codePointCount(int beginIndex, int endIndex) 지정한 범위에 있는 유니코드 개수 리턴
int  offsetByCodePoints(int index, int codePointOffset) 지정된 index부터 offset이 설정된 인덱스를 리턴, 문자열 인코딩과 관련된 문제를 해결하기 위해 사용됨

 

char 배열의 값을 String으로 변환하는 메소드

리턴 타입 메소드 이름 및 매개 변수 설명
static String copyValueOf(char[] data) char 배열에 있는 값을 문자열로 변환
static String  copyValueOf(char[] data, int offset, int count) char 배열에 있는 값을 문자열로 변환. 단 offset 위치부터 count까지의 개수만큼만 문자열로 변환

static 메소드이기 때문에 현재 사용하는 문자열을 참조하여 생성하는 것이 아닌 static하게 호출하여 사용해야 한다.

 

String 값을 char 배열로 변환하는 메소드

리턴 타입 메소드 이름 및 매개 변수 설명
char[] toCharArray() 문자열을 char 배열로 변환하는 메소드

String 객체는 내부에 char 배열을 포함하고 있어 char 배열로 변환이 쉽다.

 

문자열의 일부 값을 잘라내는 메소드

리턴 타입 메소드 이름 및 매개 변수 설명
String substring(int beginIndex) beginIndex부터 끝까지 대상 문자열을 잘라 String으로 리턴
String substring(int beginIndex, int endIndex) beginIndex부터 endIndex까지 대상 문자열을 잘라 String으로 리턴
CharSequence subSequence(int beignIndex, int endIndex) beginIndex부터 endIndex까지 대상 문자열을 잘라 CharSequence 타입으로 리턴

indexOf()와 더불어 가장 많이 사용하는 메소드들이다. substring()을 사용할 땐 indexOf()도 함께 사용해 문자열을 자르는 것이 좋다.

 

문자열을 여러 개의 Strig 배열로 나누는 split 메소드

리턴 타입 메소드 이름 및 매개 변수 설명
String[] split(String regex) regex에 있는 정규 표현식에 맞추어 문자열을잘라 String의 배열로 리턴
String[] split(String regex, int limit) regex에 있는 정규 표현식에 맞추어 문자열을잘라 String의 배열로 리턴. 이때 String 배열의 크기는 limit보다 크면 안됨

정규 표현식을 사용하여 문자열을 나눌 땐 split()을 사용하고, 정규 표현식을 사용하지 않고 나눌 땐 java.util.StringTokenizer라는 클래스를 사용하면 된다. 

String 값을 바꾸는 메소드들도 있어요

문자열을 합치는 메소드와 공백을 없애는 메소드

리턴 타입 메소드 이름 및 매개 변수 설명
String concat(String str) 매개 변수로 받은 str을 기존 문자열의 우측에 붙인 새로운 문자열 객체를 생성하여 리턴
String trim() 문자열의 맨 앞과 맨 뒤에 있는 공백들을 제거한 문자열 객체를 리턴

자바에서 +로 문자열을 더할 수 있기 때문에 concat()은 쓸일이 거의 없다.

trim()을 한 결과의 length()를 구해서 문자열에 공백을 제외한 char값이 하나라도 있는지 확인해볼 수도 있다.

 

내용을 교체(replace)하는 메소드

리턴 타입 메소드 이름 및 매개 변수 설명
String replace(char oldChar, char newChar) 해당 문자열에 있는 oldChar의 값을 newChar로 대치
String replace(CharSequence target, CharSequence replacement) 해당 문자열에 있는 target과 같은 값을 replacement로 대치
String replaceAll(String regex, String replacement) 해당 문자열 내용 중 regex에 표현된 정규 표현식에 포함되는 모든 내용을 replacement로 대치
String replaceFirst(String regex, String replacement) 해당 문자열 내용 중 regex에 표현된 정규표현식에 포함되는 첫번째 내용을 replacement로 대치

이 메소드들을 수행한다고 해서 기존 문자열의 값은 바뀌지 않고 메소드의 리턴값에만 교체된 것이 적용된다.

 

특정 형식에 맞춰 값을 치환하는 메소드

리턴 타입 메소드 이름 및 매개 변수 설명
static String format(String format, Object...args) format에 있는 문자열의 내용 중 변환해야 하는 부분을 args의 내용으로 변경
static String format(Locale l, String format, Object ...args) format에 있는 문자열 내용 중 변환해야 하는 부분을 args의 내용으로 변경. 단 첫 매개 변수인 Locale 타입의 l에 선언된 지역에 맞추어출력

Locale은 지역적으로 다른 표현 형식을 제공하기 위한 것으로 지정해주지 않으면 기본적으로 자바 프로그램이 수행되는 OS의 지역 정보를 기본으로 따른다.

format() 메소드는 정해진 기준에 맞춘 문자열이 있으면 그 기준에 있는 내용을 변환해준다.

정수형은 %d, 소수점이 있는 숫자는 %f 등 값에 맞는 기준이 있다. 메소드에 대해서는 java.util.Formatter 클래스의 API에 설명되어 있다. 

format 안의 기준과 args로 넘겨주는 인자들의 개수가 맞지 않으면 예외가 발생한다.

 

대소문자를 바꾸는 메소드

리턴 타입 메소드 이름 및 매개 변수 설명
String toLowerCase() 모든 문자열의 내용을 소문자로 변경
String toLowerCase(Locale locale) 지정한 지역 정보에 맞추어 모든 문자열의 내용을 소문자로 변경
String  toUpperCase() 모든 문자열의 내용을 대문자로 변경
String toUpperCase(Locale locale) 지정한 지역 정보에 맞추어 모든 문자열의 내용을 대문자로 변경

 

기본 자료형을 문자열로 변환하는 메소드

리턴 타입 메소드 이름 및 매개 변수
static String valueOf(boolean b)
static String valueOf(char c)
static String valueOf(char[] data)
static String valueOf(char[] data, int offset, int count)
static String valueOf(double d)
static String valueOf(float f)
static String valueOf(int i)
static String valueOf(long l)
static String valueOf(Object obj)

기본 자료형들을 String 타입으로 변환해주는 메소드이다. valueOf()로도 바꿔줄 수 있지만

byte b = 1; String byte1 = b +"";로 변환해줄 수도 있다.

 

valueOf() 메소드의 매개 변수로 null인 객체가 들어오면 "null"을 출력해주고, null이 아닌 객체가 들어오면 toString()의 결과를 리턴해준다.

절대로 사용하면 안 되는 메소드가 하나 있어요!!!!

intern()이라는 메소드는 자바로 구현되지 않고 C로 구현되어 있는 native 메소드 중에 하나로 시스템의 심각한 성능 저하를 발생시킬 수도 있기 때문에 사용을 지양해야 한다.

 

new String()으로 생성한 문자열 객체라고 해도, Constant pool에 해당 값이 있으면 풀에 있는 값을 참조하는 객체를 리턴한다. 따라서 intern()을 수행한 뒤에 나오는 문자열은 equals()가 아닌 ==으로 동일성을 확인할 수 있다.

 

==으로 비교하는 것이 equals()보다 훨신 빠르지만 새로운 문자열을 계속해서 만들어내는 프로그램에서 intern()메소드를 사용해 문자열 풀에 계속 새로운 문자열을 저장하도록 만들면 저장공간에 한계로 별도로 메모리 청소를 하는 단계가 추가된다. 결과적으로 자바 시스템 성능에 악영향을 끼치기 때문에 intern()을 사용하지 않는 것이 좋다.

immutable한 String의 단점을 보완하는 클래스에는 StringBuffer와 StringBuilder가 있다

String은 immutable(불변의)한 객체로 한번 만들어지면 더이상 그 값을 바꿀 수 없다.

String 객체에 더하기 연산을 해주면 기존의 객체는 버려지고 더하기 연산된 객체가 새로 생겨나는 것이다.

따라서, 하나의 String 객체를 만들어 계속 더하는 작업을 하면, 계속 쓰레기를 만들게 되어 GC(가비지 컬렉터)의 대상이 된다.

이런 String 클래스의 단점을 보완하기 위해 나온 클래스가 StringBuffer와 StringBuilder이다.

두 클래스에서 제공하는 메소드는 동일하다.

 

StringBuffer는 Thread safe하고, StringBuilder는 Thread safe하지 않다. 

Thread safe한게 더 안전하지만, 속도는 더 느리다.

두 클래스는 +연산으로 문자열을 더할 수 없어 문자열을 더할 때 새로운 객체를 생성해내지 않는다.

+대신에 append()라는 메소드로 문자열을 더한다.

append() 메소드의 매개변수에는 기본 자료형, 참조 자료형 둘다 들어갈 수 있어 어떤 값이든 매개변수로 넘겨줄 수 있다.

또 append() 메소드는 리턴될 때 해당되는 객체가 리턴되기 때문에, 세미콜론이 나오기 전까지 메소드를 여러개 붙여서 사용해도 된다.

 

String과 StringBuilder, StringBuffer는 모두 문자열을 다루고 CharSequence 인터페이스를 구현했다는 공통점이 있다. 

이 셋 중 하나의 클래스를 사용해 매개변수로 받는 작업을 할 때 CharSequence 타입으로 받는 것이 좋다.

 

일반적으로 하나의 메소드 내에서 문자열을 생성하여 더할 경우 => StringBuilder를 사용해도 무관

어떤 클래스에 문자열을 생성하여 더하기 위한 문자열을 처리하기 위한 인스턴스 변수가 선언되었고, 여러 쓰레드에서 이 변수를 동시에 접근하는 경우 => StringBuffer를 사용해야 함


정리해 봅시다

1. String 클래스는 final 클래스인가요? 만약 그렇다면, 그 이유는 무엇인가요?

final 클래스로, 확장할 수 없게 하기 위해서이다.

2. String 클래스가 구현한 인터페이스에는 어떤 것이 있나요?

Serializable, Comparable, CharSequence

3. String 클래스의 생성자 중에서 가장 의미없는 (사용할 필요가 없는) 생성자는 무엇인가요?

기본 생성자이다. 생성된 객체는 해당 변수에 새로운 값이 할당되자마자 GC의 대상이 되어버리기 때문이다.

4. String 문자열을 byte 배열로 만드는 메소드 이름은?

getBytes()

5. String 문자열을 메소드를 호출하기 전에 반드시 점검해야 하는 사항은 무엇인가요?

null 객체인지 체크해야 한다.

6. String 문자열의 길이를 알아내는 메소드는 무엇인가요?

length()

7. String 클래스의 equals() 메소드와 compareTo() 메소드의 공통점과 차이점은?

둘다 두 문자열이 같은지 비교하는 메소드이다. equals()는 같은 문자열을 갖는지 확인해 boolean 값을 리턴하고, compareTo()는 매개변수로 넘겨준 문자열 객체가 알파벳 순으로 앞에 있으면 int형 양수, 뒤에 있으면 음수를 리턴해준다. 숫자 값은 알파벳 순서만큼 커진다.

8. 문자열이 "서울시"로 시작하는지를 확인하려면 String의 어떤 메소드를 사용해야 하나요?

startsWith()

9. 문자열에 "한국"이라는 단어의 위치를 찾아내려고 할 때에는 String의 어떤 메소드를 사용해야 하나요?

indexOf(), contains(), matches()

10. 9번 문제의 답에서 "한국"이 문자열에 없을 때 결과 값은?

contains()와 matches()는 false, indexOf()는 -1

11. 문자열의 1번째부터 10번째 위치까지의 내용을 String으로 추출하려고 할 때 어떤 메소드를 사용해야 하나요?

substring(1, 10);

12. 문자열의 모든 공백을 * 표시로 변환하려고 합니다. 어떤 메소드를 사용하는 것이 좋을까요?

replace(" ", "*");

13. String의 단점을 보완하기 위한 두 개의 클래스는 무엇인가요?

StringBuffer, StringBuilder

14. 13번의 답에서 문자열을 더하기 위한 메소드의 이름은 무엇인가요?

append()

728x90