관리 메뉴

나구리의 개발공부기록

자바의 정석 기초편 ch9 - 7 ~ 11[String클래스, 문자열 비교, 빈 문자열, String클래스의 생성자와 메서드] 본문

유튜브 공부/JAVA의 정석 기초편(유튜브)

자바의 정석 기초편 ch9 - 7 ~ 11[String클래스, 문자열 비교, 빈 문자열, String클래스의 생성자와 메서드]

소소한나구리 2023. 12. 6. 13:39

1) String클래스

(1) 구성

  • char[] (문자타입 배열)
  • 메서드(문자열 관련)
// String 클래스의 구조
public final class String implements java.io.Serializable, Comparable {
	private char[] value;
    ...(이하 메서드들)

 

(2) 특징

  • 내용을 변경할 수 없는 불변(immutable)클래스
  • 덧셈 연산자를 이용한 문자열 결합은 성능이 떨어짐(객체가 계속 만들어짐) -> 여담이지만 slf4j로 로그 출력시 + 가아닌 ,로 문자열과 변수를 결합하는 이유가 있음
  • 문자열의 결합이나 변경이 잦다면(반복문안에서 문자를 지속적으로 결합 등) 내용을 변경할 수 있는 StringBuffer를 사용하는 것을 권장
String a = "a";
String b = "b";
a = a+b; // 새로운 객체에 "ab"가 담김, 즉 새로운 객체의 새로운 참조값으로 변수 a에 저장됨

2) 문자열의 비교

(1) String str = "abc";와 String str = new String("abc"); 차이

  • new 연산자를 이용하면 항상 새로운 인스턴스(객체)를 생성함 즉, 저장된 논리의 값이 같아도 별도의 객체로 생성했으므로 참조값이 다름
  • 문자열은 어차피 내용변경이 불가능하므로 특별한 경우를 제외하고 new 이용하여 객체를 여러번 만들 필요가 없고 new로 생성하게되면 오히려 메모리만 낭비 됨
// 빈 문자열 객체를 1개만 만들고 str1,str2,str3의 참조변수가 1개의 객체를 가르킴
// 메모리 절약
String str1 = "";
String str2 = "";
String str3 = "";

// 빈 문자열 객체를 3개나 생성하여 메모리 낭비 -> 가급적 사용 X
String str1 = new String("");
String str2 = new String("");
String str3 = new String("");

 

(2) 문자열의 비교는 equals()를 사용

  • 등가비교 연산자(==)를 활용하여 비교하면 참조값을 비교하게 되므로 상황에 따라 거짓이 나올 수 있음(new로 객체를 생성 후 그 참조값을 비교했을 때)
  • String 클래스의 equals()는 이미 오버라이딩 되어 정의되어있으므로 참조값이 아닌 논리 값을 비교함
// 문자열 리터럴 생성
String str1 = "abc";
String str2 = "abc";

// new 연산자로 String 인스턴스(객체) 생성
String str3 = new String("abc");
String str4 = new String("abc");

// str1과 str2는 하나의 문자열(내용변경불가) 주소를 공유함
// str3과 str4는 각각의 객체가 생성 되었으므로 주소가 다름

str 1 == str2	   // 등가비교연산자 비교(주소비교)
// 0x100 == 0x100  주소가 같으므로 true	

str 3 == str4	   // 등가비교연산자 비교(주소비교)
// 0x200 == 0x300  주소가 다르므로 false

// 그래서 문자열비교는 equals()를 써야함(오버라이딩 되어있음)
str1.equals(str2) // true
str3.equals(str4) // true

 

(3) 문자열 리터럴이 하나만 만들어 지는 이유

  • 문자열 리터럴은 new String()처럼 객체를 생성하지 않아도 프로그램 실행시 자동으로 생성됨
  • 별도의 constant pool에 저장하여 관리함(상수 저장소)
  • 같은 내용의 문자열 리터럴은 1개만 만들어짐 
  • String클래스는 불변클래스이므로 내용이 변하지 않으니 여러 참조변수가 하나의 주소를 공유할 수 있음.

3) 빈 문자열("",  empty string)

  • 내용이 없는 문자열(크기가 0인 char형 배열을 저장하는 문자열)
  • String str = ""; 처럼 초기화 할 수 있음
  • 자바에서는 길이가 0인 배열을 모든 타입에서 생성할 수 있음
char[] chArr = new char[0]; // 길이가 0인 char배열
int[] iArr = {}; // 길이가 0인 int배열

 

(1) 배열길이가 0인 배열은 아무것도 저장 못하는데 언제 사용하는가?

  • 기본값으로 초기화 하거나, 숫자를 문자로 바꿀 때 등에 사용
  • 빈 문자열은 다른 타입과 + 연산하여 간편히 문자열타입으로 형변환 할 때도 사용함
//기본값 초기화
String s = null;
char c = '\u0000';	// \u0000은 유니코드의 첫번째 문자

//빈 문자열, 공백으로 기본값 초기화를 간편하게(좋은 코드)
String s = "";
char c = '';

4) String클래스의 생성자와 메서드

(1) 생성자

  • String(String s)
    • 주어진 문자열을 갖는 String 인스턴스를 생성
    • 거의 사용안함
String s = new String("Hello");

결과
// s = "Hello"

 

  • String(char[] value)
    • 주어진 문자타입 배열(value)을 갖는 String인스턴스를 생성
    • 문자배열을 문자열로 반환할 때 간혹 사용함
    • 반대로 문자열을 문자 배열로 변환 할 경우 toCharArray() 메서드를 활용함
char[] c = {'H','e','l','l','o'}
String s = new String(c);

// 결과
// s = "Hello"

 

  • String(StringBuffer buf)
    • StringBuffer인스턴스가 갖고 있는 문자열(buf)과 같은 내용의 String인스턴스를 생성, 즉 StringBuffer를 String으로 변경
    • StringBuffer(내용 변경 가능한 문자열 - 이후에 배움)
// StringBuffer의 참조변수 sb를 String타입의 참조변수 s에 초기화
StringBuffer sb = new StringBuffer("Hello");
String s = new String(sb);

// 결과
// s = "Hello"

 

(1) 메서드

  • char charAt(int index)
    • 지정된 위치(index)에 있는 문자 1개를 반환
    • 반환타입이 문자 타입임
// 인덱스 번호는 0부터 시작, H = 0 ... o = 4
String s = "Hello";
String n = "0123456";

// 참조변수 s가 가르키는 문자열의 인덱스번호 1이 가르키는 문자열을 참조변수 c에 반환
char c = "s.charAt(1)";

참조변수 n이 가르키는 문자열의 인덱스번호 1이 가르키는 문자열을 참조변수 c2에 반환
char c2 = "n.charAt(1)";

// 결과
// c = 'e'
// c2 = '1'

 

  • int compareTo(String str)
    • 두개의 문자열(str)을 사전순서(Dictionary Order)로 비교하여 내용이 같으면 0을 반환(반환타입이 정수타입임)
    • 왼쪽이 작으면 음수, 오른쪽이 작으면 양수를 반환함
    • 비교할 문자열들의 첫번째 인덱스부터 순차적으로 비교하며 같으면 다음 인덱스로 넘어가서 비교하고 다르면 그둘의 아스키코드값만큼의 차이를 반환하고 남은 문자열의 비교는 하지 않음
    • 즉 왼쪽 문자의 아스키코드값 - 오른쪽 문자의 아스키코드값 연산한 값을 반환하는 것
    • 정렬할 때 사용 -> 11장에서 자세히 다룸
int i = "aaa".compareTo("aaa");		// 비교 문자열이 같음
int i2 = "aaa".compareTo("bbb");	// 왼쪽 문자열이 오른쪽보다 작음 -> 음수반환
int i3 = "bbb".compareTo("aaa");	// 오른쪽 문자열이 왼쪽보다 작음 -> 양수반환

// 결과
// i = 0
// i2 = -1
// i3 = 1

 

  • String concat(String str)
    • 기존 문자열 뒤에 인자값의 문자열을 붙힘
String s = "Hello";
String s2 = s.concat(" World");

// 결과
// s2 = "Hello World"

 

  • boolean contains(CharSequence s)
    • 인자값의 문자열이 비교 대상의 문자열에 포함되어있는지 검사하고 포함하면 true 아니면 false로 반환
    • 0번 인덱스부터 순차적으로 검사

** CharSequence 인터페이스

  • 서로 상속계층도가 다른(공통조상이 없음) CharBuffer, Segment, String, StringBuffer,StringBuilder 클래스들을 묶음
  • 위의 클래스들은 char[](문자열배열)을 다룬다는 공통점이 있어 인터페이스로 새로운 관계를 맺었기 때문에 contains()메서드를 모두 사용할 수 있음
String s = "abcdefg";
boolean b = s.contains("bc");	// 문자열 bc가 있는지 검사

// 결과
// b = true

 

  • boolean endsWith(String suffix)
    • 비교 대상의 문자열이 인자의 문자열(suffix)로 끝나는지 검사 하고 맞으면 true 틀리면 false를 반환
    • 0번 인덱스부터 순차적으로 검사
String file = "Hello.txt";
boolean b = file.endsWith("txt");	// 문자열 file이 txt문자열로 끝나는지 검사

// 결과
// b = true

 

  • boolean startsWith(String prefix)
    • 지정된 문자열(prefix)로 시작하는지 검사 맞으면 true 틀리면 false를 반환
    • 0번 인덱스부터 순차적으로 검사
String s = "java.lang.Object";
boolean b = s.startsWith("java");   // 문자열 s가 java문자열로 시작하는지 검사 
boolean b2 = s.startsWith("lang");  // 문자열 s가 lang문자열로 시작하는지 검사

// 결과
// b = true
// b2 = false

 

 

  • boolean equals(Object obj)
    • 비교대상의 문자열과 인자로 받은 문자열을 비교하고 받은 인자가 문자열이 아니거나 일치하지 않으면 false를 반환하고 정확히 일치하면 true를 반환
String s = "Hello";
boolean b = s.equals("Hello");
boolean b2 = s.equals("hello");

// 결과
// b = true
// b2 = false

 

  • boolean equalsIgnoreCase(String str)
    • 대소문자 구분없이 equals()와 동일한 기능을하는 메서드
코드
String s = "Hello";
boolean b = s.equalsIgnoreCase("Hello");
boolean b2 = s.equalsIgnoreCase("hello");

결과
b = true
b2 = true

 

  • int indexOf(int ch)
    • 검색할 문자열에 인자의 문자가 존재하는지 확인 하여 index번호를 반환
    • 인데스 0번부터 비교하며 못찾으면 -1을 반환
코드
String s = "Hello";
int idx1 = s.indexOf('o');	// o가 몇번째 인덱스에 있는지 확인
int idx2 = s.indexOf('k');	// k가 몇번째 인덱스에 있는지 확인 -> 없음 -> -1반환

결과
idx1 = 4
idx2 = -1

 

  • int indexOf(int ch, int pos)
    • 인자를 2개를 입력하면 2번째 인자의 인덱스 위치부터 indexOf와 동일한 기능을 수행하고 결과를 반환
    • 즉, 인자로 들어온 pos값의 인덱스 위치부터 문자열 끝까지 비교 (오른쪽으로 비교한다고 이해하면 됨)
String s = "Hello";
int idx1 = s.indexOf('e', 0);	// e가 인덱스 0부터 몇번째 인덱스에 있는지 확인
int idx2 = s.indexOf('e', 2);	// e가 인덱스 2부터 몇번째 인덱스에 있는지 확인 -> 없음 -> -1 반환

// 결과
// idx1 = 1
// idx2 = -1

 

 

  • int indexOf(String str)
    • 대상의 문자열과 인자로 입력된 문자열이 정확히 일치하는 첫 인덱스 위치의 번호를 반환
    • 인덱스 0번부터 비교하고 못찾으면 -1을 반환
String s = "ABCDEFG";
int idx1 = s.indexOf("CD");	// CD가 문자열에 존재하는지 확인, 처음 확인된 인덱스 번호를 반환

// 결과
// idx1 = 2

 

  • int lastIndexOf(int ch)
    • indexOf()의 기능(인자가 정수타입)을 문자열 끝에서부터 비교하여 인덱스 번호를 반환
String s = "java.lang.Object";
int idx1 = s.lastIndexOf('.');        // 문자열 뒤에서부터 '.' 을 찾는 즉시 인덱스 번호를 반환
int idx2 = s.lastIndexOf('.'idx1 - 1); // idx1의 값(8)의 위치부터 다시 '.'을 찾고 인덱스 번호를 반환

// 결과
// idx1 = 9
// idx2 = 4

 

  • int lastIndexOf(String str)
    • indexOf()의 기능(인자가 문자열타입)을 문자열 끝에서부터 비교하고 인덱스 번호를 반환
    • 비교는 뒤에서하지만 반환하는 인덱스 번호는 맨 앞의 번호를 반환
String s = "java.lang.java";

// java 문자열을 뒤에서부터 비교, java의 j가 일치한 인덱스 번호를 반환
int idx1 = s.lastIndexOf("java");

// idx1다음 인덱스위치부터 비교
int idx2 = s.lastIndexOf("java", idx1-1);

// 결과
// idx1 = 10
// idx2 = 0

 

  • int length()
    • 문자열의 길이를 반환
String s = "Hello";
int length = s.length();

// 결과
// length = 5

 

  • String[] split(String regex)
    • 문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환
    • regex(정규식) - Regular Expression 규칙이 있는 식
    • 꼭 정규식으로 표현해야하는 것은 아니고 간단히 분리하고자 하는 특정 단어나 기호로 적용하는 경우도 많음
    • 반환타입이 스트링 배열이기 때문에 배열의 각 인덱스의 0번부터 분리된 문자열이 하나씩 저장됨
String animals = "dog,cat,bear";
String[] arr = animals.split(",");

// 결과
// arr[0] = "dog"
// arr[1] = "cat"
// arr[2] = "bear"

 

  • String[] split(String regex, int limit)
    • split()의 기능을 수행하는데, 두번째 인자의 갯수만큼만 분리
    • 즉 분리할 수 있는 문자열이 더 존재해도 그 갯수만큼만 분리하고 남은 문자열은 그대로 배열에 저장됨
    • limit의 인자값이 분리할 수 있는 갯수보다 크더라도 에러가 발생하지않고 분리할 수 있을만큼 분리하여 배열에 저장함
String animals = "dog,cat,bear";

// 문자','분할, limit값이 2이므로 2개로 분할
String[] arr = animals.split(",", 2);	

// 결과
// arr[0] = "dog"
// arr[1] = "cat,bear"

 

  • String substring(int begin)
    • 인자값의 인덱스 위치부터 문자열 끝까지를 반환
  • String substring(int begin, int end)
    • 두번째 인자값이 주어지면 첫번째 인자값의 인덱스 위치부터 두번째 인자값의 -1 인덱스 위치까지 반환
    • 다만 두번째 인자값이 문자열의 길이(마지막 인덱스 번호)와 일치하면 해당 위치의 값도 적용되어 끝까지 반환함
    • 마지막 인자값의 위치의 인덱스이 문자열의 길이와 일치하지 않으면 적용대상에 포함하지 않는 것에 주의 -> 2024년도 2회차 정처기 실기 시험에도 나왔음 ㅎㅎ;
코드
String s = "java.lang.Object";
String sb1 = s.substring(10);	// 문자열의 10번재부터 문자열 끝까지 반환
String sb2 = s.substring(5,9); 	// 문자열의 5번부터 8번까지 반환(9는 반환 X)

결과
sb1 = Object
sb2 = lang

 

  • String toLowerCase()
    • 문자열을 모두 소문자로 변환
  • String toUpperCase()
    • 문자열을 모두 대문자로 변환
String s = "Hello";
String s1 = s.toLowerCase();	// 지정된 문자열을 소문자로 반환
String s2 = s.toUpperCase(); 	// 지정된 문자열을 대분자로 반환

// 결과
// s1 = hello
// s2 = HELLO

 

  • String trim()
    • 문자열의 양끝에 있는 공백을 없앤 결과를 반환
    • 문자열 중간에 있는 공백은 제거 하지 않음
    • 문자열은 변경 불가능하므로 새로운 문자열 객체로 생성 됨 -> 새로운 객체 주소 생성
String s = "  Hello World  ";
String s1 = s.trim();	// 지정된 문자열 맨앞,뒤의 공백을 제거(중간의 공백은 유지)

// 결과
// s1 = "Hello World"      // 새로운 문자열 객체 생성
// s = "  Hello World  ";  // s는 그대로 유지

 

  • static String valueOf(boolean b)
  • static String valueOf(char c)
  • static String valueOf(int i)
  • static String valueOf(long l)
  • static String valueOf(float f)
  • static String valueOf(double d)
  • static String valueOf(Object o)
    • 인자의 값을 String 타입으로 형변환 (빈 문자열과의 + 연산하는 것과 동일한 효과)
    • valueOf가 성능이 더 좋지만 빈문자열이 코드 가독성이 좋아서 빈문자열과 + 연산으로 형변환 하는 경우도 종종있음
    • 성능이 중요한 곳에서는 valueOf를 사용
    • 인자의 값이 참조변수라면 toString()을 호출한 결과를 반환하여 객체의 참조값을 반환함
String b = String.valueOf(true);
String c = String.valueOf('a');
String i = String.valueOf(100);
String l = String.valueOf(100L);
String f = String.valueOf(10f);
String d = String.valueOf(10.0);

// 날짜와시간을 저장하는 클래스 -> 10장에서 상세히
java.util.Date dd = new java.util.Date();
String date = String.valueOf(dd)

// 결과
// b = "true"
// c = "a"
// i = "100"
// l = "100"
// f = "10.0"
// d = "10.0"

// date클래스의 객체 정보를 toString()메서드의 결과로 반환
// date = "Wed Jan 27 21:26:29 KST 2016"

 

** 출처 : 남궁성의 정석코딩_자바의정석_기초편 유튜브 강의