본문 바로가기

국비 교육

2020.8.10일자 수업 : 클래스와 메서드 활용

생성자 활용

/git/eomcs-java-basic/src/main/java com.eomcs.oop.ex04.Exam0111~250.jav

String 클래스를 통해 생성자 활용하기

- 문자열 리터럴을 파라미터로 하는 생성자

String s1 = new String("Hello");

- char 배열을 파라미터로 하는 생성자

char[] chars = new char[] {'H', 'e', 'l', 'l', 'o'};
    String s2 = new String(chars);

- byte 배열을 파라미터로 하는 생성자

byte[] bytes = {
        (byte)0x48, // H 
        (byte)0x65, // e
        (byte)0x6c, // l
        (byte)0x6c, // l
        (byte)0x6f  // o 
    };
    String s3 = new String(bytes);

JDK8까지는 String은 char 배열로 구성되었지만 그 이후 버전부터는 byte 배열로 구성된다.


String 클래스의 변환 과정

byte 배열(???) => String 클래스의 문자열 표현 (UCS2 = 유니코드 = UTF-16, 2바이트)

JVM안에는 문자집합 규칙표가 들어있다.

따라서 바이트 배열을 문자열로 변환할 때 바이트 배열에 맞는 문자집합을 이용해 변환할 수 있게 한다.

String 클래스가 바이트 배열을 파라미터로 받아 문자열로 변환할 때

옵션으로 인코딩 정보를 가르쳐주지 않으면 바이트 배열이 OS의 기본 문자 코드로 되어있다고 간주한다.

OS의 기본 문자 코드

Unix/Linux/MacOS : UTF-8

Windows : MS949

따라서 이와 다른 문자코드로 짜인 바이트 배열을 파라미터로 줄 때에는

옵션으로 인코딩 정보를 함께 줘야한다.

byte[] bytes = {
        (byte)0xb0, (byte)0xa1, // 가
        (byte)0xb0, (byte)0xa2, // 각
        (byte)0xb6, (byte)0xca, // 똘
        (byte)0xb6, (byte)0x63  // 똠
    };
    String s1 = new String(bytes);
    System.out.println(s1);
    
    String s2 = new String(bytes, "EUC-KR");
    System.out.println(s2);
    // 가각똘�c  
    // 한글이 깨지는 이유 : '똠'은 ms949와 cp949에만 있는 문자이기 때문에
    // MS949(11172자) = EUC-KR(2350자) + a)
    // 똠 글자가 0xb6cb 가 아닌 0xb663인 이유 : 
    // ms949에서 새로 문자를 추가 할때 연속해서 문자를 끼워놓으면
    // 뒤에 있는 문자의 코드들이 다 뒤로 밀리기 때문에
  
    String s3 = new String(bytes, "MS949");
    System.out.println(s3);
    // 가각똘똠
더보기

♣ UTF-8 문자코드가 유니코드로 바뀌는 과정

     유니코드 (2byte : yyyyyyyy yyyyyyyy)

=> UTF-8    (3byte : 1111yyyy 11yyyyyy 11yyyyyy)

● 윈도우에서 이클립스를 통해 실행하면 깨지지 않는 이유?

이클립스는 JVM을 실행 할떄 입출력 기본 인코딩을 UTF-8로 설정하기 때문이다.

 

● 윈도우 CLI에서 깨지지 않게 하는 방법 

JVM 실행 옵션에 다음을 추가하라.

- Dfile.encoding=UTF-8

=> java -Dfile.encoding=UTF-8 -cp bin/main 클래스명

=> PowerShell 이 아닌 Command 창에서 실행하라!

PowerShell 에서는 -Dfile.encoding 옵션을 제대로 처리하지 못한다.

도트(.)를 분리 문자로 인식한다.

 

더보기

문자열의 레퍼런스 변수를 println(str) 파라미터로 넣어주면?

1) String str = "Hello World";

System.out.println(str);

=> Hello World

2) Sysstem.out.println("(" + str + ")");

=> (Hello World)

레퍼런스 변수를 그대로 파라미터로 넣어주면 주소 값이 가리키는 메모리의 문자열을 갖고 온다.

 이 변환 과정이 중요한 이유!

파일에서 데이터를 받든 네트워크에서 받든 기본적으로 byte 배열로 받는다.

그걸로 정상적인 UTF-16 값으로 바꾸려면 어떤 문자를 목적으로 짜여진 바이트 배열인지 알아야한다.

 


Date 클래스를 통해 생성자 활용하기

1. Date() 기본 생성자 : 실행하는 시점의 날짜와 시분초로 초기화

    Date d1 = new Date(); 

    System.out.println(d1.toString());

    System.out.println(d1);  // = println(d1.toString);

 Date d1 = new Date();     
 System.out.println(d1.toString());    
 System.out.println(d1);  // = println(d1.toString);

 println()의 아규먼트가 primitive 타입이나 String 타입이 아니면 인스턴스에 대해서 toString()을 자동 호출하여 그 리턴 값을 출력한다.

 

    

2. Date(int, int, int) : 년, 월, 일 값으로 날짜 인스턴스를 초기화한다. (deprecated - 비난받는 메서드)

Date d3 = new Date(120, 8, 10);
System.out.println(d3);

3. Date(int) : 1970년 1월 1일 0시 0분 0초 기준으로 측정된 밀리초를 가지고 날짜 인스턴스를 초기화한다.

Date d2 = new Date(1000L * 60 * 60 * 24 * 365 * 50);
System.out.println(d2);

 

Calendar 클래스는 생성자의 접근 제어자가 protected 로 상속 클래스 이외에 다른 클래스에서 생성자 호출이 불가능하다.

    why? 1) 같은 값을 갖는 객체를 여러개 생성하지 못하도록 제한하기 위해

             2) 객체 생성 과정이 복잡할 때 

- private : 같은 패키지 안의 클래스만 사용 가능

- protected : 같은 패키지 안의 클래스 or 상속 클래스만 사용 가능 

    

    해당 예를 보자

public class Ex {
  public static void main(String[] args) {
    // Car c1 = new Car2(); // 불가능
    Car c1 = Car.getInstance();
    Car c2 = Car.getInstance();
    System.out.println(c1 == c2);
    // => true
  }
}

class Car2 {
  String model;
  int cc;
  static Car2 obj;
  private Car2() {}
  
  static Car2 getInstance() {
    if (obj == null) {
      obj = new Car2();
    }
    return obj;
  }
}

생성자를 pruvate으로 막는 대신 getInstance를 통해 new생성자를 호출하고 

이 결과로 생성되는 인스턴스를 스태틱 주소 변수에 넣음으로써 하나의 인스턴스만 생성하게 하였다. 

보통 생성자를 private으로 막는 경우는 이와 같이 인스턴스를 한개만 생성하도록 제한하고 싶을때 이다.    

    

그렇다면 생성자가 공개 되지 않을 때는 어떻게 해야하는 가?

1) 다른 클래스의 도움을 받는다.

2) 해당 클래스에서 제공하는 스태틱 메서드(인스턴스를 생성할 수 없으므로)를 사용한다.

// Calendar c1 = new Calendar();   // 컴파일 오류!
Calendar c1 = Calendar.getInstance();    
System.out.println(c1); // to.String()

인스턴스 메서드와 클래스 메서드 활용

String 클래스

1. 인스턴스 메서드

● charAt(int) : 아규먼트에 해당하는 인덱스에 있는 char 문자를 리턴한다.

String s1 = new String("Hello");
char c = s1.charAt(1);
System.out.println(c);

● compareTo(String) : 인스턴스에 있는 문자열과 아규먼트인 문자열을 정렬하고, 각 자리의 값을 비교한다.

중요한 것은 인스턴스의 값이 아규먼트 값보다 작을 경우(문자가 앞에 있는 경우) 음수가 리턴되고

인스턴의 값이 아규먼트의 값보다 큰 경우(문자가 뒤에 오는 경우) 양수가 리턴된다.

둘의 값이 같다면 0이 리턴된다.

System.out.println(s1.compareTo("Helli"));
System.out.println(s1.compareTo("Hello"));
System.out.println(s1.compareTo("Hellu"));

● contains(String): 인스턴스 문자열이 아규먼트 문자열을 포함하고 있다면 true, 아니면 false를 리턴한다.

System.out.println(s1.contains("ll"));
System.out.println(s1.contains("ee"));

● concat(String) : 인스턴스 문자열의 가장 뒤에 아규먼트 문자열을 붙인 새 문자열을 리턴한다.

String s2 = s1.concat(", world!");
System.out.println(s1); // 기존 인스턴스의 값은 변경하지 않는다.
System.out.println(s2); // 새로 문자열을 만든다.

● equals(String): 인스턴스 문자열이 아규먼트 문자열과 같다면 true, 다르다면 false를 리턴한다.

System.out.println(s1.equals("aaa"));
System.out.println(s1.equals("Hello"));

● getBytes(): 인스턴스에 들어있는 문자 코드를 바이트 배열로 만들어 리턴한다.

중요한 것! 인코딩 문자집합을 지정하지 않으면 JVM의 기본 문자집합으로 인코딩한다.

Encodes this String into a sequence of bytes using the platform's default charset, storing the result into a new byte array

byte[] bytes = s3.getBytes(); 
    for (int i = 0; i < bytes.length; i++)
      System.out.printf("%x,", bytes[i]);
    System.out.println();
    
System.out.println(s1.getBytes());
    byte[] bytes = s2.getBytes("UTF-8"); 
    for (byte b : bytes) {
      System.out.println(Integer.toHexString(b & 0xff));
      // & 0xff를 붙이지 않으면 부호비트가 앞에 붙어 완전히 다른 int값이 나온다.
    }
더보기

두번째 코드처럼 작은 메모리의 정수값을 큰 메모리의  정수값으로 바꾼다면 무조건 &를 사용하여 원래 부분만 추출해내야한다.

왜냐하면 메모리가 커지는 순간, 나머지 자리가 부호비트로 채워져 갑자기 값이 달라질 수 있기 때문이다.

2. 스태틱 메서드

● format(String, Object...) : 형식을 갖춘 문자열을 리턴한다.

String s4 = String.format("%s님 반갑습니다", "홍길동");
System.out.println(s4);

● join(String, String...) : 구분자와 문자열을 결합한 새 문자열을 리턴한다.

String s5 = String.join(":", "홍길동", "임꺽정", "유관순");
System.out.println(s5);

● valueOf() : primitive 값을 받아 새 문자열을 리턴한다.

String s6 = String.valueOf(true);
String s7 = String.valueOf(3.14f);
String s8 = String.valueOf(100);
System.out.println(s6);
System.out.println(s7);
System.out.println(s8);

 

math 클래스

Math 클래스는 수학 관련 메서드를 모아둔 클래스로, 모든 메서드가 스태틱 메서드이다.

 

● abs(int) : 절대값을 리턴한다.

● ceil(double) : 파라미터로 주어진 부동소수점이 바로 위 큰 정수 값을 리턴한다.

● floor(double) : 파라미터로 주어니 부동소수점의 바로 밑 작은 정수 값을 리턴한다.

● round(double) : 부동소수점을 반올림한 정수 값을 리턴한다..

● pow(double, double) : 첫째 파라미터에 두번째 파라미터를 제곱수한 정수 값을 리턴한다.

 

wrapper 클래스

wrapper 클래스는 primitive 데이터 타입을 객체로 다루어야할 때 사용하는 클래스이다.

ex) Integer, Byte, Short, Long, Float, Double, Character

 

1. 스태틱 메서드

  • Integer = new Integer(int)  deprecated
  • Integer.valueOf(int or String) : 생성자 대신 이 메서드를 통해 객체를 생성해라.
    -> Integer.valueOf(int or String, int) : 두 번째 파라미터는 첫번째 파라미터를 몇 진수로 간주할 것인 지에 관한 옵션이다.
  • Integer.parseInt(String) : 문자열을 integer 값으로 바꿔주는 메서드이다.
  • Float.parseFloat(String) : 문자열을 float 값으로 바꿔주는 메서드이다.
  • Boolean.paresBoolean(String) : 문자열을 boolean 값으로 바꿔주는 메서드이다.
  • Integer.toBinaryString(int) : integer 값을 2진수 형태의 문자열로 바꿔주는 메서드이다.
  • Integer.toHexString(int) : integer 값을 16진수 형태의 문자열로 바꿔주는 메서드이다.

2. 인스턴스 메서드

  • (Integer 인스턴스).compareTo() : 인스턴스의 
  • (Integer 인스턴스).intValue()

Date 클래스

1. 인스턴스 메서드

  • getYear(), getMonth(), getDate()  deprecated 
Date d1 = new Date();
System.out.println(d1.getYear() + 1900);
System.out.println(d1.getMonth() + 1); 
System.out.println(d1.getDate());
  • toString() - 모든 클래스에 기본적으로 있는 메서드
String str = today.toString(); 
System.out.println(str);

 

2. 스태틱 메서드 

  • Date.parse(String) deprecated
long millis = Date.parse("Sat, 12 Aug 1995 13:30:25 GMT");
System.out.println(millis);

 

  • Date.valueOf(String or Object()) : "yyyy-[m]m-[d]d" 형태의 문자열을 Date 값으로 변환한다. 
    java.sql. 패키지에 해당하는 Date 클래스이다.
java.sql.Date d2 = java.sql.Date.valueOf("2020-08-10");
System.out.println(d2);

 

  • System.currentTImeMillis() : java.lang 패키지에 해당하는 System 클래스의 메서드로, 1970년 1월 1일 0시 0분 기준으로 실행하는 시점까지 흐른 밀리초를 계산하여 리턴한다.
long curr = System.currentTimeMillis();
java.sql.Date today = new java.sql.Date(curr);

    

 

Calendar 클래스

1. 스태틱 메서드

  • Calendar.get Instance() : Calendar 클래스는 생성자를 호출할 수 없기 때문에 이 메서드를 통해서 실행한 시점의 날짜와 시간을 저장한 객체를 만들어 저장할 수 있다.

 

2. 인스턴스 메서드

  • get() : 위의 메서드로 생성한 객체를 갖고 다양한 값을 리턴하게 할 수 있다. 아규먼트 값에 따라 다른 종류의 값을 리턴하는 데, 이를 외우기 어려우므로 Calendar 클래스에서 지정한 상수 값을 사용하면 된다.
    System.out.println(c.get(1)); // 년도
    System.out.println(c.get(2) + 1); // 월(0 ~ 11)
    System.out.println(c.get(5)); // 일
    System.out.println(c.get(7)); // 요일(1 ~ 7)
    System.out.println(c.get(4)); // 그 달의 몇 번째 주
    System.out.println(c.get(10)); // 시(0 ~ 11)
    System.out.println(c.get(11)); // 시(24시)
    System.out.println(c.get(12)); // 분
    System.out.println(c.get(13)); // 초
    
    System.out.println("-----------------------");
    
    // 상수의 활용
    System.out.println(c.get(Calendar.YEAR)); // 년도
    System.out.println(c.get(Calendar.MONTH) + 1); // 월(0 ~ 11)
    System.out.println(c.get(Calendar.DATE)); // 일
    System.out.println(c.get(Calendar.DAY_OF_WEEK)); // 요일(1 ~ 7)
    System.out.println(c.get(Calendar.WEEK_OF_MONTH)); // 그 달의 몇 번째 주
    System.out.println(c.get(Calendar.HOUR)); // 시(0 ~ 11)
    System.out.println(c.get(Calendar.HOUR_OF_DAY)); // 시(24시)
    System.out.println(c.get(Calendar.MINUTE)); // 분
    System.out.println(c.get(Calendar.SECOND)); // 초

실습 1 - 클래스를 이용하여 새 데이터 타입 정의하기

git/ eomcs-project-2020/ mini-pms-07/ src/ main/ java com.eomcs.pms.App.java

1. App 클래스 안에 Member, Project, Task 클래스 생성 후, 그에 해당하는 인스턴스 변수 각 클래스로 분류하기

클래스 안의 클래스는 중첩 클래스 (nested class)

일반 클래스(같은 패키지 안에 소속된 클래스)는 패키지 멤버 클래스(package member class)

2. 각 클래스들의 배열을 만들고, 각 메서드들이 각 배열을 인스턴스의 주소로 채우도록 변경하기

package com.eomcs.pms;

import java.sql.Date;
import java.util.Scanner;

public class App {

  static Scanner keyboardScan = new Scanner(System.in);
  
  static class Member {
    int no;
    String name;
    String email;
    String password;
    String photo;
    String tel;
    Date registeredDate;
  }
  
  static class Project {
    int no;
    String title;
    String content;
    Date startDate;
    Date endDate;
    String owner;
    String members;
  }
  
  static class Task {
    int no;
    String content;
    Date deadline;
    String owner;
    int status;
  }
  
  // 회원 데이터
  static final int LENGTH = 100;
  static int size = 0;
  static Member[] members = new Member[LENGTH];
  
  
  // 프로젝트 데이터
  static final int PLENGTH = 100;
  static int psize = 0;
  static Project[] projects = new Project[PLENGTH];
  
  // 작업 데이터
  static final int TLENGTH = 100;
  static int tsize = 0;
  static Task[] tasks = new Task[TLENGTH];

  public static void main(String[] args) {
    
    loop:
      while (true) {
        String command = promptString("명령> ");

        switch (command) {
          case "/member/add":
            addMember();
            break;
          case "/member/list":
            listMember();
            break;
          case "/project/add":
            addProject();
            break;
          case "/project/list":
            listProject();
            break;
          case "/task/add":
            addTask();
            break;
          case "/task/list":
            listTask();
            break;
          case "quit":
          case "exit":
            System.out.println("안녕!");
            break loop;
          default:
            System.out.println("실행할 수 없는 명령입니다.");
        }
        System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
      }

    keyboardScan.close();
  }
  
  static void addMember() {
    System.out.println("[회원 등록]");
    Member m = new Member();
    m.no= promptInt("번호? ");
    m.name = promptString("이름? ");
    m.email = promptString("이메일? ");
    m.password = promptString("암호? ");
    m.photo = promptString("사진? ");
    m.tel = promptString("전화? ");
    m.registeredDate = new Date(System.currentTimeMillis());
    members[size++] = m;
  }
  
  static void listMember() {
    System.out.println("[회원 목록]");
    
    for (int i = 0; i < size; i++) {
      Member m = members[i];
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          m.no, m.name, m.email, m.tel, m.registeredDate);
    }
  }
  
  static void addProject() {
    System.out.println("[프로젝트 등록]");
    Project p = new Project();
    p.no = promptInt("번호? ");
    p.title = promptString("프로젝트명? ");
    p.content = promptString("내용? ");
    p.startDate = promptDate("시작일? ");
    p.endDate = promptDate("종료일? ");
    p.owner = promptString("만든이? ");
    p.members = promptString("팀원? ");
    projects[psize++] = p;
  }
  
  static void listProject() {
    System.out.println("[프로젝트 목록]");
    
    for (int i = 0; i < psize; i++) {
      Project p = projects[i];
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          p.no, p.title, p.startDate, p.endDate, p.owner);
    }
  }
  
  static void addTask() {
    System.out.println("[작업 등록]");
    Task t = new Task();
    t.no = promptInt("번호? ");
    t.content = promptString("내용? ");
    t.deadline = promptDate("마감일? ");
    t.status = promptInt("상태?\n0: 신규\n1: 진행중\n2: 완료\n> ");
    t.owner = promptString("담당자? ");
    tasks[tsize++] = t;
  }
  
  static void listTask() {
    System.out.println("[작업 목록]");
    
    for (int i = 0; i < tsize; i++) {
      t = tasks[i];
      String stateLabel = null;
      switch (t.status) {
        case 1:
          stateLabel = "진행중";
          break;
        case 2:
          stateLabel = "완료";
          break;
        default:
          stateLabel = "신규";
      }
      // 번호, 작업명, 마감일, 프로젝트, 상태, 담당자
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          t.no, t.content, t.deadline, stateLabel, t.owner);
    }
  }
  
  static String promptString(String title) {
    System.out.print(title);
    return keyboardScan.nextLine();
  }

 
  static int promptInt(String title) {
    return Integer.parseInt(promptString(title));
  }


  static Date promptDate(String title) {
    return Date.valueOf(promptString(title));
  }
}

실습 2 - 클래스로 메서드 분류하기

git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.App.java
git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.MemberHandler.java
git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.ProjectHandler.java
git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.TaskHandler.java
git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.Prompt.java

1. prompt 관련 메서드를 별도의 클래스 Prompt 로 빼놓고, 메서드들의 이름을 적절하게 변경하기. 키보드 스캔 인스턴스를 close()하는 메서드를 만들기

(why? 도구를 묶은 클래스(보통은 스태틱 메서드일 것)를 만들면 그 클래스 안에 있는 스태틱 변수를 도구를 사용하는 다른 클래스가 직접 사용하는 것은 상식적이지 못하다!!! 그 변수들은 도구 클래스의 메서드만이 사용하게끔, 마치 일을 맡은 직원이 할 일을 모두 스스로 다룰 수 있게끔 하는 것처럼 해야 한다.)

2. MemberHandler, ProjectHandler, TaskHandler 클래스 별도로 만들어 분리시키고, 멤버의 이름을 적절하게 변경하기

package com.eomcs.pms;

public class App {
  
  public static void main(String[] args) {
    
    loop:
      while (true) {
        String command = Prompt.inputString("명령> ");

        switch (command) {
          case "/member/add":
            MemberHandler.add();
            break;
          case "/member/list":
            MemberHandler.list();
            break;
          case "/project/add":
            ProjectHandler.add();
            break;
          case "/project/list":
            ProjectHandler.list();
            break;
          case "/task/add":
            TaskHandler.add();
            break;
          case "/task/list":
            TaskHandler.list();
            break;
          case "quit":
          case "exit":
            System.out.println("안녕!");
            break loop;
          default:
            System.out.println("실행할 수 없는 명령입니다.");
        }
        System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
      }
    Prompt.close();
  }
}
package com.eomcs.pms;

import java.sql.Date;
import java.util.Scanner;

public class Prompt {
  
  static Scanner keyboardScan = new Scanner(System.in);
  
  static String inputString(String title) {
    System.out.print(title);
    return keyboardScan.nextLine();
  }

  static int inputInt(String title) {
    return Integer.parseInt(inputString(title));
  }

  static Date inputDate(String title) {
    return Date.valueOf(inputString(title));
  }
  
  static void close() {
    keyboardScan.close();
  }
}
package com.eomcs.pms;

import java.sql.Date;

public class MemberHandler {
  
  static final int LENGTH = 100;
  static int size = 0;
  static Member[] list = new Member[LENGTH];
  
  static class Member {
    int no;
    String name;
    String email;
    String password;
    String photo;
    String tel;
    Date registeredDate;
  }
  
  static void add() {
    System.out.println("[회원 등록]");
    Member m = new Member();
    m.no = Prompt.inputInt("번호? ");
    m.name = Prompt.inputString("이름? ");
    m.email = Prompt.inputString("이메일? ");
    m.password = Prompt.inputString("암호? ");
    m.photo = Prompt.inputString("사진? ");
    m.tel = Prompt.inputString("전화? ");
    m.registeredDate = new Date(System.currentTimeMillis());
    list[size++] = m;
  }
  
  static void list() {
    System.out.println("[회원 목록]");
    
    for (int i = 0; i < size; i++) {
      Member m = list[i];
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          m.no, m.name, m.email, m.tel, m.registeredDate);
    }
  }
}

package com.eomcs.pms;

import java.sql.Date;

public class ProjectHandler {
  
  static final int LENGTH = 100;
  static int size = 0;
  static Project[] list = new Project[LENGTH];
  
  static class Project {
    int no;
    String title;
    String content;
    Date startDate;
    Date endDate;
    String owner;
    String members;
  }
  
  static void add() {
    System.out.println("[프로젝트 등록]");
    Project p = new Project();
    p.no = Prompt.inputInt("번호? ");
    p.title = Prompt.inputString("프로젝트명? ");
    p.content = Prompt.inputString("내용? ");
    p.startDate = Prompt.inputDate("시작일? ");
    p.endDate = Prompt.inputDate("종료일? ");
    p.owner = Prompt.inputString("만든이? ");
    p.members = Prompt.inputString("팀원? ");
    list[size++] = p;
  }
  
  static void list() {
    System.out.println("[프로젝트 목록]");
    
    for (int i = 0; i < size; i++) {
      Project p = list[i];
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          p.no, p.title, p.startDate, p.endDate, p.owner);
    }
  }
}
package com.eomcs.pms;

import java.sql.Date;

public class TaskHandler {
  
  static final int LENGTH = 100;
  static int size = 0;
  static Task[] list = new Task[LENGTH];
  
  static class Task {
    int no;
    String content;
    Date deadline;
    String owner;
    int status;
  }
  
  static void add() {
    System.out.println("[작업 등록]");
    Task t = new Task();
    t.no = Prompt.inputInt("번호? ");
    t.content = Prompt.inputString("내용? ");
    t.deadline = Prompt.inputDate("마감일? ");
    t.status = Prompt.inputInt("상태?\n0: 신규\n1: 진행중\n2: 완료\n> ");
    t.owner = Prompt.inputString("담당자? ");
    list[size++] = t;
  }
  
  static void list() {
    System.out.println("[작업 목록]");
    
    for (int i = 0; i < size; i++) {
      Task t = list[i];
      String stateLabel = null;
      switch (t.status) {
        case 1:
          stateLabel = "진행중";
          break;
        case 2:
          stateLabel = "완료";
          break;
        default:
          stateLabel = "신규";
      }
      // 번호, 작업명, 마감일, 프로젝트, 상태, 담당자
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          t.no, t.content, t.deadline, stateLabel, t.owner);
    }
  }
}

실습 3 - 패키지로 클래스 분류하기

git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.App.java
git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.handler.MemberHandler.java
git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.handler.ProjectHandler.java
git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.pms.handler.TaskHandler.java
git/ eomcs-project-2020/ mini-pms-08/ src/ main/ java com.eomcs.util.Prompt.java

1. com.eomcs.pms.handler 패키지 생성하여 안에 MemberHandler, ProjectHandler, TaskHandler 클래스 위치시키고, com.eomcs.util 패키지 생성하여 안에 Prompt 클래스 위치시키기

2. 접근제어자 public 을 각 메서드에 붙여주고 App 클래스에서 패키지 임포트해주기

package com.eomcs.pms;

import com.eomcs.pms.handler.MemberHandler;
import com.eomcs.pms.handler.ProjectHandler;
import com.eomcs.pms.handler.TaskHandler;
import com.eomcs.util.Prompt;

public class App {
  
  public static void main(String[] args) {
    
    loop:
      while (true) {
        String command = Prompt.inputString("명령> ");

        switch (command) {
          case "/member/add":
            MemberHandler.add();
            break;
          case "/member/list":
            MemberHandler.list();
            break;
          case "/project/add":
            ProjectHandler.add();
            break;
          case "/project/list":
            ProjectHandler.list();
            break;
          case "/task/add":
            TaskHandler.add();
            break;
          case "/task/list":
            TaskHandler.list();
            break;
          case "quit":
          case "exit":
            System.out.println("안녕!");
            break loop;
          default:
            System.out.println("실행할 수 없는 명령입니다.");
        }
        System.out.println(); // 이전 명령의 실행을 구분하기 위해 빈 줄 출력
      }
    Prompt.close();
  }
}
package com.eomcs.pms.handler;

import java.sql.Date;
import com.eomcs.util.Prompt;

public class MemberHandler {
  
  static final int LENGTH = 100;
  static int size = 0;
  static Member[] list = new Member[LENGTH];
  
  static class Member {
    int no;
    String name;
    String email;
    String password;
    String photo;
    String tel;
    Date registeredDate;
  }
  
  public static void add() {
    System.out.println("[회원 등록]");
    Member m = new Member();
    m.no = Prompt.inputInt("번호? ");
    m.name = Prompt.inputString("이름? ");
    m.email = Prompt.inputString("이메일? ");
    m.password = Prompt.inputString("암호? ");
    m.photo = Prompt.inputString("사진? ");
    m.tel = Prompt.inputString("전화? ");
    m.registeredDate = new Date(System.currentTimeMillis());
    list[size++] = m;
  }
  
  public static void list() {
    System.out.println("[회원 목록]");
    
    for (int i = 0; i < size; i++) {
      Member m = list[i];
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          m.no, m.name, m.email, m.tel, m.registeredDate);
    }
  }

}
package com.eomcs.pms.handler;

import java.sql.Date;
import com.eomcs.util.Prompt;

public class ProjectHandler {
  
  static final int LENGTH = 100;
  static int size = 0;
  static Project[] list = new Project[LENGTH];
  
  static class Project {
    int no;
    String title;
    String content;
    Date startDate;
    Date endDate;
    String owner;
    String members;
  }
  
  public static void add() {
    System.out.println("[프로젝트 등록]");
    Project p = new Project();
    p.no = Prompt.inputInt("번호? ");
    p.title = Prompt.inputString("프로젝트명? ");
    p.content = Prompt.inputString("내용? ");
    p.startDate = Prompt.inputDate("시작일? ");
    p.endDate = Prompt.inputDate("종료일? ");
    p.owner = Prompt.inputString("만든이? ");
    p.members = Prompt.inputString("팀원? ");
    list[size++] = p;
  }
  
  public static void list() {
    System.out.println("[프로젝트 목록]");
    
    for (int i = 0; i < size; i++) {
      Project p = list[i];
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          p.no, p.title, p.startDate, p.endDate, p.owner);
    }
  }
}
package com.eomcs.pms.handler;

import java.sql.Date;
import com.eomcs.util.Prompt;

public class TaskHandler {
  
  static final int LENGTH = 100;
  static int size = 0;
  static Task[] list = new Task[LENGTH];
  
  static class Task {
    int no;
    String content;
    Date deadline;
    String owner;
    int status;
  }
  
  public static void add() {
    System.out.println("[작업 등록]");
    Task t = new Task();
    t.no = Prompt.inputInt("번호? ");
    t.content = Prompt.inputString("내용? ");
    t.deadline = Prompt.inputDate("마감일? ");
    t.status = Prompt.inputInt("상태?\n0: 신규\n1: 진행중\n2: 완료\n> ");
    t.owner = Prompt.inputString("담당자? ");
    list[size++] = t;
  }
  
  public static void list() {
    System.out.println("[작업 목록]");
    
    for (int i = 0; i < size; i++) {
      Task t = list[i];
      String stateLabel = null;
      switch (t.status) {
        case 1:
          stateLabel = "진행중";
          break;
        case 2:
          stateLabel = "완료";
          break;
        default:
          stateLabel = "신규";
      }
      // 번호, 작업명, 마감일, 프로젝트, 상태, 담당자
      System.out.printf("%d, %s, %s, %s, %s\n", // 출력 형식 지정
          t.no, t.content, t.deadline, stateLabel, t.owner);
    }
  }
}
package com.eomcs.util;

import java.sql.Date;
import java.util.Scanner;

public class Prompt {
  
  static Scanner keyboardScan = new Scanner(System.in);
  
  public static String inputString(String title) {
    System.out.print(title);
    return keyboardScan.nextLine();
  }

  public static int inputInt(String title) {
    return Integer.parseInt(inputString(title));
  }

  public static Date inputDate(String title) {
    return Date.valueOf(inputString(title));
  }
  
  public static void close() {
    keyboardScan.close();
  }
}