5. 참조 타입

참조타입에서 넘어갔던 부분

5.12 열거타입

열거 : 제한된 목록에서 값 사용
예를들어 계절 4가지중 하나
요일 7개중 하나
이런 한정된 값을 열거 타입이라고 한다.
문자열은 모든 것을 포함하는데 몇개의 값만 가지고 사용한다.
클래스처럼 직접 정의해야한다.

안의 내용은 일반적이지 않다. class선언대신 enum으로 선언한다.

public enum Week {
    MONDAY,
    TUESDAY,
    WEDNESDAY,
    THURSDAY,
    FRIDAY,
    SATURDAY,
    SUNDAY
}

열거타입은 상수와 비슷해서 대문자로 작성한다.
열거 타입. 하면정의된 값을 사용할 수있다.

//열거 타입 변수를 선언
Week today = null;
//열거값 대입
today = Week.THURSDAY;

//열거값 비교
if (today == Week.FRIDAY) {
    System.out.println("오늘은 불금이 아닙니다.");
} else {
    System.out.println("오늘은 신나는 금요일");
}

실제로 오늘의 요일과 비교해보자.

public class WeekExample {
    public static void main(String[] args) {
        // 열거 타입 변수를 선언
        Week today = null;
        /*
         * //열거값 대입 today = Week.THURSDAY;
         * 
         * //열거값 비교 if (today == Week.FRIDAY) { System.out.println("오늘은 불금이 아닙니다."); }
         * else { System.out.println("오늘은 신나는 금요일"); }
         */

        // 컴퓨터에 요일 정보 얻기
        Calendar cal = Calendar.getInstance();

        // 오늘 요일 얻기(1~7)
        int week = cal.get(Calendar.DAY_OF_WEEK);

        // 숫자를 열거 상수로 변환해서 변수에 대입
        switch (week) {
        case 1:
            today = Week.SUNDAY;
            break;
        case 2:
            today = Week.MONDAY;
            break;
        case 3:
            today = Week.TUESDAY;
            break;
        case 4:
            today = Week.WEDNESDAY;
            break;
        case 5:
            today = Week.THURSDAY;
            break;
        case 6:
            today = Week.FRIDAY;
            break;
        case 7:
            today = Week.SATURDAY;
            break;
        }

        //열거 타입 변수 사용
        if (today == Week.SUNDAY) {
            System.out.println("신나는 일요일~");
        } else {
            System.out.println("공부나 열심히 해라!");
        }
        //공부나 열심히 해라!
    }
}

12. java.base 모듈

java.base모듈은 모든 모듈이 의존하는 모듈로 모듈중 유일하게 requires하지 않아도 사용할 수 있다.
지금까지 사용한 Stirng System 등등이 java.lang 패키지에 있고 Scanner는 java.util 패키지에 있다.
java.lang은 자바언어의 기본적인 클래스를 담고 있는 패키지로 import없이 사용할 수 있다.

12.3 Object 클래스

클래스를 선언할때 extends 키워드로 다른 클래스를 상속하지 않으면 임시적으로 java.lang.Object클래스를 상속하게 된다.
따라서 자바의 모든 클래스는 Object의 자식이거나 자손 클래스이다.

그래서 Object가 가진 메소드는 모든 객체에서 사용할 수 있다. 다음은 Object가 가진 주요 메소듸다.
boolean equals(Object obj) | 객체의 번지를 비교하고 결과를 리턴
int hashCode() | 객체의 해시코드를 리턴
String toString() | 객체의 문자 정보를 리턴

12.3.1 객체 동등 비교

Object의 equals() 메소드는 객체의 번지를 비교하고 boolean 값을 리턴한다.
equals() 메소드의 매개변수 타입이 Obejct이므로 자동타입변환에 의해 모든 객체가 매개값으로 대입될 수 있다.
eqauls()메소드는 비교연산자인 ==과 동일한 결과를 리턴한다.
두 객체가 동일한 객체라면 true 아니면 false를 리턴한다.

일반적으로 Obejct의 equals메소드는 재정의해서 동등비교용으로 사용한다.
동등비교란 객체가 비록 달라도 내부의 데이터가 같은지 비교하는 것을 말한다.
예를들어 String은 equals()메소드를 재정의해서 내부 문자열이 같은지를 비교한다.

public class Member {
    public String id;

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

    @Override
    public boolean equals(Object obj) {
        if(obj instanceof Member target) {
            if (id.equals(target.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");

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

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

instaceof를 통해서 Member 객체가 들어온면 비교를 하게 만들었다.
id가 스트링이기 때문에 문자열의 값을 비교하게 된다. 같으면 true아니면 false가되게 된다.

12.3.2 객체 해시코드

객체 해시코드란 객체를 식별하는 정수를 말한다.
Object의 hashCode() 메소드는 객체의 메모리 번지를 이용해서 해시코드를 생성하기 때문에 객체마다 다른 정수값을 리턴한다.
hashCode()의 용도는 equals()와 용도가 비슷한데 두객체가 동등한지를 비교할때 주로 사용한다.

eqauls()메소드와 마찬가지로 hashCode()메소드 역시 객체의 데이터를 기준으로 재정의해서 새로운 정수값을 리턴하도록 하는 것이 일반적이다.
객체가 다르다 할지라도 내부 데이터가 동일하다면 정수값을 리턴하기 위해서이다.
자바는 두객체가 동등함을 비교할때 두 메소드를 같이 사용하는 경우가 많다.
hashCode()가 리턴하는 정수값이 같은지를 확인하고 equals()메소드가 true를 리턴하는 지를 확인해서 동등객체임을 판단한다.(컬렉션 HashSet, HashMap이 사용)

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
    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() && name.equals(target.getName())) {
                return true;
            }
        }
        return false;
    }
}

public class HashCodeExample {
    public static void main(String[] args) {
        Student s1 = new Student(1,"홍길동");
        Student s2 = new Student(1,"홍길동");

        if(s1.hashCode() == s2.hashCode()) {
            if (s1.equals(s2)){
                System.out.println("동등 객체입니다.");
            } else {
                System.out.println("데이터가 다르므로 동등객체가 아닙니다.");
            }
        } else {
            System.out.println("데이터가 다르므로 동등객체가 아닙니다.");
        }
    }
}

12.3.3 객체 문자 정보

Object의 toString()메소드는 객체의 문자정보를 리턴한다. 객체의 문자정보란 객체를 문자열로 표현한 값을 말한다.
기본적으로 Object의 toString()메소느는 클래스명@16진수해시코드로 구성된 문자열을 리턴한다.

객체의 문자 정보가 중요한 경우는 재정의해서 간결하고 유익한 정보를 리턴하도록 해야한다.
Date클래스는 현재날짜와 시간을 String클래스는 저장된 문자열을 리턴하도록 재정의하고 있다.

public class SmartPhone {
    private String company;
    private String os;

    public SmartPhone(String company, String os) {
        this.company = company;
        this.os = os;
    }

    @Override
    public String toString() {
        return company + ", " + os;
    }
}

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 레코드 선언

데이터 전달을 위한 DTO(Data Transfer Object)를 작성할때 반복적으로 사용되는 코드를 줄이기 위해 자바 14부터 레코드가 도입되었다.
데이터 필드는 읽기만 가능하도록 private final로 선언하고 필드이름과 동일한 세터 게터를 가지고 있고 동등비교를 위해 hashCode(), eqausl()메소드를 재정의하고 있다.
의미있는 문자열 출력을 위해 toString()메소드를 재정의 하고 있다.

레코드 소스를 컴파일 하면 변수와 타입을 이용해서 private final 필드가 자동생성되고 생성자및 게터메소드가 자동으로 추가된다.
hashCode등도 자동추가된다.

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

public class RecordExample {
    public static void main(String[] args) {
        Member m = new Member("winter", "눈송이", 25);
        //Getter메소드 호출
        System.out.println(m.id());
        System.out.println(m.name());
        System.out.println(m.age());
        System.out.println(m.toString());
        System.out.println();

        Member m1 = new Member("winter", "눈송이", 25);
        Member m2 = new Member("winter", "눈송이", 25);
        System.out.println("m1.hashCode(): " + m1.hashCode());
        System.out.println("m2.hashCode(): " + m2.hashCode());
        System.out.println("m1.equals(m2):" + m1.equals(m2));
    }
}

레코드는 getter가 getXXX의 형태를 가지고 있지 않다.

12.3.5 롬복

롬복은 JDK에 포함된 표준 라이브러리는 아니지만 개발자들이 즐겨사용하는 자동코드 생성 라이브러리이다.
롬복은 레코드와 마찬가지로 DTO클래스를 작성할때 사용한다.

레코드와의 차이점은 필드가 final이 아니며 값을 읽는 Getter는 getXXX로 생성된다.
@Data가 붙게되면 컴파일 과정에서 기본생성자와 함께 Getter등이 자동으로 생성된다.
@Data외에도 다양한 어노테이션이 있다.

@NoArgsConstructor |기본(매개변수가 없는)생성자 포함
@AllArgsConstructor |모든필드를 초기화시키는 생성자 포함
@RequiredArgsConstructor |기본적으로 매개변수가 없는 생성자포함 만약 final또는 @NonNull이 붙은 필드가 있다면 이필드만 초기화시키는 생성자 포함
@Getter |Getter메소드 포함
@Setter |Setter메소드 포함
@EqualsAndHashCode |eqauls()와 hashCode()메소드 포함
@ToString |toString() 메소드 포함

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

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Member {
    private String id;
    private String name;
    private int age;
}

12.4 System 클래스

자바 프로그램은 운영체제 상에서 바로 실행되는 것이 아니라 자바가상머신 JVM위에서 실행된다.
따라서 운영체제의 모든 기능을 자바 코드로 직접 접근하기란어렵다.
하지만 java.lang패키지에 속하는 System클래스를 이용하면 운영체제의 일부 기능을 이용할 수 있다.
System클래스의 정적필드와 메소드를 이용하면 프로그램 종료, 키보드입력, 콘솔출력, 현재시간읽기, 시스템 프로퍼티 읽기 등이 가능하다.

12.4.1 콘솔출력

out필드를 이용하면 콘솔에 원하는 문자열을 출력할 수 있다.
err필드도 out필드와 동일한데 차이점은 콘솔종류에 따라 에러 내용을 출력한다.

public class ErrExample {
    public static void main(String[] args) {
        try {
            int value = Integer.parseInt("1oo");
        } catch (NumberFormatException e) {
            System.err.println("[에러내용]");
            System.err.println(e.getMessage());
        }
    }
}

12.4.2 키보드 입력

자바는 키보드로부터 입력된 키를 읽기위해 System클래스에서 in필드를 제공한다.
Scanner도 이 필드를 이용해서 값을 입력받은 것이다.
in필드의 read()메소드를 호출하면 입력된 키의 코드 값을 얻을 수 있다.
유니코드의 키코드값으로 코드값을 얻는다.

public class InExample {
    public static void main(String[] args) throws Exception {
        int speed = 0;
        int keyCode = 0;

        while (true) {
            // 엔터키를 읽지 않았을 경우에만 실행
            if (keyCode != 13 && keyCode != 10) {
                if (keyCode == 49) {
                    // 숫자 1키를 읽었을 경우
                    speed++;
                } else if (keyCode == 50) {
                    // 숫자 2키를 읽었을 경우
                    speed--;
                } else if (keyCode == 51) {
                    // 숫자 3키를 읽었을 경우
                    break;
                }
                System.out.println("-----------------");
                System.out.println("1.증속|2.감속|3.중지");
                System.out.println("-----------------");
                System.out.println("현재속도: " + speed);
                System.out.println("선택: ");
            }
            // 키를 하나씩 읽음
            keyCode = System.in.read();
        }
        System.out.println("프로그램 종료");
    }
}

12.4.3 프로세스 종료

운영체제는 실행중인 프로그램을 프로세스로 관리한다. 자바 프로그램을 실행하면 JVM프로세스가 생성되고 이 프로세스가 main()메소드를 호출한다.
프로세스를 강제 종료하고 싶다면 System.exit()메소드를 사용한다.
exit()메소드는 int매개값이 필요한데 이 값을 종료상태값이라고 한다.
정상종료일경우 : 0
비정상종료일 경우 : 1 또는 -1로 주는 것이 관례이다.

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 진행시간 읽기

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

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 시스템 프로퍼티 읽기

시스템 프로퍼티란 자바 프로그램이 시작될때 자동 설정되는 시스템의 속성을 말한다.
예를들어 운영체제 종류 및 사용자 정보 자바버전 증의 기본사양정보가 해당한다.

public class GetPropertyExample {
    public static void main(String[] args) {
        //운영체제와 사용자 정보 출력
        String osName = System.getProperty("os.name");
        String userName = System.getProperty("user.name");
        String userHome = System.getProperty("user.home");
        System.out.println(osName);
        System.out.println(userName);
        System.out.println(userHome);

        //전체키와 값을 출력
        System.out.println("--------------------");
        System.out.println("key: value");
        System.out.println("--------------------");
        Properties props = System.getProperties();
        Set keys = props.keySet();

        for (Object objkey : keys) {
            String key = (String) objkey;
            String value = System.getProperty(key);
            System.out.printf("%-40s: %s\n", key, value);
        }
    }
}

12.5 문자열 클래스

자바에서 문자열과 관련된 주요 클래스는 다음과 같다.

String | 문자열을 저장하고 조작할때 사용
StringBuilder | 효율적인 문자열 조작기능이 필요할때 사용
StringTokenizer | 구분자로 연결된 문자열을 분리할때 사용

12.5.1 String 클래스

String 클래스는 문자열을 저장하고 조작할때 사용한다.
문자열 리터럴은 자동으로 String 객체로 생성되지만 String 클래스의 다양한 생성자를 이용해서 직접 객체를 생성할 수도있다.

프로그램을 개발하다보면 byte배열을 문자열로 변환하는 경우가 종종 있다.
예를들어 네트워크 통신으로 얻은 byte배열을 문자열로 변환하는 경우이다. 이때 String 생성자 중에서 두가지를 이용해 String 객체로 생성할 수있다.
1.기본 문자셋으로 byte배열을 디코딩해서 String 객체로 생성
String str = new String(btye[] bytes);
2.특정문자셋으로 byte배열을 디코딩해서 String 객체로 생성
String str = new String(btye[] bytes, String charsetName);

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

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

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

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

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

한글 1자를 UTF-8로 인코딩하면 3바이트가 되고 EUC-KR로 인코딩하면 2바이트가 된다.
따라서 인코딩할때 사용한 문자셋으로 디코딩을 해야만 한글이 올바르게 복원된다.

12.5.2 StringBuilder 클래스

String 은 내부 문자열을 수정할 수 없다.
"ABC" + "DEF"를하면 "ABCDEF"같지만 새로운 객체를 생성해서 새로운 번지를 참조하게된다.

문자열의 + 연산은 새로운 String 객체가 생성되고 이전 객체는 계속 버려지는 것이기때문에 효율성이 좋다고는 볼 수없다.
잦은 문자열 변경작업이 필요하다면 StringBuilder를 사용하는 것이 좋다.
StringBuilder는 내부 버퍼에 문자열을 저장해두고 그 안에서 추가 수정 삭제 작업을 하도록 설계되어 있다.
따라서 새로운 객체를 만들지 않고도 문자열을 조작할 수 있다.

append(문자열) |문자열을 끝에 추가
insert(위치, 문자열) |문자열을 지정위치에 추가
delete(시작위치, 끝위치) |문자열 일부를 삭제
replace(시작위치, 끝위치, 문자열) |문자열 일부를 대체
리턴타입 String toString() |완성된 문자열을 리턴

toString()을 제외한 다른 메소드는 StringBuilder를 다시 리턴하기때문에 연이어서 다른 메소드를 호출할수 있는 메소드 체이닝 패턴을 사용할 수 있다.

public class StringBuilderExample {
    public static void main(String[] args) {
        String data = new StringBuilder()
                .append("DEF")
                .insert(0, "ABC")
                .delete(3, 4)
                .toString();
        System.out.println(data); //
    }
}

12.5.3 StringTokenizer 클래스

문자열이 구분자로 되어있을 경우 구분자를 기준으로 문자열을 분리하려면
String의 split()메소드를 사용하거나
java.util패키지의 StringTokenizer클래스를 사용할 수있다.
split은 정규표현식으로 구분하고 StringTokenizer는 문자로 구분한다는 차이점이 있다.

문자열에서 & 쉼표(,) 하이픈(-)등으로 구분되잇을 경우에는 split()메소드를 사용해야한다.
한종류의 구분자만 있다면 StringTokenizer를 사용할 수도 있다. 첫번째 매개값으로는 전체문자열을 주고 두번째 매개값으로는 구분자를 준다.

StringTokenizer객체가 생성되면 다음 메소드들을 사용할 수있다.
리턴타입 |메소드(매개변수) |설명
int |countTokens() |분리할수 있는 문자열의 총수
boolean |hasMoreTokens() |남아있는 문자열이 있는지 여부
String |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 포장 클래스

자바는 기본타입의 값을 갖는 객체를 생성할 수 있다.
이런 객체를 포장 객체라고 한다. 값을 포장하고 있다고 해서 붙여진 이름이다.
char Character / int Integer만 제외하고 나머지는 첫문자를 대문자로 바꾼 이름을 가지고 있다.
포장객체는 포장하고 있는 기본타입의 값을 변경할 수 없고 객체로 생성하는데 목적이 있다.
이런 객체가 필요한 이유는 컬렉션 객체 때문이다.

12.6.1 박싱과 언박싱

기본타입의 값으르 포장 객체로 만드는 과정을 박싱이라고 하고 반대로 포장 객체에서 기본타입의 값을 얻어내는 과정이 언박싱이다.
박싱은 포장클래스 변수에 기본타입 값이 대입될때 발생한다. 반대로 언박싱은 기본타입변수에 포장객체가 대입될때 발생한다.

public class BoxingUnBoxingExample {
    public static void main(String[] args) {
        //Boxing
        Integer obj = 100;
        System.out.println("value: " + obj.intValue()); //value: 100

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

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

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

포장클래스는 문자열을 기본타입으로 변환할때도 사용된다.
대부분의 포장클래스에는 parase+기본타입 명으로 되어있는 정적메소드가 있다.
->2장10절

12.6.3 포장값 비교

포장객체는 내부 값을 비교하기 위해 ==와 != 연산자를 사용할 수 없다.
이 연산은 내부의 값을 비교하는게 아니라 포장 객체의 번지를 비교하기 때문이다.

예외도 있다. 포장객체의 효율적 사용을 위해 다음 범위의 값을 갖는 포장객체는 공유된다.
==와 !=로 비교할 수 있지만 내부값 비교가 아닌 개체 번지 비교이다.
boolean타입 true/ false
char \u0000 ~ \u007f -> 코드로 저장하기 때문
byte short int -128 ~ 127

포장객체에 정확히 어떤 값이 저장된지 모르는 상황이라면 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 클래스는 수학 계산에 사용할 수 있는 메소드를 제공한다.
모두 정적 메소드라 바로 사용이 가능하다.

public class MathExample {
    public static void main(String[] args) {
        //큰 점수 또는 작은 점수 얻기
        double v1 = Math.ceil(5.3); //올림
        double v2 = Math.floor(5.3); //내림
        System.out.println("v1= " + v1); // 6.0
        System.out.println("v2= " + v2); //5.0

        //큰값 또는 작은 값 얻기
        long v3 = Math.max(3, 7);
        long v4 = Math.min(3, 7);
        System.out.println("v3= " + v3); //7
        System.out.println("v4= " + v4); //3

        //소수 이하 두 자리 얻기
        double value = 12.3456;
        double temp1 = value * 100;
        long temp2 = Math.round(temp1); //반올림
        double v5 = temp2 / 100.0;
        System.out.println("v5= " + v5); //12.35
    }
}

random()메소드는 0.0과 1.0사이의 double타입 난수를 리턴한다.
이값을 이용해서 start부터 n개의 정수 중 하나를 얻기위한 식을 짤수 있다.
int num = (int) (Math.randon() * n) + start;

난수를 얻는 또 다른 방법으로 java.util.Random 클래스를 이용할 수 있다.
이클래스를 이용하면 boolean int double난수를 얻을 수 있다.

Random() | 현재시간을 이용해서 종자값을 자동 설정
Random(logn seed) | 주어진 종자값을 사용

종자값이란 난수를 만드는 알고리즘에 사용되는 값이다. 종자값이 같으면 같은 난수를 얻는다.
컴퓨터가 완벽한 난수를 생성할 수 없기 때문에 종자값이 사용된다.

boolean | nextBoolean() | boolean타입의 난수를 리턴
doulble | 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 날짜와 시간 클래스

자바는 컴퓨터 날짜 및 시각을 읽을 수 있도록 Date와 Calendar클래스를 제공하고 있다.
또한 날짜와 시간을 조작할수 있도록 java.time 패키지에서 LocalDateTime등의 클래스를 제공한다.

Date | 날짜 정보를 전달하기 위해 사용
Calendar | 다양한시간대별로 날짜와 시간을 얻을 때 사용
LocalDasteTime | 날짜와 시간을 조작할때 사용

12.8.1 Date클래스

현재 날짜를 표현하는 클래스로 객체 간에 날짜 정보를 주고받을때 주로 사용된다.

public class DateExample {
    public static void main(String[] args) {
        Date now = new Date();
        String strNow1 = now.toString();
        System.out.println(strNow1); //Mon Feb 20 14:42:47 KST 2023

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss");
        String strNow2 = sdf.format(now);
        System.out.println(strNow2); //2023.02.20 14:43:13
    }
}

12.8.2 Calendar 클래스

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

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 + "초 ");

        //2023년 2월 20일 월요일 오후 2시 46분 33초 
    }
}

12.8.3 날짜와 시간조작

Date와 Calneder는 날짜와 시간정보를 얻기에는 충분하지만 날짜와 시간을 조작할 수는 없다.
이때는 java.time 패키지의 LocalDateTIme클래스가 제공하는 메소드를 이요하면 매우 쉽게 조작이 가능하다.

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 날짜와 시간비교

LocalDateTime는 날짜와 시간을 비교할 수 있눈 메소드도 제공한다.
isAfter isBefore isEqual
until(other.unit) 주어진 단위차이를 리턴

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

숫자를 형식화된 문자열로 변환하는 기능을 제공한다.
DecimalFormat객체를 생성하고 format()메소드로 숫자를 제공하면 패턴에 따른 형식화된 문자열을 얻을 수 있다.

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

        DecimalFormat df;

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

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

12.9.2 SimpleDateFormat

SimpleDateFormat은 날짜를 형식화 된 문자열로 변환하는 기능을 제공한다.


패턴에는 자릿수에 맞게 기호를 반복해서 작성한다.

public class SimpleDateFormatExample {
    public static void main(String[] args) {
        Date now = new Date();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        System.out.println(sdf.format(now)); //2023-02-20

        sdf = new SimpleDateFormat("yyyy년 MM월 dd일");
        System.out.println(sdf.format(now)); //2023년 02월 20일

        sdf = new SimpleDateFormat("yyyy.MM.dd a HH:mm:ss");
        System.out.println(sdf.format(now)); //2023.02.20 오후 15:12:38

        sdf = new SimpleDateFormat("오늘은 E요일");
        System.out.println(sdf.format(now)); //오늘은 월요일

        sdf = new SimpleDateFormat("올해의 D번째 날");
        System.out.println(sdf.format(now)); //올해의 51번째 날

        sdf = new SimpleDateFormat("이달의 d번째 날");
        System.out.println(sdf.format(now)); //이달의 20번째 날
    }
}

12.10 정규표현식 클래스

문자열이 정해져있는 형식으로 구성되어 있는지를 검증해야하는 경우가 있다.
예를들어 이메일이나 전화번호를 형식에 맞춰서 검증하는 것이다. 자바는 정규표현식을 이용해 문자열구성을 검증한다.

12.10.1 정규표현식 작성방법

https://chunpinge.tistory.com/119 44번 정규표현식관련 내용
https://regexlib.com/CheatSheet.aspx 정규표현식 사이트

12.10.2 Pattern클래스로 검증

\s \d 등을 사용하려면 \를 문자로 인식하게 해야해서
이스케이프를 붙여주어서 \\d \\s이런식으로 써야한다.

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("정규식과 불일치합니다.");
        }
    }
}

12.11 리플렉션

자바는 클래스와 인터페이스 메타정보를 class객체로 관리한다.
메타정보란 패키지 정보, 타입정보, 멤버(생성자 필드 메소드) 정보 등을 말한다.
메타정보를 프로그램에서 읽고 수정하는 행위를 리플렉션 이라고 한다.
class 객체를 얻으려면 3가지 방법 중 하나를 이용할 수 있다.
클래스로부터 얻는 방법
1.Class clazz = 클래스이름.class;
2.Class clazz = Class.forName("패키지.클래스이름"); //클래스 풀네임
객체로부터 얻는 방법
3.Class clazz = 객체참조변수.getClass();

12.11.2 패키지와 타입정보 얻기

패키지와 타입(클래스 인터페이스)이름 정보는 다음메소드로 얻을 수 있다.

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

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

        //how2
        //Class clazz = Class.forName(Ch12.sec11.exam01.Car);

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

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

12.11.3 멤버 정보 얻기

타입(클래스, 인터페이스)가 가지고 있는 멤버정보는 다음 메소드를 통해 얻을 수 있다.

Constructor[] getDeclaredConstructors() | 생성자 정보 읽기
Filed[] getDeclaredFileds(); | 필드 정보 읽기
Method[] getDeclaredMethods(); | 메소드 정보 읽기
멤버는 여러개라서 배열로 받고 메소드도 복수형인듯 하다.

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.4 리소스 경로 얻기

!!! 자주 사용함 !!!
Class 객체는 클래스파일의 경로 정보를 가지고 있기 때문에
이 경로를 기준으로 상대 경로에 있는 다른 리소스파일(이미지 XML Property 파일)의 정보를 얻을 수 있다.

리턴타입 | 메소드
URL | getResource(String name) | 리소스파일의 URL리턴
InputStream | getResourceAsStream(String name) | 리소스파일의 InputStream리턴

getResource()는 경로정보가 담긴 URL을 리턴하고 getResourceAsStream()으ㅜㄴ 파일의 내용을 읽을 수 있도로고 InputStream객체를 리턴한다.(18장)

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

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

        System.out.println(photo1Path); // /F:/study/workspace/JavaPrj/bin/Ch12/sec11/exam03/photo1.jpg
        System.out.println(photo2Path); // /F:/study/workspace/JavaPrj/bin/Ch12/sec11/exam03/images/photo2.jpg
    }
}

12.12 어노테이션

스프링에서 이미 만들어진 어노테이션을 사용한다.
만드는 방법을 알면 더 잘 사용할 수 있을 것이다.

코드에서 @으로 작성되는 요소를 어노테이션이라고 한다.
어노테이션은 클래스 또는 인터페이스를 컴파일하거나 실행할때 어떻게 처리할 것인지를 아렬주는 설정정보이다.

1.컴파일시 사용하는 정보전달
2.빌드툴이 코드를 자동으로 생성할때 사용하는 정보전달 ->롬복 getter등 자동생성
3.실행시 특정 기능을 처리할때 사용하는 정보전달

컴파일시 사용하는 정보전달의 대표적인 예는 @Override 어노테이션이다.
메소드 재정의 검사를 하도록 설정한다. 정확히 재정의 되지 않았다면 컴파일러는 에러를 발생시킨다.

어노테이션은 자바 프로그램 개발할때 필수요소이다. 웹개발에 많이 사용되는 스프링/ 스프링 부트는 많이 사용한다.

12.12.1 어노테이션 타입 정의와 선언

어노테이션을 만들고 어디에 적용할 것인지
어노테이션을 사용하기 위해서는 먼저 정의 해야한다.
인터페이스를 정의 하는 것과 유사하다.
public @interface AnnotaitonName {}

어노테이션은 속성을 가질 수 있다. 속성은 타입과 이름으로 구성되며 이름뒤에 괄호를 붙인다.
속성은 기본값은 default키워드로 지정할 수 있다.

String prop1();
int prop2() default 1;
마치 추상메소드처럼 생겼다.

이렇게 정의한 어노테이션은 코드에서 다음 처럼 사용할 수 있다.
@AnnotiationName(prop1 = "값");
@AnnotiationName(prop1 = "값", prop2=3);

어노테이션은 기본속성인 value를 가질 수 있다.
기본속성은 무조건 value임 마음대로 정한게 아니다.
String value();
int prop2() default 1;

value속성을 가진 어노테이션을 코드에서 사용할때 값만 기술할 수 있다.
@AnnotiationName("값");
다른 속성값을 같이 주고싶다면 value="값"해줘야한다.

12.12.2 어노테이션 적용대상

자바에서 어노테이션은 설정정보이다. 어디에 작성할건지 정할 수 있다.
적용할 수 있는 대상의 종류는 열거상수로 정의되어 있다.
사진참조
적용대상을 지정할때는 @Target어노테이션을 사용한다.
@Target의 기본속성인 value는 ElementType배열을 값으로 가진다.

@Target({ElementType.TYPE, ElementType.FILED, ElementType.METHOD)})
public @interface AnnotaitonName {}
이러면 클래스, 필드 ,메소드에 적용할 수 있다는 것이다.

12.12.3 어노테이션 유지정책

어노테이션을 정의할때 더 추가해야할 내용은 언제까지 유지할 것인지를 지정하는 것이다.
유지정책은 RetentionPolicy 열거상수로 정의되어 있다.

RetentionPolicy열거상수 | 어노테이션적용시점 |어노테이션 제거시점
SOURCE | 컴파일할 때 적용 | 컴파일된 후에 제거 ->소스에만 유지
CLASS | 메모리로 로딩할때 적용| 메모리로 로딩된 후에 제거
RUNTIME | 실행할 때 적용 | 계속 유지

@Retention의 기본속성인 value인 RetentionPolicy열거 상수값을 가진다.
@Target({ElementType.TYPE, ElementType.FILED, ElementType.METHOD)})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnnotaitonName {}

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

어노테이션은 아무런 동작을 가지지 않는 설정정보일 뿐이다.
이 설정 정보를 이용해서 어떻게 처리할것인지는 애플리케이션의 몫이다.
애플리케이션은 12.11절에서 학습한 리플렉션을 이용해서 적용대상으로부터 어노테이션의 정보를 얻어낼 수 있다

boolean | isAnootationPresent(AnnotaitonName.class) | 지정한 어노테이션이 적용되었는지 여부
Annotation | getAnnotation(AnnotaitonName.class) | 지정한 어노테이션이 적용되어 있으면 어노테이션을 리턴하고 그렇지 않으면 null을 리턴
Annotaiton[]| getDeclaredAnnotaitonS() | 적용된 모든 어노테이션을 리턴

METHOD 유지정책을 RUNTIME으로하고 구분선에 대한 설정정보를 가지고 있는 @PrintAnnotaiton 실습

@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("*")
    public void method2() {
        System.out.println("실행 내용2");
    }

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

실행클래스인 PrintAnnotationExmaple에서는 Service클래스에 선언된 메소드를 리플렉션해서 설정정보를 얻어낸후 구분선을 출력하고 해당 메소드를 호출시킨다.

public class PrintAnnotationExmaple {
    public static void main(String[] args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
        Method[] declareMethods = Service.class.getDeclaredMethods();

        for(Method method : declareMethods) {
            //PrintAnnotation얻기 
            //메소드에 PrintAnnotation이 적용되어 있으면객체얻음
            PrintAnnotation printAnnotation = method.getAnnotation(PrintAnnotation.class);

            //설정 정보를 이용해서 value속성과 number속성을 얻어와서 선 출력
            printLine(printAnnotation);

            //메소드호출 리플렉션에서 얻은 것을 호출
            //향상된 for문으로 method에 얻은 메소드 배열을 하나씩 담아서 꺼냄
            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();
        }
    }
}

2023.02.20 후기

라이브러리는 계속해서 사용하기 떼문에 잘 이해할 필요가 있다. 여기있는 라이브러리들 보다 많은 것들이 있다. 실제 사용하면서 이해하거나 필요하다 싶으면 검색해서 이해하는 것이 좋아보인다.

'기초단계 > JAVA' 카테고리의 다른 글

2023.02.21-2 Java복습  (0) 2023.02.21
2023.02.21-1 Java복습  (0) 2023.02.21
2023.02.19 Java 복습  (2) 2023.02.19
2023.02.18 Java 복습  (0) 2023.02.19
2023.02.17-2 Java 복습  (0) 2023.02.17

+ Recent posts