9. 기본 API 클래스 3

Pattern 클래스와 정규표현식

  • 이메일, 전화번호, 주민번호 등이 양식에 맞게 입력되었는지 확인할 때 용이하다.

정규표현식 기본 룰

  • [...]: ... 중 들어간 하나의 문자를 일치시킨다.

    • [abc]: a, b, c 중 하나의 문자를 일치시킨다.

    • [^abc]: a, b, c를 제외한 하나의 문자를 일치시킨다.

    • [a-zA-Z]: a~z, A-Z 중 하나의 문자를 일치시킨다.

  • \d: 한 개의 숫자, [0-9]와 동일하다.

  • \s: 공백

  • \w: 한개의 알파벳 또는 한 개의 숫자, [a-zA-Z_0-9]와 동일하다.

  • ?: 없거나 또는 한 개

  • *: 없거나 또는 한 개 이상

  • +: 한 개 이상

  • {n}: 정확히 n개

  • {n,}: 최소한 n개

  • {n, m}: n개부터 m개까지

  • (): 그룹핑

'하나의' 문자를 일치시킨다는 말은 정규표현식을 사용하다보면 생각보다 중요한 말이란 것을 알게 된다.

문자열 대치 등을 수행할 때, 각각에 대해 대치하는가, 전체 블록에 대해 대치하는가는 많이 다르게 적용되기 때문에 주의해야 한다.

regexr.com에서 테스트를 해볼 수 있다.

  • 전화번호 정규표현식: (02|010)-\d{3,4}-\d{4}

    • 외계어같지만 한글로 해석하면,

      • (02|010): 02 혹은 010으로 시작하는 문자열이다.

      • -: -로 연결된다.

      • \d{3,4}: 숫자가 3~4자리 나온다.

      • -: -로 연결된다.

      • \d{4}: 숫자가 4자리 나온다.

  • 이메일 정규표현식: \w+@\w+\.\w+(\.\w+)?

    • 이 역시 외계어같지만 한글로 해석하면

      • \w+: 문자가 1글자 이상 나온다.

      • @: @로 연결된다.

      • \w+: 문자가 1글자 이상 나온다.

      • \.: .으로 연결된다.

      • \w+: 문자가 1글자 이상 나온다.

      • (\.\w+)?: 이후에 .1글자 이상의 문자로 구성되는 내용이 더 있을 수도 있고, 없을 수도 있다.

.과 같이 원래 정규표현식에서 쓰이는 기호들은 \를 이용해 \.과 같이 작성해주면 escape를 할 수 있다. escape란 정규표현식 기호로서 쓰이는 것이 아님을 알려주는 것이다.

이 외에도 정규표현식에는 flag라는 개념이 존재한다.

  • i: 대소문자를 구분하지 않을 때 사용

  • g: 놓치지 않고 모든 내용에 대해 매칭시킨다는 의미로 사용

  • m: 각각의 줄마다 정규표현식을 적용

  • s: dotall 모드란 것을 활성화하는데, .\n까지 매칭시키는 모드이다.

  • u: 모든 유니코드 지원 모드를 활성화한다. 서로게이트 쌍의 올바른 처리를 활성화한다.

  • y: "sticky" 모드를 활성화한다. 텍스트에서 정확한 위치에서만 검색을 한다. 해당 위치를 자바스크립트에서 lastIndex라는 프로퍼티로 사용한다.

m의 예제

s의 예제

Pattern 클래스

보통 .matches() 메소드를 통해 문자열이 정규표현식에 맞는지 검증한다.

@Test
public void isEmail() {
    String emailValidationRegex = "\\w+@\\w+\\.\\w+(\\.\\w+)?";
    boolean matches1 = Pattern.matches(emailValidationRegex, "abc@google.com");
    System.out.println("matches1 = " + matches1); // true
    boolean matches2 = Pattern.matches(emailValidationRegex, "abc@googlecom");
    System.out.println("matches2 = " + matches2); // false
}

위와 같이 사용할 수 있다. 1번째는 올바른 이메일 주소이기 때문에 true가 나오고, 2번째는 틀린 이메일 주소이기 때문에 false가 나온다.

실제 자바 코드에서 정규표현식 문자열 내부에 \를 넣을 때는 기본적으로 단순 이스케이프 문자로 인식하기 때문에 \\로 넣어야 한다.

Arrays 클래스

배열 조작 기능을 가진 클래스이다. Arrays 클래스는 System 클래스처럼 정적인 메소드만 가지고 있다.

메소드 목록

  • int binarySearch(배열, 찾는 값): 전체 배열 항목에서 찾는 값이 있는 인덱스를 반환한다.

    • binary search 알고리즘을 사용하는 것이기 때문에, 반드시 정렬된 상태여야 한다.

  • T copyOf (원본배열, 복사할 길이): 원본 배열의 0번 인덱스에서 복사할 길이만큼 복사한 배열을 반환한다. 복사할 길이는 원본 배열의 길이보다 커도 무방하다.

  • T copyOfRange(원본배열, 시작인덱스, 끝인덱스): 원본 배열의 시작 인덱스에서 끝 인덱스까지 복사한 배열 리턴.

  • boolean deepEquals(배열, 배열): 두 배열의 깊은 비교(중첩 배열의 항목까지 비교)

  • boolean equals(배열, 배열): 두 배열의 얕은 비교(중첩 배열 항목은 비교 안함)

  • void fill(배열, 값): 전체 배열 항목에 동일한 값을 저장

  • void fill(배열, 시작인덱스, 끝인덱스, 값): 시작 인덱스부터 끝 인덱스까지 항목에만 동일한 값을 저장

  • void sort(배열): 배열의 전체 항목을 오름차순으로 정렬

  • String toString(배열): "[값1, 값2, ...]"와 같은 문자열 반환

toString 메소드 테스트

public class ArrayTest {
    @Test
    public void toStringAndSortTest() {
        Student student1 = new Student();
        student1.setName("김일번");
        student1.setNumber(1);

        Student student2 = new Student();
        student2.setName("김이번");
        student2.setNumber(2);

        Student student3 = new Student();
        student3.setName("김삼번");
        student3.setNumber(3);

        Student[] students = {student2, student3, student1};
        System.out.println("students = " + Arrays.toString(students));
    }
}

위와 같이 코드를 짜면 Arrays.toString() 시에 단순히 메모리 주소 값이 나오지 않고, 내용이 나온다.

sort 메소드 테스트

sort 메소드를 사용하려면, Comparable<T> 인터페이스를 상속하고, .compareTo() 메소드를 구현해야 한다.

@Override
public int compareTo(Student student) {
    return this.number < student.number ? -1 : 0;
    // Integer.compare(this.number, student.number); 와 같다
    // 반대로 정렬하고 싶다면 부호만 반대로 바꾸어주면 된다.
}

위는 직접 작성한 .compareTo() 메소드이다. 단순히 primitive 값을 비교할 거면 래퍼 클래스에서 compare() 메소드를 가져오는 방법도 좋다. Integer.compare()와 같은 메소드가 있다.

  • 결과가 -1일 때 값이 바뀐다.

  • 위의 compareTo() 메소드의 부등호를 반대로 바꾸면 정렬 순서도 반대가 된다.

  • -1만 반환하면, 순서가 거꾸로 된다.

Wrapper(포장) 클래스

자바의 primitive 타입들 (byte, char, short, int, long, float, double, boolean) 을 감싸는 클래스를 말한다. 이러한 클래스로 만든 객체를 Wrapper 객체라고 한다.

Wrapper 객체라고 불리는 이유는 기본 타입 값을 내부에 두고 포장하기 때문이다. 포장 객체 내부에 있는 기본 타입의 값은 외부에서 변경할 수 없다. 내부의 값을 변경하고 싶다면 새로운 포장 객체를 만들어야 한다. (불변성)

각 primitive 타입의 이름 앞글자를 대문자로 바꾸면 Wrapper 클래스를 볼 수 있다.

박싱(Boxing)과 언박싱(Unboxing)

  • primitive 타입에서 Wrapper 객체로 변환하는 것을 박싱이라 한다.

  • Wrapper 객체에서 primitive 타입으로 변환하는 것을 언박싱이라 한다.

  • 박싱하는 방법

    • 간단히 생성자의 인자에 primitive 타입 값을 넘겨주거나 혹은 리터럴 그대로 넘겨주면 된다.

    • 생성자를 이용하지 않고, Wrapper 객체에서 지원하는 .valueOf() 메소드를 이용하는 방법도 있다.

  • 언박싱하는 방법

    • .타입Value()메소드를 호출하면 된다.

자동 박싱과 자동 언박싱

  • 리터럴을 Wrapper 객체에 대입하면 자동 박싱이 된다.

Integer a = 100;
  • 컬렉션의 경우에는 그냥 .add()로 리터럴 값을 추가하면 자동으로 박싱된다.

List<Integer> numbers = new ArrayList<Integer>();
numbers.add(100);
  • primitive 타입 변수에 Wrapper 객체를 대입하면 자동으로 언박싱이 된다.

Integer a = new Integer(100);
int b = a;

자동 박싱, 자동 언박싱은 자바 5부터 추가되었기 때문에, 그 이하의 버전을 쓸 때는 직접 박싱과 언박싱을 해주어야 한다.

문자열을 primitive 타입으로 변환하기

  • Wrapper 클래스의 정적 메소드에 존재하는 .parse타입명() 형태의 메소드를 이용하면, 문자열을 primitive 타입으로 변환시킬 수 있다.

int number = Integer.parseInt("100");

Wrapper 객체의 비교

  • Wrapper 객체는 일반 primitive 타입처럼 == 비교를 하여 동등한지 알 수 없다. .equals()를 이용하자.

Math 클래스

Math 클래스는 System 클래스와 비슷하게 모두 정적 메소드만 갖고 있다. Math 클래스는 이름처럼 유용한 수학 메소드를 갖고 있다.

Math 메소드 종류

  • T abs(T): 절대값을 반환한다.

  • ceil(): 올림값을 반환한다.

  • floor(): 버림값을 반환한다.

  • max(): 최대값을 반환한다.

  • min(): 최소값을 반환한다.

  • random(): 0~1사이 랜덤값을 반환한다.

  • rint(): 가까운 정수의 실수값을 반환한다.

    • 5.3이면 5.0이 나오고, 5.7이면 6.0이 나온다.

  • round(): 반올림 값을 반환한다.

Random 클래스

  • 종자값(seed)을 설정해서 완전한 난수를 만들 수 있다.

    • 생성자 Random(long seed)를 이용하면 된다.

메소드

  • nextBoolean(): boolean 타입의 난수를 반환한다.

  • nextDouble(): double 타입의 난수를 반환한다.

  • nextInt(): int 타입의 난수를 반환한다.

  • nextInt(int n): 0~n까지 범위의 정수 난수를 반환한다.

Date, Calendar 클래스

  • 날짜 및 시각을 읽을 수 있도록 제공하는 클래스이다.

  • java.util 패키지에 포함되어 있다.

Date 클래스

Date now = new Date()
  • 위와 같은 형식으로 현재 날짜를 얻을 수 있다.

  • SimpleDateFormat 클래스를 이용하여 원하는 형식의 문자열 날짜를 얻을 수 있다.

Date now = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String stringifiedDate = sdf.format(now); // 2020-05-06 19:46

Calendar 클래스

  • 달력을 표현한 클래스이다.

  • 추상 클래스로 new 연산자를 사용해서 인스턴스를 생성할 수 없다.

    • 추상 클래스인 이유는 지역마다 날짜 계산법이 달라서 설계한 것이다.

  • 사실상 실제 사용에서는 그냥 Calendar.getInstance()의 정적 메소드를 이용하여 운영체제에 설정되어 있는 시간대를 기준으로 한 Calendar의 하위 객체를 얻는다.

  • 다른 시간대를 알고 싶을 때는, java.util.TimeZone 객체를 얻어서 Calendar.getInstance() 메소드의 매개값으로 넘기면 된다.

  • TimeZone.getAvailableIDs() 메소드로 이용 가능한 시간대의 문자열을 얻을 수 있다.

TimeZone timeZone = TimeZone.getTimeZone("America/Los_Angeles");
Calendar now = Calendar.getInstance(timeZone);

Format 클래스

  • 형식화된 문자열 출력을 위해 사용되는 클래스이다.

  • java.text 패키지에 포함되어 있다.

  • 숫자를 위해 DecimalFormat을 제공한다.

  • 날짜를 위해 SimpleDateFormat을 제공한다.

  • 매개 변수화된 문자열 형식을 위해 MessageFormat을 제공한다.

DecimalFormat (숫자 형식) 클래스

DecimalFormat df = new DecimalFormat("#,###.0");
String result = df.format(1234567.89);

기호별 설명

  • 0: 10진수 (빈자리는 0으로 채운다.)

    • ex) 00.0 -> 01.5

  • #: 10진수 (빈자리는 채우지 않는다.)

    • ex) ###.### -> 1.5

  • .: 소수점

  • -: 음수 기호

    • ex) +#.0 -> +123.1

    • ex) -#.0 -> -123.1

  • ,: 단위 구분

    • ex) #,###.0 -> 1,234,567.8

  • E: 지수 문자

    • ex) 0.0E0 -> 1.2E6

  • ;: 양수와 음수의 패턴을 모두 기술할 경우 패턴 구분자

    • ex) +#,### ; -#,### -> +1,234,568(양수), -1,234,568(음수)

SimpleDateFormat (날짜 형식) 클래스

안에서 패턴 문자가 특수한 의미를 가지는데, 그 의미만 알면 자신이 원하는대로 날짜에 대한 .toString()을 작성할 수 있다고 생각하면 된다.

패턴 문자 설명

  • y: 년

  • M: 월

  • d: 일

  • D: 월 구분이 없는 일(1~365)

  • E: 요일

  • a: 오전/오후

  • w: 년의 몇 번째 주

  • W: 월의 몇 번째 주

  • H: 시(0~23)

  • h: 시(1~12)

  • K: 시(0~11)

  • k: 시(1~24)

  • m: 분

  • s: 초

  • S: 밀리세컨드

MessageFormat (문자열 형식) 클래스

  • 사용 용례

    • 데이터를 파일에 저장할 때

    • 네트워크로 전송할 때

    • 데이터베이스 SQL문을 작성할 때

    • 기타 일정한 형식의 문자열을 사용할 때

import java.text.MessageFormat;

public class Main {
    public static void main(String[] args) {
        String id = "kim ddol ddol";
        String name = "kim ddol";
        String tel = "010-1111-1111";

        String message = "회원 ID: {0} \n회원 이름: {1} \n회원 전화: {2}";
        String result = MessageFormat.format(message, id, name, tel);
        System.out.println("result = " + result);
    }
}

위와 같은 코드를 입력하면 순서에 맞게 들어간다.

들어갈 인자를 배열로 입력해도 무관하다.

  • ex) { id, name, tel }

궁금해서 자료조사를 해봤는데 MessageFormat.format()의 퍼포먼스는 매우 저조하다. 보기엔 아름다울 수 있으나 String 타입을 +concat하는 것보다 느리다. StringBuilder가 가장 빠르다. String이 그 다음

java.time 패키지

  • 자바7 이전까지는 DateCalendar 클래스를 이용해서 날짜와 시간 정보를 얻었다.

  • 자바8부터 날짜와 시간을 나타내는 여러가지 API를 새롭게 추가해서 매우 편리하다.

    • 이 API는 java.util 패키지에 없고, 별도로 java.time 패키지에 하위 패키지로 제공된다.

java.time

날짜와 시간을 나타내는 핵심 API인 LocalDate, LocalTime, LocalDateTime, ZonedDateTime을 포함한다. 이 클래스들은 ISO-8601에 정의된 달력 시스템에 기초한다.

java.time.chrono

ISO-8601에 정의된 달력 시스템 이외에 다른 달력 시스템이 필요할 때 사용할 수 있는 API들이 포함되어 있다.

java.time.format

날짜와 시간을 파싱하고 포맷하는 API들이 포함되어 있다.

java.time.temporal

날짜와 시간을 연산하기 위한 보조 API들이 포함되어 있다.

java.time.zone

타임존을 지원하는 API들이 포함되어 있다.

날짜와 시간 객체 생성 클래스들

LocalDate

로컬 날짜 클래스로 날짜 정보만 저장할 수 있다.

LocalDate currDate = LocalDate.now();
LocalDate targetDate = LocalDate.of(int year, int month, int dayOfMonth);
  • .get...() 메소드로 년, 월, 이번 년의 몇번째 일인지, 요일, 윤년 여부 등을 가져올 수 있다.

  • .minusYear(), .plusYear() 등의 메소드로 년월일을 더하거나 뺄 수도 있다.

  • .with() 메소드로 이번 해의 첫번째, 마지막 번째 일 등을 알 수도 있다.

  • .isAfter()와 같은 메소드로 이후, 이전 날짜 인지도 알 수 있다.

  • .until()과 같은 메소드로 시간, 날짜 차이 등도 알 수 있다.

  • .between()과 같은 메소드로 시작 시점과 끝 시점의 날짜 차이 등도 알 수 있다.

LocalTime

로컬 시간 클래스로 시간 정보만 저장할 수 있다.

LocalTime currTime = LocalTime.now();
LocalTime targetTime = LocalTime.of(int hour, int minute, int second, int nanoOfSecond);

마찬가지로 .get...() 메소드로 시, 분, 초 등을 가져올 수 있다.

LocalDateTime

로컬 날짜 및 시간 클래스(LocalDate + LocalTime)

LocalDateTime currDateTime = LocalDateTime.now();
LocalDateTime targetDateTime = LocalDateTime.of(int year, int month, int dayOfMonth, int hour, int minute, int second, int nanoOfSecond);

ZonedDateTime

ISO-8601 달력 시스템에서 정의하는 특정 타임존(TimeZone)의 날짜와 시간 클래스

ZonedDateTime seoulDateTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));

ZoneId는 java.util.TimeZone.getAvailableIds() 메소드로 얻을 수 있다. .getOffset() 메소드 등으로 시차도 알 수 있다.

Instant

특정 시점의 Time-Stamp 클래스로 누가 빠르고 누가 느리고 차이는 얼마나 나고 이런 것들을 알 수 있다.

public class InstantTest {
    public static void main(String[] args) throws InterruptedException {
        Instant instant1 = Instant.now();
        Thread.sleep(10);
        Instant instant2 = Instant.now();
        if(instant1.isBefore(instant2)) {
            System.out.println("instant1이 빠르다.");
        } else if (instant1.isAfter(instant2)) {
            System.out.println("instant2가 빠르다");
        } else {
            System.out.println("동일한 시간이다.");
        }
        System.out.println("차이(nanos): " + instant1.until(instant2, ChronoUnit.NANOS));
    }
}

날짜 문자열 파싱

날짜 문자열을 받아서 파싱하는 것도 가능하다.

LocalDate localDate = LocalDate.parse("2024-05-21");
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd");
LocalDate localDate = LocalDate.parse("2024.05.21", formatter);

ISO 표준을 잘 알면 ISO 기준으로 미리 만들어둔 formatter를 이용할 수도 있다.

LocalDate localdate = LocalDate.parse("2024-05-21", DateTimeFormatter.ISO_LOCAL_DATE);
// ISO_LOCAL_DATE: "yyyy-MM-dd"

foramtter와 다른 문자열을 파싱하게 되면 DateTimeParseException이 발생한다.

날짜 포맷 문자열로 변환

public class Main {
    public static void main(String[] args) {
        LocalDateTime now = LocalDateTime.now();
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일 HH시 mm분 ss초");
        String format = now.format(dateTimeFormatter);
        System.out.println("format = " + format); // format = 2021년 05월 06일 23시 31분 55초
    }
}

LocalDateTime이 제공하는 .format() 메소드에 DateTimeFormatter 객체를 넣으면 된다.

Last updated