도도한 개발자

[Java] #15-1. String - String의 생성자 본문

Backend/Java

[Java] #15-1. String - String의 생성자

Kiara Kim 2022. 3. 17. 20:27

* String 클래스

 

클래스 중에서 더하기 연산을 제공하는 클래스는 String 밖에 없다. 객체에 더하기는 그럼 어떻게 된 걸까? 객체에 더하기를 하면 toString() 메소드가 호출되고 그 결과를 더하는 것이다. String 클래스가 어떻게 선언되어 있는지 보자.

 

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

 

implements라고 적어준 뒤 인터페이스들을 나열하면 해당 인터페이스에 선언된 메소드를 이 클래스에서 구현한다는 의미다. 하지만 Serializable 인터페이스는 구현해야 하는 메소드가 하나도 없는 특이한 인터페이스다. 이를 구현한다고 선언해 놓으면, 해당 객체를 파일로 저장하거나 다른 서버에 전송 가능한 상태가 된다. 

 

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

 

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

 

 

* String의 생성자들

 

1. String() : 비어있는 String 객체를 생성한다. 이렇게 하는 것보단 String name = null;로 선언하는 것이 효율적이다.

2. String(byte[] bytes) : 디폴트 캐릭터 셋을 사용해 제공된 byte 배열을 디코딩한 String 객체를 생성한다.

3. String(byte[] bytes, Charset charset) : 지정된 캐릭터 셋을 사용해 제공된 byte 배열을 디코딩한 String 객체를 생성한다.

4. String(byte[] bytes, String charsetName) : 지정한 이름을 갖는 캐릭터 셋을 사용하여 지정한 byte 배열을 디코딩한 String 객체를 생성한다.

5. String(byte[] bytes, int offset, int length) : 디폴트 캐릭터 셋을 사용해 지정한 byte 배열의 일부를 디코딩한 String 객체를 생성한다.

6. String(byte[] bytes, int offset, int length, Charset charset) : 지정한 이름을 갖는 캐릭터 셋을 사용하여 byte 배열의 일부를 디코딩한 String 객체를 생성한다.

7. String(byte[] bytes, int offset, int length, String charsetName) : 지정한 이름을 갖는 캐릭터 셋을 사용하여 byte 배열의 일부를 디코딩한 String 객체를 생성한다.

8. String(char[] value) : char 배열의 내용들을 붙여 String  객체를 생성한다.

9. String(char[] value, int offset, int count) : char 배열의 일부 내용들을 붙여 String  객체를 생성한다.

10. String(int[] codePoints int offset, int count) : 유니코드 코드 위치로 구성되어 있는 배열의 일부를 새로운 String 객체를 생성한다.

11. String(String original) : 매개 변수로 넘어온 String과 동일한 값을 갖는 String 객체를 생성한다. 즉, 복제본을 만든다.

12. String(StringBuffer buffer) :  매개변수로 넘어온 StringBuffer  클래스에 정의되어 있는 문자열의 값과 동일한 String  객체를 생성한다.

13. String(StringBuilder builder) :  매개 변수로 넘어온 StringBuilder 클래스에 정의되어 있는 문자열의 값과 동일한 String 객체를 생성한다.

 

캐릭터 셋은 문자의 집합을 의미하며, 디코딩은 암호화되어 있거나 컴퓨터가 이해할 수 있는 값들을 알아보기 쉽게 변환하는 것을 말한다. 이 중 제일 많이 사용하는 생성자는 다음과 같다.

 

· String(byte[] bytes)

· String(byte[] bytes, String charsetName)

 

 

* String 문자열 -> byte 변환

 

String 클래스에는 현재 문자열 값을 byte 배열로 변환하는 getBytes() 하는 메소드들이 있다.

 

1. getBytes() : 디폴트 캐릭터 셋의 바이트 배열을 생성한다.

2. getBytes(Charset charset) : 지정한 캐릭터 셋 객체 타입으로 바이트 배열을 생성한다.

3. getBytes(String charsetName) : 지정한 이름의 캐릭터 셋을 갖는 바이트 배열을 생성한다.

 

보통 캐릭터 셋을 잘 알고 있거나 같은 프로그램에서 문자열을 byte 배열로 만들 때 가장 위에 있는 getBytes() 메소드를 사용하면 되지만, 다른 시스템에서 전달 받은 문자열을 byte 배열로 변환할 땐 두번째나 세번째에 있는 메소드를 사용하는 것이 좋다. 문자열이 다른 캐릭터 셋으로 되어 있을 수 있기 때문이다.

 

한글을 처리하기 위해 자바에서 많이 사용하는 캐릭터 셋은 UTF-16이다. 예제를 보자.

 

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.println(korean2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

1. "한글"이라는 값을 갖는 String 객체인 korean을 생성했다.

2. getBytes() 라는 메소드를 사용하여 korean을 byte 배열로 만들었다.

3. 만들어진 byte 배열에 어떤 값들이 있는지 살펴보기 위해 for 루프를 사용하여 각 byte 값을 출력하도록 했다.

4. byte 배열을 갖고 String 객체를 만들기 위해 byte 배열(array1)을 매개 변수로 갖는 String 객체를 생성하고, 그 문자열을 출력했다.

 

결과는 다음과 같다.

 

-57 -47 -79 -37 
한글

 

마지막 줄에는 원래의 "한글" 값이 그대로 출력된 것을 볼 수 있는데, getBytes() 메소드는 플랫폼의 디폴트 캐릭터 셋으로 변환을 하고, String(byte[])  생성자도 플랫폼의 디폴트 캐릭터 셋으로 변환하기 때문이다. 여기서 중간의 byte 배열 값을 출력하는 부분을 자주 사용될 것 같으니 따로 빼놓자.

 

이번엔 UTF-16이라는 캐릭터 셋으로 변환는 메소드를 만들고 main() 메서 호출하자.

 

package chapter15;

public class StringSample {

	public static void main(String[] args) {
		StringSample sample = new StringSample();
		sample.convertUTF16();
	}
	
	public void printByteArr(byte[] arr) {
		for(byte data: arr) {
			System.out.print(data + " ");
		}
		System.out.println();
	}
	
	public void convertUTF16() {
		try {
			String korean = "한글";
			byte[] array1 = korean.getBytes("UTF-16"); // @1
			printByteArr(array1);
			String korean2 = new String(array1); // @2
			System.out.println(korean2);
			
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

 

결과는 다음과 같다.

 

-2 -1 -43 92 -82 0 
??\?

 

잘못된 캐릭터 셋으로 변환을 했기 때문에 이상한 문자열이 나왔다. 이렇게 글자가 깨지는 현상을 방지하기 위해선 byte 배열로 생성할 때 사용한 캐릭터 셋(@1)을 @2에서 문자열로 다시 전환할 때도 동일하게 사용해야만 한다. 

 

String korean2 = new String(array1, "UTF-16");

 

그러면 코드가 정상적으로 출력되는 것을 확인할 수 있다.

 

-2 -1 -43 92 -82 0 
한글

 

한글을 byte 배열로 만들 때 어떤 캐릭터 셋을 쓰느냐에 따라 배열의 크기가 다르다. EUC-KR의 경우 한글 두 글자를 표현하기 위해 4 바이트를 사용하지만, UTF-16은 6바이트를 사용한다. 자바에서 한글이 몇 바이트를 점유하는지 알아 두는 것은 우리 나라에서 개발하면서 매우 중요하다.

 

convet메소드를 잘 보면 왜 try-catch 블록으로 감쌌을까? 이유는 캐릭터 셋을 지정하는 메소드 및 생성자들 때문이다.

 

· byte 배열과 String 타입의 캐릭터 셋을 받는 생성자

· getBytes() 메소드 중에서 String 타입의 캐릭터 셋을 받는 메소드

 

위의 생성자와 메소드는 존재하지 않는 캐릭터 셋의 이름을 지정할 경우 UnsupportedEncodingException을 발생시킬 수 있어서 반드시 try-catch로 감싸주거나 메소드 선언시 throws 구문을 추가해 주어야 한다.

 

 

그럼 이만.