개발쿠키

[JAVA]String 본문

개발/java

[JAVA]String

쿠키와개발 2023. 7. 4. 00:33

1.시작하기 전 동일성과 동등성 

동일성은 비교하는 두 객체가 완전히 같은 것을 의미한다. 다시 말해 두 객체는 하나의 객체로 봐도 무방하다라는 뜻이며 해당 두 객체의 주소값은 같은 주소값을 가리킨다. 

동등성은 두 객체가 가지고 있는 값은 같지만 서로 다른 객체라는 것을 의미한다. 쉽게 말하면 주소값은 다르지만 두 객체가 가지고 있는 값은 같다라고 볼 수 있다.   

 

자바에서는 동일성을 확인하기 위해서는 == 을 사용하고 동등성을 확인하기 위해서는 equals 메소드를 사용한다. 

equals 메소드는 모든 객체들의 부모 객체인 Object 객체에서 구현하고 있으며 해당 메소드의 구현을 보면 == 을 사용하여 결과를 return 한다. 따라서 해당 메소드를 오버라이딩 하여 재정의 하지 않고 쓴다면 결국 동일성을 확인하는 것이나 다름없다. 아래 코드는 Object 객체의 equals 메소드 코드이다.

    public boolean equals(Object obj) {
        return (this == obj);
    }

참고로 원시 타입들은  == 사용시에 값 비교를 수행하고 참조 타입에 경우에는 주소 비교를 수행한다. 


2. String에서의 equals, == , compareTo

자바에서 String 값을 비교할 때 == 을 쓰지 말고 .equals를 쓰라는 말을 아마 질리도록 들었을 것이다. 그 이유는 위에서 말한 동등성과 동일성에 답이 있다.

 

String의 값을 비교할 때 == 을 쓴다는 것은 동일성 즉 두 객체가 동일한 객체를 참조하는지를 확인하는 것이다. 다시 말해 주소값을 확인하는 것이다. 

반면 equals는 String에서 오버라이딩 하여 재정의하여 사용하기 때문에 주소값을 비교하는 것이 아닌 파라미터로 넘겨 받은 값과 비교를 한다. 즉 동일성이 아닌 동등성을 확인하는 것이다(주소가 아닌 실제 값). 

 

String v1 = "Hello";
String v2 = new String("Hello");

System.out.println(v1 == v2); //false
System.out.println(v1.equals(v2)); //true

 

compareTo는 두 개의 문자열을 사전적 순서로 비교하는 메소드다. 해당 메소드는 문자열을 비교하여 결과에 따라 0, 음수, 양수를 반환한다. 

 

0 : 두 문자열이 동일할 경우

음수 : 비교 대산 문자열이 더 작을 경우

양수 : 비교 대상 문자열이 더 큰 경우

 

String str1 = "apple";
String str2 = "banana";
String str3 = "apple";
String str4 = "zebra";

System.out.println(str1.compareTo(str2));  // -1 (str1이 사전적으로 str2보다 앞에 옴)
System.out.println(str2.compareTo(str1));  // 1 (str2가 사전적으로 str1보다 뒤에 옴)
System.out.println(str1.compareTo(str3));  // 0 (str1과 str3은 사전적으로 동일)
System.out.println(str4.compareTo(str1));  // 25 (str4의 첫 문자 'z'와 str1의 첫 문자 'a'의 유니코드 값 차이

3. String Constant Pool과 문자열 생성 방법

위에서는 String의 값 비교에 대해 알아봤다. 여기서는 JVM 내 Heap 영역에 존재하는 문자열 리터럴을 저장하는 String Constant Pool이라는 영역을 알아보자

 

자바에서는 참조 타입의 값은 힙 영역에 저장되며 stack 영역에서 있는 변수는 힙에 존재하는 값을 참조한다. 이때 리터럴로 생성한 String은 string constant pool 안에 값이 존재하게 되며 해당 변수는 heap이 아닌 string constant pool 안의 값을 참조하게 된다. 

String one = "1";

위와 같이 리터럴로 String을 생성하면 아래 그림과 같은 구조를 가지게 된다. 

String one = "1";
String temp = "1";

 

위의 코드에서 리터럴로 String을 생성하면 아래의 동작 과정을 거치게 된다.

1. 비어있는 string constant pool에 "1"을 저장한다. 

2. JVM이 temp에 "1"을 할당하기 전 string constant pool에 "1"이 존재하는지 확인하고 있다면 해당 데이터의 참조값을 할당한다. 만약 "1"이 아닌 다른 문자열을 할당할 경우 새로운 문자열이 string constant pool에 저장된다.

 

그렇다면 new를 통해서 생성한 String 또한 똑같이 동작하는가 하면 정답은 '아니다' 이다. 

String one = "1";
String sec = new String("1");

System.out.println(System.identityHashCode(one));
System.out.println(System.identityHashCode(sec));

위에 코드에서 new를 통해 생성된 sec는 string constant pool이 아닌 Heap영역에 데이터가 저장된다. 따라서 one의 identityHashCode의 값은 string constant pool의 해시코드값이며 sec의 identityHashCode의 값은 Heap의 해시코드값이다

 

그럼 마지막으로 String을 생성하는 방법 중 하나인 intern 메소드를 알아보자

 

intern 메소드는 string pool에서 해당 문자열이 있으면 해당 String을 리턴하는 메소드다. 만약 해당 문자열이 없다면 Heap 영역이 아닌 string pool 영역으로 들어간다. 

참고로 intern 메소드는 다른 언어로 구현되어있다. 아래 해당 글에서 intern이 대략적으로 어떻게 구현되었는지 알 수 있다.

String v = "apple";
String v2 = new String("apple").intern();

System.out.println(v == v2);
System.out.println(v.equals(v2));

위 코드의 결과값은 true / true가 나온다. 첫번째 == 연산시에 true가 나온다는 것은 같은 객체를 참조하고 있다는 의미이다. 

 

참고 - https://developer-talk.tistory.com/475

https://velog.io/@ilil1/%EC%9E%90%EB%B0%94%EC%97%90%EC%84%9C-equals%EC%99%80-%EC%9D%98-%EC%B0%A8%EC%9D%B4

chatGpt


 

'개발 > java' 카테고리의 다른 글

[JAVA]클래스, 객체, 인스턴스  (0) 2023.07.20
[JAVA]Call By Reference, Call By Value  (0) 2023.07.14
[JAVA]JVM  (0) 2023.07.06
[JAVA]JVM, JRE, JDK  (0) 2023.07.03
[Java]Map getOrDefault  (0) 2023.06.27