Coding/Java

Java I/O Stream

heyoon2j 2020. 10. 8. 23:58

■ Java I/O Stream

* Decorator Pattern으로 구현되어 있다.

* Java의 I/O 방식은 Node - Stream

* Node : 데이터의 소스 또는 데이터의 목적지

  - Node는 키보드(입력), 모니터(출력), 파일(입출력) 등을 말한다.

* Stream : Node로부터 데이터를 주고받는 통로

  - 입력으로 사용되는 스트림과 출력으로 사용되는 스트림은 별개

  - 입출력을 함께하는 것을 채널(Channel) : NIO(New Input/Output)


• I/O Stream 구분

* I/O 대상 기준 : Input Stream, Output Stream

* 자료의 종류 : Byte 단위 Stream, 문자 단위 Stream

* Stream 기능 : 기반 Stream, 보조 Stream


• 표준 입출력

* System 클래스의 표준 입출력 멤버

public class System{
    public static PrintStream out;
    public static InputStream in;
    public static PrintStream err;
}

• 기본 Stream 종류

1. Byte 단위 Stream

* Byte 단위로 자료를 읽고 씀(동영상, 음악 파일 등)

* InputStream : 입력 스트림

* OutputStream : 출력 스트림

 

* Method(Input)

메소드 설명
int read() byte 하나를 읽어서 int로 반환. 읽을 값이 없으면 -1 반환
int read(byte[] b) 데이터를 읽어 b에 저장하고, 읽은 바이트 수를 반환
int read(byte[] b, int offset, int len) 최대 len개의 바이트를 읽어 b의 offset 위치부터 채운다.
void close() 스트림을 종료하고 자원을 반납
int available() 읽을 수 있는 데이터의 크기를 반환
long skip(long n) 스트림에서 n 만큼 건너 뛴다.
void mark(int readLimit) reset()으로 돌아갈 위치를 표시한다. readLimit은 돌릴 수 있는 최대 바이트 수
void reset() mark()가 호출된 지점으로 돌아간다.
boolean markSupported() mark, reset 메소드의 지원 여부를 반환

 

* Method(Output)

메소드 설명
void write(int b) b의 끝 1byte를 출력 버퍼에 보낸다.
void write(byte[] b) 배열 b의 모든 Byte를 보낸다.
void flush() 출력 버퍼를 비운다.
void close() 스트림 종료. 내부적으로 flush() 호출한다.

 

* Example

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

        System.out.println("입력 ");
        try {
            int i;
            InputStreamReader isr = new InputStreamReader(System.in);
            while((i = isr.read()) != '끝') {
                System.out.print((char) i);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
} 

2. 문자 단위 Stream

* Character(2 Btye) 단위로 자료를 읽고 씀

* Reader : 입력 스트림

* Writer : 출력 스트림

 

* Method(Input)

메소드 설명
int read() char 하나를 읽어서 int로 반환. 읽을 값이 없으면 -1 반환
int read(char[] cbuf) 데이터를 읽어 cbuf에 저장하고, 읽은 char 수를 반환
int read(char[] cbuf, int offset, int len) 최대 len개의 char를 읽어 cbuf의 offset 위치부터 채운다.
int read(java.nio.CharBuffer target)
NIO target에 데이터를 저장한다.
void close() 스트림을 종료하고 자원을 반납
int available() 읽을 수 있는 데이터의 크기를 반환
long skip(long n) 스트림에서 n 만큼 건너 뛴다.
void mark(int readLimit) reset()으로 돌아갈 위치를 표시한다. readLimit은 돌릴 수 있는 최대 바이트 수
void reset() mark()가 호출된 지점으로 돌아간다.
boolean markSupported() mark, reset 메소드의 지원 여부를 반환

 

* Method(Output)

메소드 설명
void write(int c) c 내용을 char로 출력
void write(char[] cbuf) 배열 cbuf의 모든 char를 보낸다.
void write(char[] cbuf, int off, int len) cbuf의 off부터 (off + len - 1) 만큼을 문자열로 변환하여 출력
void write(String str) str을 출력한다.
void write(String str, int off, int len) str의 off부터 (off + len - 1) 만큼을 출력
Writer append(CharSequence csq) csq를 출력하고 Writer 반환
Writer append(CharSequence csq, int start, int end) csq의 start부터 end까지를 출력하고 Writer 반환
Writer append(char c) c를 출력하고 Writer 반환
void flush() 출력 버퍼를 비운다.
void close() 스트림 종료. 내부적으로 flush() 호출한다.

• File Stream

1. FileInputStrea, FileOutputStream

* 파일명이나 File 객체를 이용하여 입출력 스트림 생성 가능

* FileOutputStream에서 boolean append를 true로 하면 기존 파일에 이어서 쓴다.


2. FileReader, FileWriter

* 파일명이나 File 객체를 이용하여 입출력 Reader 생성 가능

* FileWriter에서 boolean append를 true로 하면 기존 파일에 이어서 쓴다.

 

* Example

public class ReaderWriterStream {
    public static void main(String[] args) throws IOException {
        FileReader fis = new FileReader("src/reader.txt");
//        FileInputStream fis = new FileInputStream("src/reader.txt");
//        InputStreamReader isr = new InputStreamReader(fis);
        int i;
        while((i = fis.read())!= -1){
            System.out.println((char)i);
        }
        fis.close();
    }
}

3. File Constructor & Method

생성자 및 메소드 설명
File(String pathname) pathname에 해당하는 파일 생성. 기본 경로는 애플리케이션의 시작 경로
File(String parent, String child) parent 경로 아래 child 파일 생성
File(File parent, String child) parent 경로 아래 child 파일 생성
File(URI uri) file로 시작하는 URI 객체를 이용해 파일 생성
boolean createNewFile() 새로운 파일을 생성
boolean mkdir() 새로운 디렉토리를 생성
boolean mkdirs() 경로상의 모든 디렉토리를 생성
boolean delete() 파일/디렉토리 삭제
void deleteOnExit() 애플리케이션 종료시 자동으로 삭제
boolean exist() 해당 파일이 존재하는지 여부

 

* RandomAccessFile Class

  - 입출력 클래스 중 유일하게 파일 입출력을 동시에 할 수 있는 클래스

  - 파일 포인터가 있어서 읽고 쓰는 위치의 이동이 가능하다.

 

* Example

// File.separator를 이용하여 "\" 표현할 수 있다.
String filePath = "D:" + File.separator + "Temp" + File.separator + "MyTemp";

File fileOne = new File(filePath);
fileOne.mkdir();

File fileTwo = new File(filePath, "file2.txt");
fileTwo.createNewFile();

File fileThree = new File(fileOne, "file3.txt");
fileThree.createNewFile();

File fileFour = new File(new URI("file:///d:/Temp/MyTemp/file4.txt"));
fileFour.createNewFile();
fileFour.deleteOnExit();

 

4. 그 외 File 클래스의 주요 메소드

메소드 설명
getName() 파일 이름 반환
getPath() 입력된 경로 반환
isAbsolute() 절대 경로를 사용하는지? 여부 확인
getAbsolutePath() 절대 경로 반환
getCanonicalPath() 상위 경로 기호("..")가 없어진 절대 경로 반환
isDirectory() 해당 File이 Directory인지? 여부 확인
isFile() 해당 File이 File인지? 여부 확인
list() 해당 Path의 리스트 반환, String Array로 출력
listFiles() 해당 Path의 리스트 반환, File 객체 Array로 출력

• 보조 Stream

* 실제로 읽고 쓰는 Stream이 아닌 보조적인 기능을 추가하는 Stream

* FilterInputStream과 FilterOutputStream이 보조 Stream의 상위

  - protected FilterInputStream(InputStream in)

  - public FilterOutputStream(OutputStream out)

 

1. Byte -> char Stream

* Byte를 문자 기반으로 변환시킨다.

  - InputStreamReader(InputStream is)

  - OutputStreamWriter(OutputStream os)

 

* 스트림 자료형 변경

  - Character Set : utf-8, ms949, euc-kr

InputStreamReader readerOne = new InputStreamReader(System.in);
InputStreamReader readerTwo = new InputStreamReader(System.in, "utf-8");

OutputStreamWriter writerOne = new OutputStreamWriter(System.out);
OutputStreamWriter writerTwo = new OutputStreamWriter(System.out, "ms949");

2. Buffered Stream

* 내부에 8192byte 배열을 가지고 있어, 읽거나 쓸 때의 속도를 향상(Throughput 향상)

  - throughtput : 평균 전송량 / 파일의 입출력 등 한 번에 많은 양을 보내는 것이 효율적일 때 신경 쓴다.

  - delay : 지연 시간 / 게임, 반응성이 중요한 경우, 버퍼링을 쓰게 되면 오히려 안 좋을 수 있다.

  - BufferedInputStream(InputStream is)

  - BufferedOutputStream(OutputStream os)

  - BufferedReader

  - BufferedWriter

String readLine() : Line Feed(\n)와 Carriage Return(\n)을 제거해주고 한 줄을 읽어 들인다.

File src = new File("./src.txt");
try (BufferedReader buffReader = new BufferedReader(new FileReader(src));) {
  String line = null;
  while ((line = br.readLine()) != null) {
    System.out.println(line);
  }
} catch (IOException e) {
  e.printStackTrace();
}

3. Data Stream

* 자료가 저장된 상태 그대로 자료형을 유지하며 읽기, 쓰기 기능을 제공하는 Stream

  - DataInputStream

  - DataOutputStream

  - readByte(), readShort(), writeInt(), writeUTF() 등 메서드가 있다.

File src = new File("c:/Temp/data.dat");
try (DataOutputStream out = new DataOutputStream(new FileOutputStream(src))) {
  out.writeUTF("UTF-8");
  out.writeInt(15);
  out.writeFloat(14.23);
}

try (DataInputStream in = new DataInputStream(new FileInputStream(src))) {
  String string = in.readUTF();
  int integer = in.readInt();
  float floatNum = in.readFloat();
  System.out.println(string + " " + integer + " " + floatNum);
}

4. Print Stream

* 문자열 표현 Stream

  - PrintReader

  - PrintWriter

public class SubStream{
    public static void main(String[] args){
        try(FileOutputStream fos = new FileOutputStream("data.txt");
                DataOutputStream dos = new DataOutputStream(fos);
                FileInputStream fis = new FileInputStream("data.txt");
                DataInputStream dis = new DataInputStream(fis)){

            dos.writeByte(100);
            dos.write(100);
            dos.writeChar('A');
            dos.writeUTF("안녕하세요");

            System.out.println(dis.readByte());
            System.out.println(dis.read());
            System.out.println(dis.readChar());
            System.out.println(dis.readUTF);

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

5. 직렬화(Serialization

* 인스턴스의 상태를 그대로 저장하거나 Network로 전송하고, 이를 다시 복원(Deserialization)하는 방식

* 보조 Stream으로 Object Stream 사용

  - ObjectInputStream

  - ObjectOutputStream

  - readObject(), writeObject() 메소드 이용

* 직렬화를 위해서는 해당 클래스에 Serializable Interface를 사용하여 명시해야 한다.

* HAS-A 관계인 경우, 관계 Class들 모두 Serialization 해야 한다.

class Foo implements Serializable { // has-a 관계의 모든 클래스가 Serializable이어야 함
  static final long serialVersionUID = 1L; // 객체의 버전 관리
  // 클래스 버전을 따로 관리하는 이유는 클래스 내용 자체가 바뀔 수 있기 때문에
  // 객체를 저장할 때와 불러올 때 같은지 체크하여
  // serialVersionUID가 일치하지 않으면 실패

  String userName;
  int id;

  transient String passWord; // transient 키워드, 해당 코드는 직렬화하지 말라는 의미
  // Password는 중요 정보이기 때문에 포함시키지 않기 위해서

  @Override
  public String toString() {
    return userName + " " + id + " " + passWord;
  }
}

class FooTest {
  public static void main (String [] args) {
    File dst = new File("C:/Temp/obj.data");
    Foo foo = new Foo();
    foo.userName = "yoon";
    foo.id = 142;
    foo.passWord = "qwer1234";

    try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(dst));
        ObjectInputStream in = new ObjectInputStream(new FileInputStream(dst));) {
      out.writeObject(foo);
      Object read = in.readobject();
      if (read != null && read instanceof Foo) {
        Foo readFoo = (Foo)read;
        System.out.println(readFoo);
      }
    } catch (IOException | ClassNotFoundException e) {
      e.printStackTrace();
    }
  }
}

 

* 부모 클래스가 Serializable이 아닌 경우 자식 클래스에서 직접 처리해야 한다.

  - readObject(), writeObject()를 자식 클래스에서 직접 구현

class ParentFoo {
  int memVarOne;
  double memVarTwo;
}

class ChildFoo extends ParentFoo implements Serializable {
  @Override
  private void writeObject(ObjectOutputStream out) throws IOException {
    out.writeInt(memVarOne);
    out.writeDouble(memVarTwo);
    out.defaultWriteObject();
  }
  
  @Override
  private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
    memVarOne = in.readInt();
    memVarTwo = in.readDouble();
    in.defaultReadObject();
  }
}

 

Externalizable Interface를 이용하여 읽기, 쓰기에 대해 정의할 수 있다.

  - writeExternal, readExternal 함수 Override

'Coding > Java' 카테고리의 다른 글

Java Wrapper Class  (0) 2020.10.12
Java Collection Framework  (0) 2020.10.08
Java 코드 작성 습관  (0) 2020.10.08
Java Code Convention(Google Java Style 정리)  (0) 2020.10.07
java.lang 패키지  (0) 2020.09.29