회고록 블로그
[공부 필기] Java 기본 공부하기 (10) 본문
공부 중인 강의 : 윤성우 선생님, 윤성우의 열혈 Java 프로그래밍 강의.
링크 : https://cafe.naver.com/cstudyjava
1. String 클래스
- 우리가 알고 있는 "문자열"도 String 클래스임
- String 클래스가 "문자열"을 저장하는 과정을 이해하려면 String 클래스 내부를 먼저 봐야함
→ Java SE 16 기준 공식 문서 中 "String 클래스"에 대한 설명
https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/String.html
* String 클래스에는 많은 메소드와 변수들이 있음
* 문자열을 저장하는 곳은 "value"라는 인스턴스 변수인 듯함
→ 즉, 아래와 같은 코드가 있다면 문자열은 String 인스턴스를 먼저 생성한 후 "value"라는 변수에 문자열을 저장하는 것으로 보임
...
String school = "서울대학교";
...
→ 아래의 코드에서 "school"이라는 변수는 String 인스턴스를 가리키는 참조변수라고 함
* school 이라는 변수는 문자열을 가리키고 있지 않음
문자열을 담고 있는 String 인스턴스를 가리키고 있음
* 인스턴스를 생성하고 접근하려면 "참조변수"가 필요하다고 이전에 배웠고
참조변수에는 "참조값(주소값)"이 저장된다고 배웠기 때문에, school 이라는 참조변수에도 주소값이 들어 있을 것으로 추측됨
...
String school = "서울대학교";
...
- 참고로, String 인스턴스는 아래와 같이 생성할 수도 있음 (하지만, 위의 방법과 약간의 차이가 존재함, 별첨1 참고)
String school = new String("서울대학교");
- JVM은 "문자열"을 만나면 반드시 String 인스턴스를 생성하게 됨
→ 아래와 같은 코드가 있다고 가정하면,
"Hello"라는 문자열이 들어있는 String 인스턴스를 먼저 생성하고, 문자열을 저장한 후 그 참조값(주소값)을 반환함
println 메소드는 참조값을 인수로 받아서 출력을 해줌
System.out.println("Hello");
→ 마치, 아래와 같이 보통의 정수(1, 2, 3 등)를 출력하고자 할 때, 가장 먼저 "3"이라는 숫자를 int형으로 메모리에 할당하고 출력해주듯이.
System.out.println(3);
- JDK7 버전 부터는 switch문의 소괄호 안에 문자열이 들어갈 수 있다고 함
2. String 클래스의 메소드
- String 클래스에는 많은 메소드가 있음
→ length, concat 등
- 더 많은 메소드가 궁금하다면 Java 공식 문서 참고
e.g. JDK 16 버전 기준 공식 문서 :
https://docs.oracle.com/en/java/javase/16/docs/api/java.base/java/lang/String.html
2.1. 문자열 내용 비교
→ 비교연산자(==)를 사용할 수도 있지만, 이때 비교 연산자는 "참조값"을 비교하는 것임
→ "문자열 내용"을 비교하려면 equals 메소드, compareTo 메소드, compareToIgnoreCase 메소드를 사용함
- 아래의 1번 코드는 컴파일러에 의해서 2번의 코드로 자동 변환됨
// 1번 코드
System.out.println("Pizza"+"Pasta");
// 1번 코드는 컴파일러에 의해서 2번 코드로 자동 변환된다
// 2번 코드
System.out.println("Pizza".concat("Pasta"));
2.2. 문자열 연결시키는 메소드 concat
→ String 인스턴스는 기본적으로 Immutable 인스턴스이기 때문에
문자열을 연결시켜서 나오는 새로운 문자열은 모두 "새로운 인스턴스"임
→ concat 메소드 뿐만 아니라 다른 메소드도 마찬가지임
Immutable 인스턴스이기 때문에 기존 인스턴스의 값을 수정/변경하는게 아니라 새로운 인스턴스가 생성되는 것
- 복합대입연산자를 사용할 때
→ 문자열이라 복합대입연산자는 사용 불가능할 것 같아 보이지만 가능함
→ 문자열에서 복합대입연산자를 사용하는게 좋은 코드라고 할 수는 없지만, 문법적으로 문제는 없는 코드임
→ "menu1 += menu2"는 아래와 같은 과정을 통해 새로운 인스턴스가 생성된다고 생각하면 됨
→ 이때 중요한 사실은 "menu1" 변수에 저장된 문자열은 기존 인스턴스와는 전혀 다른 '새로운 인스턴스'라는 점
- 아래와 같이 문자열과 다른 자료형의 데이터(숫자 등)를 연결할 수도 있음
// 예시
String menu1 = 1+"Americano";
→ 이때 중요한 점은 숫자 "1"이 valueOf 메소드를 통해서 "문자열"로 치환된다는 점임
2.3. 문자열 결합 최적화 진행할 때
- 아래와 같은 코드가 있다고 가정
String menu1 = 1 + ":" + "Americano" + 1; // 1번 메뉴는 Americano 1잔
String menu2 = 2 + ":" + "Latte" + 1; // 2번 메뉴는 Latte 1잔
- 위에서 본 '문자열과 다른 데이터 결합 과정'을 여기에 적용해보면 굉장히 많은 불필요한 인스턴스들이 생성되어버림
- 그렇기 때문에 컴파일러는 이렇게 비효율적으로 문자열을 결합하지 않고 아래와 같이 문자열을 결합해줌
→ 과도하게 많은 인스턴스를 생성하지 않고도 문자열을 결합해줌
- 보충설명
→ StringBuilder 인스턴스에 Buffer가 있다?
* 정확하게는 StringBuilder의 부모 클래스에게 str을 넘기고 거기에서 Buffer에 저장되는 것으로 예상됨
→ append 메소드는 인스턴스의 참조값을 반환한다?
2.4. StringBuilder
- StringBuilder는 개발자가 직접 사용할 수 있음
- 사용방법은 아래 글을 참고
https://hardlearner.tistory.com/288
- StringBuilder를 사용하는 이유는 2.3. 에서 언급했듯이 문자열 결합 최적화를 위함
2.5. StringBuffer
- StringBuilder와 기능적으로 동일함
(생성자를 포함한 메소드 수도 동일하고, 메소드의 기능도 동일하고, 메소드 이름과 매개변수의 선언도 동일하다고 함)
- 그렇다면 StringBuffer가 존재하는 이유는?
→ Thread의 안전을 위해서
→ 쓰레드가 안전하기는 한데... 불필요한 상황에서 사용하면 성능 저하를 유발함
- StringBuffer가 먼저 만들어졌는데,
쓰레드의 안정성을 필요로 하지 않을 때, 사용하기에는 너무 느렸음.. 그래서 만들어진게 StringBuilder
→ 따라서, StringBuilder가 더 빠름
> 별첨1. String 인스턴스 생성 방법의 차이
String str = "";과 String str = new String(""); 은 둘 다 문자열을 저장한다는 공통점은 있지만
분명한 차이점이 있다. 그 차이가 무엇일까 찾아봤다.
실제로는 이렇게 쉬운 개념은 아니겠지만,
아직 초보이고 쉽게 이해하기 위해서 포괄적으로 차이점을 적어봤다.
우선 코드를 하나 가져왔음
// 1번 코드
String str1 = "고등학교";
String str2 = "고등학교";
// 2번 코드
String str3 = new String("고등학교");
String str4 = new String("고등학교");
- 먼저, String pool을 간단하게 이해해보자.
→ 간단하게 말하면 Java에는 String 객체를 모아둘 수 있는 공간이 있는데 그곳이 바로 String pool임
→ 이 String pool은 Heap Memory 안에 있다고 함
→ 그림으로 표현하자면... 이런 느낌..?
- 이제 1번 코드를 보자.
// 1번 코드
String str1 = "고등학교";
String str2 = "고등학교";
→ 1번 코드에서 str1과 str2는 "고등학교"라는 문자열을 가리키고 있는데
이 "고등학교"라는 문자열은 String pool에 할당됨
→ 심지어 두 변수(str1, str2)는 "같은" 데이터(참조값)를 가리킴 ("고등학교"라는 문자열을 2개 생성해서 각각 가리키는게 아님)
→ 이때 JVM은 "고등학교"라는 문자열을 또 만들지 않고,
(효율성을 고려해서?) str1이 만들어 놓은 "고등학교"라는 문자열을 그대로 str2도 사용하게(?) 만듦
→ 즉, 완벽히 같은 문자열(여기에서는 "고등학교")을 가리키는 변수들(여기에서는 str1, str2)이 100개, 1000개 있다면
문자열을 100개, 1000개 생성해서 각각 가리키도록 하는게 아니라,
문자열을 하나만 생성하고 모든 변수가 해당 문자열을 참조하도록 함
- 2번 코드를 보자.
// 2번 코드
String str3 = new String("고등학교");
String str4 = new String("고등학교");
→ 이 경우는 String pool에 할당하는게 아닌 Java Heap에 각각의 객체를 생성하게 됨
→ 그림으로 표현하면 아래와 같은 상황..?
→ 둘은 각각의 객체이기 때문에 참조값도 다름
→ 완벽히 같은 문자열(여기에서는 "고등학교")이라도 각각의 String 인스턴스로 생성되고
가리키는 참조값(주소값)이 다 다르기 때문에
String 인스턴스를 100개, 1000개 생성하면 "고등학교"라는 문자열이 100개, 1000개 만들어져서 할당됨
- 1번 코드를 사용하면 위험하지 않을까...?
(둘은 같은 참조값을 가리키고 있으니까 str1에서 문자열을 변경해버리면 str2도 영향을 받지 않을까?)
// 1번 코드
String str1 = "고등학교";
String str2 = "고등학교";
→ String 인스턴스는 "Immutable 인스턴스"임
(이건 Java에서 기본적으로 정해놓은 규칙이라고 함)
→ Immutable(불변의) 인스턴스?
인스턴스가 자신이 가지고 있는 데이터의 변경을 허용하지 않는 것
→ 즉, 한번 생성되면 그 데이터를 수정/변경할 수 없는 것이 Immutable 인스턴스임
→ 만약 데이터의 변경이 필요하다면? 기존의 인스턴스는 버리고 새로운 인스턴스를 만듦
→ 따라서, String 인스턴스는 기본적으로 한번 생성 후에는 데이터를 수정할 수 없는 Immutable 인스턴스이기 때문에
str1의 값을 변경했다고 해서 str2의 값이 변경되는 상황은 발생할 수가 없음
※ Java의 메모리 스택과 힙에 대한 설명은 아래 블로그 참고
https://yaboong.github.io/java/2018/05/26/java-memory-management/
참고 자료 : 윤성우 선생님, 윤성우의 열혈 Java 프로그래밍 강의, https://cafe.naver.com/cstudyjava
추가 참고 자료 : https://readystory.tistory.com/140
'2. 프로그래밍 언어 공부 > Java' 카테고리의 다른 글
javap와 javac 경로 찾을 수 없을 때 (1) | 2021.11.03 |
---|---|
[공부 필기] Java 기본 공부하기 (11) (0) | 2021.10.27 |
[공부 필기] Java 기본 공부하기 (9) (0) | 2021.10.19 |
[공부 필기] Java 기본 공부하기 (8) (0) | 2021.09.25 |
[공부 필기] Java 기본 공부하기 (패키지) (0) | 2021.09.22 |