12. java.base 모듈

12.2 java.base 모듈

모든모듈이 java.base를 참조하고있음. 그래서 최소한을 알아야함.
java.lang 등 패키지 들어가잇음. String, system 등 들어가잇음.

너무 많기 때문에 다 외울 필요는 없고 자주 사용되는 것만 알아 두자.
String System Integer Double Exception RuntimeException 들은 java.lang패키지에있고
키보드를 사용한 Scanner는 java.util패키지에 있다.
java.lang은 자바 언어의 기본적인 클래스를 담고 있는 패키지로 이 패키지에 있는 클래스와 인터페이스는 import없이 사용할수있다.

12.3 Object클래스

클래스를 선언할때 extends 키워드로 다른 클래스를 상속하지 않으면 암시작으로 java.lang.Object 클래스를 상속하게된다.
모든 자바클래스의 상위클래스이다.
따라서 object가 가지고 있는 필드와 메소드는 모든 클래스에서 사용가능하다.
https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/lang/Object.html

그중 대표는 다음과 같다.
boolean equals(Object obj) 객체의 번지를 비교하고 결과를 리턴
int hashCode() 객체의 해시코드를 리턴
String toString() 객체의 문자정보를 리턴

12.3.1 객체 동등비교

객체는 달라도 그 안에 데이터가 같다면 동등하다고 본다.
객체는 다른데 똑같은거로 보겠느냐가 동등비교이다.
public boolean equals(Object obj)
equals()메소드의 매개변수 타입이 Object 이므로 자동타입 변환에 의해 모든 객체가 매개값으로 대입될 수 있다.
모든객체가 Object로부터 나오기때문임.
equals() 메소드는 비교 연산자인 ==과 동일한 결과를 리턴한다. 두객체가 동일하면 true 아니면 false 리턴
일반적으로 equals()메소드는 재정의해서 동등비교용으로 사용됨.
동등비교를 하는데 저절로 되는게 아니라 재정의를해야함.
뭘비교할지 모르니까 비교할 대상을 정해두고 재정의 해야함.
동등비교 기능을 넣고싶으면 넣어야함.

public class Member {
public String id;
public Member (String id) {
    this.id = id;
}

@Override //equals() 재정의
public boolean equals(Object obj) {
    if(obj instanceof Member target) {//obj가 Member타입인지 검사하고 타이변환후에 target변수에 대입
        if(id.equals(target.id)) { //id 문자열이 같은지 비교 + Member객체인지 비교 맞으면 true;
            return true;
        }
    }
    /*
    if(obj instanceof Member) {
        Member m = (Member) obj;
        if(id.equals(m.id)) {
            return true;
        }        
    }
    */

    return false;
}}

public class EqualsExample {
public static void main(String[] args) {

    Member obj1 = new Member("blue");
    Member obj2 = new Member("blue");
    Member obj3 = new Member("red");

    System.out.println(obj1 == obj2);
    //객체가 달라서 false

    if(obj1.equals(obj2)) {
        System.out.println("obj1과 obj2는 동등합니다.");
    }else {
        System.out.println("obj1과 obj2는 동등하지 않습니다.");
    }
    //동등비교 재정의 해두었기때문에 Member가 맞다면 문자열을 비교하도록 재정의 해놓았음.

    System.out.println(obj1 == obj3);

    if(obj1.equals(obj3)) {
        System.out.println("obj1과 obj3는 동등합니다.");
    }else {
        System.out.println("obj1과 obj3는 동등하지 않습니다.");
    }}}

12.3.2 객체 해시코드

객체 해시코드란 객체를 식별하는 정수를 말한다.
hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 생성하기 때문에 객체마다 다른 정수값을 리턴한다.
두 객체가 동등한지를 비교할때 주로 사용한다.
equals()만으로도 가능하지만 좀 더 정확하게 비교하기 위해서 hashCode를 사용한다.
public int hashCode()
해시코드 -> 이퀄스 순서로 다같으면 동등 아니면 다른 객체

public class Student {
private int no;
private String name;

public Student(int no, String name) {
    this.no = no;
    this.name = name;
}

public int getNo() {return no;}
public String getName() {return name;}

@Override 
//hashCode를 메소드를 재정의해서 학생번호와 이름 해시코드를 합한 
//새로운 해시코드를 리턴하도록함.(번호와 이름이 같으면 동일한 해시코드생성)
public int hashCode() {
    int hashCode = no + name.hashCode();
    return hashCode;
}

@Override
public boolean equals(Object obj) {
    if(obj instanceof Student target) {
        if(no == target.getNo()) {
            return true;
        }
    }
    return false;
}}

12.3.3 객체 문자정보

어떤객체를 출력햇을때어떤 내용을 출력할것인가?
객체를 왜 출력? 객체안에 어떤 데이터가 있는지를 확인하려고 함.
Object의 toString()사용하기.
toString을 안해도 자체적으로 객체가 출력이됨. java.lang.클래스이름@16진수해시코드로 구성된 문자열이 리턴됨.
만약 생성하는 객체의 데이터를 알고 싶으면 toString을 재정의해야함.
객체의 문자 정보가 중요한 경우에는 toString을 재정의해서 간결하고 유익한 정보를 리턴해야한다.
예를들어 Date클래스는 현재날짜와 시간을 출력함. String클래스는 저장된 문자열을 리턴하도록함.

public class ToStringExample {
public static void main(String[] args) {
    SmartPhone myPhone = new SmartPhone("삼성전자", "안드로이드");

    String strObj = myPhone.toString();
    System.out.println(strObj);

    System.out.println(myPhone);
    }}

public class ToStringExample {
public static void main(String[] args) {
    SmartPhone myPhone = new SmartPhone("삼성전자", "안드로이드");

    String strObj = myPhone.toString();
    System.out.println(strObj);

    System.out.println(myPhone);
    }}

12.3.4 레코드 선언

레코드 기록한다x DTO(데이터를 전달하는 객체)를 작성할때 반복적으로 사용되는 코드를 줄이기 위해 자바14부터 레코드가 도입되었다.
객체와 객체에서 전달할때 매개변수로 전달할때 데이터가 너무많으면 복잡해짐.
전달하는 객체를 만들대 getter setter를 가져야함. 등등이 자동으로 코드가 만들어짐.
public record Person(String name, int age){} 컴파일러는 private final필드, 생성자를 자동으로 생성함.
정확히 말하면 getter가 아니라 필드 이름고 동일한 메소드 만들어지고 필드값을 리턴하는 유사 게터가 나들어짐
해시코드와 이퀄스도 나와서 비교해줌 투스트링으로 자동으로 생성해줌

레코드 선언시 자동생성 및 생성자 및 개터가 메소드가 자동으로 추가

public record Member(String id, String name, int age) {}

12.3.5 롬복

롬복은 JDK에 포함된 표준 라이브러리는 아니지만 개발자들이 즐겨쓰는 자동 코드 생성라이브러리이다.
롬복은 레코드와 마찬가지로 DTO클래스를 작성할때 Getter Setter hasCode() equals() toStirng()메소드를 자동생성하기 때문에 작성할 코드의 양을 줄여준다.
레코드와의 차이점은 필드가 final이 아니며 값을 읽는 Getter는 get...(또는 is...)로 Setter는 set...로 생성된다.
생산성을 높이는 라이브러리임.


필드를 선언하고 class선언위에 @Data를 부팅면 어노테이션이라고하는데 나중에 설명됨.
컴파일과정에서 기본생성자와 함께 Getter Setter hasCode() equals() toStirng()메소드가 자동생선된다.
@NoArgsConstructor - 기본 매개변수가 없는 생서자 포함
@AllArgsConstructor- 모든필드를 초기화시키는 생성자 포함
필드옆에 @NonNull혹은 final 붙으면 이 필드만 초기화시키는 생성자를 포함시키낟.
이 둘의 차이점은 final은 필드 값을 변경불가능하지마(setter가 만들어지지않음)
@NonNull은 null값이 아닌 다른값으로 Setter를 통해 변경할 수있다.

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;

@Data
@NoArgsConstructor // 기본 매개변수가 없는 생성자 포함
@AllArgsConstructor // 모든필드를 초기화시키는 생성자 포함
public class Member {

private String id;
@NonNull private String name;
private int age;}

12.4 System 클래스

시스템클래스는 출력문 쓸때 사용해왔다.
역할? 운영체제에서 실행하는 것이아니라 자바 가상머신 위에서 실행하는 거임. 따라서 운영체제의 모든 기능을 자바코드로 직접 접근하기가 어려운 구조다.
하지만 java.lang 패키지에 속하는 시스템 클래스를 이용하면 운영체제의 일부 기능을 이용할수잇다.
시스템 클래스의 정적필드와 메소드를 이용하면 프로그램종료, 키보드 입력 , 콘솔 모니터 출력 ,현재시간읽기 , 시스템프로피터읽기등이 가능하다.
System.out.println(); 을 사용할때 그냥 사용했던게 '정적'필드라 그렇다.


5가지만 나와있지만 정말 많다.

12.4.1 콘솔출력

out필드를 이용하면 콘솔에 원하는 문자열을 출력할 수 있다.
err필드도 out필드와 동일한데 차이점은 콘솔 종류에 따라 에러내용이 빨간색으로 출력된다.
system.out.println한게 system.err.println로하면 빨갛게 나옴.

12.4.2 키보드입력

자바는 키보드로부터 입력된 키를 읽기 위해 System.in필드를 사용한다.
in필드를 이용해서 read()메소들르 호출하면 입력된 키의 코드값을 얻을 수 있다.
int keyCode = System.in.read();
read()메소드는 호출과 동시에 키 코드를 읽는 것이 아니라키를 누르기 전까지는 대기상태이다가 엔터키를 누르면 입력했던 키들을 읽는다.
단read()메소드는 IOException을 발생할 수 잇는 코드 이므로 예외처리가 필요하다.
raed는 하나의 키값만 읽을수있음. Scanner와의 차이점이다.

12.4.3 프로세스 종료

운영체제는 실행중인 프로그램을 프로세스로 관리한다.
자바프로그램을 시작하면 JVM 프로세스가 생성되고 이 프로세스가 main()메소드를 호출한다. 프로세스를 강제로 종료하고 싶다면 system.exit()메소드를 사용한다.
System.exit(int status)

exit()메소드는 int 매개값이 필요한데 이값을 종료 상태값이라고 한다. 종료 상태값을 어떤값으로 주더라도 프로세스는 종료되는데
정상종료일 경우 0 비정상종료는 1 또는 -1로 주는 것이 관례이다.
종료상태값의 활용
System에 설정된는 SecurityManager에서 활용되는데 종료상태값에 따라 특정행위를 할 수 있도록 코딩할 수 있다.
하지만 java17에서 SecurityManager가 Deprecated(더이상 사용되지않음)됨에따라 배우지 않는다.

public class ExitExample {
public static void main(String[] args) {
    for(int i = 0; i<10; i++) {
        //i값 출력
        System.out.println(i);
        if(i==5) {
            //JVM프로세스 종료
            System.out.println("프로세스 강제 종료");
            System.exit(0);
        }}}}

12.4.4 진행시간 읽기

시스템 클래스의 currentTimeMillis() 메소드와 nanoTime()메소드는 1970년 1월1일 0시부터 시작해서 현재까지 진행된 시간을 리턴한다.
long currentTimeMillis() 1/1000초 단위로 진행된 시간을 리턴
long nanoTime() 1/10^9초 단위로 진행된 시간을 리턴
이 두 메소드는 프로그램 처리 시간을 측정하는데 주로 사용된다. 프로그램 처리를 시작할때 한번 끝날때 한번 읽어서 그차이를 구하면 프로그램 처리시간이 나온다.

public class MeasureRunTimeExample {
public static void main(String[] args) {
    long time1 = System.nanoTime();
        int sum = 0;
        for (int i = 0 ; i<=1000000; i++){
            sum += i;
        }
    long time2 = System.nanoTime();

    System.out.println("1~1000000가지의 합: " + sum);
    System.out.println("계산에" + (time2-time1) + "나노초가 소요되었습니다.");
    }}

12.4.5 시스템 프로퍼티 읽기

시스템 프로퍼티란 자바 프로그래밍 시작될때 자동으로 설정되는 시스템의 속성을 말한다.
예를들어 운영체제 종류 및 사용자 정보, 자바 버전등의 기본 사양 정보가 해당한다. 다음은 시스템 프로퍼티의 주요속성과 값에 대해 설명한것이다.

12.5 문자열 클래스

String 문자열을 저장하고 조작할때 사용
StringBuilder 효율적인 문자열 조작 기능이 필요할 때 사용
String은 불변의 값이라 못바꾸는데 빌더로는 바꿀 수 있다.
StringTokenizer 구분자로 연결된 문자열을 분리할때 사용

12.5.1 String 클래스

문자열을 저장하고 조작할때 사용한다.
문자열 리터럴은 자동으로 String 객체로 생성되지만 String클래스의 다양한 생성자를 이용해서 직접 객체를 생성할수도잇다.
프로그램을 개발하다보면 byte 배열을 문자열로 변환하는 경우가 종종잇다.
예를들어 네트워크 통신으로 얻은 byte배열을 원래 문자열로 변환하는 경우이다.
이때는 String 생성자 중에서 다음 두가지를 이용해 String 객체로 생성가능하다.
기본 문자 셋으로 byte 배열을 디코딩해서 String 객체로 생성
String str = new String(byte[] bytes);
특정 문자셋으로 byte배열을 디코딩해서 String객체로 생성
String str new String(byte[] bytes, String charsetName);

public class ByteToStringExamle {
public static void main(String[] args) throws Exception {
    String data = "자바";

    //String -> byte배열(기본: UTF-8인코딩)
    byte[] arr1 = data.getBytes(); // byte[] arr1 = data.gaetBytes("UTF-8");
    System.out.println("arr1: " + Arrays.toString(arr1));

    //byte 배열 -> string
    String str1 = new String(arr1);
    System.out.println("str1: " + str1);

    //String -> byte배열(EUC-KR 인코딩);
    byte[] arr2 = data.getBytes("EUC-KR");
    System.out.println("arr2: " + Arrays.toString(arr2));

    //byte 배열 -> string(EUC-KR 인코딩);
    String str2 = new String(arr2,"EUC-KR");
    System.out.println("str2: " + str2);
    }}

12.5.2 StringBuilder 클래스

String 은 내부 문자열을 수정할 수 없다. 다음 코드를 보면 다른 문자열을 결합해서 내부 문자열을 변경하는 것처럼 보이지만
사실 ABCDEF라는 새로운 String 객체를 생성하는 것이다. 그리고 data변수는 새로 생성된 String객체를 참조하게 된다.
StringBuilder는 한번에 문자열로 저장하는게 아니라 계속조작하면서 저장하는 것임.
String data= "ABC";
data += "DEF"
"ABCDEF"
문자열의 +연산은 새로운 String 객체가 생성되고 이전 객체는 계속 버려지는 것이기때문에 효율성이 좋다고 볼수는 없다.
잦은 문자열 변경작업을 해야한다면 StringBuilder를 사용하는 것이 좋다.

StringBuilder는 내부 버퍼(데이터를 저장하는 메모리)에 문자열을 저장해두고 그 안에서 추가, 수정, 삭제 작업을 하도록 설꼐되어잇다.
따라서 String처럼 새로운 객체를 만들지 않고도 문자열을 조작할 수 잇다.
제공 메소드는 다음과 같다.


최종적으로 완성을 하면 toString() 을해서 완성된 문자열을 리턴
StringBuilder은 자기자신의 객체를 한다. 리턴값이 보통 자기자신이 나옴.

String data = new StringBuilder()
data.append("DEF");
data.insert(0,"ABC");...
이런식으로 작성해도 되지만
.append("DEF")
.insert(0,"ABC")....
메소드체이닝 기법을 이용해서 간단하게 작성할 수 있다.

public class StringBuildereExample {
public static void main(String[] args) {
    String data = new StringBuilder()
            //;를 안찍으면 걔속 추기할 수 잇음.
            .append("DEF") //제일마지막에 추가
            .insert(0,"ABC") // 인덱스 번호에 추가 
            .delete(3, 4) //시작위치 끝위치 삭제
            .toString(); //완성된 문자열을 리턴 

    System.out.println(data); //ABCEF
}}

12.5.3 StringTokenizer클래스

문자열이 구분자로 연결되어 있을경우 구분자를 기준으로 문자열을 분리하면 String의 split()메소드를 이용하거나 StringTokenizer를 사용할수있다.
split은 정규 표현식으로 구분할 수 있어서 좀더 복잡하게 구분할 수 있음.
split(String regex) regex = 정규표현식
StringTokenizer는 문자로 구분한다는 차이점이 있다.
String data = "홍길동&이수홍,박연수,김자바-최명호";
String[] names = data.split("&|,|-"); 이런식으로 다양한 구분자가 잇을경우에는 이거 사용
String data = "홍길동/이수홍/박연수";
StringTokenizer st = new StringTokenizer(data, "/");

StringTokenizer가 사용되면 다음 메소드들을 이용해서 분리된 문자열을 얻을 수잇다.
리턴타입 int / countTokens() / 분리할 수 있는 문자열의 총수
boolean / hasMoreTokens() / 남아있는 문자열이 있는지 여부
String / nextToken() / 문자열을 하나씩 가져옴
nextToken() 은 분리된 문자열을 하나씩 가져오고 더이상 가져올 문자열이 없다면 예외를 발생시킨다.
그래서 nextToken() 를 사용하기전에 hasMoreTokens() 메소드로 가져울 문자열이 있는지 먼저 조사하는 것이 좋은 방법이다.

public class StringTokenizerExample {
public static void main(String[] args) {
    String data1 = "홍길동&이수홍,박연수";
    String[] arr = data1.split("&|,");
    for(String token : arr) {
        System.out.println(token);
    }
    System.out.println();

    String data2 = "홍길동/이수홍/박연수";
    StringTokenizer st = new StringTokenizer(data2, "/");
    while(st.hasMoreTokens()) {
        String token = st.nextToken();
        System.out.println(token);
    }}}

12.6 포장클래스(wrapper)

자바는 기본타입(byte char short int long float double boolean)의 값을 갖는 객체를 생성할수있다.
이런 객체를 포장객체라고 한다. 값을 포장하고 있다고 해서 붙여진 이름이다.
포장객체를 생성하기 위한 클래스는 java.lang 패키지에 포함되어 있다.
char -> Character / int -> Integer 제외하고 기본타입의 첫 문자를 대문자로 바꾼 이름을 가지고 있다.

포장객체는 포장하고 있는 기본타입의 값을 변경할 수 없고 단지 객체로 생성하는데 목적이있다.
이런 객체가 필요한 이유는 컬렉션 객체 때문에다. 15장에서 학습할예정인 컬렉션 객체는 기본타입의 값은 저장할 수없고 객체만 저장가능하다.

12.6.1 박싱과 언박싱

기본타입의 값을 포장객체로 만드는 과정을 박싱 이라고 하고 반대로 포장객체에서 기본타입값을 얻어내는 과정을 언박싱이라고 한다.
박싱은 포장 클래스 변수에 기본타입 값이 대입될때 발생한다. 반대로 언박싱은 기본 타입변수에 포장객체가 대입될때 발생한다.
원래는 Integer obj = new Integer(100);
Integer obj = 100;
int value = obj;
원래는 int value = obj.intValue();
언박싱은 연산과정에서도 발생한다.
int value = obj +50; 언박싱후 연산

public class BoxingUnBoxingExample {
public static void main(String[] args) {

    //Boxing
    Integer obj = 100;
    System.out.println("value: " + obj.intValue()); // intValue()메소드는 Integer객체 내부의 int값을 리턴한다.
    //Integer객체의 intValue()메소드사용한것

    //Unboxing
    int value = obj;
    System.out.println("value: " + value);

    //연산시 Unboxing
    int result = obj + 100;
    System.out.println("result: " + result);
}}

12.6.2 문자열을 기본타입값으로 변환

포장클래스는 문자열을 기본타입값으로 변환할때도 사용된다.
대부분의 포장 클래스에는 parse+기본타입 명으로 되어있는 정적 메소드가잇다.
이 메소드는 문자열을 해당 기본타입으로 변환한다.(2장10챕터 참조)
포장값비교
포장객체는 내부 값을 비교하기 위해 ==와 !=연산자를 사용할 수 없다. 이 연산은 내부값을 비교하는 것이아니라 포장객체의 번지를 비교하기때문이다.
예외도 있다. 포장객체의 효율적 사용을 위해 다음 범위의 값을 갖는 포장객체는 공유된다. 이 범위의 값을 갖는 포장객체는 ==와 !=연산자로 비교할 수잇지만
내부값을 비교하는 것이 아니라 객체 번지를 비교한다는 것을 알아야한다.
boolean 값의 범위 true false
char 값의범위 \u0000 ~ \u0071
byte short int 값의범위 -128~127
이범위안이면 번지가 하나만 만들어짐. 참조하는게 같아짐.
그러나 포장객체에 정확히 어떤값이 저장될지 모르는 상황이라면 == 와 !-는 사용하지 않는 것이 좋다.
대신 equals()메소드로 내부값을 비교할 수 있다. 포장클래스의 equals()메소드는 내부 값을 비교하도록 재정의 되어잇다.

public class ValueCompareExample {
public static void main(String[] args) {
    // -128~127초과값일경우
    Integer obj1 = 300;
    Integer obj2 = 300;
    //번지가 같은지를 비교
    System.out.println("==: " + (obj1 == obj2) );
    System.out.println("equals(): " + obj1.equals(obj2));
    System.out.println();

    // -128~127 범위값일 경우
    Integer obj3 = 10;
    Integer obj4 = 10;
    System.out.println("==: " + (obj3 == obj4) );
    System.out.println("equals(): " + obj3.equals(obj4));
}}

12.7 수학클래스

수학클래스 Math클래스는 수학계산에 사용할 수 있는 메소드를 제공한다.
Math클래스의 메소드는 모두 정적이므로 Math클래스로 바로 사용이 가능하다.


random() 메소드는 0.0과 1.0사이의 double 타입 난수를 리턴한다.
이 값을 이용해서 start부터 시작하는 n개의정수 start<=...<start+n 중 하나의 정수를 얻기위한 공식을 만들면 다음과같다.
int num = (int) (Math.random * n) + start; 4장2절참조

난수를 얻는 또다른 방법으로 java.util.Random 클래스를 이용할 수 있다.
이클래스를 이용하면 boolean int double 난수를 얻을 수 있다.
Random() 현재시간을 이용해서 종자값을 자동설정한다.
Random(long seed) 주어진 종자값을 사용한다.
값을 기준으로해서 난수를 발생 똑같은 난수를 만듬. 값이 똑같으면 똑같은 난수가 나온다.?
난수를 계산할때 알고리즘에서 3을 가지고 계산하겟다?
종자값이란 컴퓨터가 실제로는 랜덤을 뽑을 수 없기때문에 미리 선형함수로 정해놓은 난수값이 정해져있는데 그게 종자값이다.
컴퓨터는 완벽한 랜덤값을 만들수가 없다.

종자값(seed)란 난수를 만든느 알고리즘에 사용되는 값으로 종자값이 같으면 같은 난수를 얻는다.
Random클래스가 제공하는 메소드
boolean / nextBoolean() / boolean타입의 난수를 리턴
double / nextDouble() / double타입의 난수를 리턴 (0.0<= ~ <1.0)
int / nextInt() / int 타입의 난수를 리턴 -2^32<= ~ <=2^32-1);
int / nextint(int n) / int 타입의 난수를 리턴 (0<= ~ <n);\

public class RandomExample {
public static void main(String[] args) {
    // 선택번호
    int[] selectNumber = new int[6]; // 선택번호 6개가저장될 배열생성
    Random random = new Random(3); // 선택번호를 얻기위한 Random 객체 생성

    System.out.print("선택번호: ");
    for (int i = 0 ; i < 6 ; i++) {
        //선택번호 6개를 얻어 배열에 저장
        selectNumber[i] = random.nextInt(45)+1;
        System.out.print(selectNumber[i]+ " ");
    }
    System.out.println();

    //당첨번호
    int[] winningNumber = new int[6]; // 선택번호 6개가저장될 배열생성
    random = new Random(5); // 선택번호를 얻기위한 Random 객체 생성

    System.out.print("당첨번호: ");
    for (int i = 0 ; i < 6 ; i++) {
        //선택번호 6개를 얻어 배열에 저장
        selectNumber[i] = random.nextInt(45)+1;
        System.out.print(selectNumber[i]+ " ");
    }
    System.out.println();

    //당첨여부
    Arrays.sort(selectNumber); //비교하기전 배열항목을 정렬
    Arrays.sort(winningNumber);
    boolean result = Arrays.equals(selectNumber, winningNumber);
    System.out.print("당첨여부: ");
    if(result) {
        System.out.println("1등에 당첨되셨습니다.");
    } else {
        System.out.println("당첨되지 않았습니다.");
    }}}

12.8 날짜와 시간클래스

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

12.8.1 Date 클래스

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

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()메소드의 매개값으로 정의된 상수를 주면 상수가 의미하는 값을 리턴한다.

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); 요일
int amPm = now.get(Calendar.AM_PM); 오전오후
int hour = now.get(Calendar.HOUR); 시
int minute = now.get(Calendar.MINUTE); 분
int second = now.get(Calendar.SECOND); 초

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();

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("7일 덧셈: " + result3.format(dtf));
    }}

12.8.4 날짜와 시간비교

LocalDataTime 클래스는 날짜와 시간을 비교할 수 있는 메소드도 제공한다.
어떤 날짜가 앞이냐 뒤냐 , 두 날짜간의 일 차이, 달차이 등
비교를 위해 특정 날짜와 시간으로 LocalDateTime 객체을 얻는 방법은 다음과 같다.
year부터 second까지 매개값을 모두 int타입값으로 제공하면된다.
LocalDateTime target = LocalDateTime.of(year, month, dayOfMonth, hour, minute, second);

2회독에서 찾은 오류 12.8.3과 12.8.4 할때 포맷적을때 월을 MM이아니라 mm으로 써놔서 분이 나옴 잘 확인하자!!

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넣기 등 +- 기호넣기 몇승 넣기

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));
}}

12.9.2 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("정규식", "검증할 문자열:);

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 리플렉션

자바의 클래스 안에 인터페이스안에 어떤 정보가 있는지를 조사하는게 리플렉션
왜 하나? outline보면 목록화되서나온다. 뭐가 있는지를 어케아냐? 이것을 조사한즌게 리플렉션이다.
리플레션은 어노테이션과도 관련이 있다.
자바는 클래스와 인터페이스의 메타 정보를 Class객체로 관리한다. 여기서 메타정보란 패키지 정보 타입정보 멤버 정보 등을 말한다.
이러한 메타정보를 프로그램에서 읽고 수정하는 행위를 '리플렉션'이라고 한다.

프로그램에서 Class객체를 얻으려면 다음 3가지 방법 중 하나를 이용하면된다.
1.Class clazz = 클래스이름.class;
2.Class clazz = Class.forName("패키지... 클래스이름"); - 클래스로 부터 얻는 방법
->JDBC Driver를 메모리로 로딩하고 DriverMangaer에 등록할때많이사용
3.Class clazz = 객체참조변수.getClass(); - 객체로부터 얻는 방법
1,2는 클래스의 이름만가지고 Class 객체를 얻는 방법이고 3은 객체로부터 Class객체를 얻는 방법이다.
셋중 어떤 방법을 이용하더라도 class객체를 얻을수 잇다.

12.11.1 패키지와 타입정보 얻기

Package getPackage() 패키지정보읽기
String getSimpleName() 패키지를 제외한 타입이름 클래스,인터페이스이름
String getName() 패키지를 포함한 전체타입이름

public class Car {}

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[] constructors = clazz.getDeclaredConstructors(); 생성자정보읽기 생성자타입의 배열
Field[] fields = clazz.getDeclaredFields(); 필드정보읽기 필드타입 배열
Method[] methods = clazz.getDeclaredMethods(); 생성자정보읽기 메소드타입배열에
Class[] parameters = constructor.getParameterTypes(); //매개변수얻어서 클래스배열에 넣기
각각 생성자 배열 필드배열 메소드배열을 리턴한다.
생성자 필드 메소드 클래스는 모두 java.lang.reflect패키지에 있는데 각각 생성자 필드 메소드에 대한 선언부 정보를 제공한다.
배열을 얻고 하나씩 뽑기

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;}
}

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

    System.out.println("[생성자 정보]");
    Constructor[] constructors = clazz.getDeclaredConstructors();
    for(Constructor constructor : constructors) {
        System.out.print(constructor.getName() + "(");
        Class[] parameters = constructor.getParameterTypes(); //매개변수
        printParameters(parameters);
        System.out.println(")");
         }
    System.out.println();

    System.out.println("[필드 정보]");
    Field[] fields = clazz.getDeclaredFields();
    for(Field field : fields) {
        System.out.println(field.getType().getName() + " " + field.getName());
    }
    System.out.println();

    System.out.println("[메소드 정보]");
    Method[] methods = clazz.getDeclaredMethods();
    for(Method method : methods) {
        System.out.print(method.getName() + "(");
        Class[] parameters = method.getParameterTypes();
        printParameters(parameters);
        System.out.println(")");
    }
}

private static void printParameters(Class[] parameters) {
    for(int i=0; i<parameters.length; i++) {
        System.out.print(parameters[i].getName());
        if(i<(parameters.length-1)) {
            System.out.print(",");
        }}}}

12.11.3 리소스 경로 얻기

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

실제 이런게 어디서 사용되나?
자바로 만드는 윈도우프로그램에 사용되는 고정적인 이미지를 넣을때 잘 사용됨. 이런이미지를 바이트파일과 같이 저장해놓기때문

public class GetResourceExample {

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

    String photo1Path = clazz.getResource("photo1.png").getPath();
    String photo2Path = clazz.getResource("images/photo2.png").getPath();

    System.out.println(photo1Path);
    System.out.println(photo2Path);

    //클래스와 동일한 위치의 이미지 위치 가져옴 
}}

12.12 어노테이션

코드에서 @으로 작성되는 요소를 어노테이션이라고 한다. 나중에 배우는 스프링에서 많이 사용한다. 이미 만들어진걸 사용하는 것임.
여기선 어노테이션을 만드는 방법을 의미한다.
만드는 방법을 알면 정확하게 사용할 수 있다. 역할과 방법을 알아보자.
클래스 또는 인터페이스를 컴파일하거나 실행할때 어떻게 처리 해야할 것인지를 알려주는 설정 정보이다.
용도 3가지
1.컴파일시 사용하는 정보전달 ->@Override
2.빌드 툴이 코드를 자동으로 생성할때 사용하는 정보전달 -> 빌드툴: 롬복 @Data사용해서 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 값만 넣으면 생략해도됨.
기본속성은 무조건 value 이다.
String value();
int prop2() default 1;
value속성을 가진 어노테이션 을 코드에서 사용할 때에는 @AnnotationName("값"); value생략가능한것.
여러개의 값을 넣을때는 위에서처럼 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() / 적용된 모든 어노테이션을 리턴
적용대상을 method 유지정책을 runtime으로하고 구분선에 대한 설정을 정보를 속성으로 가지고있다.

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

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

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

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

 public class PrintAnnotationExample {
public static void main(String[] args) throws Exception {
    Method[] declaredMethods = Service.class.getDeclaredMethods(); 
    //얘는 순서를 상관없게하기때문에 위치가 계속바뀜 순서중요x
    //Service.class서비스 클래스 객체 얻음 왜? 리플렉션하려고 메소드 정보얻으려는 것임. 메소드 3개잇으니 3번돔
    for(Method method : declaredMethods) {
        //PrintAnnotation 얻기
        PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);

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

        //메소드 호출
        method.invoke(new Service()); //invoke(객체) 호출한다. 객체의 메소드를 넣고 그 메소드를 통해서 메소드를 호출해

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

public static void printLine(PrintAnnotation printAnnotation) {
    if(printAnnotation != null) {
        //number 속성값 얻기
        int number = printAnnotation.number(); //기본값15 속성값 얻어서 그만큼 돌리기
        for(int i=0; i<number; i++) {
            //value 속성값 얻기
            String value = printAnnotation.value(); //printAnnotation의 속성인 value는 '-'임
            System.out.print(value);
        }
        System.out.println();
    }}}

2022.12.16 리뷰

시간이 정말 빨리 간다.
중요한 것은 꺾이지 않는 마음

+ Recent posts