[Java] API - 3. Stringbuilder, Wrapper, Calendar
String
불변이라는 특성을 가지고 있다.
문자열에 + 연산으로 합치기 하는 경우, 기존 인스턴스를 수정하는 것이 아닌 새로운 인스턴스를 반환한다.
따라서 문자열 변경이 자주 일어나는 경우 성능 면에서 좋지 않다.
하지만 변하지 않는 문자열을 자주 읽어 들이는 경우에는 좋은 성능을 기대할 수 있다.
StringBuilder
가변이라는 특성을 가지고 있다.
문자열에 append() 메소드를 이용하여 합치기 하는 경우 기존 인스턴스를 수정하기 때문에 새로운 인스턴스를 생성하지 않는다.
따라서 잦은 문자열 변경이 일어나는 경우 String 보다 성능이 높다.
단, JDK 1.5 버전부터 문자열의 + 연산이 StringBuilder의 append()로 컴파일 된다.
따라서 성능에 큰 차이를 보이지 않는다.
하지만 반복문에서 문자열의 + 연산을 수행하는 경우 StringBuilder 인스턴스를 반복 루프 시 마다 생성하기 때문에 역시 성능에 좋지 않다.
StringBuilder 인스턴스 생성
StringBuilder sb = new StringBuilder("java");
toString이 오버라이딩 되어 있다.
System.out.println("sb : " + sb.toString());
출력값=>
sb : java
hashCode는 오버라이딩 되어 있지 않다.
즉, 동일한 값을 가지는 경우 같은 해쉬코드 값을 반환하는 것이 아닌, 인스턴스가 동일해야 같은 해쉬코드를 반환한다.
System.out.println("sb의 hashCode : " + sb.hashCode());
출력값=>
sb의 hashCode : 1101288798
-> 문자열수정
sb.append("oracle");
System.out.println("sb : " + sb);
출력값=>
sb : javaoracle
hashCode를 다시 출력하면 기존 인스턴스와 동일한 것을 확인할 수 있다.
즉, 문자열을 변경했다고 해서 새로운 인스턴스가 생성된 것은 아니다.
System.out.println("sb의 hashCode : " + sb.hashCode());
sb의 hashCode : 1101288798 (기존 해쉬코드 동일)
StringBuilder의 자주 사용되는 메소드
StringBuilder 기본 생성자로 인스턴스 생성
StringBuilder sb1 = new StringBuilder();
capacity()
- 용량(현재 버퍼의 크기)을 정수형으로 반환하는 메소드 (문자열 길이 + 16이 기본 용량)
System.out.println(sb1.capacity()); //16
append()
- 인자로 전달된 값을 문자열로 변환 후 기존 문자열의 마지막에 추가한다.
기본 용량을 초과하는 경우 (기존 문자열 + 1) * 2 를 하여 용량을 확장시킨다.
for(int i = 0; i < 50; i++) {
sb1.append(i);
System.out.println("sb1 : " + sb1);
System.out.println("capacity : " + sb1.capacity()); //2n + 2씩 증가함
System.out.println("hashCode : " + sb1.hashCode()); //동일 인스턴스임
}
출력값=>
sb1 : 0
capacity : 16
hashCode : 1101288798
새로운 StringBuilder 인스턴스 생성
StringBuilder sb2 = new StringBuilder("javaoracle");
delete()
- 시작 인덱스와 종료 인덱스를 이용해서 문자열에서 원하는 부분의 문자를 제거한다.
deleteCharAt()
- 문자열 인덱스를 이용해서 문자 하나를 제거한다.
둘 다 원본에 영향을 미친다.
System.out.println("delete() : " + sb2.delete(2, 5));
System.out.println("delete() : " + sb2.deleteCharAt(0));
System.out.println("sb2 : " + sb2);
출력값=>
delete() : jaracle
delete() : aralce
sb2 : aralce
insert()
- 인자로 전달 된 값을 문자열로 반환 후 지정한 인덱스 위치에 추가한다.
원본에 영향을 미친다.
System.out.println("insert() : " + sb2.insert(1, "vao"));
System.out.println("insert() : " + sb2.insert(0, "j"));
/* 인덱스 번호가 문자열의 길이와 같은 경우 append()의 역할을 한다. */
System.out.println("insert() : " + sb2.insert(sb2.length(), "jdbc"));
System.out.println("sb2 : " + sb2);
출력값=>
insert() : avaoracle
insert() : javaoracle
insert() : javaoraclejdbc
sb2 : javaoraclejdbc
reverse()
- 문자열 인덱스 순번을 역순으로 재배열한다.
원본에 영향을 미친다.
System.out.println("reverse() : " + sb2.reverse());
System.out.println("sb2 : " + sb2);
출력값=>
reverse() : cbdjelcaroavaj
sb2 : cbdjelcaroavaj
String 클래스와 동일한 메소드
- CharAt(), indexOf() / lastIndexOf(), length(), replace(), substring(), toString()
Wrapper클래스
- 상황에 따라 기본 타입의 데이터를 인스턴스화 해야 하는 경우들이 발생한다.
이 때 기본타입 데이터를 먼저 인스턴스로 변환 후 사용해야 하는데 8가지에 해당하는 기본 타입의 데이터를 인스턴스화 할 수 있도록 하는 클래스를 래퍼클래스(Wrapper class)라고 한다.
Boxing과 UnBoxing
- 기본 타입을 래퍼클래스의 인스턴스로 인스턴스화 하는 것을 박싱이라고 하며, 래퍼클래스 타입의 인스턴스를 기본 타입으로 변경하는 것을 언박싱이라고 한다.
int intValue = 20;
Integer boxingNumber1 = new Integer(intValue); //인스턴스화 - 박싱(Boxing) // 생성자 이용
Integer boxingNumber2 = Integer.valueOf(intValue); //static 메소드 이용
int unBoxingNumber1 = boxingNumber1.intValue(); //언박싱(UnBoxing) // intValue() 이용
AutoBoxing과 Auto UnBoxing
- JDK 1.5부터는 박싱과 언박싱이 필요한 상황에서 자바 컴파일러가 이를 자동으로 처리해준다.
이런 자동화된 박싱과 언박싱을 오토 박싱, 오토 언박싱이라고 부른다.
Integer boxingNumber3 = intValue; //오토박싱
int unBoxingNumber2 = boxingNumber3; //오토 언박싱
Wrapper클래스 값 비교
int inum = 20;
Integer integerNum1 = new Integer(20);
Integer integerNum2 = new Integer(20);
Integer integerNum3 = 20;
Integer integerNum4 = 20;
기본 타입과 래퍼클래스 타입은 == 연산으로 비교 가능하다.
(모두 true 출력)
System.out.println("int와 Integer 비교 : " + (inum == integerNum1)); //순수한 20과 20의 값 비교
System.out.println("int와 Integer 비교 : " + (inum == integerNum2));
생성자를 이용해 생성한 인스턴스의 경우 == 로 비교하지 못한다. (주소값 비교)
단 오토 박싱을 이용해서 생성한 값은 == 로 비교 가능하다.
System.out.println("integer와 integer 비교 : " + (integerNum1 == integerNum2));//false
System.out.println("integer와 integer 비교 : " + (integerNum1 == integerNum3));//false
System.out.println("integer와 integer 비교 : " + (integerNum3 == integerNum4));//true
래퍼클래스 타입의 인스턴스의 "값"을 비교할 때는 equals()를 사용한다.
(모두 true 출력)
System.out.println("equals() : " + (integerNum1.equals(integerNum2)));
System.out.println("equals() : " + (integerNum1.equals(integerNum3)));
System.out.println("equals() : " + (integerNum3.equals(integerNum4)));
parsing
- 문자열(String) 값을 기본 자료형 값으로 변경하는 것을 parsing이라고 한다.
byte b = Byte.parseByte("1");
System.out.println("b : " + b); //1
short s = Short.parseShort("2");
System.out.println("s : " + s); //2
int i = Integer.parseInt("4");
System.out.println("i : " + i); //4
long l = Long.parseLong("8");
System.out.println("l : " + l); //8
float f = Float.parseFloat("4.0");
System.out.println("f : " + f); //4.0
double d = Double.parseDouble("8.0");
System.out.println("d : " + d); //8.0
boolean bl = Boolean.parseBoolean("hello");
System.out.println("bl : " + bl); //false
Character는 parsing 기능을 제공하지 않는다.
char c = "abc".charAt(0);
System.out.println("c : " + c); //a
parsing과 반대로 기본자료형 값을 문자열로 변경하는 경우도 필요하다.
valueOf()
- 기본자료형 값을 Wrapper 클래스 타입으로 변환시키는 메소드
toString()
- 필드 값을 문자열로 반환하는 메소드
String b = Byte.valueOf((byte)1).toString();
System.out.println("b : " + b); //1
String s = Short.valueOf((short)2).toString();
System.out.println("s : " + s); //2
String i = Integer.valueOf(4).toString();
System.out.println("i : " + i); //4
String l = Long.valueOf(8L).toString();
System.out.println("l : " + l); //8
String f = Float.valueOf(4.0f).toString();
System.out.println("f : " + f); //4.0
String d = Double.valueOf(8.0).toString();
System.out.println("d : " + d); //8.0
String bl = Boolean.valueOf(true).toString();
System.out.println("bl : " + bl); //true
String c = Character.valueOf('a').toString();
System.out.println("c : " + c); //a
String클래스의 valueOf 메소드를 사용할 수 있다.
String str = String.valueOf(10); //10
System.out.println("str : " + str); //123
Date 클래스
- JDK 1.0부터 날짜를 취급하기 위해 사용되던 Date 클래스는 생성자를 비롯하여 대부분의 메소드가 Deprecated되어 있다. (API문서 참조)
- Date는 java.sql.Date와 java.util.Date가 존재한다.
- 한 클래스에서 두 개의 타입을 전부 사용하게 되면 import를 하더라도 사용하는 타입이 어느 패키지에 있는 Date 클래스인지에 대한 모호성이 발생하여 import를 하더라도 풀 클래스 이름을 작성해주어야 한다.
- 단, 여기에서는 java.util.Date만 사용한다.
1. 기본 생성자 사용하는 방법
- 기본 생성자로 인스턴스를 생성하면 운영체제의 날짜/시간 정보를 이용해서 인스턴스를 만들게 된다.
java.util.Date today = new java.util.Date();
- toString() 메소드가 오버라이딩되어있어서 쉽게 필드 값을 출력할 수 있다.
System.out.println(today); //오늘 날짜 출력(Tue Jan 11 01:26:23 KST 2022)
2. Date(long date) 사용하는 방법
- getTime() : 1970년 1월 1일 0시 0분 0초 이후 지난 시간을 millisecond로 계산해서 long 타입으로 반환한다.
System.out.println(today.getTime());
Date time = new Date(1641519132810L);
java.util.Calnedar 클래스 사용
- API문서를 보면 Calendar 클래스는 abstract 클래스로 작성되어 있다.
- 따라서 Calendar 클래스를 이용해서 인스턴스를 생성하는 것이 불가능하다.
Calendar 클래스를 이용한 인스턴스 생성 방법
1. getInstace() static 메소드를 이용해서 인스턴스를 반환받는 방법
Calendar calendar = Calendar.getInstance();
- toString 이 오버라이딩되어있어서 모든 필드의 값을 확인할 수 있다.
- Date클래스에 비해 매우 많은 필드들이 값을 가지고 있다.
- 또한 생성된 인스턴스 타입이 후손 클래스 타입인 GregorianCalendar 타입인 것을 확인할 수 있다.
- 이 방식으로 인스턴스를 생성하게 되면 운영체제의 날짜/시간 정보를 이용해서 인스턴스를 생성하게 된다.
2. 후손 클래스인 GregorianCalendar 클래스를 이용해서 인스턴스를 생성하는 방법
-1. 기본생성자 사용
Calendar gregorianCalendar = new GregorianCalendar();
System.out.println(gregorianCalendar);
-2. 년, 월, 일, 시, 분, 초 정보를 이용해서 인스턴스를 생성
(2014년 9월 18일 16:42:00)
int year = 2014;
int month = 8; //월은 0 ~ 11월로 사용하기 때문에 8은 9월을 의미한다.
int dayOfMonth = 18;
int hour = 16;
int min = 42;
int second = 0;
Calendar birthDay = new GregorianCalendar(year, month, dayOfMonth, hour, min, second);
System.out.println(birthDay);
상황에 따라 특정 일자를 기준으로 한 Date 타입의 인스턴스가 필요한 경우도 있다.
먼저 GregorianCalendar를 이용해서 특정 날짜/시간 정보로 인스턴스를 새엇ㅇ한 후
1970년 1월 1일 0시 0분 0초를 기준으로 지난 시간을 millisecond로 계산해서 long형으로 반환하는 메소드가 있다.
System.out.println(birthDay.getTimeInMillis()); //1411026120000
출력된 정수로 인스턴스를 생성하면 해당 날짜/시간 정보를 가지는 Date인스턴스가 된다.
Date date = new Date(birthDay.getTimeInMillis());
System.out.println("date : " + date); //Thu Sep 18 16:42:00 KST 2014
실제 사용시 이렇게 많이 쓰인다.
Date date2 = new Date(new GregorianCalendar(year, month, dayOfMonth, hour, min, second).getTimeInMillis());
System.out.println(date2); //Thu Sep 18 16:42:00 KST 2014
생성된 인스턴스의 필드 정보를 Calendar 클래스에 있는 get() 메소드를 사용하여 반환 받을 수 있다.
int birthYear = birthDay.get(1);
int birthMonth = birthDay.get(2);
int birthDayOfMonth = birthDay.get(5);
System.out.println(birthYear); //2014
System.out.println(birthMonth); //8
System.out.println(birthDayOfMonth);//18
인자로 전달하는 정수에 따라 필드 값을 반환 받을 수 있다.
하지만 이렇게 사용하게 되면 각 필드에 매칭되는 정수를 다 외워야 한다.
따라서 해당 값들을 상수 필드 형태로 제공하고 있다.
System.out.println(Calendar.YEAR); //1
System.out.println(Calendar.MONTH); //2
System.out.println(Calendar.DATE); //5
get()메소드의 인자로 정수 대신 상수 필드 값을 호추랗는 식으로 코드를 개선하면 외우지 않고 상수필드로 바로 사용 가능하다.
System.out.println("year : " + birthDay.get(Calendar.YEAR));
System.out.println("month : " + birthDay.get(Calendar.MONTH)); //0~11이기때문에 9월은 8로 출력
System.out.println("date : " + birthDay.get(Calendar.DATE));
/* 요일(일(1), 월(2), 화(3)... , 토(7) 의미이다)*/
System.out.println("dayOfWeek : " + birthDay.get(Calendar.DAY_OF_WEEK));
출력값=>
year : 2014
month : 8
date : 18
dayOfWeek : 5
String day = "";
switch(birthDay.get(Calendar.DAY_OF_WEEK)) {
case Calendar.SUNDAY : day = "일"; break;
case Calendar.MONDAY : day = "월"; break;
case Calendar.TUESDAY : day = "화"; break;
case Calendar.WEDNESDAY : day = "수"; break;
case Calendar.THURSDAY : day = "목"; break;
case Calendar.FRIDAY : day = "금"; break;
case Calendar.SATURDAY : day = "토"; break;
}
System.out.println("요일 : " + day);
System.out.println("ampm : " + birthDay.get(Calendar.AM_PM)); //0은 오전 1은 오후
System.out.println(birthDay.get(Calendar.AM_PM) == Calendar.AM ? "오전" : "오후");
System.out.println("hourOfDay : " + birthDay.get(Calendar.HOUR_OF_DAY)); //24시간 체계
System.out.println("hour : " + birthDay.get(Calendar.HOUR)); //12시간 체계
System.out.println("min : " + birthDay.get(Calendar.MINUTE));
System.out.println("second : " + birthDay.get(Calendar.SECOND));
출력값=>
요일 : 목
ampm : 1
오후
hourOfDay : 16
hour : 4
min : 42
second : 0