지난 시간 배운 Object 클래스에 이어 자바의 기본 클래스에 대해 좀 더 알아보도록 하겠다.
가장 먼저 배울 것은 String 클래스이다. 다른 용어들과 달리 조금은 익숙할 것이다.
우리가 자료형에 대해 배울 때 '참조 자료형'이라고 명시했던 바로 그 String, 문자열을 말하는 것이 맞기 때문이다.

우리가 그동안 생성해왔던 모든 문자열은 사실 문자열 변수를 명시하여 객체를 선언 및 생성한 것이었다.
예를 들자면, String s1 = "Hello"의 경우 s1이라는 참조형 변수를 통해 문자열 객체의 주소를 참조해왔던 것이다.
이렇게 객체를 선언할 때의 문법을 일부 생략하여 문자열 객체를 생성하는 것을 암시적 객체 생성이라고 한다.
반면, String s2 = new String("Hello")와 같이 문법을 그대로 따라 생성자를 활용해 문자열 객체를 생성하는 것을 명시적 객체 생성이라고 한다.
느꼈다시피 문자열의 경우 암시적 객체 생성을 더 많이 사용하는 편이다.
또한, 문자열이 같을 경우 암시적으로 객체를 생성하면 그 객체를 참조형 변수들끼리 공유하지만 명시적으로 만들 경우에는 별도로 생성한다는 점에서 차이가 있다.
첨부한 이미지 속 그림에 나와 있듯 String str = "서울"과 String str3 = "서울"은 하나의 객체를 공유하지만,String str2 = new String("부산")과 String str4 = new String("부산")은 서로 다른 객체를 선언하게 되는 것이다.
이를 String 클래스가 가지고 있는 메서드를 활용하여 눈으로 확인해 보자.
package kr.s26.lang.string;
public class StringMain01 {
public static void main(String[] args) {
//암시적으로 객체 생성
String str1 = "abc";
String str2 = "abc";
//객체 비교
if(str1 == str2) {
System.out.println("str1과 str2는 같은 객체");
}else {
System.out.println("str1과 str2는 다른 객체");
}
//문자열 비교
if(str1.equals(str2)) {
System.out.println("str1의 내용(문자열)과 str2의 내용(문자열)이 같다.");
}else {
System.out.println("str1의 내용(문자열)과 str2의 내용(문자열)이 다르다.");
}
System.out.println("==================================");
//명시적으로 객체 생성
String str3 = new String("Hello");
String str4 = new String("Hello");
//객체 비교
if(str3 == str4) {
System.out.println("str3와 str4는 같은 객체");
}else {
System.out.println("str3와 str4는 다른 객체");
}
//문자열 비교
if(str3.equals(str4)) {
System.out.println("str3의 내용(문자열)과 str4의 내용(문자열)이 같다.");
}else {
System.out.println("str3의 내용(문자열)과 str4의 내용(문자열)이 다르다.");
}
System.out.println("==================================");
String str5 = "bus";
String str6 = "BUS";
if(str5.equals(str6)) {
System.out.println("str5와 str6의 문자열은 같다");
}else {
System.out.println("str5와 str6의 문자열은 다르다.");
}
System.out.println("==================================");
if(str5.equalsIgnoreCase(str6)) {
System.out.println("[대소문자 구분 없이 비교]str5와 str6의 문자열은 같다");
}else {
System.out.println("[대소문자 구분 없이 비교]str5와 str6의 문자열은 다르다.");
}
}
}
str1과 str2는 암시적으로 객체를 생성하였고, 그 문자열이 같기 때문에 하나의 객체를 공유한다.
객체 비교 시 활용하는 비교 연산자를 통해 이 사실을 검증해볼 수 있다.
if(str1 == str2) {
System.out.println("str1과 str2는 같은 객체");
}else {
System.out.println("str1과 str2는 다른 객체");
}
출력 결과)
str1과 str2는 같은 객체
if(str1.equals(str2)) {
System.out.println("str1의 내용(문자열)과 str2의 내용(문자열)이 같다.");
}else {
System.out.println("str1의 내용(문자열)과 str2의 내용(문자열)이 다르다.");
}
출력 결과)
str1의 내용(문자열)과 str2의 내용(문자열)이 같다.
반면 str3와 str4 같이 명시적으로 객체를 생성하면, 같은 문자열을 사용해도 각각 새로운 객체를 생성한다.
따라서 비교 연산자를 통해 객체 비교 시, 앞선 예제와 다른 결과를 얻게 된다.
if(str3 == str4) {
System.out.println("str3와 str4는 같은 객체");
}else {
System.out.println("str3와 str4는 다른 객체");
}
출력 결과)
str3와 str4는 다른 객체
if(str3.equals(str4)) {
System.out.println("str3의 내용(문자열)과 str4의 내용(문자열)이 같다.");
}else {
System.out.println("str3의 내용(문자열)과 str4의 내용(문자열)이 다르다.");
}
출력 결과)
str3의 내용(문자열)과 str4의 내용(문자열)이 같다.
if(str5.equals(str6)) {
System.out.println("str5와 str6의 문자열은 같다");
}else {
System.out.println("str5와 str6의 문자열은 다르다.");
}
출력 결과)
str5와 str6의 문자열은 다르다.
if(str5.equalsIgnoreCase(str6)) {
System.out.println("[대소문자 구분 없이 비교]str5와 str6의 문자열은 같다");
}else {
System.out.println("[대소문자 구분 없이 비교]str5와 str6의 문자열은 다르다.");
}
출력 결과)
[대소문자 구분 없이 비교]str5와 str6의 문자열은 같다
앞서 배운 equals()와 equalsIgnoreCase() 외에도 String에는 다양한 메서드들이 존재하며, 이들은 활용도가 높기 때문에 암기하는 것이 좋다.
package kr.s26.lang.string;
public class StringMain02 {
public static void main(String[] args) {
String s1 = "Kwon Sun Ae";
//012345678910, 길이: 11
int index = s1.indexOf('n');
System.out.println("맨 처음 문자 n의 위치: " + index);
index = s1.indexOf("Sun");
System.out.println("문자열 Sun의 위치: " + index);
index = s1.lastIndexOf('n');
System.out.println("마지막 문자 n의 위치: " + index);
char c = s1.charAt(index);
System.out.println("추출한 문자: " + c);
index = s1.indexOf('s');
System.out.println(index);
index = s1.indexOf('S');
String str = s1.substring(index);
System.out.println("대문자 S부터 끝까지 잘라내기: " + str);
str = s1.substring(index, index+3);
System.out.println("시작 인덱스부터 끝 인덱스 전까지 문자열 추출: " + str);
int length = s1.length();
System.out.println("s1의 길이 : " + length);
String[] array = s1.split(" ");
for(int i=0;i<array.length;i++) {
System.out.println("array[" + i + "]: " + array[i] );
}
}
}
String s1 = "Kwon Sun Ae";
012345678910
int index = s1.indexOf('n');
System.out.println("맨 처음 문자 n의 위치: " + index);
출력 결과)
맨 처음 문자 n의 위치: 3
index = s1.indexOf("Sun");
System.out.println("문자열 Sun의 위치: " + index);
출력 결과)
문자열 Sun의 위치: 5
index = s1.indexOf('s');
System.out.println(index);
출력 결과)
-1
index = s1.lastIndexOf('n');
System.out.println("마지막 문자 n의 위치: " + index);
출력 결과)
마지막 문자 n의 위치: 7
char c = s1.charAt(index);
System.out.println("추출한 문자: " + c);
출력 결과)
추출한 문자: n
index = s1.indexOf('S');
String str = s1.substring(index);
System.out.println("대문자 S부터 끝까지 잘라내기: " + str);
출력 결과)
대문자 S부터 끝까지 잘라내기: Sun Ae
str = s1.substring(index, index+3);
System.out.println("시작 인덱스부터 끝 인덱스 전까지 문자열 추출: " + str);
출력 결과)
시작 인덱스부터 끝 인덱스 전까지 문자열 추출: Sun
int length = s1.length();
System.out.println("s1의 길이 : " + length);
출력 결과)
s1의 길이 : 11
String[] array = s1.split(" ");//공백을 구분자로 처리
for(int i=0;i<array.length;i++) {
System.out.println("array[" + i + "]: " + array[i] );
}
출력 결과)
array[0]: Kwon
array[1]: Sun
array[2]: Ae
package kr.s26.lang.string;
public class StringMain03 {
public static void main(String[] args) {
String s1 = " aBa ";
String s2 = "abc";
int a = 100;
String msg = null;
msg = s1.toUpperCase();
System.out.println("msg:" + msg);
msg = s1.toLowerCase();
System.out.println("msg:" + msg);
msg = s1.replace("aB", "b");
System.out.println("msg:" + msg);
msg = s1.trim();
System.out.println("msg:" + msg);
boolean f = s1.contains("aB");
System.out.println("f:" + f);
f = s2.startsWith("ab");
System.out.println("f:" + f);
f = s2.endsWith("bc");
System.out.println("f:" + f);
//int -> String
msg = String.valueOf(a);
msg = a + "";
System.out.println(msg.length());
}
}
String s1 = " aBa ";//앞 뒤로 공백 두 칸씩
String s2 = "abc";
int a = 100;
String msg = null;//객체 생성이 되지 않아 주소가 없다는 뜻
msg = s1.toUpperCase();
System.out.println("msg:" + msg);
출력 결과)
msg: ABA
msg = s1.toLowerCase();
System.out.println("msg:" + msg);
출력 결과)
msg: aba
msg = s1.replace("aB", "b");
System.out.println("msg:" + msg);
출력 결과)
msg: ba
msg = s1.trim();
System.out.println("msg:" + msg);
출력 결과)
msg:aBa
boolean f = s1.contains("aB");
System.out.println("f:" + f);
출력 결과)
f:true
f = s2.startsWith("ab");
System.out.println("f:" + f);
출력 결과)
f:true
f = s2.endsWith("bc");
System.out.println("f:" + f);
출력 결과)
f:true
int a = 100;가 String으로 변환되어 length()로 길이를 구할 수 있게 된 것을 확인할 수 있다.msg = String.valueOf(a);
System.out.println(msg.length());
출력 결과)
3
msg = a + "";
System.out.println(msg.length());
출력 결과)
3
이를 바탕으로 간단한 실습 2가지를 진행하였다.
package kr.s26.lang.string;
import java.util.Scanner;
public class StringMain04 {
public static void main(String[] args) {
/*
* [실습]
* 입력받은 문자열을 한 문자씩 읽어서 역순으로 표시하시오.
*
* [입력 예시]
* 문자열: hello
*
* [출력 예시]
* olleh
*/
Scanner input = new Scanner(System.in);
System.out.print("문자열: ");
String st = input.nextLine();
for(int i=st.length()-1;i>=0;i--) {
System.out.print(st.charAt(i));
}
input.close();
}
}
Scanner를 통해 문자열을 입력 받는다.
for문을 돌며 역순으로 인덱스를 반복할 수 있도록 '입력받은 문자열의 길이 - 1'에서 0까지
*입력받은 문자열의 길이 -1 = 끝 인덱스
감소 연산자를 사용해 i를 줄여나가는 조건문을 작성한다.
ex. for(int i=st.length()-1;i>=0;i--)
수행문에서는 charAt()에 동적으로 변화하는 i를 넘겨 전달된 인덱스에 맞는 문자열을 출력할 수 있도록 한다.
이때 줄바꿈되지 않도록 System.out.print()를 사용한다.
작업이 끝나면 자원을 정리한다.
우리가 입력한 순서대로 출력하는 코드는 아래와 같다.
for(int i=0;i<n.length();i++){
System.out.print(ch.charAt(i));
}
package kr.s26.lang.string;
public class StringMain05 {
public static void main(String[] args) {
/*
* [실습]
* str 변수에 저장된 값을 대문자 -> 소문자로
* 소문자 -> 대문자로 변환하시오.
*
* [출력 예시]
* ABCmdYE-4w?ewZZ
*
*/
String str = "abcMDye-4W?EWzz";
String result = "";
for(int i=0;i<str.length();i++) {
char c = str.charAt(i);
if(c >= 65 && c <= 90) {//대문자
result += String.valueOf(c).toLowerCase();
}else if(c >= 97 && c <= 122) {//소문자
result += String.valueOf(c).toUpperCase();
}else {
result += c;
}
}
System.out.println(result);
}
}
가장 먼저 for문 안에서 charAt() 메서드를 통해 루프를 돌며 문자열 str을 하나씩 읽은 다음, 문자 변수 char c에 담는다.
해당 문자열이 대문자인지, 소문자인지, 특수문자인지 경우의 수를 나누어 변환해주기 위해 대소문자를 판별할 수 있도록 아스키코드를 활용해 if문으로 분기한다.
대문자 : A(65) ~ Z(90)
소문자 : a(97) ~ z(122)
만약 대문자라면 = if(c >= 65 && c <= 90) 우선 String.valueOf()를 통해 문자를 다시 문자열로 변환한 다음, toLowerCase()를 통해 소문자로 변환해준다.
이때 객체는 메서드 동작 후 자기 자신을 그대로 반환하기 때문에 String.valueOf() 뒤에 .을 붙인 다음 다른 메서드를 이을 수 있다.
그 다음 String result라는 새로운 변수에 누적(+=)한다.
만약 소문자라면 = else if(c >= 97 && c <= 122) 동일하게 String.valueOf()를 통해 문자를 다시 문자열로 변환한 다음, toUpperCase()를 통해 대문자로 변환해 준다.
그리고 역시 String result에 누적해준다.
만약 대소문자가 아니라면, 변환 작업이 불필요하므로 그냥 누적한다. 특수문자의 경우 추가로 변환 작업이 필요하지 않기 때문에 누적 과정에서 자동으로 문자 → 문자로 형변환된다.
그렇게 차곡차곡 누적된 변수 result를 출력하면 대문자는 소문자로, 소문자는 대문자로 변환된 것을 확인할 수 있다.