Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

개발자입니다

[비트캠프] 59일차(13주차1일) - Java(컬렉션 API, 파일 입출력), myapp-26 본문

네이버클라우드 AIaaS 개발자 양성과정 1기/Java

[비트캠프] 59일차(13주차1일) - Java(컬렉션 API, 파일 입출력), myapp-26

끈기JK 2023. 1. 30. 10:52

 

Collection API

 

 

List 관련 클래스 계층도(hierarchy)

 

List 관련 클래스 계층도는 위와 같다.

《interface》Iterable 에 iterator(), forEach() 표준 정의하였다.

  • 이를 상속받은 《interface》Collection 에 add(), contains(), remove(), size(), toArray() 표준 정의하였다.
    • 이를 상속받은 《interface》List에 add(int), set(int), remove(int), get(int) 표준 정의하였다.
      • 이를 구현한 《abstract》abstractList 를 상속받은 vector 를 상속받은 Stack 에 push(), pop(), peek() 를 구현하였다.
    • 이를 상속받은 《interface》Queue에 peek(), poll(), offer() 표준 정의하였다.

 

 

향상된 for문 : 우측에 올 수 있는 건 배열 또는 iterable 구현체이다.

  static void print(MyLinkedList<String> list) {
    for (String e : list) {
      System.out.println(e);
    }
    System.out.println("------------------------");

  }

아래와 같이 구현하면 향상된 for 를 사용할 수 있다.

public class MyLinkedList<E> implements Cloneable, Iterable<E> {

  @Override
  public Iterator<E> iterator() {
    return new Iterator<E>() {
      int cursor;
      @Override
      public boolean hasNext() {
        return cursor >= 0 && cursor < MyLinkedList.this.size();
      }

      @Override
      public E next() {
        return MyLinkedList.this.get(cursor++);
      }
    };
  }

 

 

 

Set 관련 클래스 계층도(hierarchy)

 

《interface》Set 은 중복 불가! = "집합" 이다.

 

 

 

Map 관련 클래스 계층도(hierarchy)

 

《interface》Map 에 put(k, v), get(k), keySet(), values(), entrySet() 표준 정의하였다. 《abstract》AbstractMap 가 구현하고 HashMap 이 상속하였다.

 

entrySet() 은 Set으로 원소는 Map.Entry 이다. Map.Entry 는 key, value 를 갖는다.

 

 

 

 

File I/O API

 

 

FileOutputStream / FileInputStream

 

객체를 byte[ ] 로 변환해 FileOutputStream 을 사용해 파일로 만든다.

파일을 FileInputStream 을 사용해 byte[ ] 로 만들어 객체로 변환한다.

 

 

 

 

myapp

 

 

26. 바이너리 데이터 입출력

 

### 26. 파일 API를 사용하여 데이터를 바이너리 형식으로 입출력하기: FileInputStream/FileOutputStream 
- 입출력 스트림 API를 사용하여 데이터를 파일로 저장하고 읽는 방법 
- 바이너리 형식으로 데이터를 입출력하는 방법

Board 객체를 byte[ ] 로 변환하고 파일로 만든다. 파일을 byte[ ] 로 만들고 Board 객체로 변환한다.

Board 객체들을 byte[ ], byte[ ], ... 로 변환한다. 객체별 배열 크기를 2byte 로 저장하고 byte[ ] 를 저장한다. 뒤이어 배열 크기를 2byte 로 저장하고 byte[ ] 를 저장한다. 이를 Board 객체로 만들어 List에 add() 한다.

List 에서 get() 하여 Board 객체를 만들고 이를 byte[ ] 로 변환한다. 이를 byte[ ], byte[ ], ... 로 변환하고 Board 객체로 만든다.

 

 

저장과 로드 각각 따로 코드 확인한다.

1. 파일 저장 : 게시판 나가려고 0 입력시 boardDao.save() 호출한다.

2. 파일 로드 : 게시판 들어가려고 BoardHandler 객체의 service() 실행할때 boardDao.load() 호출한다.

public class BoardHandler {

/* 수정 */

  public void service() {

    boardDao.load("board.data");  // 추가

    while (true) {
      System.out.printf("[%s]\n", this.title);
      System.out.println("1. 등록");
      System.out.println("2. 목록");
      System.out.println("3. 조회");
      System.out.println("4. 변경");
      System.out.println("5. 삭제");
      System.out.println("6. 검색");
      System.out.println("0. 이전");
      int menuNo = Prompt.inputInt(String.format("%s> ", this.title));

      switch (menuNo) {
        case 0:
          boardDao.save("board.data");  // 추가
          return;
        case 1: this.inputBoard(); break;
        case 2: this.printBoards(); break;
        case 3: this.printBoard(); break;
        case 4: this.modifyBoard(); break;
        case 5: this.deleteBoard(); break;
        case 6: this.searchBoard(); break;
        default:
          System.out.println("잘못된 메뉴 번호 입니다.");
      }
    }
  }
}

 

public class BoardDao {

/* 아래 코드 추가 */

  public void save(String filename) {
    try (
        // 1) 바이너리 데이터(바이트배열)를 출력할 도구를 준비한다.
        FileOutputStream out = new FileOutputStream(filename)) {

      // 2) 게시글 개수를 저장 : 4byte
      out.write(BinaryEncoder.write(list.size()));

      // 3) 게시글 출력
      // 목록에서 Board 객체를 꺼내 바이트 배열로 만든 다음 출력한다.
      for (Board b : list) {
        out.write(BinaryEncoder.write(b.getNo()));
        out.write(BinaryEncoder.write(b.getTitle()));
        out.write(BinaryEncoder.write(b.getContent()));
        out.write(BinaryEncoder.write(b.getPassword()));
        out.write(BinaryEncoder.write(b.getViewCount()));
        out.write(BinaryEncoder.write(b.getCreatedDate()));
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void load(String filename) {
    if (list.size() > 0) {  // 중복 로딩 방지!
      return;
    }

    try (
        // 1) 바이너리 데이터를 읽을 도구 준비
        FileInputStream in = new FileInputStream(filename)) {

      // 2) 저장된 게시글 개수를 읽는다: 4byte
      int size = BinaryDecoder.readInt(in);

      // 3) 게시글 개수 만큼 반복해서 데이터를 읽어 Board 객체에 저장한다.
      for (int i = 0; i < size; i++) {
        // 4) 바이너리 데이터를 저장한 순서대로 읽어서 Board 객체에 담는다.
        Board b = new Board();
        b.setNo(BinaryDecoder.readInt(in));
        b.setTitle(BinaryDecoder.readString(in));
        b.setContent(BinaryDecoder.readString(in));
        b.setPassword(BinaryDecoder.readString(in));
        b.setViewCount(BinaryDecoder.readInt(in));
        b.setCreatedDate(BinaryDecoder.readString(in));

        // 5) Board 객체를 목록에 추가한다.
        list.add(b);
      }

      if (list.size() > 0) {
        lastNo = list.get(list.size() - 1).getNo();
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

}
public class TeacherDao {

/* 아래 코드 추가 */

  public void save(String filename) {
    try (FileOutputStream out = new FileOutputStream(filename)) {

      out.write(BinaryEncoder.write(list.size()));

      for (Teacher t : list) {
        out.write(BinaryEncoder.write(t.getNo()));
        out.write(BinaryEncoder.write(t.getName()));
        out.write(BinaryEncoder.write(t.getTel()));
        out.write(BinaryEncoder.write(t.getCreatedDate()));
        out.write(BinaryEncoder.write(t.getEmail()));
        out.write(BinaryEncoder.write(t.getDegree()));
        out.write(BinaryEncoder.write(t.getSchool()));
        out.write(BinaryEncoder.write(t.getMajor()));
        out.write(BinaryEncoder.write(t.getWage()));
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void load(String filename) {
    if (list.size() > 0) {
      return;
    }

    try (FileInputStream in = new FileInputStream(filename)) {

      int size = BinaryDecoder.readInt(in);

      for (int i = 0; i < size; i++) {
        Teacher t = new Teacher();
        t.setNo(BinaryDecoder.readInt(in));
        t.setName(BinaryDecoder.readString(in));
        t.setTel(BinaryDecoder.readString(in));
        t.setCreatedDate(BinaryDecoder.readString(in));
        t.setEmail(BinaryDecoder.readString(in));
        t.setDegree(BinaryDecoder.readInt(in));
        t.setSchool(BinaryDecoder.readString(in));
        t.setMajor(BinaryDecoder.readString(in));
        t.setWage(BinaryDecoder.readInt(in));

        list.add(t);
      }

      if (list.size() > 0) {
        lastNo = list.get(list.size() - 1).getNo();
      }

    } catch (FileNotFoundException e) {
      System.out.println("데이터 파일이 존재하지 않습니다!");

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}
public class StudentDao {

/* 아래 코드 추가 */

  public void save(String filename) {
    try (FileOutputStream out = new FileOutputStream(filename)) {

      out.write(BinaryEncoder.write(list.size()));

      for (Student s : list) {
        out.write(BinaryEncoder.write(s.getNo()));
        out.write(BinaryEncoder.write(s.getName()));
        out.write(BinaryEncoder.write(s.getTel()));
        out.write(BinaryEncoder.write(s.getCreatedDate()));
        out.write(BinaryEncoder.write(s.getPostNo()));
        out.write(BinaryEncoder.write(s.getBasicAddress()));
        out.write(BinaryEncoder.write(s.getDetailAddress()));
        out.write(BinaryEncoder.write(s.isWorking()));
        out.write(BinaryEncoder.write(s.getGender()));
        out.write(BinaryEncoder.write(s.getLevel()));
      }

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  public void load(String filename) {
    if (list.size() > 0) {
      return;
    }

    try (FileInputStream in = new FileInputStream(filename)) {

      int size = BinaryDecoder.readInt(in);

      for (int i = 0; i < size; i++) {
        Student s = new Student();
        s.setNo(BinaryDecoder.readInt(in));
        s.setName(BinaryDecoder.readString(in));
        s.setTel(BinaryDecoder.readString(in));
        s.setCreatedDate(BinaryDecoder.readString(in));
        s.setPostNo(BinaryDecoder.readString(in));
        s.setBasicAddress(BinaryDecoder.readString(in));
        s.setDetailAddress(BinaryDecoder.readString(in));
        s.setWorking(BinaryDecoder.readBoolean(in));
        s.setGender(BinaryDecoder.readChar(in));
        s.setLevel(BinaryDecoder.readByte(in));

        list.add(s);
      }

      if (list.size() > 0) {
        lastNo = list.get(list.size() - 1).getNo();
      }

    } catch (FileNotFoundException e) {
      System.out.println("데이터 파일이 존재하지 않습니다!");

    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

 

util 패키지에 Binary Encoder, Decoder 생성한다.

package bitcamp.util;

public class BinaryEncoder {

  public static byte[] write(byte value) {
    return new byte[] {value};
  }

  public static byte[] write(char value) {
    return new byte[] {
        (byte) (value >> 8),
        (byte) (value)};
  }

  public static byte[] write(boolean value) {
    return new byte[] {(byte) (value ? 1 : 0)};
  }

  public static byte[] write(int value) {
    byte[] bytes = new byte[4];
    bytes[0] = (byte) (value >> 24);
    bytes[1] = (byte) (value >> 16);
    bytes[2] = (byte) (value >> 8);
    bytes[3] = (byte) value;
    return bytes;
  }

  public static byte[] write(String value) {
    // [2byte: 문자열의 바이트 배열 길이][n바이트: 문자열의 바이트 배열]
    byte[] strBytes = value.getBytes();
    byte[] bytes = new byte[strBytes.length + 2];

    System.arraycopy(strBytes, 0, bytes, 2, strBytes.length);
    bytes[0] = (byte) (strBytes.length >> 8);
    bytes[1] = (byte) (strBytes.length);

    return bytes;
  }

  public static void main(String[] args) {
    //    int value = 0xabcdef31;
    //    byte[] bytes = ByteArrayGenerator.write(value);
    byte[] bytes = BinaryEncoder.write("ABC가각간");
    for (int i = 0; i < bytes.length; i++) {
      System.out.printf("%02x", bytes[i]);
    }
  }
}
package bitcamp.util;

import java.io.ByteArrayInputStream;
import java.io.InputStream;

public class BinaryDecoder {

  public static byte readByte(InputStream in) throws Exception {
    return (byte) in.read();
  }

  public static char readChar(InputStream in) throws Exception {
    int value = in.read() << 8;
    value  |= in.read();
    return (char) value;
  }

  public static boolean readBoolean(InputStream in) throws Exception {
    return in.read() == 1 ? true : false;
  }

  public static int readInt(InputStream in) throws Exception {
    int value = 0;
    value  = in.read() << 24;
    value  |= in.read() << 16;
    value  |= in.read() << 8;
    value  |= in.read();
    return value;
  }

  public static String readString(InputStream in) throws Exception {
    // [2byte: 문자열의 바이트 배열 길이][n바이트: 문자열의 바이트 배열]

    // 1) 2바이트를 읽어 문자열의 배열 개수를 알아낸다.
    int length = in.read() << 8;
    length |= in.read();

    // 2) 문자열의 배열을 읽어 들일 빈 배열을 준비한다.
    byte[] bytes = new byte[length];

    // 3) 문자열의 배열을 읽어 빈 배열에 담는다.
    in.read(bytes, 0, length);

    // 4) 배열에 들어 있는 문자 코드를 가지고 String 객체를 생성한다.
    String str = new String(bytes);

    return str;
  }

  public static void main(String[] args) throws Exception {

    // 데이터가 저장된 바이트 배열 준비
    byte[] bytes = new byte[] {0x00, 0x05, (byte)0x41, (byte)0x42, (byte)0xea, (byte)0xb0, (byte)0x80};

    // 바이트 배열에서 데이터를 읽는 도구 준비
    ByteArrayInputStream in = new ByteArrayInputStream(bytes);

    // 바이트 배열을 읽어서 String을 리턴 받는다.
    String str = BinaryDecoder.readString(in);

    // 잘읽었는지 출력해 본다.
    System.out.println(str);

    in.close();
  }
}

 

 

 

String →  byte[]

 

"AB가각".getBytes() 로 8byte 배열을 얻는다. length 를 구하고 byte[ ] bytes = new byte[8 + 2] 로 새 배열을 만든 다음, length 정보를 맨 앞 2byte에 저장한다.

System.arraycopy(strBytes, 0 , bytes, 2, 8); 로 배열 정보 생성한다.

 

 

 

byte[ ] → String

 

byte[ ] 를 String 으로 바꾼다. readString() 을 사용한다. 먼저 cursor 가 위처럼 있다고 가정한다. 2byte를 읽어 4byte인 int에 저장한다. 얻어낸 길이 5byte 정보를 토대로 다음 5byte를 읽어 byte[ ] 에 저장한다. 이를 new String( ) 괄호에 넣는다.

 

 

 

Board ↔ 파일

 

BoardDao 에서 Board 객체를 BinaryEncoder를 사용해 byte[ ] 로 만든다. FileOutputStream 의 write() 사용해 파일로 만든다.

파일을 FileInputStream 사용해 byte[ ] 로 만든다. byte[ ] 를 BinaryDecoder 사용해 Board 객체로 만들어 BoardDao 에 리턴한다.

 

 

 


 

 

조언

 

*SI에서 1억 연봉 뚫으려면 그 당시 트렌드인 신기술을 다뤄야 한다. 하지만 기술 초기에는 연봉 높지만 시간이 지나면 사람들이 많이 진입하므로 연봉이 다시 돌아오게 된다.

*전문가가 되려면 결국 수학을 잘해야한다. 또는 한 기술의 전문가가 되어야한다. 아니면 대량의 데이터를 처리한 경험이 있어야한다.

*객체 지향은 어느 클래스와 어떤 관계를 맺고 있느냐를 파악하는 것이 중요하다.

 

 

 

 


 

과제

 

저녁 학습

- com/eomcs/io/ex01~02

 

파일 입출력 완성

- BoardDao 참고하여 StudentDao, TeacherDao 완성하라