나다.
이 글이 뭐하는 글이냐면
문자는 우리가 필수적으로 사용하는 요소다.
지금 당신이 보고있는 이 글 또한 문자로 구성된 하나의 정보집합체라고 할 수 있다.
자바를 배우며 우리는 숫자만 출력하지 않고 어떤 문장 또는 문자를 표현할 때가 있다.
그런 경우 우리는 주로 String을 사용하는데
당신은 String이 어떤 방식으로 우리가 입력한 문자 또는 문자열을 저장하고
사용할 수 있게 하는지 알고있는가.
이번 글에서는 String의 개념과 작동원리에 대해 설명한다.
String과 char의 차이
먼저 String은 문자열이다. 문자 여러 개 붙혀놓은거 맞음 그거 ㅇㅇ
당신은 char라는 원시타입의 문자 변수를 알고있는가.
통상 char라 함은 하나의 문자를 저장하고 사용하기위해 사용된다.
public char choose(boolean OX){
char o = "O";
char x = "X";
return OX ? o : x;
}
파라미터로 OX 받아서 true면 O보내주고 false면 X 보내줌.
예를 들어, 누군가 당신에게 이런 주문을 했다고 가정하자.
"어떤 개X끼가 O,X만 보내주니까 삭아지 없다고 컴플레인을 넣었어.
그러니까 "Yes", "No"로 바꿔줘."
이 주문을 받은 당신은 대부분 이런 식으로 처리할 것이다.
public String choose(boolean OX){
String o = "Yes";
String x = "No";
return OX ? o : x;
}
char를 String으로 바꾼 후 Yes와 No를 넣어 보내주면 간단히 끝날 일이다.
그렇다면 char는 1개의 문자만을 담을 수 있는데 String은 저런 여러 개의 문자를 통째로 담을 수 있는 것일까.
이것을 알기 위해서 우리는 먼저 String이 원시타입이 아닌 하나의 클래스라는 것을 알아야한다.
우리는 IDE가 제공하는 자동완성 기능을 사용한다.
그 과정에서 살펴보면 왜 String이 하나의 클래스인지 이유가 나온다.
클래스란 객체를 정의하여 기능을 강화하고 편의를 제공하며 객체지향을 실현하기 위해 사용한다.
C언어에서 문자열은 char의 배열로 표현된다.
char hi[] = {h,e,l,l,o}; // 이거 맞음 ㅇㅇ
char hi[5] = "hello"; // 이것도 되긴 함.
나는 배열을 싫어한다.
아마 자바를 만들때 제임스 형님께서도 생각하셨을지 모른다.
"시X 배열을 선언하다니. 미개하군요.
걍 문자 다 때려박으면 배열 만들어주는 클래스 하나 만들겠음."
그래서 저 과정을 생략하고 그냥 값만 집어넣으면 자동으로 배열로 쓰까주는 클래스(String)가 탄생함.
일단 String은 여러 개의 char를 하나로 합쳐 돌려준다는 것만 알면 된다.
String의 사용법과 동작
대표적으로 String을 사용하는 방법에는 두 가지가 있다.
String hi = "hello";
String hello = new String("hi");
두 개의 차이를 알고있다면 이 글을 읽고있을 필요가 없으니 지나가시면 되겠다.
먼저 1번인 " " 사이에 다 때려박는 형식의 경우
heap 메모리 영역의 String constant pool이라는 곳의 HashMap 형태의 자료에 저장한다.
HashMap 모르면 안됨. 빨리 구글에 검색하셈.
다음 2번인 new 연산자의 경우
heap 메모리에 그냥 객체의 형태로 저장된다.
사용법은 이게 끝임.
그러나 비교에서 차이를 보이게 된다. 두개의 생성 영역이 다른 것을 볼 수 있다.
그렇기 때문에 비교하고 싶을 경우 다음과 같이 사용해야한다.
예를 들어 진양철 할배가 얘네를 구분하고 싶다고 가정할 때.
String 진도준 = "손자";
String 진예준 = "손녀";
String 진형준 = "손자";
String 진성준 = new String("손자");
진도준 == 진성준 // false 객체 주소가 다름.
진도준.equals(진성준) // true 객체 주소는 다르지만 똑같은 "손자"임.
진도준 == 진예준 // false 값이 달라서 객체 주소 다름.
진도준.equals(진예준) // false 값이 다름.
진도준 == 진형준 // true 값이 같아서 객체 주소도 같음.
진도준.equals(진형준) // true 값도 "손자"로 같음.
다음과 같은 결과가 나온다.
참고로, 주소는 가비지 컬렉터가 돌며 계속 변하기때문에 찾아봐야 별 의미 없다.
보안상 제공을 원하지 않을 뿐더러 굳이 캐가면서 알아내야 할 의미가 없기 때문이다.
방법이 없는 것은 아니니 구글에 쳐보셈. 나옴.
혹시 String끼리 더해본(+) 적이 있는가?
String real = "ㄹㅇ";
String kk = "ㅋㅋ";
System.out.print(real + kk);
기본적으로 String은 Immutable 속성의 객체다.
변화하지 않는다는 뜻이다.
한번 값이 들어가면 그 값으로 고정임.
그러나 우리는 String을 통해 문자열을 더하는 등의 연산을 한다.
"변하지 않는 요소를 어떻게 더하죠?"
그렇기때문에 자바는 real + kk라는 연산이 완료된 객체를 완전히 새로 뽑아내 돌려준다.
전체적 과정은 이렇게 흘러간다.
StringBuilder sb = new StringBuilder(); // 내부에서 스스로 StringBuilder를 만듬.
sb.append("ㄹㅇ"); // real을 먼저 append를 호출해 당겨옴.
sb.append("ㅋㅋ"); // 이후 kk를 append로 당겨 real 뒤에 붙힘.
return sb.toString(); // toString()을 통해 합친 새로운 문자열을 내보내줌.
기존의 ㄹㅇ에 ㅋㅋ를 끼얹은 것이 아닌 ㄹㅇ도 가져오고 ㅋㅋ도 가져온 후 두 개를 합친
완전히 새로운 객체의 인스턴스를 돌려주게된다.
딱 봐도 성능이 존X 구리게 생겼다.
저렇게 한개를 더하는 것은 상관 없으나 만약 문자열을 1만개 정도 더해야 한다고 가정해보자.
StringBuilder를 생성해서 append로 당겨온다음 toString으로 내보내고
내보낸걸 다시 당겨오는 과정을 1만번 반복해야 한다.
당연히 성능이 개같이 구려질 수 밖에 없다.
그렇기 때문에
public String 만번더하기(){
StringBuilder sb = new StringBuilder();
for(int i = 0; i < 10000; i++{
sb.append("더하기");
}
String 꽉찬깡통 = sb.toString();
sb.setLength(0); // 다음에 또 써야될 수 있으니 미리 초기화
return 꽉찬깡통;
}
다음과 같이 한다면 더 빠르지 않을까?
결론이 뭐냐면
아마 이정도만해도 사용에는 문제가 없지 않을까싶다.
정말 자세하게 설명하려면 한도 끝도 없기때문이다.
String 클래스 3347줄임.
3줄 요약하자면
- String은 "문자배열"을 묶어 문자열로 반환해준다.
- " "로 생성하는 것과 new로 생성하는 것은 다르다. new로 생성한 String 객체의 값을 비교하고싶다면
.equals() 를 사용할 것. - 길고 반복적인 String 연산이 필요하다면 StringBuilder를 선언 후 사용하도록 하자.
정도로 볼 수 있겠다.
혹시나 틀린 점이 있다면 그건 내가 허접이라 그런것이니 너른 양해를 부탁드린다.
그럼 컴파일 오류 없는 개발하시길 :)
'Java' 카테고리의 다른 글
[Java] HashMap이란 무엇인가. (5) | 2023.01.01 |
---|---|
[Java] int와 Integer의 차이 (4) | 2022.12.19 |
[Java] List<Integer> <-> int[] 변환 (4) | 2022.12.18 |
[JAVA] 다이아몬드 연산자가 뭐임? (1) | 2022.12.17 |