String 클래스
- 기본형 char는 문자 하나만 다룰 때 사용하므로 여러 문자를 나열하려면 char 배열을 사용한다.
- 자바에서는 char 배열을 다루는 것은 불편하기 때문에 문자열 String 클래스를 제공한다.
- String을 통해 문자열을 생성하는 방법
- 문자열 리터럴(쌍따옴표) 사용 - "문자열"
- 객체 생성 - new String("문자열")
String str1 = "hello"; //기존
=> String str1 = new String("hello"); //변경, 위 코드를 자바에서 new를 사용해 변경해준다.
- 자바에서는 쌍 따옴표로 문자열을 감싸는 형태로 문자열을 생성 시 객체 생성을 통해 문자열을 생성하는 방식으로 변경해준다.
- String 클래스 내부에서 필드의 char 배열을 가지고 데이터를 처리한다.
- Java 9부터 char 배열대신 byte 배열을 사용한다.
- 문자 char는 2byte를 차지하는데 영어, 숫자는 보통 1byte로 표현이 가능하다.
그렇기에 byte 배열을 사용함으로 메모리를 더 효율적으로 사용할 수 있게 됐다. - 숫자, 영어로만 표현된 문자열의 경우는 1byte를 사용하고 아닌 경우 2byte인 UTF-16 인코딩을 사용한다.
- 문자 char는 2byte를 차지하는데 영어, 숫자는 보통 1byte로 표현이 가능하다.
- String 클래스의 메소드
- length(): 문자열 길이를 리턴한다.
- charAt(int index) : 특정 인덱스의 문자를 리턴한다.
- substring(int beginIndex, int endIndex) : 문자열의 부분 문자열을 리턴한다.
- indexOf(String str) : 특정 문자열이 시작되는 인덱스를 리턴한다.
- toLowerCase() / toUpperCase() : 문자열을 소문자 / 대문자로 변환한다.
- trim() : 문자열 양 끝 공백을 제거한다.
- concat(String str) : 문자열을 더한다.
- String은 클래스로 기본형이 아닌 참조형이다.
- 참조형은 계산할 수 있는 값이 아닌 참조 값이 있기에 원칙적으로 + 같은 연산을 사용할 수 없다.
- 문자열 String을 더할 때는 concat() 메소드를 사용해야 하지만 문자열은 너무 자주 다뤄지기 때문에 + 연산을 제공한다.
- String 클래스를 비교할 때는 ==(동일성) 비교가 아닌 equals()(동등성) 비교를 해야한다.
- String 클래스에서 equals()를 통해 동등성 비교를 할 수 있도록 오버라이딩 해뒀다.
문자열 리터럴, 문자열 풀
//1번 코드
String str3 = "hello";
//2번 코드
String str4 = "hello";
- 1번, 2번 코드와 같이 문자열 리터럴을 사용하는 경우 자바는 메모리 효율성과 성능 최적화를 위해 문자열 풀을 사용한다.
- 자바 실행 시점에 클래스에 문자열 리터럴이 있으면 문자열 풀에 String 인스턴스를 미리 만들어둔다.
만약 같은 문자열이 있으면 만들지 않는다. - 1번 코드에서 문자열 리터럴을 사용했으므로 문자열 풀에서 "hello"를 가진 인스턴스를 찾고 없으면 인스턴스를 생성한다.
2번 코드에서도 문자열 리터럴을 사용했지만 문자열 풀에서 "hello"를 가진 인스턴스가 있기에 새로 생성하지 않고 그 인스턴스를 참조해서 사용한다.
str3과 str4 참조 변수는 모두 같은 인스턴스를 참조하게 된다. - 문자열 풀 덕분에 같은 문자를 사용하는 경우 메모리 효율과 시간 감소로 성능 최적화가 가능하다.
- 문자열 리터럴을 사용해 값이 같은 경우 참조 값도 같기 때문에 == 연산을 통한 비교도 가능해진다.
- 그래도 문자열 비교는 항상 equals()로 해야한다!!
- 자바 실행 시점에 클래스에 문자열 리터럴이 있으면 문자열 풀에 String 인스턴스를 미리 만들어둔다.
문자열 풀 : 풀은 자원이 모여있는 곳을 의미하며 여러 곳에서 함께 사용할 수 있는 객체를 필요 시마다 생성하고 제거하는 것은 비효율적이다.
그렇기에 인스턴스를 미리 만들어두고 여러 곳에서 재사용함으로 성능과 메모리를 더 최적화하는 것이 목적이다.
문자열 풀은 힙 영역을 사용하고 해시 알고리즘을 사용해 빠른 속도로 String 인스턴스를 찾을 수 있다.
String 클래스 - 불변 객체
- String도 불변 객체다.
- concat()은 문자열을 합치는 메소드 즉, 불변 객체의 값을 변경하는 메소드로 필드 값을 변경하는 것이 아닌 새로운 불변 객체를 생성해서 리턴해준다.
- 불변인 String 객체를 더하거나 변경할 때마다 새로운 객체를 생성해야 한다는 단점이 있다.
- GC로 인해 CPU, 메모리 자원이 더 많이 사용되게 된다.
- String이 불변 객체인 이유는?
- 만약 가변 객체라면 문자열 변경 시 문자열 풀에서 같은 문자열을 참조하는 다른 변수들에게도 영향을 미친다.
(사이드 이펙트 문제 발생)
- 만약 가변 객체라면 문자열 변경 시 문자열 풀에서 같은 문자열을 참조하는 다른 변수들에게도 영향을 미친다.
StringBuilder
- StringBuilder은 가변 String으로 내부 값을 변경할 수 있기에 새로운 객체를 생성할 필요가 없고 메모리, 성능 측면에서 불변 객체보다 훨씬 좋다.
- 가변 객체이기에 사이드 이펙트 문제에 대해서 조심해야한다.
- String vs StringBuilder
- String은 불변이므로 한번 생성하면 내용을 변경할 수 없다.
문자열에 변화를 줄 때마다 새로운 객체가 생성되고 기존 객체는 버려진다.
이 과정에서 메모리와 처리 시간을 더 많이 소모한다. - StringBuilder는 가변이며 객체 안에서 문자열 추가, 수정, 삭제등을 할 수 있다.
변경 시 새로운 객체를 생성하지 않기 때문에 메모리 사용을 줄이고 성능 향상을 할 수 있다.
가변이므로 사이드 이펙트 문제를 조심해야한다. - 그렇기에 문자열 변경을 하는 동안 StringBuilder로 사용하다가 문자열 변경이 끝나면 안전한(불변) String으로 변환하는 것이 좋다.
- String은 불변이므로 한번 생성하면 내용을 변경할 수 없다.
String 최적화
//컴파일 전
String string = "Hello, " + "World!";
//컴파일 후
String string = "Hello, World!";
- 자바 컴파일러는 문자열 리터럴 + 연산 부분을 자동으로 합쳐준다.
- 런타임에 별도로 연산을 수행하지 않기 때문에 성능이 향상된다.
//String 변수 최적화
String result = str1 + str2
//컴파일 후
String result = new StringBuilder().append(str1).append(str2).toString();
- 문자열 변수의 경우에는 컴파일 시점에 변수에 어떤 값이 있는지 알 수 없다.
- StringBuilder를 사용해 한 번에 연산 후 String으로 변환해 저장하는 방법으로 최적화를 한다.
- 간단한 문자열의 경우에는 자바에서 최적화를 처리해주기 때문에 StringBuilder를 굳이 사용하지 않아도 된다.
- 반복문에서 String을 + 연산하는 경우 최적화가 이루어지지 않는다.
- 이런 경우 직접 StringBuilder를 사용하는 것이 더 좋다.
- 반복문, 조건문, 복잡한 문자열 변경, 대용량 문자열을 다룰 경우 StringBuilder가 더 좋다.
StringBuffer : StringBuilder와 똑같은 기능을 수행하는 클래스
내부에 동기화가 되어 있어서 멀티 쓰레드 상황에 안전하지만 동기화 오버헤드로 인해 성능이 느리다.
StringBuilder는 멀티 쓰레드 상황에 안전하지 않지만 동기화 오버헤드가 없으므로 성능이 빠르다.
- 메소드 체이닝 (Method Chaining)
StringBuilder sb = new StringBuilder();
//메소드 체이닝
sb.append("a").append("b").append("c");
- StringBuilder / StringBuffer는 메소드 체이닝 기법을 사용해서 append(), delete() ...등을 사용시 기존 문자열에 매개 변수 문자열을 더하고 자신 인스턴스의 참조 값을 리한다.
- 메소드 체이닝: 메소드 호출 결과로 자신 인스턴스의 참조 값을 리턴하는 것.
- 메소드 체이닝을 사용 시 자신의 참조 값을 리턴하므로 append()처럼 계속 .을 찍고 메소드를 연결해서 사용할 수 있다.
- 기존 메소드 호출처럼 메소드 호출 시마다 변수명으로 호출하지 않아도 된다는 장점이 있다.
- 메소드 체이닝 사용 시 코드를 간결하고 읽기 쉽게 해준다.
출처: [인프런 김영한 실전 자바 - 중급편]
김영한의 실전 자바 - 중급 1편 강의 | 김영한 - 인프런
김영한 | 실무에 필요한 자바의 다양한 중급 기능을 예제 코드로 깊이있게 학습합니다., 국내 개발 분야 누적 수강생 1위, 제대로 만든 김영한의 실전 자바[사진][임베딩 영상]단순히 자바 문법을
www.inflearn.com
'Java > [인프런 김영한 실전 자바 - 중급편]' 카테고리의 다른 글
[인프런 김영한 실전 자바 - 중급편] 날짜와 시간 (1) | 2024.07.21 |
---|---|
[인프런 김영한 실전 자바 - 중급편] 열거형 - ENUM (0) | 2024.07.19 |
[인프런 김영한 실전 자바 - 중급편] 래퍼, Class 클래스 (0) | 2024.07.19 |
[인프런 김영한 실전 자바 -중급편] 불변 객체 (0) | 2024.07.18 |
[인프런 김영한 실전 자바 - 중급편] Object 클래스 (1) | 2024.07.18 |