# JAVA # 형변환(Casting) # 변환(Conversion) # List <-> Array 간 형변환 ### 1. List → Array 형변환
List 타입 변환 방법
`List` `list.toArray(new String[0])`
`List` `list.toArray(new Integer[0])`
`List``int[]` 반복문으로 수동 변환 필요
##### 1) List → Array (참조형, 예: Integer, String 등) - toArray() 메서드로 변환이 가능하다. ```java List list = Arrays.asList("a", "b", "c"); String[] arr = list.toArray(new String[0]); ``` - list.toArray(new String\[0\]): 타입을 명시한 배열로 변환 - new String\[0\]은 사이즈가 0이어도 자동으로 맞춰준다. ```java List list = Arrays.asList(1, 2, 3); Integer[] arr = list.toArray(new Integer[0]); ``` ##### 2) List → Array (기본형, 예: int) - Java의 List<Integer>는 int\[\]로 직접 변환 불가능 - 직접 반복문으로 넣어줘야 한다. ```java List list = Arrays.asList(1, 2, 3); int[] arr = new int[list.size()]; for (int i = 0; i < list.size(); i++) { arr[i] = list.get(i); // 언박싱 자동 수행 } ``` --- ### 2. Array → List 형변환
변환 방법
`String[]` `Arrays.asList(arr)`
`Integer[]` `Arrays.asList(arr)`
`int[]` `Arrays.stream(arr).boxed.collect(Collectors.toList())`
##### 1) 참조형 배열 → List (예: String\[\], Integer\[\] 등) ```java String[] arr = {"a", "b", "c"}; List list = Arrays.asList(arr); ``` - Arrays.asList()는 배열을 고정 크기 리스트로 바꿔준다. - 추가/삭제는 불가능 → 변경 가능한 리스트로 바꾸려면 ```java List modifiableList = new ArrayList<>(Arrays.asList(arr)); ``` ##### 2) 기본형 배열 → List (예: int\[\], double\[\] 등) - 자바의 int\[\]는 바로 List<Integer>로 바꿀 수 없다. - Stream + boxed()를 활용한다. ```java int[] arr = {1, 2, 3, 4}; List list = Arrays.stream(arr) // IntStream 생성 .boxed() // int → Integer (박싱) .collect(Collectors.toList()); ``` # int -> String, char 변환 ### 1. int → String 변환 ​아래와 같은 세 가지 방법이 있다. ##### ✅ 방법 1. String.valueOf(int) 가장 범용적으로 쓰이고, null 처리가 가능하다. ```java int num = 3; String str = String.valueOf(num); System.out.println(str); // 출력: "3" ``` ##### ✅ 방법 2. Integer.toString(int) 오직 int → String 변환에만 사용된다. ```java int num = 3; String str = Integer.toString(num); System.out.println(str); // 출력: "3" ``` ✅ 방법 3. 문자열 더하기 (+ 연산자) 가장 짧고 직관적이지만 권장 방식은 아니다. ``` int num = 3; String str = num + ""; // 문자열과 더하면 문자열로 변환됨 System.out.println(str); // 출력: "3" ``` ### 2. int → char 변환 ##### ✅ 방법 1. '0'을 더한다. ```java int num = 3; char ch = (char) (num + '0'); // '0'의 아스키 값은 48 System.out.println(ch); // 출력: '3' ``` 📌 왜 이렇게 될까? - '0'은 아스키 코드 48이다 - 3 + 48 = 51, - 아스키 51은 문자 '3' 이기 때문에 이렇게 출력된다. ##### ✅ 방법 2. Character.forDigit() ``` int num = 3; char ch = Character.forDigit(num, 10); System.out.println(ch); // 출력: '3' ``` 🔎 Character.forDigit(value, radix)는 정수 값을 해당 진법(radix)의 문자로 바꿔​준다. ##### ❌ int → char 변환시 주의사항 아래과 같이 단순 캐스팅은 잘못된 것이다. ``` int num = 3; char ch = (char) num; // ❌ 결과: 이상한 제어문자 (아스키 3 → ETX) ``` # String, char > int 변환 ### 1. int → String 변환 자바에서 문자열(String) '123'을 정수(int) 로 바꾸는 방법이다. ##### ✅ 방법 1. Integer.parseInt() 가장 많이 쓰이는 방법이다. ```java String str = "123"; int num = Integer.parseInt(str); System.out.println(num); // 출력: 123 ``` - 문자열이 숫자 형태여야만 정상 작동한다. - "12a3"처럼 숫자가 아닌 문자가 포함되면 예외가 발생한다. ##### ✅ 방법 2. Integer.valueOf() Integer.valueOf()는 Integer 객체를 반환하지만, 필요하면 int로 자동 변환된다. ```java String str = "123"; int num = Integer.valueOf(str); // 반환 타입은 Integer (객체) int primitive = Integer.valueOf(str); // 자동 언박싱됨 ``` ##### ✅ 방법 3. 예외 처리 (안전하게 변환하기) 사용자가 입력한 문자열이 숫자가 아닐 수도 있으니 예외 처리를 하는 것이 권장된다. ```java String str = "123"; try { int num = Integer.parseInt(str); System.out.println(num); } catch (NumberFormatException e) { System.out.println("유효한 숫자가 아닙니다."); } ``` ❌ 이런 건 에러가 난다. ```java String str = "12a3"; int num = Integer.parseInt(str); // ❌ NumberFormatException ``` ### 2. char → int 변환 자바에서 '3'과 같은 문자(char) 를 정수(int) 로 바꾸는 방법이다. ##### ✅ 방법 1. '0'을 빼기 (가장 일반적이고 직관적) ```java char ch = '3'; int num = ch - '0'; // 결과: 3 ``` 📌 왜 이렇게 될까? - 문자 '3'의 아스키 코드: 51 - 문자 '0'의 아스키 코드: 48 - 51 - 48 = 3 ##### ✅ 방법 2. Character.getNumericValue() ``` char ch = '3'; int num = Character.getNumericValue(ch); // 결과: 3 ``` 이 방식은 'A' → 10, 'B' → 11 같은 16진 문자도 지원한다. 단, '0'~'9'만 처리할 거라면 첫 번째 방법이 더 간단하고 빠르다. ##### ✅ 방법 3. String으로 바꿔서 Integer.parseInt() 문자열로 바꿔서 정수화하는 방법인데 이건 다소 무거운 방법이고 잘 쓰지 않는다. ``` char ch = '3'; int num = Integer.parseInt(String.valueOf(ch)); // 결과: 3 ``` # 연산자 # 삼항 연산자 (Ternary Operator) --- ## 1. 개요 ### 1.1 삼항 연산자란? 삼항 연산자(Ternary Operator)는 ****조건에 따라 값을 선택****하는 Java의 축약 표현식이다. 일반적인 `if-else` 구문보다 간결하게 작성할 수 있으며, ****값을 반환****하는 표현식으로 활용된다. ``` int max = (a > b) ? a : b; ``` - 첫 번째 피연산자: 조건 (boolean 결과) - 두 번째 피연산자: 조건이 참일 때 반환 값 - 세 번째 피연산자: 조건이 거짓일 때 반환 값 ### 1.2 사용 목적 - UI 메시지 처리: 상황에 따라 메시지 문구 설정 - 값 분기 처리: 조건에 따라 상수/계산식 결정 - 간단한 로직 처리에서 ****코드 길이 절감**** --- ## 2. 문법 ``` 조건식 ? 참일 때의 값 : 거짓일 때의 값; ``` ### 2.1 예시 ``` int age = 20; String type = (age >= 18) ? "성인" : "미성년자"; // 출력: 성인 ``` - `조건식`: `age >= 18` → true - 참일 때: "성인" - 거짓일 때: "미성년자" --- ## 3. 삼항 연산자 vs if-else ### 3.1 차이점
항목 삼항 연산자 if-else 구문
반환 가능 여부 ✅ 값 반환 (표현식) ❌ 문(statement)
간결함 ✅ 매우 간결 ❌ 구조적으로 길어질 수 있음
가독성 ✅ 단순 조건일 때만 ✅ 복잡 조건/로직에 적합
### 3.2 비교 예시 ``` // 삼항 연산자 int max = (a > b) ? a : b; // 동일한 if-else int max; if (a > b) { max = a; } else { max = b; } ``` --- ## 4. 중첩 삼항 연산자 ### 4.1 기본 중첩 예제 ``` int score = 85; String grade = (score >= 90) ? "A" : (score >= 80) ? "B" : (score >= 70) ? "C" : "F"; System.out.println(grade); // 출력: B ``` ### 4.2 주의사항 - 중첩이 많아질수록 ****가독성이 급격히 저하됨**** - 중첩보다는 `if-else` 또는 `switch`를 고려할 것 --- ## 5. 실전 예제 모음 ### 5.1 나이 판별 ``` int age = 16; String result = (age >= 18) ? "성인" : "미성년자"; System.out.println(result); // 출력: 미성년자 ``` ### 5.2 최대값 구하기 ``` int a = 25, b = 17; int max = (a > b) ? a : b; System.out.println("최대값: " + max); // 출력: 25 ``` ### 5.3 짝수/홀수 판별 ``` int num = 9; String parity = (num % 2 == 0) ? "짝수" : "홀수"; System.out.println(parity); // 출력: 홀수 ``` ### 5.4 로그인 상태 메시지 ``` boolean isLoggedIn = false; String message = isLoggedIn ? "환영합니다!" : "로그인이 필요합니다."; System.out.println(message); // 출력: 로그인이 필요합니다. ``` ### 5.5 다중 조건 - 세금율 계산 예시 ``` double income = 45000; double taxRate = (income > 80000) ? 0.35 : (income > 40000) ? 0.25 : 0.15; System.out.println("세율: " + taxRate); // 출력: 세율: 0.25 ``` --- ## ✅ 요약
특징 설명
장점 간결한 조건 처리, 변수 할당 가능
단점 복잡한 로직에는 가독성 저하
사용 추천 UI 메시지, 단순 조건 분기, 코드 압축
사용 주의 중첩 사용은 피하고 if-else 대체로 제한
> 삼항 연산자는 ****작고 간단한 판단 로직****에 매우 유용하지만, ****가독성을 해치는 복잡한 분기****에는 적합하지 않다는 점을 기억하자. # String(문자열) # Java: String이란? --- ## 1. 개요 `String`은 Java에서 ****문자들의 연속(문자열)**** 을 표현하는 대표적인 클래스이며, `java.lang` 패키지에 포함되어 있어 별도의 `import` 없이 바로 사용할 수 있다. - 단일 문자는 `char` 타입으로, 복수 문자의 집합은 `String`으로 다룬다. - `String`****객체****이며, 내부적으로 `char[]` 배열을 기반으로 구성된다. - Java의 `String`****불변(immutable)**** 구조를 갖고 있어, 생성 이후 변경이 불가능하다. --- ## 2. 주요 특징 ### 2.1 문자열은 객체다 ```java String str = "hello"; // str은 String 클래스의 인스턴스 ``` - 문자열은 ****객체****이므로, 메서드와 연산을 통해 다양한 처리가 가능하다. - 내부적으로 `char[]` 배열을 가지며, 다양한 유틸리티 메서드 (`length()`, `substring()`, `toUpperCase()` 등)를 제공한다. ### 2.2 불변성 (Immutable) - `String`은 한 번 생성되면 그 내용을 변경할 수 없다. - 문자열을 수정하는 모든 작업은 ****새로운 String 객체를 생성****한다. ```java String a = "hi"; a += "!"; System.out.println(a); // hi! ``` `"hi"`는 그대로 있고 `"hi!"`는 새 객체로 Heap에 생성됨. ### 2.3 문자열 리터럴은 상수 풀(String Constant Pool)에 저장 ```java String s1 = "java"; String s2 = "java"; System.out.println(s1 == s2); // true → 같은 주소 참조 ``` → 동일한 리터럴은 JVM 내의 ****String Constant Pool****에서 공유됨 --- ## 3. 문자열 생성 방법 ### 3.1 리터럴 방식 (권장) ```java String s = "Hello"; ``` - JVM이 문자열 상수 풀(Constant Pool)에서 공유 → 메모리 효율적 - 가장 일반적이고 직관적인 방식 ### 3.2 생성자 방식 (비권장) ```java String s2 = new String("Hello"); ``` - 항상 새로운 객체를 Heap에 생성 - 리터럴과는 다른 주소를 갖게 됨 → 메모리 낭비 가능 📌 사용 예시 ```java String a = "abc"; String b = new String("abc"); System.out.println(a == b); // false (주소 비교) System.out.println(a.equals(b)); // true (내용 비교) ``` --- ## 4. 문자열 비교 ### 4.1 `==` vs `.equals()` ```java String a = "test"; String b = "test"; String c = new String("test"); System.out.println(a == b); // true (상수 풀에서 공유) System.out.println(a == c); // false (Heap에 새 객체 생성) System.out.println(a.equals(c)); // true (내용 비교) ```
비교 방식 설명
`==` 객체의 주소값 비교 (레퍼런스)
`.equals()` 객체가 가진 ****내용값**** 비교
--- ## 5. 문자열 길이와 메서드 ```java String s = "hello world"; System.out.println(s.length()); // 11 System.out.println(s.charAt(0)); // h System.out.println(s.substring(0, 5)); // hello System.out.println(s.toUpperCase()); // HELLO WORLD System.out.println(s.replace(" ", "_")); // hello_world ``` --- ## 6. 기타 특징 및 내부 구조 ### 6.1 String 내부 구조 - Java 9 이전: `char[]`로 저장 - Java 9 이후: `byte[]` + `coder` (압축을 위해 Compact Strings 적용) ```java public final class String implements java.io.Serializable, Comparable, CharSequence { private final char value[]; private final int offset; private final int count; ... } ``` ### 6.2 문자열 연결 (Concatenation) ```java String a = "Hello"; String b = "World"; String c = a + " " + b; // Hello World ``` - `+` 연산자는 내부적으로 `StringBuilder`로 변환되어 처리됨 (컴파일러 최적화) - 반복적인 연결에는 직접 `StringBuilder` 사용 권장 ```java StringBuilder sb = new StringBuilder(); sb.append("Hello"); sb.append(" "); sb.append("World"); System.out.println(sb.toString()); // Hello World ``` --- ## 7. 정리
항목 String
불변성 ✅ 한 번 생성된 값은 변경 불가
생성 방식 리터럴(권장), 생성자(비권장)
비교 방식 `==` 주소 비교 / `.equals()` 값 비교
내부 구조 Java 9 이전: `char[]`
연결 방식 `+`, `concat()`, `StringBuilder` 사용
사용 시기 고정된 문자열, 메시지, 상수 등
--- # Java: String의 주요 메서드 --- ### 1. String의 인스턴스 메서드 #### 1.1 개요 String 클래스의 인스턴스 메서드는 문자열 객체를 통해 호출하는 메서드입니다. 즉, "hello".length()처럼 문자열 인스턴스를 기준으로 작동합니다. #### 1.2 주요 인스턴스 메서드 + 메모리 작동 Java의 String 클래스는 다양한 문자열 조작 기능을 제공하는 인스턴스 메서드를 포함하고 있다. 아래는 자주 사용되는 메서드들의 설명과 예시이다. 모든 String 인스턴스 메서드는 원본 문자열을 절대 수정하지 않으며, 필요한 경우 항상 새로운 문자열 객체를 Heap에 생성한다. (\*불변성(immutability)\*) --- - `length()` 문자열의 길이를 반환한다. 예: `"hello".length()``5` 📌 ****설명:**** 내부 `char[]` 배열의 길이를 반환하며, 연산은 매우 빠름 (O(1)). --- - `charAt(int index)` 지정한 인덱스의 문자를 반환한다. 예: `"java".charAt(1)``'a'` 📌 ****설명:**** 내부 배열에서 해당 인덱스의 문자에 직접 접근. `IndexOutOfBoundsException` 주의. --- - `substring(int begin, int end)` 지정한 범위의 부분 문자열을 반환한다. 예: `"hello".substring(1, 3)``"el"` 📌 ****설명:**** 새로운 `String` 객체가 생성됨. 원본 문자열은 변하지 않음. --- - `indexOf(String s)` 주어진 문자열이 처음 등장하는 인덱스를 반환한다. 예: `"hello".indexOf("l")``2` 📌 ****설명:**** 내부적으로 왼쪽부터 한 문자씩 비교. 시간복잡도는 O(n). --- - `lastIndexOf(String s)` 주어진 문자열이 마지막으로 등장하는 인덱스를 반환한다. 예: `"hello".lastIndexOf("l")``3` 📌 ****설명:**** 내부적으로 오른쪽부터 역방향 검색. 불변 객체지만 탐색은 가변적으로 수행됨. --- - `contains(String s)` 특정 문자열이 포함되어 있는지를 boolean으로 반환. 예: `"hello".contains("he")``true` 📌 ****설명:**** 내부적으로 `indexOf(...) >= 0`과 동일한 방식으로 작동. --- - `startsWith(String prefix)` 주어진 문자열로 시작하는지 확인. 예: `"hello".startsWith("he")``true` 📌 ****설명:**** 접두어 길이만큼의 앞쪽 문자들을 순차 비교. --- - `endsWith(String suffix)` 주어진 문자열로 끝나는지 확인. 예: `"hello".endsWith("lo")``true` 📌 ****설명:**** 끝부분에서 길이만큼 잘라 비교. 새로운 객체는 생성되지 않음. --- - `equals(String s)` 문자열의 내용을 비교한다. 예: `"hi".equals("hi")``true` 📌 ****설명:**** 참조값이 다르더라도, 내부 문자 배열이 동일하면 true. 대소문자 구분. --- - `equalsIgnoreCase(String s)` 대소문자 무시하고 내용을 비교. 예: `"Hi".equalsIgnoreCase("hi")``true` 📌 ****설명:**** 내부적으로 `toLowerCase()``equals()` 수행. 추가 연산 포함됨. --- - `toUpperCase()` 문자열을 모두 대문자로 변환. 예: `"java".toUpperCase()``"JAVA"` 📌 ****설명:**** 원본 문자열은 유지되고, 대문자로 된 ****새 문자열 객체****가 생성됨. --- - `toLowerCase()` 문자열을 모두 소문자로 변환. 예: `"JAVA".toLowerCase()``"java"` 📌 ****설명:**** `toUpperCase()`와 동일하게 ****새 객체 생성****됨. 원본 유지. --- - `trim()` 문자열의 앞뒤 공백을 제거한다. 예: `" hello ".trim()``"hello"` 📌 ****설명:**** 공백이 없는 경우에도 새로운 문자열 객체가 생성될 수 있음 (JDK 버전에 따라 다름). --- - `replace(CharSequence old, CharSequence new)` 문자열 내에서 지정한 문자열을 다른 문자열로 치환. 예: `"apple".replace("p", "b")``"abble"` 📌 ****설명:**** 모든 치환 결과를 담은 ****새 문자열 객체**** 생성. 원본 변경 없음. --- - `split(String regex)` 지정한 정규 표현식을 기준으로 문자열을 나누고 배열로 반환. 예: `"a,b,c".split(",")``["a", "b", "c"]` 📌 ****설명:**** 내부적으로 정규식 패턴을 컴파일하여 배열을 생성. 반환 타입은 `String[]`. #### 1.3 특징 - 모두 String 객체를 통해 호출한다. - String은 불변(immutable)이라, 원본 문자열은 변경되지 않고 새 문자열을 반환한다. ### 2. String의 클래스 메서드 #### 2.1 개요 String 클래스의 클래스 메서드는 static 메서드이며, 객체 생성 없이 String.으로 호출할 수 있다. 주로 타입 변환이나 문자열 생성에 사용된다. #### 2.2 주요 클래스 메서드 목록 Java의 String 클래스는 객체 없이도 호출 가능한 클래스 메서드(static method)들을 제공한다. 자주 쓰이는 타입 변환, 문자열 생성, 서식 처리 작업을 편리하게 처리하기 위해 고안되었다. --- - `String.valueOf(...)` 다양한 타입의 값을 문자열로 변환한다. 예: `String.valueOf(123)``"123"` 📌 ****설명:**** 내부적으로 해당 타입을 문자열로 바꿔 새로운 `String` 객체를 Heap에 생성한다. `null`이 들어가면 `"null"` 문자열이 반환됨. --- - `String.copyValueOf(char[])` 주어진 `char[]` 배열을 문자열로 변환한다. 예: `String.copyValueOf(new char[]{'h','i'})``"hi"` 📌 ****설명:**** `valueOf(char[])`과 동일하며, 내부적으로 새 `String` 객체가 생성됨. --- - `String.format(String format, Object... args)` 형식 지정 문자열을 생성한다. 예: `String.format("%04d", 5)``"0005"` 📌 ****설명:**** `printf` 스타일 형식을 기반으로 포맷 처리. 내부에서 `Formatter` 클래스를 사용해 문자열을 구성하고, Heap에 새 객체로 반환. --- - `String.join(String delimiter, CharSequence...)` 여러 문자열을 하나로 연결하고, 사이에 지정된 구분자를 넣는다. 예: `String.join("-", "a", "b", "c")``"a-b-c"` 📌 ****설명:**** 내부적으로 `StringBuilder`를 이용하여 문자열을 효율적으로 이어붙인 후, 최종적으로 `String` 객체로 반환됨. --- - `String.valueOf(char[])` 문자 배열 전체를 문자열로 변환한다. 예: `String.valueOf(new char[]{'j','a','v','a'})``"java"` 📌 ****설명:**** 배열 전체를 읽어서 새로운 `String` 객체를 생성한다. 부분 변환도 `valueOf(char[], offset, count)`로 가능. #### 2.3 특징 - 클래스 이름으로 직접 호출 (String.format(...))한다. - 보통 타입 변환, 포맷팅, 조합 등에 사용된다. - 객체 없이도 사용할 수 있다. - static 키워드로 정의되며, String 클래스 이름으로 직접 호출 - 내부적으로는 대부분 새로운 문자열을 생성하여 Heap에 저장 --- # Java: String의 불변성 (Immutability) --- ## 1. 개요 Java의 `String` 클래스는 ****불변 객체(Immutable Object)**** 로 설계되어 있다. - 즉, 한 번 생성된 문자열은 ****절대로 수정되지 않으며****, 문자열을 변경하려고 시도하면 ****항상 새로운 객체가 생성된다****. - 이러한 불변성은 ****보안, 성능, 스레드 안정성**** 등 다양한 이유로 매우 중요한 특징이다. --- ## 2. 왜 불변(Immutable)한가? 불변성은 단순히 설계 철학이 아니라, 실제 Java 플랫폼의 안정성과 효율성을 위한 필수적인 요소이다. ### 2.1 보안(Security) - 문자열은 종종 ****파일 경로****, ****데이터베이스 연결 문자열****, ****네트워크 주소**** 등에 사용됨. - 가변 문자열이라면 악의적인 코드가 문자열을 수정해 ****보안 구멍을 만들 수 있음****. ```java String dbUrl = "jdbc:mysql://localhost"; dbUrl.replace("localhost", "hacker-site.com"); // String은 수정되지 않음 ``` ### 2.2 성능(Performance) - ****String Constant Pool****을 통해 동일한 문자열을 재사용할 수 있음. - 이 기능이 제대로 작동하려면 문자열이 ****절대 바뀌지 않아야 함****. ```java String a = "hello"; String b = "hello"; System.out.println(a == b); // true → 동일 객체 공유 ``` ### 2.3 스레드 안전성(Thread Safety) - 불변 객체는 내부 상태가 변경되지 않기 때문에, ****동기화 없이도 안전하게 공유 가능****하다. - 멀티스레드 환경에서 추가 비용 없이 안정적인 동작 보장. --- ## 3. 작동 방식 – 변경은 실제로 “새 객체 생성” ### 3.1 코드 예시 ```java String a = "hello"; String b = a; a = a + " world"; System.out.println(a); // hello world System.out.println(b); // hello ``` - `"hello"`는 리터럴 풀에 존재 - `"hello world"`는 Heap에 ****새로운 객체로 생성**** - 변수 `b`는 여전히 `"hello"`를 참조 ### 3.2 메모리 구조 흐름 ```text [초기 상태] a ───▶ "hello" b ───┘ [수정 후] a ───▶ "hello world" b ───▶ "hello" ``` 📌 ****변경이 아닌 “대체”다.**** 기존 객체는 절대 바뀌지 않음! --- ## 4. 불변 객체의 특징 요약
항목 설명
변경 가능 여부 ❌ 불가능 – 항상 새로운 객체 생성됨
스레드 안전성 ✅ 동기화 없이 공유 가능
공유 가능 여부 ✅ 문자열 리터럴 풀에서 객체 공유 가능
메모리 구조 Heap 영역 + String Constant Pool 최적화
관련 API 특징 `replace()` , `concat()` 등 모두 새 객체 반환
--- ## 5. 관련 클래스 구분 ### 5.1 불변 객체 (Immutable) - `String` - `Integer` - `Boolean` - `LocalDate`, `LocalTime`, `BigDecimal` ### 5.2 가변 객체 (Mutable) - `StringBuilder` - `StringBuffer` - `ArrayList` - `HashMap` 📝 ****불변 클래스는 equals(), hashCode() 재정의에 유리하며, 객체 캐싱 및 보안 측면에서도 장점을 가짐.**** --- ## 6. 성능상의 주의점 ### 6.1 반복 연결 시 성능 저하 ```java String s = ""; for (int i = 0; i < 1000; i++) { s += "a"; // 매 반복마다 새 객체 생성 } ``` → 이 방식은 ****1000개의 String 객체를 생성****함 → 해결 방법: `StringBuilder` 사용 ```java StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append("a"); } String result = sb.toString(); ``` --- ## 7. 결론 - `String`의 불변성은 Java 언어의 안정성과 성능 최적화를 위한 중요한 설계 원칙이다. - 문자열이 자주 변경된다면 `StringBuilder``StringBuffer`를 적극적으로 활용해야 한다. - 불변 객체는 ****보안성, 공유성, 스레드 안전성**** 측면에서 뛰어난 장점을 가진다. --- # Java: StringBuilder 클래스 --- ## 1. 개요 `StringBuilder`는 Java에서 ****가변(mutable) 문자열****을 처리하기 위한 클래스이다. - `java.lang` 패키지에 포함되어 있으며 import 없이 사용 가능 - 문자열을 반복적으로 추가/삭제/수정할 때 매우 유용 - 내부적으로 ****char\[\] 버퍼****를 사용하여 ****객체 생성 없이**** 문자열을 조작함 - `StringBuffer`와 기능은 거의 동일하지만, ****동기화를 지원하지 않아 더 빠름**** ```java StringBuilder sb = new StringBuilder("Hello"); sb.append(" World"); System.out.println(sb); // 출력: Hello World ``` --- ## 2. 주요 특징
항목 설명
가변성 ✅ 내부 문자열을 직접 수정 (객체 생성 없음)
동기화 ❌ 지원하지 않음 (→ 단일 스레드에서만 안전)
성능 `StringBuffer`보다 빠름
내부 구조 `char[]` 배열을 이용한 버퍼
기본 크기 초기 용량 16 → 필요 시 자동 확장됨
--- ## 3. 생성자 종류 ```java StringBuilder sb1 = new StringBuilder(); // 초기 버퍼 크기 16 StringBuilder sb2 = new StringBuilder(50); // 초기 버퍼 크기 지정 StringBuilder sb3 = new StringBuilder("Hello"); // 초기 문자열 지정 ``` --- ## 4. 주요 메서드 정리
메서드 설명
`append(String)` 문자열 끝에 추가
`insert(int, String)` 특정 위치에 문자열 삽입
`delete(int, int)` 지정 구간 문자 삭제
`deleteCharAt(int)` 지정 인덱스 문자 삭제
`replace(int, int, String)` 지정 구간을 문자열로 대체
`reverse()` 문자열을 뒤집음
`toString()` 최종 문자열로 변환 (`String`으로 반환)
`length()` 현재 문자열 길이 반환
`capacity()` 내부 버퍼의 총 용량 반환
`ensureCapacity(int)` 최소 버퍼 크기 확보
`setCharAt(int, char)` 특정 위치 문자 변경
`charAt(int)` 특정 인덱스 문자 반환
--- ## 5. 사용 예제 ```java public class StringBuilderExample { public static void main(String[] args) { StringBuilder sb = new StringBuilder("Hello"); sb.append(" World"); // Hello World sb.insert(5, ","); // Hello, World sb.replace(0, 5, "Hi"); // Hi, World sb.delete(3, 4); // Hi World sb.reverse(); // dlroW iH System.out.println(sb.toString()); // 출력: dlroW iH } } ``` --- ## 6. 성능 비교: String vs StringBuilder ```java // 느린 방식: String String str = ""; for (int i = 0; i < 10000; i++) { str += "a"; // 매 반복마다 새 객체 생성 } // 빠른 방식: StringBuilder StringBuilder sb = new StringBuilder(); for (int i = 0; i < 10000; i++) { sb.append("a"); // 동일 객체 안에서 처리 } ``` `StringBuilder`는 수천 배 빠를 수 있으며 GC 부담도 낮다. --- ## 7. 주의 사항 - ❗ 멀티스레드 환경에서는 `StringBuilder`****스레드 안전하지 않음**** - → 이 경우 `StringBuffer` 사용 - 반복 수정이 없고 단순한 문자열만 다룬다면 `String` 사용이 더 직관적일 수 있음 --- ## 8. 요약
항목 StringBuilder
불변성 ❌ 가변
스레드 안전성 ❌ 비동기 (단일 스레드만 안전)
성능 ✅ 매우 빠름
용도 반복적인 문자열 연결/수정에 최적
권장 사용 환경 루프 내 문자열 처리, 대용량 로그 조립 등
--- # Java: StringBuilder의 주요 메서드 --- ## 1. `StringBuilder`의 인스턴스 메서드 ### 1.1 개요 `StringBuilder` 클래스는 ****가변 문자열 처리****를 위해 설계된 클래스이다. 모든 메서드는 `StringBuilder` 객체 자체를 수정하며, ****새로운 객체를 생성하지 않는다****. 이는 `String`과의 가장 큰 차이점이다. ```java StringBuilder sb = new StringBuilder("hello"); sb.append(" world"); // 같은 객체 내에서 문자열을 수정 ``` --- ### 1.2 주요 메서드 + 내부 동작 > 모든 메서드는 시간복잡도가 평균적으로 ****O(1)**** 또는 \*\*O(n)\*\*이며, 대부분 ****String보다 빠름****. > 대부분 메서드가 `return this`를 통해 ****메서드 체이닝****을 지원한다. --- ### 🔹 `append(String str)` - 문자열을 뒤에 이어붙임 - 예: `new StringBuilder("hi").append(" there") → "hi there"` - 📌 내부 작동: `char[]` 배열 뒤쪽에 복사 → 공간 부족 시 자동 확장 - 시간복잡도: 평균 O(1), 확장 시 O(n) --- ### 🔹 `insert(int offset, String str)` - 지정 위치에 문자열 삽입 - 예: `"abc".insert(1, "X") → "aXbc"` - 📌 내부 작동: offset 뒤쪽 문자들을 밀고 `str` 삽입 - 시간복잡도: O(n) --- ### 🔹 `delete(int start, int end)` - 주어진 범위의 문자를 삭제 (`start <= i < end`) - 예: `"abcdef".delete(2, 4) → "abef"` - 📌 내부 작동: end 이후 문자들을 앞당김 - 시간복잡도: O(n) --- ### 🔹 `deleteCharAt(int index)` - 특정 인덱스의 문자 1개 삭제 - 예: `"abc".deleteCharAt(1) → "ac"` - 📌 내부 작동: `delete(index, index+1)` 호출과 동일 --- ### 🔹 `replace(int start, int end, String str)` - 주어진 범위를 `str`로 대체 - 예: `"abcde".replace(1, 4, "X") → "aXe"` - 📌 내부 작동: `delete``insert` 조합 - 시간복잡도: O(n) --- ### 🔹 `reverse()` - 문자열을 역순으로 뒤집음 - 예: `"hello".reverse() → "olleh"` - 📌 내부 작동: `char[]`를 양 끝에서 swap - 시간복잡도: O(n) --- ### 🔹 `toString()` - `StringBuilder` 내용을 `String` 객체로 반환 - 예: `sb.toString()` - 📌 내부 작동: 새로운 `String` 객체 생성 (불변 객체) - 시간복잡도: O(n) --- ### 🔹 `setCharAt(int index, char c)` - 지정한 인덱스의 문자를 변경 - 예: `"java".setCharAt(1, 'o') → "jova"` - 📌 내부 작동: 배열 원소 직접 교체 - 시간복잡도: O(1) --- ### 🔹 `charAt(int index)` - 특정 위치의 문자 반환 - 예: `"java".charAt(2) → 'v'` - 📌 설명: 내부 `char[]` 배열의 직접 접근 - 시간복잡도: O(1) --- ### 🔹 `length()` - 현재 문자열 길이 반환 - 예: `"abc".length() → 3` - 📌 설명: 버퍼 내 유효 문자 수 (`count` 필드) - 시간복잡도: O(1) --- ### 🔹 `capacity()` - 내부 버퍼의 현재 용량 반환 (할당된 char 배열 길이) - 예: `new StringBuilder(10).capacity() → 10` - 📌 동작: 초기 용량 16, 필요 시 자동 확장 (공식: `newCapacity = (old * 2) + 2`) - 시간복잡도: O(1) --- ### 🔹 `ensureCapacity(int minimumCapacity)` - 최소 버퍼 크기를 보장 (수동 확장) - 예: `sb.ensureCapacity(100)` - 📌 설명: 대량 조작 전 성능 최적화에 사용 --- ## 2. 메서드 체이닝 예시 ```java StringBuilder sb = new StringBuilder(); String result = sb.append("Hi") .append(" ") .append("there") .replace(0, 2, "Bye") .reverse() .toString(); System.out.println(result); // "ereht eyB" ``` --- ## 3. 주의 사항
주의 포인트 설명
`null` 값 전달 `append(null)` 은 문자열 `"null"` 로 처리됨
✅ 내부 수정 객체 내부 수정 → 참조가 유지됨
❌ 멀티스레드 비안전 멀티스레드 환경에서는 `StringBuffer` 사용
--- ## ✅ 마무리 요약
메서드 설명
`append()` 문자열 끝에 추가
`insert()` 중간에 삽입
`delete()` 구간 삭제
`replace()` 문자열 교체
`reverse()` 문자열 뒤집기
`charAt()` 문자 추출
`setCharAt()` 문자 수정
`toString()` 최종 문자열 반환
`capacity()` 내부 버퍼 용량 확인
`ensureCapacity()` 버퍼 미리 확장
--- # Java: StringBuffer 클래스 --- ## 1. 왜 필요한가? (등장 배경 및 필요성) ### 1.1 `StringBuffer` #### 📌 문제 상황 - 기존 `String`****불변(immutable)**** → 문자열을 수정할 수 없음 - `"abc" + "def"`처럼 문자열을 반복적으로 연결하면 ****새로운 객체가 계속 생성됨**** - 루프 내 문자열 누적, 로그 생성, 텍스트 조립 시 성능 저하 & 메모리 낭비 발생 #### ✅ 등장 이유 - ****가변(mutable)**** 문자열 필요 - 기존 객체 안에서 문자열을 ****효율적으로 수정/추가/삭제**** 가능해야 함 - 그리고 멀티스레드 환경에서도 ****안전하게 사용 가능****하도록 설계 필요 #### → 그래서 등장한 것이 **`StringBuffer` **(JDK 1.0부터 존재)**** (후속으로 성능 개선 목적의 `StringBuilder`는 JDK 1.5에 등장) --- ### 1.2 `StringTokenizer` #### 📌 문제 상황 - 문자열을 ****구분자(delimiter)**** 로 나누고 싶을 때마다 `indexOf()`, `substring()`을 반복하는 건 번거롭고 비효율적 - 문자열을 공백, 콤마, 탭 등으로 분리하는 ****간단한 기능이 자주 필요**** #### ✅ 등장 이유 - `split()` 등장 이전부터 빠르게 문자열을 분리할 수 있는 도구 필요 - 성능이 중요했던 옛 시절, 반복문에서 빠르게 동작하는 문자열 분리기 #### → 그래서 등장한 것이 **`StringTokenizer` **(JDK 1.0)**** → 이후 등장한 `split()`에 비해 ****속도는 빠르지만 정규표현식은 불가**** --- ### 1.3 `split()` (String 클래스의 메서드) #### 📌 문제 상황 - `StringTokenizer`****정규표현식을 못 씀**** - 복잡한 구분자(예: 공백 여러 개, 특수문자 등)는 처리 어렵다 #### ✅ 등장 이유 - 문자열을 자유로운 규칙(정규표현식)으로 분리할 수 있는 유연한 기능 필요 #### → 그래서 등장한 것이 **`split()` **메서드 (JDK 1.4부터 안정화)**** - 정규식 기반, 가독성 높고 활용도 넓음 --- ## 2. StringBuffer – 선언 방법 및 주요 사용법 ### 2.1 선언 방법 ```java StringBuffer sb1 = new StringBuffer(); // 빈 버퍼 (기본 용량 16) StringBuffer sb2 = new StringBuffer(50); // 초기 버퍼 크기 지정 StringBuffer sb3 = new StringBuffer("Hello"); // 초기 문자열 지정 ``` ### 2.2 주요 메서드 예제 ```java StringBuffer sb = new StringBuffer("Java"); sb.append(" is awesome"); // "Java is awesome" sb.insert(4, " 8"); // "Java 8 is awesome" sb.delete(0, 5); // "8 is awesome" sb.reverse(); // "emosewa si 8" System.out.println(sb.toString()); ``` --- ## 3. StringTokenizer – 선언 방법 및 사용법 ### 3.1 선언 방법 ```java StringTokenizer st = new StringTokenizer("a,b,c", ","); ``` ### 3.2 반복 사용법 ```java while (st.hasMoreTokens()) { System.out.println(st.nextToken()); } ``` #### 출력 ``` a b c ``` ### 3.3 기타 특징
특징 설명
기본 구분자 공백 ( `" \t\n\r\f"` )
성능 `split()` 보다 빠름
정규식 지원 여부 ❌ 불가능
--- ## 4. split() – 선언 방법 및 사용법 ### 4.1 기본 사용법 ```java String str = "one,two,three"; String[] result = str.split(","); ``` ### 4.2 정규표현식 사용 예 ```java String s = "apple banana melon"; String[] arr = s.split("\\s+"); // 공백 하나 이상 기준 ``` #### 출력 ``` apple banana melon ``` ### 4.3 특수 문자 분리 (예: |, . 등) ```java String s = "a|b|c"; String[] arr = s.split("\\|"); // |는 정규식 특수문자라 \\로 이스케이프 필요 ``` --- ## 5. 코딩 테스트 실전 예제 ### 5.1 공백 구분 숫자 입력 ```java BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); String[] input = br.readLine().split(" "); int a = Integer.parseInt(input[0]); int b = Integer.parseInt(input[1]); System.out.println(a + b); ``` ### 5.2 빠른 입력 + 반복 분리 (`StringTokenizer`) ```java BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); StringTokenizer st = new StringTokenizer(br.readLine()); int sum = 0; while (st.hasMoreTokens()) { sum += Integer.parseInt(st.nextToken()); } System.out.println(sum); ``` --- ## 6. 요약 비교
항목 StringBuffer StringTokenizer split()
용도 가변 문자열 조작 문자열 구분자 분할 정규식 기반 문자열 분할
동기화 ✅ 있음 ❌ 필요 없음 ❌ 필요 없음
성능 중간 빠름 약간 느림
정규표현식 ✅ 지원
등장 시기 오래됨 (JDK 1.0) 오래됨 (JDK 1.0) 나중에 도입됨 (JDK 1.4 이후)
사용 추천 환경 멀티스레드 조작 빠른 단순 입력 분할 복잡한 기준 분할 or 가독성 우선
--- ## 7. 결론 - **`StringBuffer`**: 멀티스레드 환경에서 가변 문자열 조작이 필요할 때 사용 - **`StringTokenizer`**: ****빠르고 간단한 문자열 분할****이 필요할 때 유용 (특히 코테) - **`split()`**: ****정규표현식으로 복잡한 기준 분할****이 필요할 때 적합 - 코딩 테스트에서는 상황에 따라 도구를 구분해서 사용하는 것이 중요 --- # Java: StringBuffer 주요 메서드 정리 --- ## 1. `StringBuffer`란? - ****가변(mutable) 문자열 클래스**** - `StringBuilder`와 API는 동일하지만, ****멀티스레드 환경에서 안전하도록 모든 메서드에** `synchronized` **처리**** - 성능은 `StringBuilder`보다 약간 느리지만, ****스레드 안정성 보장**** --- ## 2. 주요 메서드 + 내부 동작 > 아래 메서드들은 `StringBuilder`와 메서드명이 동일하며, 내부적으로 ****synchronized 블록****을 포함한다는 점만 다름. --- ### 🔹 `append(String str)` - 문자열을 끝에 추가 - 예: `new StringBuffer("hi").append(" there") → "hi there"` - 시간복잡도: O(1) (평균), O(n) (버퍼 확장 시) --- ### 🔹 `insert(int offset, String str)` - 지정 위치에 문자열 삽입 - 예: `"abc".insert(1, "X") → "aXbc"` - 시간복잡도: O(n) --- ### 🔹 `delete(int start, int end)` - 주어진 범위 삭제 - 예: `"abcdef".delete(2, 4) → "abef"` - 시간복잡도: O(n) --- ### 🔹 `deleteCharAt(int index)` - 인덱스에 해당하는 문자 하나 삭제 - 예: `"abc".deleteCharAt(1) → "ac"` - 시간복잡도: O(n) --- ### 🔹 `replace(int start, int end, String str)` - 지정 범위 문자열을 새 문자열로 교체 - 예: `"abcde".replace(1, 4, "X") → "aXe"` - 시간복잡도: O(n) --- ### 🔹 `reverse()` - 문자열을 뒤집음 - 예: `"hello".reverse() → "olleh"` - 시간복잡도: O(n) --- ### 🔹 `toString()` - 최종 문자열을 `String` 객체로 반환 - 예: `sb.toString()` - 시간복잡도: O(n) --- ### 🔹 `setCharAt(int index, char c)` - 인덱스 위치의 문자를 변경 - 예: `"java".setCharAt(1, 'o') → "jova"` - 시간복잡도: O(1) --- ### 🔹 `charAt(int index)` - 지정 위치 문자 반환 - 예: `"java".charAt(2) → 'v'` - 시간복잡도: O(1) --- ### 🔹 `length()` - 문자열 길이 반환 - 예: `"abc".length() → 3` - 시간복잡도: O(1) --- ### 🔹 `capacity()` - 내부 버퍼 용량 반환 - 예: `new StringBuffer(10).capacity() → 10` - 시간복잡도: O(1) --- ### 🔹 `ensureCapacity(int minimumCapacity)` - 버퍼 크기 미리 확보 (확장용) - 예: `sb.ensureCapacity(100)` - 시간복잡도: O(n) (버퍼 확장 시) --- ## 3. 체이닝 예시 ```java StringBuffer sb = new StringBuffer("Hi"); String result = sb.append(" there") .replace(0, 2, "Bye") .reverse() .toString(); System.out.println(result); // "ereht eyB" ``` --- ## 4. 주의사항
항목 설명
스레드 안전성 `synchronized`로 멀티스레드 환경에서 안전
성능 `StringBuilder`보다 약간 느림
내부 수정 ✅ 같은 객체 내에서 수정
메서드 반환값 대부분 `this` → 체이닝 가능
--- ## ✅ 요약 테이블
메서드 설명 시간복잡도 리턴값
`append(String)` 문자열 끝에 추가 O(1~n) `StringBuffer`
`insert(int, str)` 중간에 삽입 O(n) `StringBuffer`
`delete(start, end)` 구간 삭제 O(n) `StringBuffer`
`deleteCharAt(idx)` 문자 하나 삭제 O(n) `StringBuffer`
`replace(s, e, str)` 구간 대체 O(n) `StringBuffer`
`reverse()` 문자열 뒤집기 O(n) `StringBuffer`
`charAt(int)` 문자 조회 O(1) `char`
`setCharAt(i, c)` 문자 수정 O(1) void
`length()` 문자열 길이 O(1) `int`
`capacity()` 버퍼 크기 조회 O(1) `int`
`ensureCapacity(n)` 최소 용량 확보 O(n) void
`toString()` 문자열 객체 반환 O(n) `String`
--- # Java 코딩 테스트: StringBuilder 활용 사례 모음 --- ## 1. 왜 `StringBuilder`를 쓸까? 코딩 테스트에서 문자열을 다룰 때는 `String`보다 `StringBuilder``StringBuffer`를 활용하는 것이 ****성능 면에서 매우 유리****하다. ### 1.1 `String`은 불변 (immutable) - `String str = "a"; str += "b";` 는 사실상 매번 ****새로운 객체를 생성****합니다. - 반복 연결 시 매우 비효율적 → `O(n^2)` 시간복잡도 발생 가능 ### 1.2 `StringBuilder`는 가변 (mutable) - 내부 `char[]` 버퍼를 직접 수정 - 반복적으로 append해도 ****객체 1개로 해결**** → 훨씬 빠름 📌 참고: `StringBuilder``synchronized`****아닌**** 대신 더 빠르다. 멀티스레드가 아니면 `StringBuffer`보다 성능상 유리하다. --- ## 2. 자주 나오는 활용 패턴 ### 2.1 문자열 뒤집기 (문제 유형: 팰린드롬 판단, 좌우 비교) ``` String str = "hello"; String reversed = new StringBuilder(str).reverse().toString(); System.out.println(reversed); // 출력: "olleh" ``` - 빠르게 문자열을 뒤집는 가장 간단한 방법 - 팰린드롬 문제에서 `str.equals(reversed)`로 비교 --- ### 2.2 대량 문자열 이어붙이기 (문제 유형: 문자열 압축, 변환 등) ``` StringBuilder sb = new StringBuilder(); for (char c : arr) { sb.append(Character.toUpperCase(c)).append(", "); } String result = sb.toString(); ``` - 루프 안에서 문자열을 누적할 때 `+` 대신 `append()` 사용 - `String`으로 누적하면 매번 새로운 객체가 생김 → 성능 저하 --- ### 2.3 조건에 따라 문자열 조립 (문제 유형: 포맷팅, 규칙 변환) ``` StringBuilder sb = new StringBuilder(); for (int i = 1; i <= 5; i++) { sb.append("[").append(i).append("]"); if (i < 5) sb.append("-"); } System.out.println(sb.toString()); // 출력: [1]-[2]-[3]-[4]-[5] ``` - 특정 규칙을 가진 문자열을 만들어야 할 때 - 공백, 구분자, 특수문자 등을 유동적으로 넣기 좋음 --- ### 2.4 문자열 일부 제거 (문제 유형: 특정 문자 제거, 구간 삭제) ``` StringBuilder sb = new StringBuilder("abcdef"); sb.delete(2, 4); // 인덱스 2~3 삭제 → "abef" System.out.println(sb); ``` - 특정 구간 제거, 문자 삭제 등 깔끔하게 가능 - 문자열 슬라이싱처럼 사용할 수 있음 --- ## 3. 코딩 테스트 팁
상황 추천 도구 이유
문자열을 자주 수정/누적할 때 `StringBuilder` 가변 객체, 빠른 속도
문자열을 뒤집을 때 `StringBuilder.reverse()` 메서드 하나로 간단 처리
멀티스레드 고려 필요 없음 `StringBuilder` 동기화 없음 → 더 빠름
🧠 참고: 코테에서 `StringBuilder`를 쓰는 것이 대부분 OK, `StringBuffer`는 멀티스레드 환경에서만 고려 --- ## ✅ 마무리 요약 - 문자열 반복 조작 문제에는 ****절대** `String +="..."` **쓰지 말기!**** - 대신 `StringBuilder`로 누적하자 - `.append()`, `.delete()`, `.insert()`, `.reverse()`****다양한 메서드****를 적재적소에 활용 > 코테에서 성능이 중요한 대형 문자열 문제에서는 `StringBuilder`는 최고의 무기 --- # Java: String.split() 정규식 예제 모음 ## ✅ 기본 문법 ```java String[] result = 문자열.split("정규표현식"); ``` `split()`은 인자로 ****정규표현식(String regex)**** 을 받기 때문에, ****메타 문자(예:** `.` `|` `*`**)는 반드시 이스케이프**** 해야 한다. --- ## 1. ****공백 기준 분할**** ### 1.1 공백 하나 ```java String s = "hello world"; String[] arr = s.split(" "); ``` ### 1.2 공백 여러 개 (1개 이상) ```java String s = "hello world java"; String[] arr = s.split("\\s+"); // \\s = 공백 문자, +는 1개 이상 ``` ### 1.3 탭 또는 공백 ```java String s = "a\tb c"; String[] arr = s.split("[ \t]+"); ``` --- ## 2. ****콤마(,) 기준 분할**** ```java String s = "apple,banana,grape"; String[] arr = s.split(","); ``` ### 2.1 콤마 + 공백 제거 (CSV 정리) ```java String s = "apple, banana, grape"; String[] arr = s.split(",\\s*"); // , 뒤 공백 무시 ``` --- ## 3. ****특수 문자 기준 분할**** > 정규표현식에서 특수 문자는 반드시 ****이스케이프(\\)**** 필요!
문자 정규표현식에서 의미 split에 쓰는 방식
`.` 모든 문자 `"\\."`
` ` OR (또는)
`*` 반복자 `"\\*"`
`(` `)` `[` `]` 그룹 지정 `"\\("`, `"\\["`
### 3.1 마침표(`.`) 기준 ```java String s = "www.example.com"; String[] arr = s.split("\\."); ``` ### 3.2 파이프(`|`) 기준 ```java String s = "red|green|blue"; String[] arr = s.split("\\|"); ``` --- ## 4. ****숫자/문자 기준 분할**** ### 4.1 숫자만 기준으로 나누기 ```java String s = "abc123def456ghi"; String[] arr = s.split("\\d+"); // 숫자(1개 이상) 기준 나눔 ``` ### 4.2 문자 기준 나누기 ```java String s = "123abc456def"; String[] arr = s.split("[a-zA-Z]+"); // 알파벳 기준 나눔 ``` --- ## 5. ****복수 구분자 분리**** ### 5.1 콤마(,) 또는 세미콜론(;) 또는 공백 ```java String s = "apple,banana;grape orange"; String[] arr = s.split("[,; ]"); ``` ### 5.2 AND/OR 조건 키워드 분리 ```java String s = "red and blue or green"; String[] arr = s.split("\\s+(and|or)\\s+"); // 공백 포함한 and/or 기준 ``` --- ## 6. ****문장 끝 단위 분리 (구두점)**** ```java String s = "Hi. I am John! Nice to meet you?"; String[] arr = s.split("[.!?]\\s*"); // . ! ? 뒤 공백까지 포함 ``` --- ## 7. ****빈 문자열 처리**** ```java String s = "a,,b,c"; String[] arr = s.split(",", -1); // -1 옵션으로 빈 항목도 유지 System.out.println(Arrays.toString(arr)); // 출력: [a, , b, c] ``` --- ## 🧾 보너스: 정규표현식 요약표
패턴 의미 예시
`\\s` 공백 문자 (스페이스, 탭 등) `"\\s+"` → 연속된 공백
`\\d` 숫자 (0~9) `"\\d+"` → 숫자 1개 이상
`\\w` 문자/숫자/밑줄 `"\\w+"`
`.` 아무 문자 한 개 `"a.b"` → "acb", "a1b" 가능
`[abc]` a 또는 b 또는 c `"a[bc]d"` → "abd", "acd"
`[^abc]` a,b,c 제외한 문자 `"[^0-9]"` → 숫자 제외
`+` 앞의 패턴 1번 이상 반복 `"\\s+"`
`*` 0번 이상 반복 `"a*"`
` ` OR
--- ## ✅ 결론 - `split()`****정규표현식 기반의 유연한 문자열 분리****가 가능하다. - 단순 입력에는 `StringTokenizer`, 복잡한 기준 분리에는 `split()`이 적합. - 특수문자 분리 시 반드시 ****이스케이프(**`\\`**)**** 를 주의할 것! --- # Java: String vs StringBuilder 비교 --- ## 1. 개요 자바에서 문자열을 다룰 수 있는 대표 클래스는 `String``StringBuilder`이다. 두 클래스 모두 문자열을 표현하는 데 사용되지만, \*\*불변성(immutability)\*\*과 ****메모리 구조****, ****성능**** 면에서 근본적인 차이가 존재한다. - `String`: 불변(immutable) 객체. 문자열을 수정하는 것처럼 보이지만 실제로는 새로운 객체가 생성됨. - `StringBuilder`: 가변(mutable) 객체. 동일 인스턴스 내의 버퍼를 직접 수정하여 효율적인 메모리 사용과 빠른 성능을 제공함. 이러한 차이로 인해, 문자열 처리 방식에 따라 두 클래스의 선택은 전혀 달라져야 한다. --- ## 2. 메모리 관점에서의 차이 ### 2.1 String – 불변 객체의 메모리 동작 `String` 클래스는 한 번 생성된 이후로 내용을 절대 변경할 수 없는 ****불변 객체****이다. 문자열을 수정할 때마다 ****새로운 객체가 생성****되어 Heap 메모리에 누적된다. #### 예시 ```java String s = "hi"; s = s + "!"; // "hi!"라는 새로운 String 객체가 생성됨 ``` #### 메모리 흐름 ```text [Heap Memory] "hi" ← 초기 객체 "hi!" ← 새로운 객체 (변수 s가 참조) → 기존 객체는 GC 대상이 되기 전까지 Heap에 남아 있음 → 반복적인 수정 시 객체 수 증가 → GC 부하 및 메모리 낭비 ``` #### 주요 특징 - 내부적으로 `char[]` 배열을 `final`로 선언하여 불변성 보장 - 멀티스레드 환경에서 안전하게 공유 가능 - 문자열 상수 풀(String Constant Pool)을 활용한 최적화 가능 --- ### 2.2 StringBuilder – 가변 객체의 효율적 구조 `StringBuilder`는 내부에 ****동적으로 크기 조절이 가능한 char\[\] 버퍼****를 가지고 있으며, 문자열 조작 시 이 버퍼를 직접 수정한다. 불필요한 객체 생성이 없고, 성능과 메모리 측면에서 뛰어나다. #### 예시 ```java StringBuilder sb = new StringBuilder("hi"); sb.append("!"); System.out.println(sb); // 출력: hi! ``` #### 메모리 흐름 ```text [Heap Memory] StringBuilder 객체 └─ char[] buffer = ['h', 'i', '!'] ← 내부 버퍼가 직접 수정됨 → 동일 객체 내에서 작업 → 메모리 재사용 → 객체 수 증가 없음 → GC 부담 없음 ``` #### 내부 구조 예시 ```java public final class StringBuilder { char[] value; int count; public StringBuilder append(String str) { ensureCapacityInternal(count + str.length()); str.getChars(0, str.length(), value, count); count += str.length(); return this; } } ``` --- ## 3. 성능 차이 비교 ### 3.1 코드 성능 테스트 ```java // String (비효율적 방식) String s = ""; for (int i = 0; i < 1000; i++) { s += "a"; // 매번 새로운 객체 생성됨 } ``` ```java // StringBuilder (효율적 방식) StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append("a"); // 내부 버퍼만 수정됨 } ``` ### 3.2 성능 분석
항목 String StringBuilder
메모리 구조 불변 객체, 매번 새 객체 생성 가변 객체, 하나의 버퍼 사용
연결 방식 `+` 또는 `concat()` 호출마다 새 객체 `append()` 는 동일 객체에 누적
GC 부하 심함 적음
속도 느림 빠름
💡 ****결론****: 루프나 재귀에서 문자열을 누적해야 할 경우, `StringBuilder`가 수십 배 빠를 수 있음. --- ### 3.3 스레드 안전성 관점 - `String`: 불변 → 스레드 안전 - `StringBuilder`: 가변 + 동기화 없음 → 스레드 ****안전하지 않음**** - 다중 스레드에서 문자열 조작이 필요하다면 → `StringBuffer` 사용 고려 ```java // 멀티스레드 환경에서 StringBuilder는 위험 StringBuilder sb = new StringBuilder(); Runnable task = () -> { for (int i = 0; i < 1000; i++) { sb.append("x"); // 동기화 안 되어 경합 발생 가능 } }; ``` → 해결책: `StringBuffer` 또는 `synchronized` 블록 사용 --- ## 4. 결론 및 실무 가이드라인 ### 4.1 어떤 클래스가 적절한가?
사용 상황 적절한 클래스 이유
고정된 문자열 / 변경 없음 `String` 불변성으로 안전하고, 메모리 캐싱 가능
루프 내 문자열 누적 / 대량 조작 `StringBuilder` 메모리 효율 및 성능 우수
멀티스레드 환경에서 동기화가 필요한 경우 `StringBuffer` 메서드 동기화로 스레드 안전성 확보
### 4.2 실전 팁 - `String`으로 반복 연결하지 말 것 (예: `s = s + "a"` X) - ✅ 대량 처리 시에는 반드시 `StringBuilder` 사용 - ✅ 멀티스레드 환경에서는 `StringBuffer` 또는 동기화 처리 필수 --- # Java: String vs StringBuilder vs StringBuffer 비교 --- ### 1. 개요 자바에서 문자열을 다룰 때 흔히 사용하는 세 가지 클래스인 String, StringBuilder, StringBuffer는 모두 CharSequence 인터페이스를 구현하지만, 불변성, 동기화, 성능 측면에서 명확한 차이를 보인다. - String은 문자열 변경 시마다 새로운 객체를 생성 → 여러 객체가 Heap에 누적 - StringBuilder와 StringBuffer는 동일 객체 내에서 내부 버퍼를 수정 → Heap 메모리를 효율적으로 사용 - StringBuffer는 동기화를 위한 추가 비용으로 인해 메모리뿐 아니라 CPU 리소스도 더 소모됨 이 문서에서는 다음과 같은 관점에서 세 클래스의 차이점을 심층 분석한다. - 변경 가능 여부 (immutable vs mutable) - 성능 및 메모리 구조 - 스레드 안전성 및 동기화 여부 - 사용 예시 및 실제 적용 시 고려사항 --- ## 2. String ### 2.1 불변 객체(Immutable)의 특징 - `String` 객체는 \*\*불변(immutable)\*\*이다. - 문자열이 변경되는 것처럼 보이지만 실제로는 ****새로운 객체가 생성****된다. ```java String str = "hello"; str += " world"; // 실제로는 "hello", "hello world" 두 개의 객체가 Heap에 존재 ``` #### 이유 - 내부적으로 `char[]` 배열을 final로 가지고 있으며, 변경이 불가능 - `final class String` → 상속 불가 - 안전성, 보안, 캐싱, 문자열 상수 풀(String Pool) 최적화에 유리 ### 2.2 성능 및 메모리 측면 - ****연결이 많을수록 비효율적****: 반복적인 문자열 조작 시 매번 새로운 객체 생성 - ****Garbage Collector 부하**** 증가 ### 2.3 사용 예시 - 단순 문자열 선언 및 불변 문자열 상수 - 파라미터 값, 로그인 ID, 파일 경로 등 변경되지 않는 문자열 --- ## 3. StringBuilder ### 3.1 가변 객체(Mutable) + 비동기 환경에 적합 - 내부에 `char[]` 버퍼를 가지고 있으며, ****동기화를 고려하지 않음**** - 성능과 메모리 측면에서 매우 효율적 ```java StringBuilder sb = new StringBuilder("hello"); sb.append(" world"); System.out.println(sb.toString()); // hello world ``` ### 3.2 주요 메서드 ```java sb.append("a"); sb.insert(2, "x"); sb.replace(1, 3, "yz"); sb.delete(0, 2); sb.reverse(); ``` ### 3.3 성능 테스트 예시 ```java public class BuilderTest { public static void main(String[] args) { long start = System.currentTimeMillis(); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 100_000; i++) { sb.append("abc"); } long end = System.currentTimeMillis(); System.out.println("StringBuilder 소요 시간: " + (end - start)); } } ``` ### 3.4 사용 시점 - 단일 스레드 환경에서 반복적인 문자열 조작 - 파일 생성, 로깅 등 실시간 데이터 처리 --- ## 4. StringBuffer ### 4.1 가변 객체(Mutable) + 스레드 안전(Thread-safe) - `StringBuilder`와 거의 동일하나, ****모든 메서드가 synchronized 처리**** - 멀티스레드 환경에서 안정성 보장 ```java StringBuffer sb = new StringBuffer("safe"); sb.append(" thread"); System.out.println(sb); // safe thread ``` ### 4.2 내부 구조 ```java public synchronized StringBuffer append(String str) { super.append(str); return this; } ``` ### 4.3 성능 비교 - 동기화 오버헤드로 인해 성능은 `StringBuilder`보다 낮음 - 병렬 처리나 스레드풀 환경에서 안정성을 확보해야 할 때 적합 ### 4.4 멀티스레드 예시 ```java public class BufferThreadTest { static StringBuffer sb = new StringBuffer(); public static void main(String[] args) throws InterruptedException { Runnable task = () -> { for (int i = 0; i < 1000; i++) { sb.append("a"); } }; Thread t1 = new Thread(task); Thread t2 = new Thread(task); t1.start(); t2.start(); t1.join(); t2.join(); System.out.println("최종 길이: " + sb.length()); // 2000 보장됨 } } ``` --- ## 5. 성능 및 메모리 비교 요약
항목 String StringBuilder StringBuffer
변경 가능 여부 ❌ 불변 ✅ 가변 ✅ 가변
동기화 지원 ❌ 없음 ❌ 없음 `synchronized` 지원
성능 느림 (매번 객체 생성) 빠름 중간 (동기화 오버헤드 존재)
메모리 효율성 낮음 높음 중간
적합한 환경 상수, 설정값, 로그 ID 등 단일 스레드 문자열 조작 멀티스레드 문자열 조작
--- ## 6. 결론 및 실무 팁 - ****String****: 변경이 없는 정적 문자열 (상수, config 값 등) - ****StringBuilder****: 반복문 내에서 문자열 누적, 대량 처리 시 - ****StringBuffer****: 동기화가 필요한 멀티스레드 환경에서만 사용 ### `StringBuilder` vs `StringBuffer` – 언제 뭐 써야 해?
항목 `StringBuilder``StringBuffer`
****스레드 안전성**** ❌ 비동기(스레드 안전 아님) ✅ 동기화(synchronized, 스레드 안전)
****속도**** 빠름 상대적으로 느림
****코딩 테스트/일반 개발**** ✅ 단일 스레드 환경에서 최적 ❌ 보통 코테에선 불필요한 오버헤드 발생
****멀티스레드 환경**** ❌ 사용 시 동기화 문제 생길 수 있음 ✅ 멀티스레드 환경에서 안전하게 사용 가능
****사용 예**** 코딩테스트, 파일 파싱, 로그 누적, 텍스트 생성 서버에서 공유 자원 처리, 멀티스레드 로그 등
- ****코딩 테스트, 단일 스레드 로직****: `StringBuilder` 사용 (기본값) - ****멀티스레드 환경 (예: 서버 개발)****: `StringBuffer` 사용 💡 ****JDK 1.5 이상에서는** `StringBuilder`**를 기본으로 고려하고, 동기화가 꼭 필요한 경우에만** `StringBuffer`**를 선택하자.**** --- # Java: 기본형(int) vs String(불변 객체)의 메모리 구조 비교 --- ## 1. Java의 메모리 구조 Java 프로그램은 크게 세 가지 메모리 공간을 사용한다: ### 1.1 Stack 영역 - ****지역 변수********기본형(primitive type)**** 값 저장 - 메서드 호출 시마다 프레임이 생성되고, 종료되면 자동으로 해제됨 - 매우 빠르며, GC 대상이 아님 ### 1.2 Heap 영역 - `new` 연산자 등을 통해 생성된 ****객체****가 저장됨 - `ArrayList`, `String`, `Integer`, 사용자 정의 클래스 등 - GC(Garbage Collector)에 의해 관리됨 ### 1.3 String Constant Pool (리터럴 풀) - Heap 내부의 특별한 공간 - 동일한 문자열 리터럴을 ****공유****하여 메모리 절약 - `"hello"`와 같은 리터럴은 Pool에서 재사용됨 --- ## 2. 기본형 변수 (예: `int`) 기본형은 값 그 자체가 ****Stack**** 메모리에 저장된다. 변수 간의 대입은 ****값 복사****이며, 변수 간 영향이 없다. ### 2.1 코드 예시 ```java int a = 10; int b = a; a = 20; ``` ### 2.2 메모리 흐름 ```text 초기 상태: [Stack] a → 10 b → 10 (값 복사됨) a = 20; 이후: a → 20 b → 10 (영향 없음) ``` 📌 `int`****값 자체(value)**** 를 저장하며, 변수 간 대입은 ****값 복사****다. 값을 변경해도 ****기존 변수나 참조에 영향을 주지 않는다.**** --- ## 3. String (불변 객체) `String`****참조형(reference type)**** 객체이며, Stack에는 참조만 저장되고 ****실제 문자열은 Heap 또는 Constant Pool에 존재****한다. ### 3.1 코드 예시 ```java String s1 = "hello"; String s2 = s1; s1 = s1 + " world"; ``` ### 3.2 메모리 흐름 ```text [초기 상태] [Stack] [Heap (String Pool)] s1 ─┐ "hello" └────────▶ s2 ─┘ (s1, s2 둘 다 "hello" 참조) [변경 후] [Stack] [Heap] s1 ──────────▶ "hello world" ← 새 객체 생성 s2 ──────────▶ "hello" ← 원본 유지 → s1 + " world" 는 기존 "hello"를 수정하는 것이 아니라 **"hello world"라는 새로운 객체를 생성** ``` 📌 `String`****불변(immutable)**** 이기 때문에 수정이 아닌 ****새 객체 생성**** 방식으로 동작한다. 따라서 ****반복 수정이 많으면 Heap에 객체가 누적 → GC 부담 증가**** 가능. --- ## 4. 예제 비교 요약 ```java // 기본형 int a = 10; int b = a; a = 20; // b는 여전히 10 // 참조형 (String) String s1 = "hi"; String s2 = s1; s1 = s1 + "!"; // s2는 여전히 "hi" ```
항목 기본형 (int 등) 참조형 (String)
저장 위치 Stack Stack(참조), Heap(객체)
대입 방식 값 복사 참조값 복사
수정 시 영향 원본과 무관 불변 → 새 객체 생성
메모리 부담 없음 반복 수정 시 Heap 객체 증가 가능
--- ## 5. 요약 및 실무 팁 - 🔹 ****기본형****은 값 자체를 Stack에 저장 → 변경 간섭 없음, 매우 빠름 - 🔹 ****String****은 참조형이며 불변 → 수정 시 마다 새 객체 생성 - 🔸 반복적인 문자열 연결/수정이 필요한 경우 → **`StringBuilder` **사용이 권장됨**** ```java // 비효율 (String) String result = ""; for (int i = 0; i < 1000; i++) { result += "a"; // 1000개 객체 생성 } // 효율적 (StringBuilder) StringBuilder sb = new StringBuilder(); for (int i = 0; i < 1000; i++) { sb.append("a"); } String result = sb.toString(); // 단 1개 객체 사용 ``` --- ## 6. 관련 참고 - 불변 객체: `String`, `Integer`, `Boolean`, `LocalDate` - 가변 객체: `StringBuilder`, `ArrayList`, `HashMap` 📌 ****불변성 덕분에 String은 안정적이고 안전하지만, 반복 수정 시에는 가변 객체 사용이 메모리 효율에 유리하다.**** ### 1. Java 의 메모리 구조 세 가지 메모리 영역 - Stack : 지역 변수, 기본형 변수 저장 (`int`, `boolean`, etc) - Heap : `new` 키워드나 객체 생성 시 사용 (`String`, `ArrayList`, etc) - String Constant Pool : 같은 문자열 리터럴을 재사용하는 공간 (Heap 내부 특수 영역) ### 2. 기본형 변수(int 등) 값 자체가 Stack에 저장됨 ``` int a = 10; int b = a; a = 20; ``` 📌 메모리 흐름: ``` [Stack] a → 10 b → 10 (복사됨) a = 20; 이후 a → 20 b → 10 (영향 없음) ``` - int는 값(value) 자체가 저장되므로 대입 시 값만 복사됨 - 변경해도 다른 변수에 영향 없음 ### 3 . String (불변 객체) 참조값이 Stack에, 실제 값은 Heap/String Pool에 저장 ``` String s1 = "hello"; String s2 = s1; s1 = s1 + " world"; ``` 📌 메모리 흐름: ``` 초기 상태: [Stack] [Heap (String Pool)] s1 ─┐ "hello" └────────────▶ s2 ─┘ 변경 후: [Stack] [Heap] s1 ──────────────▶ "hello world" ← 새 객체 (불변성) s2 ──────────────▶ "hello" ← 원본은 그대로 유지 ``` - s1 + " world"는 기존 문자열을 수정하지 않고, 새로운 문자열 객체 생성 - 기존 "hello"는 s2가 여전히 참조 중 - 따라서 메모리 활용방식이 더 복잡 (GC 대상 증가 가능) ### 4. 예제 비교 ```java // 기본형 int a = 10; int b = a; a = 20; // b는 여전히 10 // String String s1 = "hi"; String s2 = s1; s1 = s1 + "!"; // s2는 여전히 "hi" ``` - 기본형은 값 그 자체를 다루고, 수정 시 그냥 덮어쓰기 - String은 객체의 참조를 다루며, 수정 시 새 객체 생성 = 불변성 유지 이 불변성 덕분에 String은 안정적이고 안전하지만, 반복 수정이 많을 땐 StringBuilder를 쓰는 게 더 효율적이다. --- # Java: 문자열 리터럴 vs new String() 객체 생성 차이 --- ## 1. 개요 Java에서 문자열은 두 가지 방식으로 생성할 수 있다: ```java String s1 = "hello"; // 리터럴 방식 String s2 = new String("hello"); // new 키워드 방식 ``` 두 방식은 ****겉보기에는 동일한 문자열 값을 가지지만****, 내부 메모리 구조, 생성 방식, 비교 결과, 성능 등에서 중요한 차이를 가진다. --- ## 2. 메모리 구조 차이 ### 2.1 리터럴 방식 (`String s = "hello"`) - 문자열 리터럴은 ****String Constant Pool****이라는 특별한 메모리 영역에 저장된다. - 동일한 리터럴이 여러 번 사용되더라도 ****중복 없이 하나의 객체로 공유****됨. - JVM이 ****자동 최적화****해 메모리 사용이 매우 효율적이다. #### 예시 ```java String a = "hello"; String b = "hello"; System.out.println(a == b); // true (같은 객체 참조) System.out.println(a.equals(b)); // true (내용도 동일) ``` ****같은 상수 풀 객체****를 참조하고 있기 때문에 `a == b``true`. --- ### 2.2 new 연산자 방식 (`String s = new String("hello")`) - `new String()`은 무조건 Heap 메모리에 ****새로운 객체를 생성****한다. - 내부적으로 리터럴 `"hello"`를 참조하더라도, 그 값을 복제한 ****별도 인스턴스****가 생성됨. #### 예시 ```java String a = "hello"; String b = new String("hello"); System.out.println(a == b); // false (다른 참조) System.out.println(a.equals(b)); // true (내용은 같음) ``` `==` 결과는 `false`, `.equals()``true` --- ## 3. 객체 비교 방식 ### 3.1 `==` 연산자 (참조 비교) - 두 객체의 ****참조 주소값을 비교****함 - ****리터럴 방식****끼리는 상수 풀 공유로 `true`일 수 있음 - `new`로 생성한 객체는 항상 `false` ### 3.2 `.equals()` 메서드 (값 비교) - 두 문자열의 ****내용이 같은지**** 비교함 - 방식과 무관하게, ****내용이 같으면 항상** `true`** ```java String s1 = "test"; String s2 = new String("test"); System.out.println(s1 == s2); // false System.out.println(s1.equals(s2)); // true ``` --- ## 4. 성능 및 메모리 효율
항목 리터럴 방식 ( `"hello"`) new 방식 ( `new String("hello")`)
메모리 위치 Constant Pool (Method Area) Heap 영역
객체 중복 여부 없음 (공유됨) 항상 새 객체 생성
`==` 비교 결과 true (동일 참조) false (서로 다른 참조)
`.equals()` 결과 true true
메모리 효율 높음 낮음
성능 빠름 느림 (GC 대상 증가)
GC 부담 낮음 높음
사용 권장 여부 ✅ 일반적으로 권장 ❌ 특별한 경우에만 사용
--- ## 5. 실제 사용 시 차이 ### 5.1 리터럴 방식 – 일반적으로 권장 - ****정적 값****, ****설정 상수****, ****조건 분기**** 등에 적합 - 성능과 메모리 효율이 우수함 ```java if (userRole.equals("ADMIN")) { // 권한 체크 } ``` ### 5.2 new 방식 – 특수 목적용 - 동일한 문자열이더라도 ****서로 다른 객체를 명시적으로 생성****할 때 - 예: ****문자열 캐시 우회****, ****디버깅용 비교****, ****참조를 강제로 분리해야 할 경우**** - 필요하다면 `.intern()` 메서드를 사용해 수동으로 상수 풀 등록 가능 ```java String s = new String("hello").intern(); // Constant Pool 등록 ``` --- ## 6. 정리: 리터럴 vs new String()
비교 항목 리터럴 방식 ( `"hello"`) new 방식 ( `new String("hello")`)
저장 위치 String Constant Pool Heap 메모리
객체 공유 여부 ✅ 공유됨 (중복 방지) ❌ 매번 새 객체 생성
`==` 비교 결과 true false
`.equals()` 결과 true true
성능 빠름 느림
메모리 효율 높음 낮음
GC 부담 낮음 높음
사용 추천 여부 ✅ 일반적 상황에서 적극 권장 ⚠️ 특별한 목적이 없으면 지양
--- ## 7. 결론 - 문자열은 ****리터럴 방식으로 생성하는 것이 가장 효율적****이다. - `new String()` 방식은 ****불필요한 Heap 객체 생성****으로 인해 성능과 메모리 측면에서 불리하다. - 문자열 비교 시에는 항상 `==`이 아닌 **`.equals()`** 를 사용할 것. - ****고급 최적화****가 필요한 경우에는 `.intern()`으로 Constant Pool을 수동 활용할 수 있다. --- 이 정리는 `Java` 메모리 구조, 성능 최적화, 참조와 객체 비교에 대해 정확한 이해를 돕기 위한 위키 스타일 자바에서 문자열은 두 가지 방법으로 생성할 수 있다. String str = "hello";와 String str = new String("hello"); 두 가지 방식은 같아 보이지만, 작동 방식, 메모리 구조, 성능, 객체 비교 등 여러 측면에서 차이가 난다. ```java String s1 = "hello"; // 리터럴 방식 String s2 = new String("hello"); // new 연산자 사용 ``` 두 방식은 겉보기에는 같은 값을 가지지만, 메모리 처리, 비교 방법, 성능 등의 측면에서 분명한 차이가 존재한다. ### 1. 메모리 구조 차이 #### 1.1 리터럴 방식: String s1 = "hello"; 문자열 리터럴은 String Constant Pool에 저장된다. JVM은 동일한 문자열 리터럴이 여러 번 등장하더라도 중복을 제거하여 하나의 객체만 저장한다. 메모리 효율이 높고, 비교 연산 시 빠르다. 📌 예시 ```java String a = "hello"; String b = "hello"; System.out.println(a == b); // true (같은 상수 풀 객체) ``` #### 1.2 new 연산자 방식: String s2 = new String("hello"); new 키워드를 사용하면 Heap에 새로운 객체가 강제로 생성된다. 비록 내부적으로 "hello"라는 리터럴이 사용되더라도, Constant Pool의 객체를 복제한 별도 인스턴스가 Heap에 만들어진다. 따라서 리터럴과는 다른 참조값을 가진다. 📌 예시 ```java String a = "hello"; String b = new String("hello"); System.out.println(a == b); // false (주소 다름) System.out.println(a.equals(b)); // true (값 같음) ``` ### 2. 객체 비교 (== vs equals()) #### 2.1 == (참조 비교) 리터럴 방식은 JVM이 Constant Pool을 공유하므로 == 결과가 true일 수 있다. new 키워드는 항상 새로운 객체를 생성하므로 == 비교 시 false가 된다. #### 2.2 .equals() (내용 비교) 두 방식 모두 문자열 내용을 같게 만들면 .equals()는 항상 true를 반환한다. 문자열 비교에서는 항상 .equals()를 사용하는 것이 안전하다. ### 3. 성능 및 메모리 효율 3.1 리터럴 방식 - 런타임 시 상수 풀을 이용하므로 메모리 사용이 효율적이다. - JVM이 자동으로 중복 문자열을 제거해주므로 불필요한 객체 생성을 방지할 수 있다. - 가장 선호되는 방식. 3.2 new 방식 - new String()을 사용할 경우 매번 객체가 새로 만들어지므로 메모리 낭비 가능성이 있다. - GC(Garbage Collector) 부담 증가 → 성능 저하로 이어질 수 있음. - 특별한 이유(예: 강제로 다른 참조를 만들어야 할 필요)가 없다면 지양하는 것이 좋다 ### 4. 실제 사용 시 차이점 4.1 리터럴 방식의 사용 예 - 상수 정의, 비교가 필요한 경우 - 조건 분기, 설정 값 체크 등에서 성능 이점 있음. ``` if (userRole.equals("ADMIN")) { ... } ``` 4.2 new 방식의 사용 예 - 객체 생성을 명시적으로 컨트롤해야 하는 특수한 경우 - 외부 API나 라이브러리에서 intern() 메서드를 사용하여 상수 풀로 다시 보낼 수도 있음 ``` String s = new String("hello").intern(); // 풀로 보냄 ``` ### 5. 정리
리터럴 방식 (`"hello"`) new 방식 (`new String("hello")`)
메모리 위치 Constant Pool (Method Area) Heap 영역
객체 중복 여부 없음 (공유됨) 항상 새 객체 생성
`== ` 비교 결과 true (같은 참조) false (다른 객체)
`.equals()` 결과 true true
메모리 효율 높음 낮음
성능 빠름 느림
GC 부담 낮음 높음
사용 권장 여부 일반적으로 권장 특별한 경우에만 사용
--- ### 6. 결론 - 문자열을 선언할 때는 특별한 이유가 없다면 "hello"와 같은 리터럴 방식을 사용하는 것이 가장 효율적이다. - new String()은 불필요한 객체 생성을 유발하며, 메모리와 성능 측면에서 손해를 볼 수 있다. - 문자열 비교 시에는 항상 ==가 아닌 .equals()를 사용할 것. - 최적화를 원할 경우 intern()을 활용해 상수 풀을 직접 사용할 수도 있다. --- # Java: BufferedReader # Java: BufferedWriter # Stream