본문 바로가기

Language & Framework/Java

자바 String에 대한 이해와 기본 메서드 총 정리

1. String 기본 개념

String은 문자열이고 char는 문자다. 그러면 String과 char는 어떤 차이가 있는 것일까?

String은 char[](문자 배열)에 대한 참조 변수를 가지고 있는 객체이다.

그러니까 예를 들어 new String("안녕하세요~")라는 String 객체의 변수는 { '안','녕','하','세','요','~' }라는 char 배열을 참조하고 있다는 것이다.

이것을 알면 왜 String str = "";은 사용할 수 있지만 char c = '';는 사용할 수 없는지 알 수 있다.

String str = "";은 new char[0]과 같다. (자바에서는 크기가 0인 배열이 허용된다.)

그러면 String str = "안녕하세요";와 String str = new String("안녕하세요")는 같은 것일까? 아래의 예시를 보자.

 

        String a = "응애";
        String b = "응애";

        String A = new String("응애");
        String B = new String("응애");

        System.out.println("a == b ? " + (a==b)); 
        System.out.println("a.equals(b) ? " + (a.equals(b))); 
        // a == b ? true
        // a.equals(b) ? true

        System.out.println("A == B ? " + (A==b));
        System.out.println("A.equals(B) ? " + (A.equals(B)));
        // A == B ? false
        // A.equals(B) ? true

        System.out.println("a == A ? " + (a==A));
        System.out.println("a.equals(A)? " + (a.equals(A)));
        // a == A ? false
        // a.equals(A)? true

 

String 클래스에서 equals는 객체의 주소가 아닌 값을 기준으로 비교하기 때문에 (참고:https://7357.tistory.com/77) equals를 사용하여 비교하면 true를 리턴하지만 단순히 동등 연산자를 사용해서 비교하면 new String으로 생성한 문자열은 모두 다른 객체를 참조하고 있다는 것을 알 수 있다.

이유는 String a와 b의 경우 Heap Stack에 위치하고 있는 constant pool에 존재하는 문자열을 사용하지만 A와 B는 그와 전혀 별개의 객체를 생성하기 때문이다.

클래스 파일에는 소스 파일에 포함된 모든 리터럴의 목록이 존재하기 때문에 해당 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때, 이 리터럴들이 JVM의 constant pool에 저장되고, new String이 아닌 문자열 리터럴로 저장한 변수는 constant pool에 있는 문자열을 참조하기 때문에 같은 문자열이라면 모두 같은 메모리를 참조하게 되는 것이다.

 

        char[] c = new char[0];
        String str = new String(c);
        System.out.println("str = " + str);
        // str =

참고로 new String 메서드는 문자열 리터럴이 아닌 char 배열을 넣어도 문자열을 넣을 때와 똑같이 작동한다.

 

 

2. String 메서드

2-1. new String

// new String(StringBuffer|String|char[] str)
char[] c2 = {'안','녕','하','세','요','.'};
String str2 = new String(c2); // 주어진 문자열을 가진 String 인스턴스 생성
System.out.println("str2 = " + str2);

앞으로 계속해서 마주하게 될 new String이다.

매개변수로 StringBuffer , String , char[]를 받을 수 있다. 위에서 계속해서 언급했으니 추가적인 설명은 생략한다.

 

 

2-2. charAt

// charAt(int index)
char[] c2 = {'안','녕','하','세','요','.'};
String str2 = new String(c2);
char c3 = str2.charAt(3); // 문자열 인덱스에 해당하는 문자 반환
System.out.println("str2.charAt(3) = " + c3);
// str2.charAt(3) = 세

charAt()은 매개변수로 int를 받으며 정수에 해당하는 인덱스에 있는 char 타입의 문자를 반환한다. 

 

 

2-3. compareTo

// compareTo(String str)
String s = "aa";
String s2 = "bb";
String s3 = "cc";
System.out.println("\"aa\".compareTo(\"bb\") = " + s.compareTo(s2)); // 사전순으로 비교
System.out.println("\"aa\".compareTo(\"cc\") = " + s.compareTo(s3));
System.out.println("\"cc\".compareTo(\"bb\") = " + s3.compareTo(s2));
//"aa".compareTo("bb") = -1
//"aa".compareTo("cc") = -2
//"cc".compareTo("bb") = 1

compareTo()는 String을 매개변수로 받으며 비교 대상이 사전 순으로 더 뒤에 있다면 차이만큼의 음수를 반환하고, 더 앞에 있다면 양수를 반환한다.

 

 

2-4. concat

// concat(String str)
String s4 = "Hello, ";
String s5 = "World!";
System.out.println("\"Hello, \".concat(\"World!\") = " + s4.concat(s5));
// "Hello, ".concat("World!") = Hello, World!

concat()은 String을 매개변수로 받으며 문자열을 합치는 기능을 가지고 있다.

 

 

2-5. endWith, startWidth

//endWith(String str)
//startWith(String str)
String doc = "(충격)자바고수되는법ㄷㄷ.txt";
System.out.println("doc.endWith(\"txt\") = " + doc.endsWith("txt"));
System.out.println("doc.startWith(\"(충격)\") = " + doc.startsWith("(충격)"));
//doc.endWith("txt") = true
//doc.startWith("(충격)") = true

endWith는 문자열의 마지막이 매개변수로 받은 문자열과 같은지 boolean의 값을 반환하고 startWidth는 반대로 문자열의 시작이 매개 변수 문자열과 같은지 검사한다.

 

 

2-6. equals & equalsIgnoreCase

//equals(Object obj)
String same = "자.바.좋.아";
String same2 = "자.바.좋.아";
System.out.println("same.equals(same2) = " + same.equals(same2));

//equalsIgnoreCase(String str)
String same3 = "I love JAVA";
String same4 = "i love java";
System.out.println("same3.equalsIgnoreCase(same4) = " + same3.equalsIgnoreCase(same4));
//same.equals(same2) = true
//same3.equalsIgnoreCase(same4) = true

String 클래스에서 equals는 참조 변수가 아닌 값을 기준으로 비교하여 문자열이 같다면 true, 서로 다르다면 false를 반환한다.

만약 대소문자를 무시하고 값을 비교하고 싶다면 equalsIgnoreCase()를 사용하면 된다.

 

 

2-7. indexOf & lastIndexOf

//indexOf(int ch, int pos)
//lastIndexOf(String str)
String strIndex = "HTML은 프로그래밍 언어입니다.";
System.out.println("strindex.indexOf(\"뿡\") =" + strIndex.indexOf("뿡")); // false가 아니라 -1
System.out.println("strindex.indexOf(\"입\") =" + strIndex.indexOf("입")); // 몇 번째 인덱스인지
System.out.println("strindex.indexOf(\"입\", 10) =" + strIndex.indexOf("입", 15));
// 15번째 인덱스부터~
System.out.println("strindex.indexOf(\"입니다\") =" + strIndex.indexOf("입니다"));
//strindex.indexOf("뿡") =-1
//strindex.indexOf("입") =14
//strindex.indexOf("입", 10) =-1
//strindex.indexOf("입니다") =14

indexOf는 매개 변수를 두개까지 받을 수 있다.

첫번째 매개변수만 사용할 경우 해당하는 문자열 혹은 문자가 몇 번째 인덱스에 있는지 int값으로 반환하고, 두 번째 매개변수로 int값을 넣어줄 경우 해당 인덱스로부터 존재하는지 검사한다. (단, 결과 값은 해당 위치부터가 아니라 0번째 인덱스부터의 인덱스다.)

또한 indexOf의 반환 값은 boolean이 아니기 때문에 해당 값이 존재하지 않을 경우 -1을 반환한다.

lastIndexOf는 indexOf와 달리 0번째 인덱스 부터가 아니라 마지막 인덱스부터 체크한다.

 

 

2-8.intern()

String strIntern = new String("자바스크립트와 자바의 관계는 강아지와 땅강아지간의 관계와 같다.");
String strIntern2 = new String("자바스크립트와 자바의 관계는 강아지와 땅강아지간의 관계와 같다.");
String strIntern3 = "자바스크립트와 자바의 관계는 강아지와 땅강아지간의 관계와 같다.";
System.out.println("strIntern == strIntern2 ? " + (strIntern == strIntern2));
System.out.println("strIntern == strIntern3 ? " + (strIntern == strIntern3));
System.out.println("strIntern.intern() == strIntern2.intern() ? " + (strIntern.intern() == strIntern2.intern()));
System.out.println("strIntern.intern() == strIntern3 ? " + (strIntern.intern() == strIntern3));
System.out.println("strIntern.intern() == strIntern3.intern() ? " + (strIntern.intern() == strIntern3.intern()));
// HeapStack의 constant pool 등록된 문자열의 주소값을 반환하기 때문에..
//strIntern == strIntern2 ? false
//strIntern == strIntern3 ? false
//strIntern.intern() == strIntern2.intern() ? true
//strIntern.intern() == strIntern3 ? true
//strIntern.intern() == strIntern3.intern() ? true

intern 메서드는 해당 문자열을 constant pool에 저장한다.

제일 위에서 언급했던 그 constant pool이다.

new String으로 생성된 문자열 객체는 서로 다른 값을 참조하지만 intern 메서드를 사용한다면 constant pool에 있는 같은 값을 참조하기 때문에 동등 연산자를 사용해도 true를 반환한다.

 

2-9. length()

//length() => return int
String length = "length";
System.out.println("length.length() ? " + length.length());
// length.length() ? 6

length 메서드는 문자열의 길이 (char[] 배열의 길이)를 반환한다.

 

 

2-10. replace() | replaceAll() | replaceFirst()

// replace(char||CharSequence old, char||CharSequence new)
// replaceAll(String regex, String replacement) => 차이점은 정규식이 사용 가능함.
// replaceFirst(String regex, String replacement) => 처음 만나는 문자열만 변경함
String strReplacement = "자바는 정말 전설이다 ㄷㄷ";
System.out.println("strReplacement.replace(\"자바\",\"자바스크립트\") = " + strReplacement.replace("자바","자바스크립트"));
String strReplacement2 = "AABBCCBB";
String strReplaceAll = "AABBCCBB";
System.out.println("strReplacement2.replace(\"[ABC]\",\"DD\") = " + strReplacement2.replace("[ABC]","DD"));
System.out.println("strReplacement2.replaceFirst(\"BB\",\"ZZ\") = " + strReplacement2.replaceFirst("BB","ZZ"));
System.out.println("strReplaceAll.replaceAll(\"[ABC]\",\"DD\") = " + strReplaceAll.replaceAll("[ABC]","DD"));
//strReplacement.replace("자바","자바스크립트") = 자바스크립트는 정말 전설이다 ㄷㄷ
//strReplacement2.replace("[ABC]","DD") = AABBCCBB
//strReplacement2.replaceFirst("BB","ZZ") = AAZZCCBB
//strReplaceAll.replaceAll("[ABC]","DD") = DDDDDDDDDDDDDDDD

replace 메서드는 문자열을 탐색한 뒤 해당 문자열을 두번째 매개변수로 받는 문자열로 변환한다.

replaceAll 메서드는 기본적으로는 완전히 같은 기능을 가지고 있으나 정규표현식(regex)를 매개변수로 받을 수 있다는 것이 차이점이다.

replaceFirst 메서드는 replace나 replaceAll과 달리 해당하는 모든 문자열을 변경하는 것이 아니라 인덱스의 시작점으로부터 가장 첫번째의 문자열만을 변경한다.

 

 

2-11. split() | join() .. 그리고 StringJoiner

//split(String regex, int limit) => return String[]
//join(String str, String[] arr) => return String
String strSplit = "자바,자바스크립트,러스트,GO";
String[] strSplitArr = strSplit.split(",");
String[] strSplitArr2 = strSplit.split(",", 3);
System.out.println("strSplit.split(\",\") = " + Arrays.toString(strSplitArr));
System.out.println("strSplit.split(\",\", 1) = " + Arrays.toString(strSplitArr2));
System.out.println("strSplitArr2[2] = " + strSplitArr2[2]);
String[] splitedStr = strSplit.split(",");
String joinStr = String.join("-", splitedStr);
System.out.println("String.join(\"-\", splitedStr) = " + joinStr);
//strSplit.split(",") = [자바, 자바스크립트, 러스트, GO]
//strSplit.split(",", 1) = [자바, 자바스크립트, 러스트,GO]
//strSplitArr2[2] = 러스트,GO
//String.join("-", splitedStr) = 자바-자바스크립트-러스트-GO

split()메서드는 문자열의 구분자를 통해 문자열 배열(String[])로 반환한다.

(문자열은 char[], 즉 문자 배열이니 문자 배열을 문자열 배열로 변환한다고 생각하면 된다.)

split 메서드는 매개변수를 두 개 받을 수 있는데, 두번째 매개변수로는 생성되는 배열의 총 개수를 지정할 수 있다.

만약 "자바,자바스크립트,러스트,go"라는 문자열을 split(",","3")으로 배열화한다면 { "자바", "자바스크립트", "러스트, go" } 3개를 제외한 모든 문자열은 마지막 인덱스에 묶여서 들어가게 될 것이다.

 

join은 split과 반대로 구분자를 이용해 문자열 배열을 다시 하나의 문자열로 반환한다.

추가적으로 String의 메서드는 아니지만 비슷한 역할을 해주는 클래스가 존재한다. 바로 StringJoiner이다.

 

StringJoiner stringJoiner = new StringJoiner("-","[","]");
for ( String str10 : splitedStr ) {
    stringJoiner.add(str10);
}
System.out.println("new StringJoiner(\"-\",\"[\",\"]\").add(splitedStr 반복문) = " + stringJoiner);
//new StringJoiner("-","[","]").add(splitedStr 반복문) = [자바-자바스크립트-러스트-GO]

사용법은 위와 같다. stringJoiner를 사용하면 문자열에 구획문자(Delimiter), 접두사(Prefix)와 접미사(Suffix)를 붙여줄 수 있다.

 

 

2-12. subString()

//String substring(int begin, int end)
//시작부터 끝으로 지정할 경우 시작 <= 결과 < 끝이다. 끝은 결과에 포함되지 않음을 주의.
String subStringEx = "러스트가 주도하는 질서는 막을 수 없다";
System.out.println("subStringEx.substring(10) = " +subStringEx.substring(10));
System.out.println("subStringEx.substring(10, 15) = " +subStringEx.substring(10,15));
//subStringEx.substring(10) = 질서는 막을 수 없다
//subStringEx.substring(10, 15) = 질서는 막

subString은 첫번째 매개변수로 받는 인덱스부터 두 번째 매개변수로 받는 인덱스의 범위에 포함되는 문자열을 반환한다. 

(2번째 매개변수가 없을 경우 끝까지)

중요한 것은 첫번째 매개변수로 받는 인덱스는 포함되지만 두번째 매개변수로 받는 인덱스는 포함되지 않는다는 것이다.

즉 (10, 15)로 매개변수를 받았다면 10번째 인덱스부터 14번째 인덱스까지 반환한다.

 

 

2-13. toLowerCase() | toUpperCase()

// String toLowerCase() | toUpperCase()
String strCase = "I am Iron-man";
System.out.println("strCase.toLowerCase() = " + strCase.toLowerCase());
System.out.println("strCase.toUpperCase() = " + strCase.toUpperCase());
//strCase.toLowerCase() = i am iron-man
//strCase.toUpperCase() = I AM IRON-MAN

toLowerCase는 문자열의 모든 문자를 소문자로 변환하고 toUpperCase는 문자열의 모든 문자를 대문자로 변환한다.

 

 

2-14. toString()

// toString()
String ToString = "toString";
System.out.println("ToString.toString() = " + ToString.toString());
//ToString.toString() = toString

toString은 String 인스턴스의 문자열을 반환한다.

 

 

2-15. trim()

// trim()
String sideEmpty = "     xl지존x자바lx     ";
System.out.println("sideEmpty.trim() = " + sideEmpty.trim());
//sideEmpty.trim() = xl지존x자바lx

trim() 메서드는 문자열의 앞뒤 공백을 제거해준다. 단 문자 사이의 공백까지 제거해주지는 않는다.

 

 

 

2-16. valueOf()

// String.valueOf(boolean | char | int | long | float | double | Object all )
// 지정 값을 문자열로 반환 (생략)
// + 해당 값들을 다시 원래 값으로 반환하려면 ?
// Boolean.valueOf , Integer.valueOf , Boolean.parseBoolean 등 ..
// String으로 변환 시 값 + ""도 편리하게 사용 가능하나 성능은 valueOf보다 좋지 않다.
// String을 값으로 재변환 시 공백이 있을 경우 예외가 발생할 수 있기 때문에 trim()을 같이 사용하면 좋다.
// 숫자 변환 시 자료형에 의한 예외가 발생하기 쉽기 떄문에(ex ) Integer.parseInt("1.0F")) 예외 처리를 잘 해줘야함.

너무 단순한 메서드라 별도 예제를 작성하지는 않았다.

valueOf는 다른 대부분의 클래스도 가지고 있는 메서드이기 때문에 응용해서 사용할 수 있다.

또한 다른 타입의 값을 문자열로 변환 시 성능이 아주 중요한 부분이 아니라면 + 빈문자열("")을 해주는 방법이 더 편하게 사용할 수 있다.