String
String 클래스 선언 부분
public final class String extneds Object
implements Serializable, Compareable<String>, CharSequence
String의 클래스를 보면 final
로 선언된 클래스이다.
final
가 붙은 클래스는 확장할 수 없다. (즉, 자식 클래스를 만들 수 없다)
implements Serializable, Compareable<String>, CharSequence
Serialzable
: 이 인터페이스는 구현해야 하는 메소드가 하나도 없는 특별한 인터페이스이다. 해당 객체를 파일로 저장하거나 다른 서버에 전송 가능한 상태가 되게 해준다.Compareable<String>
: 이 인터페이스는compareTo()
라는 메소드 하나만 선언되어 있다. 이 메소드는 매개 변수로 넘어가는 객체와 현재 객체각 같은 지 비교하는 데 사용된다.리턴 타입
은int
다. 같으면0
, 순서상 앞에 있으면-1
, 뒤에 있으면1
을 리턴한다.CharSequence
: 이 인터페이스는 해당 클래스가 문자열을 다루기 위한 클래스라는 것을 명시적으로 나타내는 데 사용된다.
String의 생성자
String name = "Dante";
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(btyte[] bytes, int offset, int length) | 현재 사용중인 플랫폼의 기본 캐릭터 셋을 사용하여 지정한 byte 배열의 일부를 디코딩한 String 객체를 생성한다. |
String(btyte[] bytes, int offset, int length, Charset charset) | 지정된 캐릭터 셋을 사용하여 byte 배열의 일부를 디코딩한 String 객체를 생성한다. |
String(btyte[] bytes, int offset, int length, String charsetName)) | 지정한 이름을 갖는 개릭터 셋을 사용하여 byte 배열의 일부를 디코딩한 String 객체를 생성한다. |
String(char[] vlaue) | 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, StringcharsetName)
위의 생성자가 많이 쓰이는 이유는 영어권 국가가 아닌 한글을 사용하는 국가권이기 때문이다.
String 문자열을 byte로 변환하기
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
byte[] | getByte() | 기본 캐릭터 셋의 바이트 배열을 생성한다. |
byte[] | getByte(Charset charset | 지정한 캐릭터 셋 객체 타입으로 바이트 배열을 생성한다. |
byte[] | getBytes(String charsetName) | 지정한 이름의 캐릭터 셋을 갖는 바이트 배열 생성한다. |
“캐릭터 셋”
에 대해서 좀 자세히 살펴보면..
한글은 기본적으로 알파벳이 아니기 때문에 고유의 캐릭터 셋을 가진다.
가끔 웹 페이지를 서핑하다가 보면 한글이 깨지는 경우를 보았을 것이다.
한글이 깨지는 이유는 브라우저에서 생각하는 캐릭터 셋과 웹 페이지에 지정된 캐릭터 셋이 다르기 때문이다.
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의 일종이며 대표적인 “한글 완성형” 인코딩 |
MS9949 | MIcrosoft에서 만든 “한글 확장 완성형” 인코딩 |
getBytes()
메소드와 String 생성자 예제
public class StringSample{
public static void main(String[] args){
StringSample sample = new Stringsample();
sample.convert();
}
public void convert() {
try {
String korean = "한글";
byte[] array1 = korean.getBytes();
for (byte data : array1) {
System.out.print(data + " ");
}
System.out.println();
String korean2 = new String(array1);
System.out.print(korean2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- “한글” 이라는 값을 String 객체인 korean을 생성했다.
getBytes()
라는 메소드를 사용하여 korean을 byte 배열로 만들었다.- 만들어진 byte 배열에 어떤 값들이 있는지 살펴보기 위해서 for 루프를 사용하여 각각의 byte값을 출력하도록 해 놓았다.
- byte 배열을 갖고 String객체를 만들기 위해서 byte배열(array1)을 매개 변수로 갖는 String 객체를 생성하고, 그 문자열을 출력했다.
- 결과
-19 -107 -100 -22 -72 -128
한글
나의 개발툴에서 캐릭터 셋은 UTF-8로 설정되어 있어서 위와 같은 값이 나온다.
만약 UTF-16으로 해놨다면 어떤 결과가 나올까 ?
- 결과
-57 -47 -79 -37
한글
이제 “UTF-16”
이라는 캐릭터 셋으로 변환해보자.
- UTF-16으로 변환
public void convertUTF16(){
try {
String korean = "자바의 신 최고!!!";
byte[] array1 = korean.getBytes("UTF-16");
printByteArray(array1);
String korean2 = new String(array1);
System.out.print(korean2);
}catch(Exception e) {
e.printStackTrace();
}
}
- 결과
-2 -1 -43 92 -82 0
���\�
한글 이라는 글자가 위와 같이 깨져서 보인다.
이런 현상을 방지하기 위해서는 byte 배열로 생성할 때 사용한 캐릭터 셋을 문자열로 다시 전환할 때에도 동일하게 사용해야만 한다.
따라서 convertUTF16()
메소드는 다음과 같이 변경해야만 한다.
public void convertUTF16(){
try {
String korean = "자바의 신 최고!!!";
byte[] array1 = korean.getBytes("UTF-16");
printByteArray(array1);
String korean2 = new String(array1, "UTF-16");
System.out.print(korean2);
}catch(Exception e) {
e.printStackTrace();
}
}
UTF-16
의 결과6 바이트
를 사용한다.
-2 -1 -43 92 -82 0
한글
한글을 byte 배열로 만들 때 어떤 캐릭터 셋을 쓰느냐에 따라서 배열의 크기가 다르다.
EUC-KR
의 결과EUC-KR
의 경우 한글 두 글자를 표현하기 위해서4 바이트
를 사용한다.
-57 -47 -79 -37
한글
UTF-8
의 결과UTF-8
의 경우 한글 두 글자를 표현하기 위해서4 바이트
를 사용한다.
-19 -107 -100 -22 -72 -128
한글
객체의 Null 체크는 반드시 필요
public class StringNull{
public static void main(String[] args){
StringNull sample = new StringNull();
sample.nullCheck(null);
}
public boolean nullCheck(String text) { // text = null
int textLength = text.length(); // text.length()는 Error
System.out.println(textLength);
if(text == null) return true; // text 값이 null이면 true
else return false; // 아니면 false
}
}
- 결과
Exception in thread "main" java.lang.NullPointerException
at main.Chapter15.StringNull.nullCheck(StringNull.java:11)
at main.Chapter15.StringNull.main(StringNull.java:6)
NullPointerException
뜬다. 즉, null인 객체의 메소드에 접근하면 이런 에러를 발생시킨다.
nullCheck
를 체크 예제
public class StringNull{
public static void main(String[] args){
StringNull sample = new StringNull();
sample.nullCheck2(null);
}
public boolean nullCheck2(String text) {
if (text == null) {
return true;
} else{
int textLength = text.length();
System.out.println(textLength);
return false;
}
}
}
- 결과
true 이므로 함수 종료가 된다.
null 체크하는 습관은 무지 중요하다. 얼핏 들었다. 이 에러가 얼마나 무서운건지…
메소드의 매개 변수로 넘어오는 객체가 널이 될 확률이 조금이라도 있다면 반스디 한 번씩 확인하는 습관을 갖고 있어야만 한다.
String 클래스에 메소드들
String의 내용을 비교하고 검색하는 메소드들
String 클래스 객체의 내용을 비교하고 검색하는 메소드들을 알아보자. 이 메소드들을 조금 더 세밀하게 분류하면 다음과 같다
- 문자열의 길이를 확인하는 메소드
- 문자열이 비어 있는지 확인하는 메소드
- 문자열이 같은지 비교하는 메소드
- 특정 조건에 맞는 문자열이 있는지를 확인하는 메소드
public class StringCompare {
public static void main(String[] args){
StringCompare sample = new StringCompare();
}
}
➡️문자열의 길이를 확인하는 메소드
리턴타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
int | length() | 문자열의 길이를 리턴한다. |
문자열의 길이를 확인할 때에는 length()
메소드를 사용하면된다.
배열도 객체이긴 하지만 메소드는 없는 특수한 객체다.
그래서 배열의 크기를 확인할 때에는 괄호가 없는 length를 사용한다.
하지만 그 이외의 클래스는 메소드를 호출해야하며, String 객체의 길이를 확인하기 위해서는 length()
라는 메소드를 사용해야만 한다.
public void checkString(){
String text = "You must know String class";
System.out.println("text.length() =" + text.length());
}
- 결과
text.length() =27
➡️문자열이 비어 있는지 확인하는 메소드
리턴타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
boolean | isEmpty() | 문자열이 비어 있는지를 확인한다. 비어 있으면 true를 리턴한다. |
문자열의 길이가 0인지 아닌지를 확인하는 것보다, 이 메소드를 이용하는 것이 훨씬 간단함.
System.out.println("text.isEmpty()=" + text.isEmpty());
- 결과
text.isEmpty() = false
➡️문자열이 같은지 비교하는 메소드
String 클래스에서 제공하는 문자열이 같은지 비교하는 메소드들은 많다.
리턴 타입 | 메소드 이름 및 매개 변수 |
---|---|
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가 붙은 메소드들은 대소문자 구분을 할지 안할지 여부만 다르다.
➡️equals()
equals()
예제
public void checkCompare(){
String text = "Check value";
String text2 = "Check value";
if (text == text2) {
System.out.println("text==text2 result is same.");
}else{
System.out.println("text==text2 result is different");
}
if(text.equals("Check value")){
System.out.println("text.equals(text2) result is same");
}
}
- 결과
text==text2 result is same.
text.equals(text2) result is same
equals()
메소드는 메소드로 비교해야하는게 아닌가 ?
String
클래스도 기본적으로 == 비교가 아닌 equals() 메소드를 사용해서 비교를 해야만 한다.
하지만..
이렇게 결과가 나오는 이유는 자바에서 Constant Pool
이라느 것이 존재하기 때문이다.
Constant Pool
은 자바에서 객체들을 재사용하기 위해서 만들어져 있고, String의 경우 동일한 값을 갖는 객체가 있으면 이미 만든 객체를 재사용한다.
따라서 text
와 text2
객체는 실제로는 같은 객체다.
- String text2 = “Check value” → String text2 = new String(“Check value”); 변경
public void checkCompare(){
String text = "Check value";
String text2 = new String("Check value"); // String text2 = "Check value"; 에서 변경
if (text == text2) {
System.out.println("text==text2 result is same.");
}else{
System.out.println("text==text2 result is different");
}
if(text.equals("Check value")){
System.out.println("text.equals(text2) result is same");
}
}
- 결과
text==text2 result is different
text.equals(text2) result is same
equalsIgnoreCase()
String text3 = "check value";
if(text.equalsIgnoreCase(text3)){
System.out.println("text.equalsIgnoreCase(text3) result is same.");
}
- 결과
text.equalsIgnoreCase(text3) result is same.
➡️String 클래스의 compareTo() 메소드
compareTo()
메소드는 Compareable
인터페이스에 선언되어 있다.
public void checkCompareTo(){
String text = "a";
String text2 = "b";
String text3 = "c";
System.out.println(text2.compareTo(text));
System.out.println(text2.compareTo(text3));
System.out.println(text.compareTo(text3));
}
- 결과
1
-1
-2
compareTo()
메소드는 보통 정렬(sorting)을 할 때 사용한다.
따라서 true, false의 결과가 아니라 비교하려는 매개 변수로 넘겨준 String 객체가 알파벳 순으로 앞에 있으면 양수를, 뒤에 있으면 음수를 리턴한다.
그리고 알파벳 순서만큼 그 숫자값은 커진다.
CompareToIgnoreCase()
메소드는 equalsIgnoreCase()
메소드와 마찬가지로, 대소문자 구분을 하지 않고 compareTo()
메소드를 수행하는 것과 같다.
➡️특정 조건에 맞는 문자열이 있는지를 확인하는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 |
---|---|
boolean | startWith(String prefix) |
boolean | startsWith(String prefix, int toffset) |
boolean | endsWith(String suffix) |
boolean | contains(CharSequence s) |
boolean | matches(String regex) |
boolean | regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) |
boolean | regionMatches(int toffset, String other, int ooffset, int len) |
startsWith()
와endsWith()
메소드를 이용하여 문자열로 시작하는 시작하는 개수와 끝나는 개수를 세어 보는 예제
public class StringCheck {
public static void main(String[] args) {
StringCheck sample = new StringCheck();
String addresses[] = new String[]{
"서울시 구로구 신림동", "경기도 성남시 분다옹 정자동 개발 공장", "서울시 구로구 개봉동",
};
sample.checkAddress(addresses);
}
public void checkAddress(String[] addresses){
int startCount = 0;
int endCount = 0;
String startText = "서울시";
String endText = "동";
for (String address : addresses) {
if (address.startsWith(startText)) {
startCount++;
}
if (address.endsWith(endText)) {
endCount++;
}
}
System.out.println("Start with " + startText + " count is " + startCount);
System.out.println("Ends with " + endText + " count is " + endCount);
}
}
startsWith(”서울시”)
를 호출하면 “서울시”
의 주소를 갖는 모든 문자열을 쉽게 찾을 수 있다.
endsWith("동")
메소드는 매개 변수로 넘어온 값으로 해당 문자열을 끝나는지를 확인하는 메소드다.
- 결과
Start with 서울시 count is 2
Ends with 동 count is 2
“서울시” 로 시작하는 문자열 2개, “동”으로 끝나는 문자열 2개를 카운팅한 결과다.
그렇다면 중간값을 찾는 메소드도 있을까? 있다.
- contains() 메소드
public void containsAddress(String[] adresses){
int containCount = 0;
String containText = "구로";
for(String address : adresses){
if(address.contains(containText)){
containCount++;
}
}
System.out.println("Contains " + containText + " count is " + containCount);
}
regionMatches()
메소드는 문자열 중에서 특정 영역이 매개 변수로 넘어온 문자열과 동일한지를 확인하는 데 사용된다.
리턴 타입 | 메소드 이름 및 매개 변수 |
---|---|
boolean | regionMatches(boolean ignoreCase, int toffset, String other, int ooffset, int len) |
boolean | regionMatches(int toffset, String other, int ooffset, int lne) |
- 첫 번째 형태는 대소문자를 구분할지 여부를 지정할 수 있다.
- 두 번째 형태는 지정 자체가 불가능하다.
매개 변수 어떤 값을 뜻하는지 알아보자.
매개 변수 | 의미 |
---|---|
ignoreCase | true일 경우 대소문자 구분을 하지 않고, 값을 비교한다. |
toffset | 비교 대상 문자열의 확인 시작 위치를 지정한다. |
other | 존재하는지를 확인할 문자열을 의미한다. |
ooffset | other 객체의 확인 시작 위치를 지정한다. |
len | 비교할 char의 개수를 지정한다. |
public void checkMatch(){
String text = "This is a text";
String compare1 = "is";
String compare2 = "this";
System.out.println(text.regionMatches(2, compare1, 0, 1)); // 매개 변수가 4개인 메소드
System.out.println(text.regionMatches(5, compare1, 0, 2)); // 매개 변수가 4개인 메소드
System.out.println(text.regionMatches(true, 0, compare2, 0, 4)); // 매개 변수가 5개인 메소드
}
T | h | i | s | i | s | a | t | e | x | t | |||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 |
text
값이 있고, 비교할 compare1
과 compare2
가 있다.
text.regionMatches(2, compare1, 0, 1);
첫 번째 regionMatch()
메소드를 자세히 살펴보자. 첫 번째 매개 변수인 2라는 위치 값은 제대로 맞았다.
비교하려고 하는 compare1
의 값이 “is”
다.
그런데 세 번째 매개 변수가 0이고, 네 번째 매개 변수가 1이기 때문에 비교하려는 것은 “i”
인지 아닌지만 확인하면 된다.
따라서 첫 번째 출력문의 결과는 true
이다.
- 결과
true
true
true
그런데 regionMatches()
메소드를 잘못 사용하면, 원하는 결과를 얻지 못할 수도 있다.
regionMatchs(boolean ignoreCase, int toffset, String other, int ooffset, int len)
여러 매개 변수 중에서 값이 다음과 같은 경우에는 “무조건 false”로 나온다.
- toffset이 음수일 때
- ooffset이 음수일 때
- toffset + len이 비교 대상의 길이보다 클 때
- ooffset + len이 other 객체의 길이보다 클 때
- ignoreCase가 false인 경우에는 비교 범위의 문자들 중 같은 위치(index)에 있는 char가 다를 때
- ignoreCase가 true인 경우에는 비교 범위의 문자들을 모두 소문자로 변경한 후 같은 위치(index)에 있는 char가 달라야 한다.
String내에서 위치를 찾아내는 방법은 여러 가지이다.
“Java technology is both a programming language and a platform.”
- 이 문장에서 “both”라는 단어가 시작하는 위치를 알고 싶을때 어떻게 해야할까 ?
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() : 뒤에서부터(가장 오른쪽부터) 찾는다.
int를 매개 변수로 갖는 메소드는 어떻게 사용해야 할까 ?
이 메소드의 매개 변수로 char를 넘겨주면 자동으로 형 변환이 일어난다.
public void checkIndexOf() {
String text = "Java technology is both a programming language and a platform.";
System.out.println(text.indexOf('a'));
System.out.println(text.indexOf("a "));
System.out.println(text.indexOf('a', 20));
System.out.println(text.indexOf("a ", 20));
System.out.println(text.indexOf('z'));
}
- 필자가 말 한대로, ‘a’의 형태로 매개 변수를 넘겨주어도 컴파일 및 실행하는 데 전혀 문제가 발생하지 않는다.
- String 타입의 매개 변수를 넘겨 주었으며, a 뒤에는 공백이 하나 있다.
- 세 번째와 네 번째 출력문은 모두 text 문자열의 20번째 자리부터 값을 확인한다.
- 마지막에 있는 출력문은 이 문장에 없는 “z”를 찾은 결과를 출력한다.
| | J | a | v | a | | t | e | c | h | n | o | … | | — | — | — | — | — | — | — | — | — | — | — | — | — | | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | … |
- 결과
1
3
24
24
-1
“Java”의 “a” 중 첫 번째 “a”가 가장 앞에 있는 “a”이므로 첫 출력문의 결과는 1이다.
두 번째 출력문은 a 다음에 공백이 있는 위치를 찾는 것이므로, “java” 단어 뒤에 공백이 있으므로 3이다.
세 번째와 네 번째의 결과는 20번째 자리부터 찾는 작업을 하고, 제일 처음 나온 관사 a의 위치가 24번째이므로 결과가 동일하다.
b | o | t | h | a | p | r | o | g | … | ||
---|---|---|---|---|---|---|---|---|---|---|---|
19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | … |
마지막 indexOf()
의 경우 z가 문장에 없으므로 -1을 출력하였다.
- 문자열의 가장 뒤부터(오른쪽부터) 검색하는
lastIndexOf()
메소드의 예제
public void checklastIndexOf(){
String text = "Java technology is both a programming language and a platform.";
System.out.println(text.lastIndexOf('m'));
System.out.println(text.lastIndexOf("a "));
System.out.println(text.lastIndexOf('a', 20));
System.out.println(text.lastIndexOf("a ", 20));
System.out.println(text.lastIndexOf('z'));
}
| a | | f | l | a | t | f | o | r | m | . | | | — | — | — | — | — | — | — | — | — | — | — | — | | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 |
- 결과
55
51
3
3
-1
- 검색 시작 위치(fromIndex)는 무엇인가 ?
시작 위치는 가장 왼쪽에서부터의 위치를 말한다. 그 위치부터 왼쪽으로 값을 찾는다.
String의 값을 일부를 추출하기
위에서 문자열을 찾는 방법을 했다. 이제는 찾는 부분을 추출하고 그 값이 존재하는지를 알 수 있는 메소드를 알아보자.
- char 단위의 값을 추출하는 메소드
- char 배열의 값을 String으로 변환하는 메소드
- String의 값을 char 배열로 변환하는 메소드
- 문자열의 일부 값을 잘라내는 메소드
- 문자열을 여러 개의 String배열로 나누는 메소드
➡️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지만, 이 값을 char로 형 변환하면 char 값을 출력할 수 있다. |
int | codePointBefore(int index) | 특정 위치 앞에 있는 char의 유니코드 값을 리턴한다. 리턴 타입은 int지만, 이 값을 char로 형 변환하면 char 값을 출력할 수 있다. |
int | codePointCount(int beginIndex, int endIndex) | 지정한 범위에 있는 유니코드 개수를 리턴한다. |
int | offsetByCodePoints(int index, int codePointOffset) | 지정한 index 부터 오프셋(offset)이 설정된 인덱스를 리턴한다. |
offsetByCodePoints()
메소드는 문자열 인코딩과 관련된 문제를 해결하기 위해서 사용된다.
보통 자주 사용되는 메소드는 charAt()
메소드다.
charAt()
메소드를 사용하여 indexOf()
로 탐색한 위치에 있는 값을 확인하는 작업
public void charAtIndexOf(){
String text = "한글";
char ch = text.charAt(text.indexOf("한"));
char ch2 = text.charAt(1);
System.out.println(ch);
System.out.println(ch2);
}
- 결과
한
글
➡️char 배열의 값을 String으로 변환하는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
static String | copyValueOf(char[] data) | char 배열에 있는 값을 문자열로 변환한다. |
static String | copyValueOf(char[] data, int offset, in count) | char 배열에 있는 값을 문자열로 변환한다. 단 offset 위치부터 count까지의 개수만큼만 문자열로 변환한다. |
copyValueOf()
라는 메소드는 사용 빈도수가 많지 않다. 하지만 알면 유용하게 쓸 수 있다.
이 메소드는 static
메소드이기 때문에 현재 사용하는 문자열을 참조하여 생성하는 것이 아닌, static하게 호출하여 사용해야 한다.
예제를 통해서 알아보기
char values[] = new char[]{'J','a','v','a'};
String javaText = String.copyValueOf(values);
- 결과
Java
➡️String의 값을 char 배열로 변환하는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
char[] | toCharArray() | 문자열을 char 배열로 변환하는 메소드 |
➡️문자열의 일부 값을 잘라내는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
String | substring(int beginIndex) | beginIndex부터 끝까지 대상 문자열을 잘라 String으로 리턴한다. |
String | substring(int beginIndex, int endIndex) | beginIndex부터 endIndex까지 대상 문자열을 잘라 String으로 리턴한다. |
CharSequence | subSequence(int beginIndex, int endIndex) | beginIndex부터 endIndex까지 대상 문자열을 잘라 CharSequence 타입으로 리턴한다. |
- 예제
public void checkSubstring(){
String text = "Java technology";
String technology = text.substring(5);
System.out.println(technology);
}
- 결과
technology
J | a | v | a | t | e | c | h | n | o | l | o | g | y | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
요구 사항 중 tech라는 단어만 잘라달라고한다. 그렇다면 어떻게 해야할까 ?
public String checkSubstring2(){
String text = "Java technology";
String tech = text.substring(5, 4);
return tech;
}
- 결과
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: String index out of range: -1
at java.base/java.lang.String.checkBoundsBeginEnd(String.java:3319)
at java.base/java.lang.String.substring(String.java:1874)
예외가 발생한다. → 왜?
예외를 읽어보면 첫 번째 매개 변수는 beginIndex이고, 두 번째 매개 변수는 데이터의 길이가 아닌 substring이 끝나는 위치를 말한다.
따라서 다음과 같이 끝나느 위치를 5+4인 9로 지정해줘야한다.
String tech = text.substring(5,9);
➡️문자열을 여러 개의 String 배열로 나누는 split 메소드
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
String[] | split(String regex) | regex에 정규 표현식에 맞추어 문자열을 잘라 String의 배열로 리턴한다. |
String[] | split(String regex, int limit | regex에 있는 정규 표현식에 맞추어 문자열을 잘라 String의 배열로 리턴한다. 이때 String 배열의 크기는 limit보다 커서는 안된다. |
문자열을 여러 개의 문자열의 배열로 나누는 방법은 String 클래스에 선언된 split()
메소드를 사용하는 것과 java.util.StringTokenizer
라는 클래스를 사용하는 것이다.
정규 표현식을 사용하여 문자열을 나누려고 한다면 String 클래스의 split()
메소드를 사용하면 된다.
그렇지 않고 특정 String으로 문자열을 나누려고 한다면 StringTokenizer
클래스를 사용하는 것이 편리하다.
- 정규 표현식 사용 문자열 나누기 → String 클래스의 split() 사용
- String문자열을 나누기 →
StringTokenizer
클래스 사용 - 특정 알파벳이나 기호 하나로 문자열을 나누려고한다면 → 둘 다 사용해도 됨. 상관없음
public void checkSplit() {
String text ="Java technology is both a programming language and a platform.";
String[] splitArray = text.split(" ");
for (String temp : splitArray) {
System.out.println(temp);
}
}
- 결과
Java
technology
is
both
a
programming
language
and
a
platform.
String 값을 바꾸는 메소드
String 값을 바꾸는 메소드는 무엇이 있을까 ?
- 문자열을 합치는 메소드와 공백을 없애는 메소드
- 내용을 교체(replace)하는 메소드
- 특정 형식에 맞춰 값을 치환하는 메소드
- 대소문자를 바꾸는 메소드
- 기본 자료형을 문자열로 변환하는 메소드
➡️문자열을 합치는 메소드와 공백을 없애는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
String | concat(String str) | 매개 변수로 받은 str을 기존 문자열의 우측에 붙인 새로운 문자열 객체를 생성하여 리턴한다. |
String | trim() | 문자열의 맨 앞과 맨 뒤에 있는 공백들을 제거한 문자열 객체를 리턴한다. |
concat()
메소드는 잘 사용하지 않는다. 대체제로는 StringBuffer나 StringBuilder클래스를 사용한다.
trim()
메소드는 공백을 제거할 때 매우 유용하게 사용된다.
trim()
메소드 예제
public void checkTrim() {
String strings[] = new String[]{" a", " b ", " c", "d ", "e f", " "};
for (String string : strings) {
System.out.print("[" + string + "]" + ", ");
}
System.out.println();
for(String string: strings) {
System.out.print("[" + string.trim() + "]" + ", ");
}
}
- 결과
[ a] [ b ] [ c] [d ] [e f] [ ]
[a] [b] [c] [d] [e f] []
trim()
메소드의 용도- 작업하려는 문자열이 공백으로만 이루어진 값인지, 아니면 공백을 제외한 값이 있는지 확인하기에 딱 좋은 용도이다.
- if문을 통과하여 “OK”를 출력하면 해당 문자열은 공백을 제외한 char 값이 하나라도 존재함.
String text = " a ";
if(text != null && text.trim().length() > 0 ){
System.out.println("OK");
}
- 결과
OK
null 체크를 하지않으면, 값이 null인 객체의 메소드를 호출하면 NullPointerException
이 발생한다.
String을 조작하기 전에 null 체크하는 습관을 들이자.
➡️내용을 교체(replace)하는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
String | replace(char oldChar, char new Char) | 해당 문자열에 있는 oldChar의 값을 new Char로 대치한다. |
String | replace(CharSequence target, charSequence replacement) | 해당 문자열에 있는 target과 같은 값을 replacement로 대치한다. |
String | replaceAll(String regex, String replacement) | 해당 문자열의 내용 중 regex에 표현된 정규 표현식에 포함되는 첫번째 내용을 replacement로 대치한다. |
String | replaceFirst(String regex, String replacement) | 해당 문자열의 내용 중 regex에 표현된 정규 표현식에 포함되는 첫번째 내용을 replacement로 대치한다. |
replace
메소드를 수행한다고 해서, 기존 문자열의 값은 바뀌지 않는다.
- replace 예제
public void checkReplace(){
String text = "The String class represents character strings.";
System.out.println(text.replace('s', 'z')); // `s` -> `z` 로 바꿔서 출력
System.out.println(text); // 원래 text가 바뀌었는지, 안바뀌었는지 확인하기
System.out.println(text.replace("tring", "trike"));
System.out.println(text.replaceAll(" ", "|"));
System.out.println(text.replaceFirst(" ", "|"));
}
- text 객체에 있는 char ‘s’ 를 ‘z’로 변환하는 작업을 수행한다.
replace()
메소드를 수행한 후에 기존의 값이 변경되는지를 확인하기 위해서 현재 값을 출력한다.CharSequence
타입의 매개 변수를 사용하여 값을 변경한다.정규 표현식을 사용하는 replaceAll()
메소드의 예이며, 공백을 파이프()로 변환한다. replaceFirst()
메소드를 사용하여 첫 번째 공백만 파이프로 변환한다.
- 결과
The String clazz reprezentz character ztringz.
The String class represents character strings.
The Strike class represents character strikes.
The|String|class|represents|character|strings.
The|String class represents character strings.
➡️ 특정 형식에 맞춰 값을 치환하는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
static String | format(String format, Object… args) | format에 있는 문자열의 내용 중 변환해야 하는 부분을 args의 내용으로 변경한다. |
static String | format(Locale l, String format, Object… args) | format에 있는 문자열의 내용 중 변환해야 하는 부분을 args 의 내용으로 변경한다. 단 첫 매개 변수인 Locale 타입의 1애 선언된 지역에 맞추어 출력한다. |
format()
메소드는 정해진 기준에 맞춘 문자열이 있으면, 그 기준에 있는 내용을 변환한다.
- %s : String
- %d : 정수형
- %f : 소수점이 있는 숫자
- %% : %를 의미
format
을 더 알기 위해서는 java.util.Fomatter
클래스의 API문서에 자세히 나와있음
- Format 예제
public void checkFormat(){
String text = "제 이름은 %s입니다. 지금까지 %d 권의 책을 썻고, " + "하루에 %f %%의 시간을 책을 쓰는데 할애하고 있습니다.";
String realText = String.format(text, "dante", 7,10.5);
System.out.println(realText);
}
- 결과
제 이름은 dante입니다. 지금까지 7 권의 책을 썻고, 하루에 10.500000 %의 시간을 책을 쓰는데 할애하고 있습니다.
- 주의 사항
대치해야할 문자열이 3개이다. 그러면 format은 3개를 나열한다. 3개 이상 나열해도 상관없다. 알아서 개수에 맞게 처리한다.
하지만
2개 이하로 매개 변수만 명시할 경우 예외가 발생한다.
String realText = String.format(text, "dante", 7); // 2개 매개 변수
- 결과
Exception in thread "main" java.util.MissingFormatArgumentException: Format specifier '%f'
at java.base/java.util.Formatter.format(Formatter.java:2672)
at java.base/java.util.Formatter.format(Formatter.java:2609)
➡️대소문자를 바꾸는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 | 설명 |
---|---|---|
String | toLowerCase() | 모든 문자열의 내용을 소문자로 변경한다. |
String | toLowerCase(Locale locale) | 지정한 지역 정보에 맞추어 모든 문자열의 내용을 소문자로 변경한다. |
String | toUpperCase() | 모든 문자열의 내용을 대문자로 변경한다. |
String | toUpperCase(Locale locale) | 지정한 지역 정보에 맞추어 모든 문자열의 내용을 소문자로 변경한다. |
- 대소문자 변환 예제
public void checkToCase(){
String text = "jAVA IS GOOD.";
String toLowerText = text.toLowerCase();
System.out.println(toLowerText);
}
➡️기본 자료형을 문자열로 변환하는 메소드
리턴 타입 | 메소드 이름 및 매개 변수 |
---|---|
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 타입으로 변환가능하다.
그리고 다른 방법도 있다.
byte b = 1;
String byte1 = String.valueOf(b);
String byte2 = b + " ";
byte1 처럼 변환하거나, byte2 처럼 변환하거나 출력해 보면 동일한 값이 나온다.
대부분 기본 자료형을 String타입으로 변환할 필요가 있을 때에는 String과 합치는 과정을 거친다.
그럴경우 valueOf()
메소드를 사용할 필요까지는 없다.
하지만 String
으로 변환만 해놓고 별도의 문자열과 합치는 과정이 없을 경우에는 valueOf()
메소드를 사용하는 것을 권장한다.
- 기억해야할 것
- valueOf() 메소드의 매개 변수로 객체(Object)가 넘어 왔을 경우
- toString()을 구현한 객체나, 정상적인 객체를 valueOf() 메소드에 넘겨주면 toString()의 결과를 리턴한다.
- null인 객체의 경우
- null인 객체는
toString()
메소드를 사용할 수 없다. →NullPointerException
에러 나온다. - 만약 null 아니면 toString() 메소드를 호출한 결과가 리턴한다.
- 해결방법 : 객체를 출력할 때
valueOf()
메소드를 사용하면 좋다. valueOf() 는“null”
이라는 문자열을 리턴해준다.
- null인 객체는
- valueOf() 메소드의 매개 변수로 객체(Object)가 넘어 왔을 경우
System.out.print()
, System.out.println()
메소드에서 null인 객체를 출력했을 때 NullPointerException
이 발생하지 않는 이유이기도하다 .
➡️System.out.println() 내부
- println()의 구조
- println() → print()구조 →
String.valueOf(s)
발견
➡️절대로 사용하면 안되는 메소드
intern()
메소드는 절대 사용하면 안 된다.
이 메소드는 자바로 구현되지 않고 C로 구현되어 있는 native 메소드 중 하나이다.
native라서 사용 하지말라는 것이 아니고 시스템의 심각한 성능 저하를 발생 시킬 수도 있기 때문이다.
intern()
예제
String text1 = "Java Basic";
String text2 = "Java Basic";
String text3 = new String("Java Basic");
System.out.println(text1 == text2); // true
System.out.println(text1 == text3); // false
System.out.println(text1.equals(text3)); // true
}
- 결과
true
false
true
text1 과 text2와 같이 객체를 생성하면, String 클래스에서 관리하는 Constant Pool에 해당 값이 있으면 기존에 있는 객체를 참조한다.
text3는 String 객체를 생성하면 같은 문자열이 풀에 있든 말든 새로운 객체를 생성한다.
String text1 = "Java Basic";
String text2 = "Java Basic";
String text3 = new String("Java Basic");
text3 = text3.intern();
System.out.println(text1 == text2);
System.out.println(text1 == text3);
System.out.println(text1.equals(text3));
}
- 결과
true
true
true
text3.intern()
new String(String)으로 생성한 문자열 객체라고 할지라도, 풀에 해당 값이 있으면, 풀에 있는 값을 참조하는 객체를 리턴한다.
만약 동일한 문자열이 존재하지 않으면 풀에 해당 값을 추가한다.
intern()
메소드를 수행한 뒤에 문자열은 equals()메소드가 아닌, ==으로 동일한지 비교할 수가 있다.
- equals() 메소드로 비교하는 것과 ==으로 비교하는것
- == 으로 비교하는 것이 훨씬 빠르다.
그렇다면 intern()
를 사용하면 좋은 것이 아닌가? 아니다..
가정을 해보면 새로운 문자열을 쉴새 없이 만드는 프로그램에서 intern()
메소드를 사용하여 억지로 문자열 풀에 값을 할당하도록 만들면, 저장되는 영역은 한계가 있기 때문에 그 영역에 대해서 별도로 메모리를 청소하는 단계를 거친다.
작은 연산 하나를 빠르게 하기 위해서 전체 자바의 시스템의 성능에 악영향을 주게 되는것이다…
그래서 쓰지말라는것…
immutable한 String의 단점을 보완하는 클래스 - StringBuffer와 StringBuilder
String은 immutable한 객체다.
더하기하면 더해지면서 바뀌는것 그럼 immutable하지 않는 것아닌가 ? 라고 생각할 수 있다.
하지만 String 문자열을 더하면 새로운 String객체가 생성되고 , 기존의 객체를 버려진다.
String text = "Hello";
text = text + "world";
위와 같은 경우 “Hello”라는 단어를 갖고 있는 객체는 더 이상 사용할 수 없다. GC(Garbage Collection)이 처리한다.
이러한 String 클래스의 단점을 보완하기 위해서 나온 클래스가 StringBuffer
와 StringBuilder
이다.
두 클래스에서 제공하는 메소드는 동일하다.
그럼 무엇이 다를까 ?
간략히 말하면 아래와 같다.
- StringBuffer는
Thread safe
하다 - StringBuilder는
Thread safe
하지 않다.
이제 메소드를 알아보자.
추가하는 메소드 append()
append() 메소드 예제
StringBuilder sb = new StringbBuilder();
sb.append("Hello");
sb.append(" world");
append() 메소드는 매개 변수로 모든 기본형 자료형과 참조 자료형을 모두 포함한다.
append() 메소드는 매개 변수가 변수로 받은(항상 변하는) 값이라면 매우 유용하다.
StringBuilder sb = new StringbBuilder();
sb.append("Hello").append(" world");
세미콜론이 나오기 전에 계속 append() 메소드를 붙여도 상관없다.
append() 메소드를 수행한 후에는 해당 StringBuilder 객체가 리턴되므로, 그 객체에 계속 붙이는 작업을 해도 상관없다.
String, StringBuffer, StringBuilder 클래스들의 공통점
- 모든 문자열을 다룬다.
- CharSequence 인터페이스를 구현함
- 세 가지 중 하나의 클래스를 사용하여 매개 변수로 받는 작업을 할 때 String이나 StringBuilder타입으로 받는 것 보다는 CharSequence타입으로 받는 것이 좋다.
StringBuilder, StringBuffer 클래스를 언제 사용할까 ?
- 하나의 메소드 내에서 문자열을 생성하여 더할 경우에는 StringBuilder를 사용해도 상관없다.
- 어떤 클래스에 문자열을 생성하여 더하기 위한 문자열을 처리하기 위한 인스턴스 변수가 선언되었고, 여러 쓰레드에서 이 변수를 동시에 접근하고 있을 경우
StringBuffer
를 사용해야만한다.
String회고
어마어마한 양이다. 정리하는데 너무 오랜 시간이 걸렸다.
이제 스스로의 자바 중간 점검일이 얼마 안남았다.
String은 정말 중요하다고 생각한다. 뭐 다른 분들도 그렇게 생각하는 것 같다.
String 클래스를 배우면서도 아는 것들도 많았지만 새로 알게된 사실도 많았다.
알고리즘 문자열 부분에서 많이 쓰는 메소드들이 나와서 반가웠다.
다음 장도 열심히 해보자..
참고 자료
- 자바의 신