---
title: "Java 정리 17"
date: "2018-12-05"
tags: ["Java"]
status: archived
verified: false
---

## Java 정리 17 - StringBuffer, StringBuilder, StringTokenizer, Math, Date, Calendar

---

### 문자열 다루기

* **String**
  * **문자열 저장소의 주소를 참조, 같은 문자열이 없도록 하나만 생성된다.**
  * method를 호출하여 일을 하면 **원본 문자열이 변경되지 않는다.**
* **StringBuffer**
  * **multi thread 환경에서 동시접근이 불가능**
* **StringBuilder**
  * jdk 1.5에서 추가된 클래스
  * **StringBuffer와 사용법은 동일**
  * **String-StringBuffer-StringBuilder 순으로 빠르다.**
  * **객체의 문자열을 바로 저장, 같은 문자열이 생성된다.**
    * StringBuffer도 동일
  * method 호출하여 일을 하면 **문자열이 변경된다.**
  * **multi thread 환경에서 동시접근이 가능**
  * 문자열 +연산시 Compiler가 **jdk1.5 이하면 StringBuffer**로 처리하고 **jdk 1.5이상이면 StringBuilder**로 처리한다.

---

### StringBuffer, StringBuilder

* **문자열을 heap에 저장**
  * 같은 문자열을 여러개를 저장하게 됨
  * 메소드를 불러서 일을하면 원본문자열이 변경이 됨
* 둘 중 하나만 사용가능하면 다 쓸 수 있게됨 **(사용법 동일)**
* 차이점
  * **StringBuffer - 멀티쓰레드 동시접근 불가능**
  * **StringBuilder - 멀티쓰레드 동시접근가능**
    * 속도때문에 Builder 많이 사용한다.

```java
// 생성
StringBuffer sb = new StringBuffer();

// 값 덧붙임(오버로드로 많이 구현되어 있음)
// 정수, 실수, 문자, 문자열, boolean들을 문자열로 추가 가능
// 추가한 값을 저장한 문자열의 주소를 반환
sb.append(값);

// append 반환형이 StringBuffer이므로 메소드체인 가능
sb.append("Hello ").append("World!");

// 문자열로 반환할 때
String s = sb.toString();

// 값을 삽입, 0번 인덱스에 값 삽입
sb.insert(인덱스, 값);

// 문자열의 순서를 뒤집을 수 있음
sb.reverse();

// 값 삭제
sb.delete(시작인덱스, 끝인덱스+1);

// String과 동일한 기능의 메소드들이 많다
sb.indexOf("12");
sb.lastIndexOf("5");
sb.charAt(23);
sb.substring(sb.indexOf("안녕"));
```


* **String은 짧은 문자열** 다룰 때 **StringBuilder와 StringBuffer는 긴 문자열**을 다룰 때 사용
  * **짧은 문자열**은 글자수 상관없이 **"로 시작, "로 끝나는 문자열**
  * **긴 문자열은 "안"+"녕"과 같이 추가적인 연산**이 들어가는 문자열
    * **String은 '+'사용하지 않는다.(StringBuilder 사용!)**

```java
String str1 = "안";
// 문자열에 '+'가 붙어서 긴 문자열이 된다.
System.out.println(str1+"녕"+"하"+"세"+"요"+".");
// new  StringBuilder().append(str1).append("녕").append("하")...;

// decompiler로 바이트코드를 보면 자바는 긴문자열을 StringBuilder로 처리
```

![01](/md/legacy-assets/java/17/01.webp)

---

### StringTokenizer

* java.util 패키지에서 제공
* 문자열을 짧은 한 마디(token)로 구분할 때 사용
* **StringTokenizer는 포인터**
  * 단어 하나하나를 토큰으로 분리
  * **nextToken()**메소드를 호출하여 값을 얻고 다음 토큰으로 포인터 이동
  * **포인터는 뒤로 갈 수 없다.**
* String의 split과는 다르게 StringTokenizer는 구분자로 **'.'로도 자를 수 있다.**

```java
// 공백으로 토큰을 구분한다
String s = "";
StringTokenizer stk = new StringTokenizer(s);

// 토큰의 개수를 알아내는 countTokens()
stk.countTokens();

// 토큰이 존재하면 true, 없으면 false를 리턴하는 hasMoreTokens()
stk.hasMoreTokens();

// 값(String)을 얻고 다음 토큰으로 포인터를 이동하는 nextToken()
stk.nextToken();

// 문자열을 구분자 나눌 땐 인수2개짜리 생성자 호출
String = str = "오늘은, 수요일, 입니다.";
StringTokenizer strStk = new StringTokenizer(str, ",");

// 구분자도 토큰으로 사용할 땐 인수3개짜리 생성자 호출, true면 보호
StringTokenizer strStk2 = new StringTokenizer(str, ",", true);
```

```java
import java.util.StringTokenizer;
public class STKTest {
  public static void main(String[] args) {
    
    String s = "";
    s = "오늘은 수요일 입니다.";
    StringTokenizer stk = new StringTokenizer(s);
    
    System.out.println(stk.countTokens()); // 3
    
    while(stk.hasMoreTokens()) {
      System.out.println(stk.nextToken());
    }

    String str = "오늘은, 수요일, 입니다.";
    StringTokenizer stk1 = new StringTokenizer(str, ",");
    
    while(stk1.hasMoreTokens()) {
      System.out.println(stk1.nextToken());
    }
    
    StringTokenizer stk2 = new StringTokenizer(str, ",", true);

    while(stk2.hasMoreTokens()) {
      System.out.println(stk2.nextToken());
    }

    String names =  "이재현,이재찬~정택성,김희철.김정운~김정윤,공선의";
    // ',~.'로 구분
    StringTokenizer stk4 = new  StringTokenizer(names, ",~.");
    System.out.println("토큰의 수 :  "+stk4.countTokens());
    while(stk4.hasMoreTokens()) {
      System.out.println(stk4.nextToken());
    }
  }
}
```

### Math

* java.lang 패키지에 있는 클래스
* **수학적인 일을 할 때 사용하는 클래스**
* API Doc을 보면 생성자가 없음
  * **생성자에 private이 붙은 것**
  * **객체화해서 사용하는 클래스가 아님**
* **method만 호출하여 사용하는 클래스**

```java
// 절대값
Math.abs(-10); // 10

// 반올림, 자바는 소수점이하 첫 번째자리에서 반올림(오라클 처럼 자리수 지정 못함)
Math.round(10.4); // 10
Math.round(10.5); // 11

// 올림, 0은 올리지 않는다. 
Math.ceil(10.1); // 11

// 절삭, 소수 이하를 절삭하는 메소드는 없다. => 그냥 casting
(int)10.1

// 내림, 소수점 이하 첫번째자리에서 내림
Math.floor(10.6); // 10

// 난수, 0~1사이 임의의 실수(double, 소수점이하 16자리)을 반환
Math.random();
// 0~9 사이 난수를 얻기 위해선 
Math.random()*10;
```

```java
// A-Z 중 하나의 문자를 반환하는 일.알파벳은 26개. 65-90(대문자),  97-122(소문자)
// 랜덤 대문자 출력
for(int j=0; j<5; j++) {
  System.out.println((char)((int)(Math.random()*26)+65));
}
// 랜덤 소문자 출력
for(int j=0; j<5; j++) {
  System.out.println((char)((int)(Math.random()*26)+97));
}
```

```java
// 난수 메소드를 이용해서 임의의 비밀번호 구하기
// 계정 비번찾기시 새비번을 반환해주는 메소드
// 48~57 숫자 65~90 대문자, 97~122 소문자
// 영문자대문자,소문자,숫자로 이루어진
// 임의의 비밀번호 8자리를 생성하여 반환하는 일
public class UseMath {
  public char[] createPassword() {
    char[] tempPass = new char[8];
    
    int rNum=0;
    for(int i=0; i<tempPass.length; i++) {
      while(true) {
        rNum = (int)(Math.random()*122)+1;
        if ((rNum > 47 && rNum < 58) || (rNum  > 64 && rNum <91)
                || (rNum > 96 && rNum <  123)) {
          break;
        }
      }
      System.out.println((char)rNum);
      tempPass[i] = (char)rNum;
    }
    System.out.println(tempPass);
    return tempPass;
  }

  // 다른 짧은 방식
  public char[] createPassword2() {
    char[] tempPass = new char[8];
    String flag=
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";

    for(int i=0; i<tempPass.length; i++) {
      tempPass[i] = flag.charAt((int)(Math.random()*flag.length()));
    }
    
    System.out.println(tempPass);
    return tempPass;
  }
  
  public static void main(String[] args) {
      
    new UseMath().createPassword();
    new UseMath().createPassword2();
  }

  // switch문 사용한 방식(if보다 좋은 방식)
  public char[] createPassword3() {
    char[] tempPass = new char[8];
    
    int flag = 0;
    
    for(int i=0; i<tempPass.length; i++) {
      flag = (int)(Math.random()*3)+1;
      
      switch(flag) {
      case 1: // 숫자
        tempPass[i] =  (char)((int)(Math.random()*10)+48);
        break;
      case 2: // 대문자
        tempPass[i] =  (char)((int)(Math.random()*26)+65);
        break;
      case 3: // 소문자
        tempPass[i] =  (char)((int)(Math.random()*26)+97);
        break;
      }
    }

    System.out.println(tempPass);
    return tempPass;
  }
}
```

---

### System.currentTimeMillis

![02](/md/legacy-assets/java/17/02.webp)

* 메인보드 **CMOS**가 날짜정보를 갖고 있고 OS는 CMOS로부터 날짜를 받아온다. 
  * JVM **System클래스가 OS로부터 날짜정보를 전달받아 사용**
* **시간을 얻는 일을 하는 메소드 System.currentTime**
  * 1970-01-01 00:00:00 부터 현재 시간의 차를 long형으로 반환
  * System클래스엔 더 정밀한 시간을 반환하는 **nanoTime()** 메소드(1/억)도 있다.
* **코드 수행시간을 잴 때 많이 사용**

```java
public class UseCurrentTime {
  public void test() {

    long st = System.currentTimeMillis();
    long sum = 0;
    
    for(int i=0; i<10000; i++) {
      sum = sum + i;
      // System.out.println(); 출력하면  처리속도가 엄청 느려진다.
      // 테스트 때를 제외하면  System.out.println은 거의 안쓴다
      System.out.println(i);
    }
    
    long et = System.currentTimeMillis();
    // 코드 수행 시간
    System.out.println((et-st)+"ms");
  }
  public static void main(String[] args) {
    
    UseCurrentTime uct = new UseCurrentTime();
    uct.test();
  }
}
```

```java
// 프로그램 평균 시간 구하기 - 10~15회 반복, 
// 평균값을 구하면 평균수행시간을 구할 수 있음
public class ProcessTime {
  public static void main(String[] args) {
    // 프로그램 평균시간 구하기
    long[] processTime = new long[17];
    long st = 0, et = 0;
    long sumTime = 0;
    long avgTime = 0;
    for (int i = 0; i < processTime.length; i++) {
      st = System.currentTimeMillis();
      for (int j = 0; j < 50000; j++) {
        System.out.print(j);
      }
      System.out.println();
      et = System.currentTimeMillis();
      processTime[i] = (et - st);
      sumTime += processTime[i];
    }
    avgTime = sumTime / processTime.length;
    System.out.println("소요되는 평균시간(ms) : " +  avgTime);
  }
}
```
---

### 날짜 관련 클래스

* java.util 패키지에 존재
* **Date : 형식이 있는 날짜 정보를 얻을 때** 
  * 객체 생성해서 사용
  * java.text 패키지에 있는 **SimpleDateFormat과 같이 사용**
  * Date 클래스는 거의 모든 메소드가 **Deprecated** 따라서 Calendar 클래스 사용
* **Calendar : 단일 날짜 정보를 얻을 때**

---

### Date

* **날짜 정보를 얻을 때 사용**
  * **형식을 맡는 SimpleDateFormat과 같이 사용**
  * 객체 생성해서 사용
* Date 클래스는 거의 모든 메소드가 Deprecated 따라서 **Calendar 클래스 사용권장**

![03](/md/legacy-assets/java/17/03.webp)

### SimpleDateFormat

* java.text 패키지에서 제공
* **날짜 형식을 제공**
  * **pattern**을 사용해서 형식을 지정
    * 년 - y, 월 - M, 일 - d, 오전 - a
    * 시 
      * h - 12시간제
      * K  - 0~11시간제
      * H - 0~23시간제
      * k - 1~24시간제
    * 분 - m, 초 - s, 요일 - E

![04](/md/legacy-assets/java/17/04.webp)

* **패턴 예시**

![05](/md/legacy-assets/java/17/05.webp)

```java
// 1.생성 - 사용할 날짜 형식을 만드는 일
SimpleDateFormat sdf = new SimpleDateFormat(String pattern);

// 2.날짜 객체를 생성
Date d = new Date();

// 3.날짜 객체를 넣어서 지정한 형식의 날짜를 얻는다.
// has-a 관계라 말한다(SimpleDateFormat객체가 Date객체를 갖는 관계)
sd.format(date);
```

![06](/md/legacy-assets/java/17/06.webp)


* 오라클과 다르게 패턴이 길어도 에러가 없다.

```java
public class UseDate {
     
  public UseDate() {
    Date date = new Date();
    
    System.out.println(date);
    // 1900을 더해야 제대로된 Year나옴(Deprecated)
    System.out.println(date.getYear()+1900);

    SimpleDateFormat sdf = new SimpleDateFormat("y");
    String formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018
    sdf = new SimpleDateFormat("yy");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 18
    
    sdf = new SimpleDateFormat("yyyy");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018
    
    sdf = new SimpleDateFormat("yyyy년MM");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12
    
    sdf = new SimpleDateFormat("yyyy년M");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일 a");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일  오후
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일 a HH");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일  오후 15
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일 a hh");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일  오후 03
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일 a  HH(hh,kk)");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일  오후 15(03,15)
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일 a  HH(hh,kk):mm");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일  오후 15(03,15):39
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일 a  HH(hh,kk):mm:ss");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일  오후 15(03,15):39:36
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일 a  HH(hh,kk):mm:ss E요일");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일  오후 15(03,15):39:36 수요일
    
    sdf = new SimpleDateFormat("yyyy년MM월dd일 a  HH(hh,kk):mm:ss EEEE");
    formatDate = sdf.format(date);
    System.out.println(formatDate); // 2018년12월05일  오후 15(03,15):39:36 수요일
  }
  
  public static void main(String[] args) {
    new UseDate();
  }
}
```

* 로케일을 사용하면 다른 나라의 날짜형식을 사용가능

```java
// 2018 12 05 PM 15(03,15):47:23 Wednesday
sdf = new SimpleDateFormat("yyyy MM dd a HH(hh,kk):mm:ss EEEE", Locale.UK);

// 2018 12 05 午後 15(03,15):47:23 水曜日
sdf = new SimpleDateFormat("yyyy MM dd a HH(hh,kk):mm:ss EEEE", Locale.JAPAN);

// 2018 12 05 PM 15(03,15):47:23 mercredi
sdf = new SimpleDateFormat("yyyy MM dd a HH(hh,kk):mm:ss EEEE", Locale.FRANCE);

// 2018 12 05 下午 15(03,15):47:23 星期三
sdf = new SimpleDateFormat("yyyy MM dd a HH(hh,kk):mm:ss EEEE", Locale.CHINESE);

// 2018 12 05 PM 15(03,15):47:23 Mittwoch
sdf = new SimpleDateFormat("yyyy MM dd a HH(hh,kk):mm:ss EEEE", Locale.GERMANY);
```

![07](/md/legacy-assets/java/17/07.webp)

---

### Calendar

* java.util 패키지에서 제공
* **단일 날짜정보를 얻을 때 사용**
* **Calendar는 추상클래스**
  * **객체화가 안된다**
  * **GregorianCalendar(자식클래스)를 객체화하거나 getInstance라는 static 메소드를 사용해서 Calendar 인스턴스를 받아와야 사용가능**
* Calendar 클래스는 생성되는 시점의 시간정보를 갖는다.

```java
// Calendar 클래스가 제공하는 getInstance메소드로 사용하는 방법
Calendar c = Calendar.getInstance();

// is-a 관계 객체화로 Gregorian Calendar를 사용하는 방법
Calendar c1 = new GregorianCalendar();
GregorianCalendar c2 = new GregorianCalendar();
```

* get, set메소드에 넣는 field와 그렇지 않은 field로 나뉨
  * java API doc참조

```java
// 사용방법) get메소드에 field(Constant)를 집어넣어서 값을 얻는다.
System.out.println(c.get(Calendar.YEAR)); // 2018

// MONTH는 0~11을 반환 +1을 해줘야 함
System.out.println(c.get(Calendar.MONTH)+1); 

// 요일을 나타낼 때 DAY_OF_MONTH 사용
System.out.println(c.get(Calendar.DAY_OF_MONTH));

// 숫자로 1~7 출력됨(일~토). 숫자는 요일의 인덱스를 뜻함
System.out.println(c.get(Calendar.DAY_OF_WEEK));

// 오늘이 올해 몇번째 날인지 출력
System.out.println(c.get(Calendar.DAY_OF_YEAR));

// AM/PM 출력 : 0(오전) 1(오후)
System.out.println(c.get(Calendar.AM_PM));

// 12시간제
System.out.println(c.get(Calendar.HOUR));

// 24시간제
System.out.println(c.get(Calendar.HOUR_OF_DAY));

// 분, 초
System.out.println(c.get(Calendar.MINUTE));
System.out.println(c.get(Calendar.SECOND));
```

---

[숙제풀이1 Lotto 클래스](https://github.com/younggeun0/SSangYoung/blob/master/dev/workspace/sistJavaStudy/src/date181205/Lotto.java) [숙제풀이2 UseLotto클래스](https://github.com/younggeun0/SSangYoung/blob/master/dev/workspace/sistJavaStudy/src/date181205/UseLotto.java)

[숙제풀이2](https://github.com/younggeun0/SSangYoung/blob/master/dev/workspace/sistJavaStudy/src/date181205/StringTokenizerHW.java)
