신세게 - Java 공부

9주차 배운점 느낀점 - 입출력스트림, 보조스트림, 직렬화 역직렬화

휘로그 2024. 10. 7. 21:03
728x90
반응형

9주차

 

지금까지 데이터베이스를 수업한 이유는 정보를 만들기 위해서였다.
데이터베이스로부터 끌어와서 자바어플리케이션으로 불러와야한다.
입출력 스트림, 보조 스트림에 대해 배우고 실습을 했다.

 

입출력스트림을 선택에따라 바이트와 문자로 처리하게되는데 바이트 최상의 클래스 inputstream, outputstream, 문자의 최상위 클래스 reader, writer 기억하고 있으라고하셨다.

 

배운점

 

입력 스트림과 출력 스트림

프로그램을 기준으로 데이터가 들어오면 입력 스트림, 데이터가 나가면 출력 스트림
프로그램이 다른 프로그램과 데이터를 교환하려면 양쪽 모두 입력 스트림과 출력 스트림이 필요

 

 

포트를통해 데이터가 입출력된다! 우리는 포트번호 3306을통해 입출력할 것
바이트 스트림: 그림, 멀티미디어, 문자 등 모든 종류의 데이터를 입출력할 때 사용
문자 스트림: 문자만 입출력할 때 사용, 웹에서는 utf-8 표준으로 사용

 

자바는 데이터 입출력과 관련된 라이브러리를 java.io 패키지에서 제공

 

 

바이트 입출력 스트림의 최상위 클래스는 InputStream과 OutputStream
문자 입출력 스트림의 최상위 클래스는 Reader와 Writer

 

 

OutputStream

 

OutputStream은 바이트 출력 스트림의 최상위 클래스로 추상 클래스
모든 바이트 출력 스트림 클래스는 이 OutputStream 클래스를 상속받아서 만들어짐

 

 

OutputStream 클래스에는 모든 바이트 출력 스트림이 기본적으로 가져야 할 메소드가 정의됨

 

 

1 바이트 출력

 

write(int b) 메소드: 매개값 int(4byte)에서 끝 1byte만 출력. 매개변수는 int 타입

 

 

바이트 배열 출력

 

write(byte[ ] b) 메소드: 매개값으로 주어진 배열의 모든 바이트를 출력
배열의 일부분을 출력하려면 write(byte[ ] b, int off, int len) 메소드를 사용
배열, offset(시작값), 길이 사용

 

 

InputStream

 

InputStream은 바이트 입력 스트림의 최상위 클래스로, 추상 클래스
모든 바이트 입력 스트림은 InputStream 클래스를 상속받아 만들어짐

 

InputStream 클래스에는 바이트 입력 스트림이 기본적으로 가져야 할 메소드가 정의됨
출력이 아니니 flush 가 없다.

 

 

1 바이트 입력

 

read() 메소드: 입력 스트림으로부터 1byte를 읽고 int(4byte) 타입으로 리턴. 리턴된 4byte 중 끝
1byte에만 데이터가 들어 있음

 

 

더 이상 입력 스트림으로부터 바이트를 읽을 수 없다면 read() 메소드는 -1을 리턴. 읽을 수 있는 마지막 바이트까지 반복해서 한 바이트씩 읽을 수 있음

 

 

c://temp/test1.text 파일에 10 20 30 데이터를 쓰자

 

try catch 항상필요함, 외부에 서 인터럽트 발생할 수 있기 때문

 

public static void main(String[] args) {
    try{
      OutputStream os = new FileOutputStream("C:/Temp/test1.text");
      byte a = 10;
      byte b = 20;
      byte c = 30;

      os.write(a);
      os.write(b);
      os.write(c);

      os.flush();
      os.close();

    }catch(IOException io){
      io.printStackTrace();
    }
  }

결과:

 

 

출력스트림

public static void main(String[] args) {
    //trty catch 항상필요함, 외부에 서 인터럽트 발생할 수 있기 때문
    try{
      OutputStream os = new FileOutputStream("C:/Temp/test1.text");
      byte[] a = {10,20,30,40,50};
      os.write(a);
      os.flush();
      os.close();


    }catch(IOException io){
      io.printStackTrace();
    }
  }

 

지정된 경로에 파일이 새로 만들어 졌다. byte로 써서 읽을 수는 없다.

 

인풋스트림

public static void main(String[] args) {
    try{
      InputStream is = new FileInputStream("C:/Temp/test1.text");
      while(true){
        int data = is.read(); // 1바이트씩 읽어오겠다.
        if(data == -1 )break; // EOF 파일의 끝(-1)
        System.out.println(data);
      }
      is.close();
    }catch (FileNotFoundException e){  // 파일없으면 잡아낼 필요없다.
      e.printStackTrace();
    }
    catch(IOException io){
      io.printStackTrace();
    }
  }

 

결과: 복원시켜서 출력됨

 

파일 복사 원리
그림파일을 같은 위치에 복사한다.

public class FileCopyExample {

    public static void main(String[] args) throws IOException {
        // 소스 파일 경로
        String originFileName = "C:/Temp/dandi.png";

        // 복사한 파일을 저장할 대상 파일 경로
        String targetFileName = "C:/Temp/dandi1.png";

        // 소스 파일에서 읽기 위한 InputStream 생성
        InputStream is = new FileInputStream(originFileName);

        // 대상 파일에 쓰기 위한 OutputStream 생성
        OutputStream os = new FileOutputStream(targetFileName);

        // 소스 파일에서 데이터를 읽어오기 위한 버퍼
        byte[] data = new byte[1024];

        // 소스 파일에서 데이터를 읽어와 대상 파일에 쓰기
        while (true) {
            int num = is.read(data); // 데이터를 버퍼에 읽어옴
            if (num == -1) break;    // 파일의 끝에 도달하면 루프를 종료
            os.write(data, 0, num);  // 버퍼에서 읽어온 데이터를 대상 파일에 씀
        }

        // OutputStream을 플러시하고 자원을 해제하기 위해 닫음
        os.flush();
        os.close();

        // InputStream을 닫아 자원을 해제
        is.close();
    }
}

 

결과: 카피가 되어 있따.

 

여기서 byte배열 생성과, while문 부분을 한 메소드로 대체가 가능하다.
java 9 버전부터 추가된 메소드

//    byte[] data = new byte[1024];
//    while(true) {
//      int num = is.read(data);
//      if(num == -1) break;
//      os.write(data,0,num);
//    }

    is.transferTo(os);

 

문자 출력

Writer는 문자 출력 스트림의 최상위 클래스로, 추상 클래스. 모든 문자 출력 스트림 클래스는 Writer 클래스를 상속받아서 만들어짐

 

Writer 클래스에는 모든 문자 출력 스트림이
기본적으로 가져야 할 메소드가 정의됨

 

Reader

 

Reader는 문자 입력 스트림의 최상위 클래스로, 추상 클래스
모든 문자 입력 스트림 클래스는 Reader 클래스를 상속받아서 만들어짐

 

Reader 클래스에는 문자 입력 스트림이 기본적으로 가져야 할 메소드가 정의됨

 

Writer

public static void main(String[] args) throws IOException {
    Writer writer = new FileWriter("C:/Temp/test3.text");
    char a = 'A';
    writer.write(a);
    char b = 'B';
    writer.write(b);

    char[] c = {'서','유','미'};
    writer.write(c);

    String str = "신세계 자바 과정";
    writer.write(str);

    writer.flush();
    writer.close();
  }

 

결과:

reader
버퍼에 담아서 빠르게 출력한다.

 public static void main(String[] args) throws IOException {
        // 파일에서 문자를 읽기 위한 Reader 생성
        Reader reader = new FileReader("C:/Temp/test3.text");

        char[] data = new char[100];  // 읽은 문자를 임시로 저장할 버퍼

        // 파일에서 문자를 읽어와 배열에 저장하고 출력
        while (true) {
            int n = reader.read(data);  // 읽은 문자를 배열에 저장하고 읽은 문자의 수를 반환
            if (n == -1) break;  // 파일의 끝에 도달하면 루프를 종료

            for (int i = 0; i < n; i++) {
                System.out.println(data[i]);  // 배열에 저장된 문자를 출력
            }
        }

        // Reader를 닫아 자원을 해제
        reader.close();
    }

 

1문자씩 읽어 들이기

//1문자씩 읽어들이기
    Reader reader1 = new FileReader("C:/Temp/test3.text");


    while(true){
      int n = reader1.read();  //읽은 문자를 배열에 저장해서 읽은 문자를 리턴하겠다.
      if(n == -1 ) break;
      System.out.println((char)n);
    }

    reader.close();

 

보조 스트림

 

다른 스트림과 연결되어 여러 편리한 기능을 제공해주는 스트림. 자체적으로 입출력을 수행할 수
없기 때문에 입출력 소스로부터 직접 생성된 입출력 스트림에 연결해서 사용

 

 

입출력 스트림에 보조 스트림을 연결하려면 보조 스트림을 생성할 때 생성자 매개값으로 입출력
스트림을 제공
보조스트림 변수 = new 보조스트림(입출력스트림);

 

보조 스트림은 또 다른 보조 스트림과 연결되어 스트림 체인으로 구성할 수 있음

 

보조스트림2 변수 = new 보조스트림2(보조 스트림1);

 

BufferedReader

public static void main(String[] args) throws IOException {
        // 파일에서 한 줄씩 읽기 위한 BufferedReader 생성
        BufferedReader br = new BufferedReader(new FileReader("C:/temp/test3.text"));
        String str;

        // 파일에서 한 줄씩 읽어와 출력
        while (true) {
            str = br.readLine();  // 한 줄을 읽어와 변수에 저장
            if (str == null) break;  // 파일의 끝에 도달하면 루프를 종료
            System.out.println(str);  // 읽은 한 줄을 출력
        }

        // BufferedReader를 닫아 자원을 해제
        br.close();
    }

 

BufferedWriter ,reader
메모장에서 save, read

public static void main(String[] args) throws IOException {
        // BufferedWriter를 사용하여 파일에 문자열을 쓰기 위한 객체 생성
        BufferedWriter bw = new BufferedWriter(new FileWriter("test.txt"));

        // 쓸 문자열 생성
        String str1 = "text 생성";
        String str2 = "text 생성";

        // 파일에 문자열 쓰기
        bw.write(str1, 0, str1.length());  // 첫 번째 문자열 쓰기
        bw.newLine();  // 새로운 라인 추가
        bw.write(str2, 0, str2.length());  // 두 번째 문자열 쓰기
        bw.flush();  // 버퍼 비우기

        // BufferedReader를 사용하여 파일에서 문자열 읽기 위한 객체 생성
        BufferedReader br = new BufferedReader(new FileReader("test.txt"));

        String str;

        // 파일에서 한 줄씩 읽어와 출력
        while (true) {
            str = br.readLine();  // 한 줄을 읽어와 변수에 저장
            if (str == null) break;  // 파일의 끝에 도달하면 루프를 종료
            System.out.println(str);  // 읽은 한 줄을 출력
        }

        // BufferedWriter와 BufferedReader를 닫아 자원을 해제
        bw.close();
        br.close();
    }

 

UTF-8 문자셋으로 문자열을 파일에 저장하고, 파일에 저장된 문자를 다시 읽어서 콘솔에 출력하는 프로그램

public static void main(String[] args) throws Exception {
    write("너에게 나를 보낸다.");
  }

  public static void write(String str) throws Exception{
    String fileName = "data.txt";
    OutputStream os = new FileOutputStream("data.txt");
    Writer writer = new OutputStreamWriter(os,"UTF-8"); // 사람이 볼 수 있는 utf-8로 변경하라
    writer.write(str);
    writer.flush();
    writer.close();

    read(fileName);  // read호출
  }

  public static void read(String fileName) throws Exception{
    InputStream is = new FileInputStream(fileName);
    Reader reader = new InputStreamReader(is,"UTF-8");
    char[] data = new char[100];  // 임시 버퍼
    int num = reader.read(data); // read
    reader.close();
    
    String str = new String(data);
    System.out.println(str);
  }

 

Reader를 BufferedReader로 변경
bufferedReader는 reader.readLine-> String을 반환

 

public static void read1(String fileName) throws Exception{
    FileReader is = new FileReader(fileName);
    BufferedReader reader = new BufferedReader(is);
    String data = reader.readLine();
    reader.close();

    System.out.println(data);
  }

 

기본 타입 스트림

 

바이트 스트림에 DataInputStream과 DataOutputStream 보조 스트림을 연결하면 기본 타입(boolean, char, short, int, long, float, double) 값을 입출력할 수 있음

 

PrintStream과 PrintWriter

 

프린터와 유사하게 출력하는 print(), println(), printf() 메소드를 가진 보조 스트림

 

 

PrintStream은 바이트 출력 스트림과 연결되고, PrintWriter는 문자 출력 스트림과 연결

 

파일 출력도 시키고 print이용해서 출력도 시킬 수 있음

public static void main(String[] args) throws Exception{
    //printf() : format string 형식화된 문자열을 출력
    //FileOutputStream에 보조 PrintStream을 연결하여 print(), println(), printf()로 문자열 출력

    FileOutputStream fs = new FileOutputStream("printStream.txt"); //프로젝트안에 만들어짐
    PrintStream ps = new PrintStream(fs);
    ps.print("hi!");
    ps.println("오늘은 강사님이 시키는게 너무 많아서 힘들어");
    ps.printf("%d 빨리 되었으면 좋겠어",6);
    ps.flush();
    ps.close();

  }

 

직렬화와 역직렬화

 

직렬화: 메모리에 생성된 객체를 파일 또는 네트워크로 출력하기 위해 필드값을 일렬로 늘어선 바이트로 변경하는 것
역직렬화: 직렬화된 바이트를 객체의 필드값으로 복원하는 것.
ObjectOutputStream은 바이트 출력 스트림과 연결되어 객체를 직렬화하고, ObjectInputStream은 바이트 입력 스트림과 연결되어 객체로 복원하는 역직렬화

바이트로 보내면 인풋스트림으로 받고 복원

 

Serializable 인터페이스

 

멤버가 없는 빈 인터페이스이지만, 객체를 직렬화할 수 있다고 표시하는 역할
인스턴스 필드값은 직렬화 대상. 정적 필드값과 transient 로 선언된 필드값은 직렬화에서 제외되므로 출력되지 않음

 

serialVersionUID 필드

 

직렬화할 때 사용된 클래스와 역직렬화할 때 사용된 클래스는 동일한 클래스여야 함
클래스 내용이 다르더라도 두 클래스가 동일한 serialVersionUID 상수값을 가지면 역직렬화 가능

public class Member implements Serializable { // 이거 구현한객체만 직렬화 가능
  private static final long serialVersionUID = 10000000L;
  String name;
  Integer age;

  Member(String name, Integer age){
    this.name = name;
    this.age = age;
  }
}
public class ObjectInputStreamEx {
  public static void main(String[] args) throws Exception{
    FileOutputStream fos = new FileOutputStream("object.dat");
    ObjectOutputStream oos = new ObjectOutputStream(fos); // 직렬화해서 object.dat에 쓰겠다.

    // 객체생성
    Member member = new Member("박진영",20);
    int[] arry = {1,2,3};

    //객체를 역직렬화해서 파일에 저장
    oos.writeObject(member);
    oos.writeObject(arry);

    oos.flush();
    oos.close();
    fos.close();

    FileInputStream fis = new FileInputStream("object.dat");
    ObjectInputStream ois = new ObjectInputStream(fis);

    Member m1 = (Member)ois.readObject();
    int[] arr2 = (int[]) ois.readObject();

    ois.close();
    fis.close(); // 바깥꺼서부터 차례차례 닫기

    System.out.println(m1.name);
    System.out.println(m1.age);
    System.out.println(Arrays.toString(arr2));
  }
}

 

자바는 시리얼라이즈 인터페이스를 구현한 객체만 직렬화 가능
static 필드값은 직렬화 대상 제외, transient 키워드 작성하면 직렬화에서 제외
필드 갯수가 달라도 uid가 같으면 같은 객체로 판단하고 직렬화 가능

 

private static final long serialVersionUID = 10000000L;
String name;
transient Integer age;

 

값에서 제외 됐기 때문에 null로 뜸

 

 

 

회고

 

코테 문제를 풀때 BufferedReader을 사용했었다. 그 안에 들어가는 new FileReader의 의미를 몰랐었다.
입력스트림이 존재하고, 보조스트림을 사용하기위해 매개변수로 받아 사용한다.

오늘 블로그관리에 대해 말씀해 주셨다. 이렇게 공부 내용 정리하는건 크게 의미가 없다.


내가 구현한 간단한 프로젝트에 대해 목표와 배경을 적고 요약한내용을 적으라고 하셨다.
클래스 다이어그램, 유스케이스, 플로우 차트 를 쓰고 코드는 올릴필요없이 깃허브 링크만 필요하다. 이때까지한 과제들, 워크숍을 2월까지 정리해볼 예정이다.

반응형