기초단계/JAVA

2022.11.28-1 JAVA java.base 모듈

춘핑이 2022. 11. 28. 20:14

12. java.base 모듈

12.8 날짜와 시간클래스

자바는 컴퓨터의 날짜 및 시각을 읽을 수 있도록 java.util패키지에서 Date와 Calendar클래스를 제공하고 있다.
또한 날짜와 시간을 조작할 수 있도록 java.time 패키지에서 LocalDateTime등의 클래스를 제공한다.
Date 날짜 정보를 전달하기 위해 사용 , Calendar 다양한 시간대별로 날짜와 시간을 얻을 때 사용
LocalDateTime 날짜와 시간을 조작할때 사용

12.8.1 Date 클래스

Date는 날짜를 표현하는 클래스로 객체 간에 날짜 정보를 주고받을 때 사용된다.
여러개의 생성자가 선언되어 있지만 대부분 Deprecated(더 이상 사용되지 않음)되어 Date() 생성자만 주로 사용된다.
Date() 생성자는 컴퓨터의 현재 날짜를 읽어 Date 객체로 만든다.
Date now = new Date();
현재 날짜를 문자열로 얻고 싶다면 toString() 메소드를 사용할 수 있지만 영문으로 출력된다.
우리가 원하는 형식으로 얻고 싶다면 SimpleDateFormat 클래스와 함께 사용해야한다.

package ch12.sec08;

import java.text.SimpleDateFormat;
import java.util.Date;

public class DateExample {
public static void main(String[] args) {
    Date now = new Date();
    String strNow1 = now.toString();
    System.out.println(strNow1);

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy.mm.dd HH:MM:SS");
    String strNow2 = sdf.format(now);
    System.out.println(strNow2);
}
}

12.8.2 Calendar 클래스

Calendar 클래스는 달력을 표현하는 '추상 클래스'이다. 날짜와 시간을 계산하는 방법이 지역과 문화에 따라 다르기 때문에
특정역법에 따르는 달력은 자식클래스에서 구현하도록 되어있다.
특별한 역법을 사용하는 경우가 아니라면 직접 하위클래스를 만들필요는 없고 Calendar 클래스의 정적메소드인 getInstance()메소드를 이용하면 컴퓨터에 설정되어 있는 시간대를 기준으로 Calendar 하위 객체를 얻을 수있다.
Calendar now = Calendar.getInstance();
Calendar가 제공하는 날짜와 시간에 대한 정보를 얻기 위해서는 get()메소드를 이용한다. get()메소드의 매개값으로 정의된 상수를 주면 상수가 의미하는 값을 리턴한다.

package ch12.sec08;

import java.util.Calendar;

public class CalendarExample {
public static void main(String[] args) {
    Calendar now = Calendar.getInstance();

    int year = now.get(Calendar.YEAR);
    int month = now.get(Calendar.MONTH) +1;
    int day = now.get(Calendar.DAY_OF_MONTH);
    int week = now.get(Calendar.DAY_OF_WEEK);
    String strWeek = null;
    switch(week){
        case Calendar.MONDAY: strWeek = "월"; break;
        case Calendar.TUESDAY: strWeek = "화"; break;
        case Calendar.WEDNESDAY: strWeek = "수"; break;
        case Calendar.THURSDAY: strWeek = "목"; break;
        case Calendar.FRIDAY: strWeek = "금"; break;
        case Calendar.SATURDAY: strWeek = "토"; break;
        case Calendar.SUNDAY: strWeek = "일"; break;
    }
    int amPm = now.get(Calendar.AM_PM);
    String strAmPm = null;
    if(amPm == Calendar.AM) {
        strAmPm = "오전";
    } else {
        strAmPm = "오후";
    }
    int hour = now.get(Calendar.HOUR);
    int minute = now.get(Calendar.MINUTE);
    int second = now.get(Calendar.SECOND);

    System.out.print(year + "년 ");
    System.out.print(month + "월 ");
    System.out.print(day + "일 ");
    System.out.print(strWeek + "요일 ");
    System.out.print(strAmPm + " ");
    System.out.print(hour + "시 ");
    System.out.print(minute + "분 ");
    System.out.print(second + "초 ");
}
}


getInstance() 메소드를 이요하면 미국/로스엔제레스와 같은 다르느 시간대의 캐린더를 얻을 수 있다.
알고싶은 시간대의 TimeZone 객체를 얻어 getInstance()의 매개값으로 넘겨주면된다.
TimeZone timeZone = TimeZone.getTimeZone("America.Los_Angeles");
Calendar now = Calendar.getInstance(timeZone);

12.8.3 날짜와 시간조작

Date와 Calendar는 날짜와 시가정보를 얻기에는 충분하지만 날짜와 시간을 조작할수는 없다.(연산하기)
이때는 java.time 패키지의 LocalDateTime 클래스가 제공하는 메소드를 이요하면 매우 쉽게 날짜와 시간을 조작할수잇다.
정적메소드이기때문에 그냥 불러오면된다.
LocalDateTime now = LocalDateTime.now();

package ch12.sec08;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

public class DateTimeOperationExample {

public static void main(String[] args) {
    LocalDateTime now = LocalDateTime.now();
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy.mm.dd a HH:mm:ss");
    // import java.time.format.DateTimeFormatter; 포맷불러옴.
    System.out.println("현재 시간: " + now.format(dtf));

    LocalDateTime result1 = now.plusYears(1);
    System.out.println("1번 덧셈: " + result1.format(dtf));

    LocalDateTime result2 = now.minusMonths(2);
    System.out.println("2번 덧셈: " + result2.format(dtf));

    LocalDateTime result3 = now.plusDays(7);
    System.out.println("1번 덧셈: " + result3.format(dtf));
}
}

12.8.4 날짜와 시간비교

LocalDateTime 클래스는 날짜와 시간을 비교할 수잇ㄴ느 메소드도 제공한다.
어떤 날짜가 앞이냐 뒤냐 , 두 날짜간의 일 차이, 달차이 등

비교를 위해 특정 날짜와 시간으로 LocalDateTime 객체을 얻는 방법은 다음과 같다.
year부터 second까지 매개값을 모두 int타입값으로 제공하면된다.

LocalDateTime target = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second);

package ch12.sec08;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;

public class DateTimeCompareExample {
public static void main(String[] args) {
    DateTimeFormatter dtf = DateTimeFormatter.ofPattern("yyyy.mm.dd a HH:mm:ss");

    LocalDateTime startDateTime = LocalDateTime.of(2021, 1, 1, 0, 0, 0);
    System.out.println("시작일: " + startDateTime.format(dtf));

    LocalDateTime endDateTime = LocalDateTime.of(2021, 12, 31, 0, 0, 0);
    System.out.println("종료일: " + endDateTime.format(dtf));

    if(startDateTime.isBefore(endDateTime)) {
        System.out.println("진행 중입니다.");
    } else if(startDateTime.isEqual(endDateTime)) {
        System.out.println("종료합니다.");
    } else if(startDateTime.isAfter(endDateTime)) {
        System.out.println("종료했습니다.");
    }

    long remainYear = startDateTime.until(endDateTime, ChronoUnit.YEARS);
    long remainMonth = startDateTime.until(endDateTime, ChronoUnit.MONTHS);
    long remainDay = startDateTime.until(endDateTime, ChronoUnit.DAYS);
    long remainHour = startDateTime.until(endDateTime, ChronoUnit.HOURS);
    long remainMinute = startDateTime.until(endDateTime, ChronoUnit.MINUTES);
    long remainSecond = startDateTime.until(endDateTime, ChronoUnit.SECONDS);

    System.out.println("남은 해: " + remainYear);
    System.out.println("남은 달: " + remainMonth);
    System.out.println("남은 일: " + remainDay);
    System.out.println("남은 시: " + remainHour);
    System.out.println("남은 분: " +  remainMinute);
    System.out.println("남은 초: " + remainSecond);
}
}

12.9 형식 클래스

format(형식)클래스는 숫자 또는 날짜를 원하는 형태의 문자열로 변환해주는 기능을 제공한다.
format클래스는 java.text패키지에 포함되어 있다.
DecimalFormat 숫자를 형식화된 문자여로 변환
SimpleDateFormat 날짜를 형식화된 문자열로 변환

12.9.1 DecimalFormat 데시멀 포맷

세자리 수마다 ,넣기 등 숫자의 형식 소수이하자리 몇개. 고정하고 남은자리 0넣기 등 +- 기호넣기 몇승 넣기

package ch12.sec09;

import java.text.DecimalFormat;

public class DecimalFormatExample {

public static void main(String[] args) {
    double num = 1234567.89;

     DecimalFormat df;

     //정수자리까지표시
     df = new  DecimalFormat("#,###");
     System.out.println(df.format(num));

     //무조건 소수 첫째자리까지 표기
     df = new  DecimalFormat("#,###.0");
     System.out.println(df.format(num));
}
}

SimpleDateFormat

이전 장 참조하기
SimpleDateFormat sdf = new SimpleDateFormat("yyyy년 MM월 dd일");
String strDate = sdf.format(new.Date()); 2022년 11월 28일

12.10 정규표현식 클래스

문자열이 정해져있는 형식으로 구성되어있는지를 검증해야하는 경우가잇다.
예를들어 이메일안 전화번호를 사용자가 제대로 입력했는지를 검증

12.10.1 Pattern 클래스로 검증

java.util.regex패키지의 Pattern클래스는 정규표현식으로 문자열을 검증하는 matches() 메소드를 제공한다.
첫번째 매개값은 정규표현식이고 두번째 매개값은 검증할 문자열이다. 검증한 후 결과는 boolean 타입으로 리턴된다.
boolean result = Pattern.matches("정규식", "검증할 문자열:);

package ch12.sec10;

import java.util.regex.Pattern;

public class PatternExaple {
public static void main(String[] args) {
    String regExp = "(02|010)-\\d{3,4}-\\d{4}";
    String data = "010-123-4567";
    boolean result = Pattern.matches(regExp, data);
    if(result) {
        System.out.println("정규식과 일치합니다.");
    } else {
        System.out.println("정규식과 불일치합니다.");
    }

    regExp = "\\w+@\\w+\\.\\w+(\\.\\w+)?"; 
    data = "angel@mycompanycom"; 
    result = Pattern.matches(regExp, data);
    if(result) {
        System.out.println("정규식과 일치합니다.");
    } else {
        System.out.println("정규식과 불일치합니다.");
    }
    // \\는 이스케이프 문자로 역슬래쉬\ 하나를 문자열로 포함시킨다.
    // \s \d 등을 사용하려면 \를 문자로 인식하게 해야해서 이스케이프를 붙여주어서 \\d \\s이런식으로 써야함.
}}

12.11 리플렉션

자바는 클래스와 인터페이스의 메타 정보를 Class객체로 관리한다. 여기서 메타정보란 패키지 정보 타입정보 멤버 정보 등을 말한다.
이러한 메타정보를 프로그램에서 읽고 수정하는 행위를 '리플렉션'이라고 한다.
프로그램에서 Class객체를 얻으려면 다으 ㅁ3가지 방법 중하난를 이용하면된다.
Class clazz = 클래스이름.class;
Class clazz = Class.forName("패키지... 클래스이름"); - 클래스로 부터 얻는 방법
Class clazz = 객체참조변수.getClass(); - 객체로부터 얻는 방법
셋중 어떤 방법을 이용하더라도 class객체를 얻을수 잇다.

12.11.1 패키지와 타입정보 얻기

Package getPackage() 패키지정보읽기
String getSimpleName() 패키지를 제외한 타입이름
String getName() 패키지를 포함한 전체타입이름

package ch12.sec11.exam01;
public class Car {
}

 package ch12.sec11.exam01;

public class GetClassExample {

public static void main(String[] args) {
    // how1
    Class clazz = Car.class;

    //how 2 
    // Class clazz = Class.forName("ch12.sec11.exam01.Car");

    //how3
    // Car car = new Car();
    // Class clazz = car.getClass();

    System.out.println("패키지: " + clazz.getPackage().getName() );
    System.out.println("클래스 간단 이름: " + clazz.getSimpleName() );
    System.out.println("클래스 전체 이름: " + clazz.getName() );
}
}

12.11.2 멤버정보 얻기

타입(클래스, 인터페이스)ㅏ 가지고 있는 멤버정보는 다음메소드를 통해 얻을 수 있다.
Constructor[] getDeclaredConstructors() 생성자정보읽기
Field[] getDeclaredFields() 필드정보읽기
Method[] getDeclaredMethods() 생성자정보읽기
각각 생성자 배열 필드배열 메소드배열을 리턴한다
생성자 필드 메소드 클래스는 모두 java.lang.reflect패키지에 있는데 각각 생성자 필드 메소드에 대한 선언부 정보를 제공한다.

package ch12.sec11.exam02;

public class Car {
//필드
private String model;
private String owner;

//생성자
public Car() {

}
public Car(String model) {
    this.model = model;
}

//메소드
public String getModel() { return model;}
public void setModel(String model) {this.model = model;}
public String getOwner() {return owner;}
public void setOwner(String owner) {this.owner = owner;}
}

package ch12.sec11.exam01;

public class GetClassExample {

public static void main(String[] args) {
    // how1
    Class clazz = Car.class;

    //how 2 
    // Class clazz = Class.forName("ch12.sec11.exam01.Car");

    //how3
    // Car car = new Car();
    // Class clazz = car.getClass();

    System.out.println("패키지: " + clazz.getPackage().getName() );
    System.out.println("클래스 간단 이름: " + clazz.getSimpleName() );
    System.out.println("클래스 전체 이름: " + clazz.getName() );
}
}

12.11.3 리소스 경로 얻기

Class 객체는 클래스 파일(~.class)의 경로 정보를 가지고 있기 때문에
이 경로를 기준으로 상대 경로에있는 다른 리소스파일(이미지, XML, Property파일) 정보를 얻을수잇다.
URL getResource(String name) 리소스파일의 URL리턴
inputStream getResourceAsStream(String name) 리소스 파일의 inputStream 리턴
inputStream은 18장에서 학습한다.
이미지의 절대 경로가 필요할 경우 Car.class가 있느 곳에서 상대 경로로 얻을수잇다.
이걸이용해서 파일안의 리소스의 정보를 읽거나 변경이 가능하다.

12.12 어노테이션

코드에서 @으로 작성되는 요소를 어노테이션이라고 한다. 스프링에서 많이 사용한다.이미 만들어진걸 사용하는 것임.
여기선 어노테이션을 만드는 방법을 의미한다.
만드는 방법을 알면 정확하게 사용할 수 있다. 역할과 방법을 알아보자.
클래스 또는 인터페이스를 컴파일하거나 실행할때 어떻게 처리 해야할 것인지를 알려주는 설정 정보이다.
용도 3가지
1.컴파일시 사용하는 정보전달
2.빌드 툴이 코드를 자동으로 생성할때 사용하는 정보전달 -> 빌드툴: 롬복 getter setter등 자동 생성한거
3.실행시 특정기능을 처리할때 사용하는 정보전달

컴파일시 사용하는 정보 전달의 대표적인 예는 @Override이다. 컴파일러가 멤소드 재정의 검사를 하도록 설정한다
어노테이션은 자바프로그램을 개발할때 필수요소가 되었다.
웹개발에 많이 사용되는 스프링, 스프링부트는 다양한 종류의 어노테이션을 사용해서 웹어플리케이션을 설정하는데 사용한다.

12.12.1 어노테이션 타입 정의와 적용

어노테이션도 하나의 타입이므로 어노테이션을 사용하기 위해서는 먼저 정의부터 해야한다.
어노테이션을 정의하는 방법은 인터페이스를 정의하는 것과 유사하다. @interface 뒤에 사용할 어노테이션 이름이 온다.
public @interface AnnotationName{}
이렇게 정의한 어노테이션은 @어노테이션 으로 사용된다. @AnnotationName
어노테이션은 속성을 가질수 잇다. 속성은 타입과 이름으로 구성되며 이름뒤에 괄호를 붙인다.
String prop1(); int prop2() default 1;
마치 추상메소드처럼 작성하지만 어노테이션에선 속성이다. 속성의 기본값은 default 키워드로 지정할수있다.

이렇게 정의한 어노테잉션은 코드에서 다음과 같이 사용할수잇다.
prop1은 기본값이 없어서 반드시 기술이 필요하고 prop2는 기본값이 잇어 생략가능
@AnnotationName(prop1 = "값"); @AnnotationName(prop1="값" , prop2=3);

어노테이션은 기본속성인 value를 가질수잇다.
기본속성과 다른 속성의 차이 속성이름 = 속성 없이 value 값만 넣으면 생략해도됨.
String value(); int prop2() default 1;
value속성을 가진 어노테이션 을 코드에서 사용할 때에는 @AnnotationName("값");
여러개의 값을 넣을때는 위에서처럼 value = 해줘야함.

12.12.2 어노테이션 적용대상

자바에서 어노테이션은 설정정보이다. 어노테이션은 클래스, 필드, 생성자, 메소드의 선언부에 사용가능하다.
적용대상을 지정할때ㅔ는 @Target 어노테이션을 사용해서 기본속성인 value는 ElementType배열을 값으로 가진다.
적용대상을 복수개로 지정하기 위해서이다.

@Target( {ElementType.TYPE, ElementType.FIELD, ElementType.METHOD}) -> 선언한 부분에만 사용가능
public @interface AnnotationName{}

예시는 타입,필드,메소드에만 적용하엿으니 생성자에는 사용못한다.

12.12.3 어노테이션 유지정책

어노테이션을 정의할때 한가지더 추가해야할 내용은 @AnnotationName을 언제까지 유지할것인지를 지정하는 것이다.
어노테이션 유지정책은 RetendtionPolicy 열거상수로 다음과 같이 정의되어있다.
SOURCE 어노테이션 적용시점: 컴파일할때 적용 어노테이션 제거시점: 컴파일된 후에 제거됨
CLASS 어노테이션 적용시점: 메모리 로딩할때 적용 어노테이션 제거시점: 메모리 로딩후에 제거
RUNTIME 어노테이션 적용시점: 실행할때 적용 어노테이션 제거시점: 계속유지됨

유지정책을 지정할때는 @Retention어노테이션을 사용한다.

@Target( {ElementType.TYPE, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotationName{}

12.12.4 어노테이션 설정정보 이용

어노테이션은 아무런 동작을 가지지 앟는 설정정보일뿐이다. 이 설정정보를 이용해서 어떠헤 처리할것인지는 애플리케이션의 몫이다.
애플리케이션은 12.11절에서 학습한 리플렉션을 이용해서 적용대상으로부터 어노테이션의 정보를 다음 메소드로 얻어낼수잇다.
boolean / isAnnotationPresent(AnnotationName.class) / 지정한 어노테이션이 적용되엇는지 여부
Annotation / getAnnotation(AnnotationName.class) / 지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지않으면 null을 리턴
Annotation[] / getDeclaredAnnotations() / 적용된 모든 어노테이션을 리턴

package ch12.sec12;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface PrintAnnotation {
    String value() default "-";
    int number() default 15;
}

package ch12.sec12;

public class Service {
@PrintAnnotation
public void method1() {
    System.out.println("실행 내용2");
}

@PrintAnnotation("*")
public void method2() {
    System.out.println("실행 내용3");
}

@PrintAnnotation(value = "*", number = 20)
public void method3() {
    System.out.println("실행 내용3");
}}

package ch12.sec12;

import java.lang.reflect.Method;

public class PrintAnnotationExample {
public static void main(String[] args) throws Exception {
    Method[] declaredMethods = Service.class.getDeclaredMethods();
    for(Method method : declaredMethods) {
        //PrintAnnotation 얻기
        PrintAnnotation printAnnotation = method.getAnnotation
                (PrintAnnotation.class);

        //설정 정보를 이용해서 선 출력
        printLine(printAnnotation);

        //메소드 호출
        method.invoke(new Service());

        //설정 정보를 이용해서 선 출력
        printLine(printAnnotation);
    }
    }

public static void printLine(PrintAnnotation printAnnotation) {
    if(printAnnotation != null) {
        //number 속성값 얻기
        int number = printAnnotation.number();
        for(int i=0; i<number; i++) {
            //value 속성값 얻기
            String value = printAnnotation.value();
            System.out.print(value);
        }
        System.out.println();
    }}}