String
변수를 비교할 땐, 비교연산자 ==
대신 String 클래스의 equals()
를 사용해야한다. 왜 그럴까? 아래 코드를 통해 확인해보자.
1 | String greeting = "Hello World."; |
위에서 greeting
과 hi
는 서로 같은 문자열 리터럴을 갖고있으므로 비교연산자로 비교하면 같을것 같지만, ==
연산자는 객체를 비교하는 연산자 인데 두 변수는 두 서로 다른 객체이기 때문에, false
를 반환한다.
따라서 문자열을 비교할땐, ==
대신 String
클래스의 equals()
를 이용해서 비교해야 한다. equals()
는 객체가 다르더라도 문자열 값만 비교하므로 원하는 결과를 반환받을 수 있다.
1 | String greeting = "Hello World."; |
1 | greeting==hi : false |
System
클래스의 identityHashCode()
를 이용해서 주소값을 확인해보니 greeting
과 hi
는 다른 객체로 보여진다. 그래서 비교연산자를 통한 비교에서 false
를 반환받은 것이다.
구글링해보니 일반적으로 객체는 new
키워드를 이용하여 생성자로 생성해야하나 String
변수에 한해 리터럴 대입을 허용한다고 한다. 그렇다면 문자열 리터럴을 대입하는것과 new
키워드로 String
객체를 생성하는 것은 어떤 차이가 있을까.
문자열 리터럴을 대입하여 변수를 생성하면, 해당 변수는 JVM의 String Constant Pool 에 할당된다고 한다. 이름에서도 알 수 있듯이 상수로 사용되기 때문에 여기에 할당되면 가변성(immutable)이 허용되지 않는다.
반면 생성자를 통해 문자열 변수를 생성하면, Heap 메모리에 할당되어 값의 변경이 가능해진다고 한다. 정말 그런지 주소값을 통해 확인해보겠다.
1 | String constantStr01 = "Hello"; |
1 | constantStr01 주소값 : 225534817 |
같은 값을 갖는 두개의 문자열 변수를 각각 리터럴 대입방식(String Constant Pool에 할당)과 생성자 생성방식(Heap에 할당)으로 선언하여 주소값을 비교한 결과이다.
리터럴 대입 방식을 통해서 선언한 문자열 변수들은 서로 같은 주소값을 갖고 있었다. String Constant Pool을 자세히 알지는 못하지만, 참조하는 값이 이미 할당된 변수가 있을 경우, 해당 변수가 바라보는 주소값을 참조하는것 같다.
반면 생성자를 통해 선언한 문자열 변수는 같은 값을 갖더라도 서로 다른 주소값을 갖는걸 알 수 있다.
결과적으로 문자열 변수 4개를 선언했지만, 주소값을 통해 확인된 객체는 3개이므로 메모리를 효율적으로 관리하기 위해서는 생성자를 통해서 문자열 변수를 선언하는것보단 리터럴을 대입하는 방식으로 선언하는게 더 나을것 같다.
출처