[Java] - String 클래스

janjanee·2021년 6월 14일
0

Java

목록 보기
8/19
post-thumbnail

String

다른 언어에서는 문자열을 char형의 배열로 다루기도 하는데 자바에서는 문자열을 위한 클래스를 제공한다.
바로 String 클래스이다.
String 클래스는 자주 사용되고 중요한 클래스이므로 이번에 잘 정리해야겠다.

변경 불가능한(immutable) 클래스

String hi = "hi";
String there = "there";
hi = hi + there;

'+' 연산자를 이용해서 문자열을 결합하는 경우 인스턴스 내의 문자열이 바뀌는 것이 아니다.
문자열 "hithere"인 새로운 String 인스턴스가 생성되는 것이다.

따라서, '+' 연산자를 사용해서 문자열을 결합하면 연산 마다 새로운 문자열을 가진
String 인스턴스가 생성되어 메모리공간을 차지하게 되므로 가능한 결합횟수를 줄이는 것이 좋다.


문자열의 비교

문자열을 만드는 방법은 두 가지이다.

  1. 문자열 리터럴 지정
  2. String 클래스 생성자 사용
String s1 = "hi";   //  문자열 리터럴 "hi"의 주소가 s1에 저장
String s2 = "hi";   //  문자열 리터럴 "hi"의 주소가 s2에 저장
String s3 = new String("hi");   //  새로운 String 인스턴스 생성
String s4 = new String("hi");   //  새로운 String 인스턴스 생성

두 방식의 차이점은 String 클래스 생성자를 이용한 경우에는 항상 새로운 String 인스턴스가 생성되지만
문자열 리터럴은 이미 존재하는 것을 재사용한다.

s1 == s2    //  true
s1 == s3    //  false
s3 == s4    //  false

equals()를 사용하면 문자열의 내용인 "hi"를 가지고 비교해서 모두 같다는 결과가 나오기 때문에
주소를 비교하는 연산자로 비교해보면 (s1, s2)는 같고, (s1, s3) / (s3, s4)는 다르다는 결과를 알 수 있다.


문자열 리터럴

public static void main(String[] args) {
    String s1 = "hi";
    String s2 = "hi";
    String s3 = "hello";
}

위와 같이 문자열 리터럴 방식으로 코드를 작성하고 컴파일을 해보자.

// parameter  args
L0
LINENUMBER 5 L0
LDC "hi"
ASTORE 1
L1
LINENUMBER 6 L1
LDC "hi"
ASTORE 2
L2
LINENUMBER 7 L2
LDC "hello"
ASTORE 3
L3
LINENUMBER 8 L3
RETURN
...

class 파일을 열어본 결과 클래스 파일에 문자열 리털("hi", "hello)이 저장되어 있는것을 확인할 수 있다.
이와같이 String 리터럴들은 컴파일시에 클래스파일에 저장된다.

코드를 실행하면 "hi", "hello"를 담고 있는 String 인스턴스가 하나씩 생성된 후,
참조변수들은 아래와같이 각각을 참조하게 된다.

  • s1, s2 => String 인스턴스("hi") 참조
  • s3 => String 인스턴스 ("hello") 참조

정리하자면 위의 컴파일된 클래스 파일이 클래스 로더에 의해 메모리에 올라갈 때, 리터럴 목록에 있는 리터럴들
JVM내에 있는 '상수풀(constant pool)' 에 저장되는데
이 때, "hi"와 같은 문자열 리터럴이 자동적으로 생성되어 저장되는 것이다.


빈 문자열(empty string)

String s = "";

위의 코드는 String 인스턴스 내부에 'new char[0]' 와 같이 길이가 0인 char형 배열을 저장하고 있는것과 같다.


String 클래스의 생성자와 메소드

자주 사용될만한 String 클래스의 생성자와 메소드에 대해 알아보자.
간단하게 사용법위주로 정리했다.

String(char[] value)

char[] c = {'H', 'e', 'l', 'l', 'o'};
String hello = new String(c);
c = hello.toCharArray();
  • char[] -> String으로 변환할 때 사용
  • 반대 경우로는 toCharArray() 사용

char charAt(int index)

char c1 = "hello".charAt(1);  //  'e'
  • 지정된 위치(index)에 있는 문자를 반환

int compareTo(String str)

int i = "aaa".compareTo("aaa");      //  0
int i2 = "aaa".compareTo("bbb");     //  -1
int i3 = "bbb".compareTo("aaa");     //  1
  • 문자열(str)과 사전순서로 비교
  • 같으면 0, 이전이면 음수, 이후면 양수

String concat(String str)

String helloWorld = "Hello".concat(" World");
  • 문자열(str)을 뒤에 붙인다.

boolean contains(CharSequence s)

boolean result = helloWorld.contains("lo");     //  true
  • 지정된 문자열(s)이 포함되어있는지 확인

boolean startsWith(String prefix)

String file = "Hello.jpg";
boolean b = file.startsWith("Hello");   //  true
  • 지정된 문자열(prefix)로 시작하는지 확인

boolean endsWith(String suffix)

String file = "Hello.jpg";
boolean b = file.endsWith("jpg");   //  true
  • 지정된 문자열(suffix)로 끝나는지 확인

boolean equalsIgnoreCase(String str)

boolean b = "hello".equalsIgnoreCase("HELLO");  //  true
  • 대소문자 구분없이 지정된 문자열(str)과 비교

int indexOf(int ch)

int index = hello.indexOf('l');     //  2
  • 문자(ch)가 문자열에 존재하는지 확인하여 위치(index)를 반환
  • 존재하지 않으면 -1을 반환

int indexOf(int ch, int pos)

int index = hello.indexOf('e', 0);  //  1
int index2 = hello.indexOf('e', 2);  //  -1
  • 문자(ch)가 문자열에 존재하는지 지정된 위치(pos)부터 확인하여 위치(index)를 반환
  • 존재하지 않으면 -1을 반환

int indexOf(String str)

String s = "HI THERE"
int idx = s.indexOf("THERE");   //  3
  • 문자열(str)이 존재하는지 확인하여 위치(index)를 반환
  • 존재하지 않으면 -1을 반환

int lastIndexOf(int ch)

String s = "java.lang.String";
int idx = s.lastIndexOf('.');   //  10
int idx2 = s.indexOf('.');  //  4
  • 문자(ch)를 문자열의 오른쪽 끝에서부터 찾아서 위치(index)를 반환
  • 존재하지 않으면 -1을 반환

int length()

String s = "Hello".length();    //  5
  • 문자열의 길이를 반환

String[] split(String regex)

String books = "JPA, SPRING, JAVA";
String[] arr = books.split(",");    //  [JPA, SPRING, JAVA];
  • 문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환

String substring(int begin)

String substring(int begin, int end)

String s = "java.lang.Object";
String object = s.substring(10);    //  Object
String lang = s.substring(5, 9));   //  lang
  • 시작 위치(begin) 부터 끝 위치(end) 범위에 포함된 문자열을 반환
  • 시작 위치 문자는 포함되지만 끝위치 문자는 포함되지 않음 (begin <= x < end)

String toLowerCase()

String lower = "HellO".toLowerCase();   //  hello
  • 모든 문자열을 소문자로 변경

String toUpperCase()

String upper = "heLLo".toLowerCase();   //  HELLO
  • 모든 문자열을 대문자로 변경

String trim()

String trim = "    hi hello  ".trim();      //  hi hello
  • 문자열의 왼쪽 끝과 오른쪽 끝에 있는 공백을 없앤 결과 반환
  • 문자열 중간의 공백은 제거되지 않음

String valueOf()

String b = String.valueOf(true);
String i = String.valueOf(100);
String c = String.valueOf('a');
String l = String.valueOf(100L);
String d = String.valueOf(100.0);
  • 지정된 값을 문자열로 반환
  • 참조변수의 경우 toString()을 호출한 결과 반환

join() / StringJoiner

join()은 여러 문자열 사이에 구분자를 넣어서 결합한다.
구분자로 문자열을 자르는 split()과 반대되는 작업이다.

String coffees = "latte, americano, mocha";
String[] arr = coffees.split(",");
String str = String.join("/", arr);     //  latte/americano/mocha

StringJoiner클래스를 사용해서 문자열을 결합할 수도 있다.

StringJoiner sj = new StringJoiner("/", "[", "]");
for (String s : arr) {
    sj.add(s.toUpperCase());
}

System.out.println(sj.toString());  //  [LATTE|AMERICANO|MOCHA]

문자 인코딩 변환

getBytes(String charsetName) 를 사용하면 문자열의 문자 인코딩을 다른 인코딩으로 변경 가능하다.

public static void main(String[] args) {
    String han = "한";
    byte[] bArr = han.getBytes(StandardCharsets.UTF_8);
    byte[] bArr2 = han.getBytes(StandardCharsets.UTF_16);

    System.out.println("UTF-8:" + joinByteArr(bArr));
    System.out.println("UTF-16:" + joinByteArr(bArr2));

    System.out.println("UTF-8:" + new String(bArr, StandardCharsets.UTF_8));
    System.out.println("UTF-16:" + new String(bArr2, StandardCharsets.UTF_16));
}

private static String joinByteArr(byte[] bArr) {
    StringJoiner sj = new StringJoiner(":", "[", "]");

    for (byte b : bArr) {
        sj.add(String.format("%02X", b));
    }
    return sj.toString();
}
UTF-8:[ED:95:9C]
UTF-16:[FE:FF:D5:5C]
UTF-8:한
UTF-16:한

기본형 <-> String 변환

기본형 -> String

int i = 100;
String str1 = i + "";
String str2 = String.valueOf(i);
  • 두 가지 방법이 있는데, 이 때 성능은 valueOf()가 더 좋다.

String -> 기본형

int i = Integer.parseInt("100");
int i2 = Integer.valueOf("100");
boolean b = Boolean.parseBoolean("true");
boolean b2 = Boolean.valueOf("false");
  • valueOf()를 쓰거나 parseXXX()를 사용한다.
  • valueOf()의 반환 타입은 래퍼 클래스인데, 오토박싱을 통해 자동 변환된다.
  • 메소드의 이름을 통일하기 위해 valueOf()가 나중에 추가되었다.
  • valueOf()는 메소드 내부에서 parseInt()를 호출하기 때문에, 반환 타입이 다를 뿐 같은 메소드이다.

References

profile
얍얍 개발 펀치

0개의 댓글