입출력스트림이란?

  네트워크에서 자료의 흐름이 물과 같다는 의미에서 유래했다.

  다양한 입출력 장치에 독립적으로 일관성 있는 입출력방식을 제공한다.

  입출력이 구현되는 곳에서는 모두 I/O 스트림을 사용한다.(키보드, 파일디스크, 메모리등)

 

입출력 스트림의 구분

  I/O 대상 기준 : 입력 스트림, 출력 스트림

  자료의 종류 : 바이트 스트림, 문자 스트림

  스트림의 기능 : 기반 스트림, 보조 스트림

 

  베이스가 되는 기반스트림이냐 아니면 기능을 보조해주는 보조스트림이냐로 나눌 수 있다.

  자바 IO는 데코레이트패턴(Decorate Pattern)으로 구현되어 있는데 보조스트림은 읽거나 쓰는 기능은 없다.

  기반 스트림을 서포트 해주는 기능을 추가하는 것이다.

  바이트를 문자로 바꾼다거나 버퍼링 기능을 제공한다거나 직렬화 같은 것이 보조스트림에서 제공된다.

 

입출력 스트림

  입력스트림은 대상으로부터 자료를 읽어들이는 스트림이고 출력스트림은 대상으로 자료를 출력하는 스트림이다.

종류

예시

입력스트림

FileInputStream, FileReader, BufferedInputStream, BufferedReader

출력스트림

FileOutputStream, FileWriter, BufferedOutputStream, BufferedWriter

 

  바이트 단위 스트림은 바이트 단위로 자료를 읽고 쓴다.(동영상, 음악파일 등)

  문자단위 스트림은 2바이트씩 문자를 처리한다.

종류

예시

바이트스트림

FileInputStream, FileOutputStream, bufferedInputStream, BufferedOutputStream

문자스트림

FileReader, FileWriter, BufferedReader, BufferedWriter

 

  기반스트림은 대상에 직접 자룔를 읽고 쓰는 기능의 스트림이다.

  보조스트림은 직접 읽고 쓰는 기능은 없고 추가적인 기능을 제공해주는 스트림이다.

  기반스트림이나 또다른 보조 스트림을 생성자의 매개변수로 포함한다.

종류

예시

기반스트림

FileInputStream, FileOutputStream, FileReader, FileWriter

보조스트림

InputStreamReader, OutputStreamWriter, BufferedInputStream, BufferedOutputStream

 

 

표준 입출력

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

public class System {
  public static PrintStream out; //표준 출력 스트림
  public static InputStream in;  //표준 입력 스트림
  public static PrintStream err; //표준 에러 스트림
}

  System.in을 사용하여 입력받기

    System.in을 사용하여 입력받으면 한바이트씩 읽어들이며 한글과 같은 여러 바이트로 된 문자를 읽기 위해서는

    InputStreamReader와 같은 보조스트림을 사용해야 한다.

 

//System.in Test

import java.io.IOException;
//모든 IO는 Exception 처리를 해야한다.

public class SystemInTest {
  
  public static void main(String[] args) {
    System.out.println("입력 : ");
    
    try {
      int i = System.in.read();
      System.out.println(i);
      System.out.println((char)i);
    }catch(IOException e) {
      e.printStackTrace();
    }
  }
}
/*
  a라고 입력하면
  97
  a
  라고 출력된다.
  i는 int형이므로 아스키코드값으로 97이 출력된다.
*/
//System.in Test2

import java.io.IOException;
import java.io.InputStreamReader;

public class SystemInTest2 {
  
  public static void main(String[] args) {
    System.out.println("입력 : ");
    
    /*
      try {
        int i;
        while((i = System.int.read()) != '\n') {
          System.out.println((char)i);
        }
      }catch (IOException e) {
        e.printStackTrace();
      }
      
      엔터를 누르기 전까지 입력한 모든 문자를 출력하게 한다.
      
      주석해제하면 정상 동작하는 코드다.
    */
    
    System.out.println("입력 후 끝이라고 쓰세요 : ");
    
    /*
      try {
        int i;
        while((i = System.in.read()) != '끝') {
          System.out.println((char)i);
        }
      }catch (IOException e) {
        e.printStackTrace();
      }
      
      끝이라는 문자를 인식하지 못한다.
      한글은 2바이트고 i는 int지만 1바이트씩만 읽기 때문에
      끝이라는 문자를 인식할 수 없다.
      in은 inputStream이기 때문에 1바이트씩밖에 읽지못하기 때문에
      여기서는 int가 1바이트씩만 인식하게 된다.
    */
    
    try {
      int i;
      InputStreamReader isr = new InputStreamReadert(System.in);
      while((i = isr.read()) != '끝') {
        System.out.println((char)i);
      }
    }catch (IOException e) {
      e.printStackTrace();
    }
    /*
      보조스트림인 InputStreamReader를 사용해서 멀티바이트를 인식할 수 있도록 해야한다.
      InputStreamReader는 읽어들인 것을 문자로 반환한다.
      항상 다른 스트림을 생성자의 매개변수로 받는다.
      InputStreamReader에서 System.in을 매개변수로 받았기 때문에
      while에서는 보조스트림으로 읽으면 된다.
      
      ajskdfjlasd 끝 이렇게 입력하면
      ajskdfjlasd 이렇게 출력된다.
      
      바이트단위로 자료를 읽을 때 그 자료를 문자로 변환해주는 것이
      InputStreamReader다.
    */

 

  Scanner 클래스

    java.util 패키지에 있는 클래스다.

    문자뿐 아니라 정수, 실수 등 다양한 자료형을 읽을 수 있다.

    생성자가 다양하여 여러 소스로부터 자료를 읽을 수 있다.

생성자

설명

Scanner(File source)

파일을 매개변수로 받아 Scanner를 생성한다.

Scanner(InputStream source)

바이트 스트림을 매개변수로 받아 Scanner를 생성한다.

Scanner(String source)

String을 매개변수로 받아 Scanner를 생성한다.

 

  Console 클래스

    System.in을 사용하지 않고 콘솔에서 표준 입출력이 가능하다.

    이클립스와는 연동되지 않는다.

    Console 클래스의 메서드

메서드

설명

String readLine()

문자열을 읽는다.

char[] readPassword()

사용자에게 문자열을 보여주지 않고 읽는다

Reader reader()

Reader 클래스를 반환

PrintWriter writer()

PrintWriter 클래스를 반환

 

바이트단위 입출력 스트림

  InputStream은 바이트 단위 입력 스트림의 최상위 클래스다.

  OutputStream은 바이트 단위 출력 스트림의 최상위 클래스다.

 

  추상 메서드를 포함한 추상클래스로 하위 클래스가 구현하여 사용한다.

 

  주요 하위 클래스

스트림 클래스

설명

FileInputStream

파일에서 바이트 단위로 자료를 읽는다.

ByteArrayInputStream

Byte 배열 메모리에서 바이트 단위로 자료를 읽는다.

FilterInputStream

기반 스트림에서 자료를 읽을 때 추가 기능을 제공하는 보조스트림의 상위클래스다.

FileOutputStream

바이트 단위로 파일에 자료를 쓴다.

ByteArrayOutputStream

Byte배열에 바이트 단위로 자료를 쓴다.

FilterOutputStream

기반 스트림에서 자료를 쓸 때 추가기능을 제공하는 보조스트림의 상위 클래스다.

 

  FileInputStream과 FileOutputStream 사용하기

    파일에 한 바이트씩 자료를 읽고 쓰는데 사용한다.

    입력스트림은 파일이 없는 경우에 예외가 발생하고 출력스트림은 파일이 없는 경우 파일을 생성한 뒤 출력한다.

   

    예제코드

//File Input Test

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputTest {
  
  public static void main(String[] args) {
    FileInputStream fis = null;
    
    try {
      fis = new FileInputStream("input.txt");
      
      int i;
      while((i = fis.read()) != -1) {
        System.out.println((char)i);
      }
      
      System.out.println();
      
    }catch(IOException e) {
      e.printStackTrace();
    }finally {
      try {
        fis.close();
      }catch (Exception e) {
        e.printStackTrace();
      }
    }
    System.out.println("end");
  }
}
/*
  FileInputStream을 작성하면 FileNotFoundException을 처리하라고 하는데
  이 코드에는 처리가 안보이는 이유는
  FileNotFoundException이 IOException의 하위에 존재하기 때문에
  IOException을 처리하도록 하면 알아서 처리되기 때문이다.
  
  IOException은 .read() 때문에 처리해야 한다.
  
  여기서 결과값은 프로젝트내에 만들어둔 input.txt의 내용이 출력되고
  end가 출력된다.
*/



//try with resource로 구현

import java.io.FileInputStream;
import java.io.IOException;

public class FileInputTest {
  
  public static void main(String[] args) {
    
    try(FileInputStream fis = new FileInputStream("input.txt")) {
      int i;
      while((i = fis.read()) != -1) {
        System.out.println((char)i);
      }
      
      System.out.println();
    
    }catch(IOException e) {
      e.printStackTrace();
    }
    System.out.println("end");
  }
}

    예제코드2

//File Output Test

import java.io.FileOutputStream;
import java.io.IOException;

public class FileOutputTest {
  
  public static void main(String[] args) {
    
    try(FileOutputStream fos = new FileOutputStream("output.txt", true)) {
      //파일명 뒤에 true를 쓰면 계속 append가 된다.
      fos.write(65);
      fos.write(66);
      fos.write(67);
      
    }catch(IOException e) {
      System.out.println(e);
    }
  }
}
/*
  실행하면
  output.txt가 프로젝트내에 생성된다
  그리고 주석을 보면 true를 쓰면 계속 append가 된다고 했는데
  한번 실행하면 ABC만 들어가있지만 한번 더 실행하면
  ABCABC가 들어가있게 된다.
  새로 덮어쓰는 것이 아닌 추가해주는것이다.
*/

  

    예제코드3

//File In Out Test

/*
  OutputStream으로 쓰고
  InputStream으로 읽도록 만드는 코드.
*/

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class FileInOutStream {
  
  public static void main(String[] args) {
    
    byte[] bs = new byte[26];
    byte data = 65;
    
    for(int i = 0; i < bs.length; i++) {
      bs[i] = data;
      data++;
    }
    
    try(FileInputStream fos = new FileOutputStream("InOut.txt", true);
        FileInputStream fis = new FileInputStream("InOut.txt")) {
        
      fos.write(bs);
      
      int ch;
      while((ch = fis.read()) != -1) {
        System.out.println((char)ch);
      }
      
    }catch (IOException e) {
      System.out.println(e);
    }
  }
}

/*
  프로젝트 폴더 내에 InOut.txt파일이 생성되며
  안에는
  ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMNOPQRSTUVWXYZ
  이렇게 작성되어있다.
  그리고 InputStream으로 읽었기 때문에
  파일 내용이 출력된다.
*/

 

문자단위 입출력 스트림

  문자단위 스트림

  Reader는 문자단위로 읽는 최상위 스트림이다.

  Writer는 문자단위로 쓰는 최상위 스트림이다.

  추상메서드를 포함한 추상클래스로 하위클래스가 상속받아 구현한다.

 

  하위클래스

스트림 클래스

설명

FileReader

파일에서 문자 단위로 읽는 스트림 클래스다

InputStreamReader

바이트 단위로 읽은 자료를 문자로 변환해주는 보조스트림 클래스다

BufferedReader

문자로 읽을 때 배열을 제공하여 한꺼번에 읽을 수 있는 기능을 제공해주는 보조스트림

FileWriter

파일에 문자단위로 출력하는 스트림 클래스다

OutputStreamWriter

파일에 바이트단위로 출력한 자료를 문자로 변환해주는 보조스트림이다

BufferedWriter

문자로 쓸 때 배열을 제공하여 한꺼번에 쓸 수 있는 기능을 제공해주는 보조스트림

 

    FileReader와 FileWriter는 파일에 문자를 읽고 쓸 때 가장 많이 사용하는 클래스다.

    문자의 인코딩 방식을 지정할 수 있다.

 

    예제코드

//Reader

import java.io.FileReader;
import java.io.IOException;

public calss FileReaderTest {
  
  public static void main(String[] args) throws IOException {
    /*
      FileInputStream fis = new FileInputStream("reader.txt");
      
      int i;
      while((i = fis.read()) != -1) {
        System.out.println((char)i);
      }
      
      fis.close();
      
      이렇게 inputStream을 쓰면 바이트단위로 읽기 때문에 한글은 깨져서 출력된다.
    */
    
    FileReader fis = new FileReader("reader.txt");
    
    int i;
    while((i = fis.read()) != -1) {
      System.out.println((char)i);
    }
    
    fis.close();
  }
}


//Reader 다른 방법

import java.io.FileReader;
import java.io.IOException;

public calss FileReaderTest {

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

/*
  이렇게 바이트 단위로 읽어오는 FileInputStream을
  InputStreamReader로 감싸주어 constructor에 넣어주면 된다.
  InputStreamReader는 보조스트림이고
  현재 InputStreamReader가 감싸고 있는 상태이기 때문에
  읽을때도 isr로 읽어야 하며
  닫을때도 마찬가지로 isr을 닫아주면 된다.
  보조스트림을 close하면 원래 스트림까지 close가 된다.
*/

    예제코드2

//Writer

import java.io.FileWriter;
import java.io.IOException;

public class FileWriterTest {

  public static void main(String[] args) throws IOException {
    
    FileWriter fw = new FileWriter("writer.txt");
    fw.write('A');  //char형태
    
    char[] buf = {'B', 'C', 'D', 'E', 'F'};
    fw.write(buf);  //char buffer형태
    
    fw.write(" 안녕하세요"); //String 형태
    
    fw.write(buf, 2, 2); //offset, length 형태.
    //buf의 2번 인덱스부터 2개인 D와 E를 의미한다.
    
    fw.close();
    
    System.out.println("end");
  }
}
/*
  프로젝트내에 writer.txt파일이 생성되고
  ABCDEF 안녕하세요DE 이러한 데이터가 들어간다.
*/

 

 

보조스트림

  보조스트림은 실제 읽고 쓰는 스트림이 아닌 보조적인 기능을 추가하는 스트림이다.

  FilterInputStream과 FilterOutputStream이 보조스트림의 상위 클래스다.

 

  보조스트림은 생성자의 매개변수로 또 다른 스트림을 갖는다.

생성자

설명

protected FilterInputStream(InputStream in)

생성자의 매개변수로 InputSteram을 받는다.

protected FilterOutputStream(OutputStrea out)

생성자의 매개변수로 OutputStream을 받는다.

 

  여러가지 보조스트림 사용하기

    Buffered 스트림 : 내부에 8192 바이트 배열을 갖고 있으며 읽거나 쓸 때 속도가 빠르다.

    DataInputStream / DataOutputStream : 자료가 저장된 상태 그대로 자료형을 유지하며 읽거나 쓰는 기능을 제공한다

 

    예제코드

//Buffered  FileCopy

/*
  프로젝트내에 a.zip이라는 파일을 만들어두고
  실행하면 복사하도록 하고
  처리시간을 확인하는 코드
*/

import java.io.*;
import java.net.Socket;

public class FileCopy {

  public static void main(String[] args) throws IOException {
    
    long milliseconds = 0;
    
    try(FileInputStream fis = new FileInputStream("a.zip");
        FileOutputStream fos = new FileOutputStream("copy2.zip");
        BufferedInputStream bis = new BufferedInputStream(fis);
        BufferedOutputStream bos = new BufferedOutputStream(fos)) {
      
      milliseconds = System.currentTimeMillis(); //현재시간
      
      int i;
      while((i = bis.read()) != -1) {
        bos.write(i);
      }
      
      milliseconds = System.currentTimeMillis() - milliseconds;
      //현재시간 - 이전시간으로 시간차를 구해 처리시간을 구한다.
      
    }catch(IOException e) {
      System.out.pritln(e);
    }
    
    Socket socket = new Socket();
    
    BufferedReader isr = new BufferedReader(new InputStreamReader(socket.getInputStream()));
    isr.readLine(0;
    
    System.out.println("시간 : " + milliseconds);
  }
}
/*
  5MB정도 되는 zip파일을 프로젝트 내에 a.zip으로 만들어 넣어두었고
  처음에는 BufferedInputStream과 BufferedOutputStream은 사용하지 않고
  FileInputStream과 FileOutputStream만 사용했었다.
  그랬을 때 milliseconds의 값은 32724가 출력되었다.
  그럼 처리되는데 32초 정도 걸렸다고 볼 수 있다.
  
  그리고 BufferedInputStream과 BufferedOutputStream을 사용해 감싸준뒤
  처리했을 때는 77이 출력되었다.
  
  이렇게 Buffered 스트림을 쓰면 읽거나 쓸 때의 속도가 많이 빠르다는 것을
  확인할 수 있다.
  
  그런 목적의 코드이다.
*/

    예제코드2

//Data Stream

import java.io.*;

public class DataStreamTest {
  
  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("안녕하세요");
      
      /*
        writeByte는 int라고 되어있기 때문에 1바이트만 쓴다.
        write는 writeByte와 같은 100이라는 값이 들어가있지만 4바이트로 쓰인다.
        writeChar은 char형식으로 UTF는 UTF형태로 값이 들어가기 때문에
        한글이 그대로 들어간다.
        
        그리고 읽을때는 쓸때와 같은 형식으로 읽어야 한다.
      */
      
      System.out.println(dis.readByte());
      System.out.println(dis.read());
      System.out.println(dis.readChar());
      System.out.println(dis.readUTF());
      
    }catch (IOException e) {
      System.out.println(e);
    }
    
  }
}
/*
  결과값은
  100
  100
  A
  안녕하세요
  이렇게 출력된다.
  하지만 data.txt를 확인해보면
  ddA안녕하세요 이렇게 저장되어있다.
  아스키코드값으로 100은 d이므로 바이트 형태로 인해 d로 저장된 것이고
  A는 Char타입으로 그대로 쓰도록 했으니 A가 들어간것이다.
*/

 

 

직렬화(serialization)

  인스턴스의 상태를 그대로 저장하거나 네트워크로 전송하고 이를 다시 복원(Deserialization)하는 방식이다.

  ObjectInputStream과 ObjectOutputStream을 사용한다.

  보조스트림이다.

 

Serializable Interface

    직렬화는 인스턴스의 내용이 외부(파일, 네트워크)로 유출되는 것이므로 프로그래머가 객체의 직렬화 가능 여부를

    명시한다.

//구현코드가 없는 markInterface

class Person implements Serializable { //직렬화 하겠다는 의도를 표시
  ......
  String name;
  String job;
  .....
}

 

    예제코드

import java.io.*;

class Person implements Serializable {
  String name;
  String job;
  
  public Person(String name, String job) {
    this.name = name;
    this.job = job;
  }
  
  public String toString() {
    return name + ", " + job;
  }
}
/*
  transient 라는 키워드가 있다.
  transient String job;
  이렇게 하면 직렬화 되지 않아서 null로 출력된다.
  이 변수는 직렬화 하지 말라는 것이다.
  
  Externalizable이라는 인터페이스가 있는데
  serializable은 구현할 메서드가 없는 마크인터페이스이지만
  Externalizable은 구현할 수 있는 메서드가 있다.
  writeExternal과 readExternal이 있는데
  직접 읽고 쓰는것을 구현할 수 있다.
  writeObject와 readObject가 호출이 되면
  호출되는 메서드들이다
  그래서 메서드의 변수를 이용해서 구현할 수 있다.
*/

public class SerializationTest {
  
  public static void main(String[] args) {
    
    Person personLee = new Person("이순신", "엔지니어");
    Person personKim = new Person("김유신", "선생님");
    
    try(FileOutputStream fos = new FileOutputStream("serial.dat");
        ObjectOutputStream oos = new ObjectOutputStream(fos)) {
      
      oos.writeObject(personLee);
      oos.writeObject(personKim);
      
    }catch (IOException e) {
      System.out.println(e);
    }
    
    try(FileInputStream fis = new FileInputStream("serial.dat");
        ObjectInputStream ois = new ObjectInputStream(fis)) {
        
      //writeObject로 써줬으니 readObject로 쓰면 된다.
      
      Person p1 = (Person)ois.readObject();
      Person p2 = (Person)ois.readObject();
      //이렇게 하면 Object로 반환된다.
      
      System.out.println(p1);
      System.out.println(p2);
      
    }catch (IOException e) {
      System.out.println(e);
    }catch (ClassNotFoundException e) {
      System.out.println(e);
    }
  }
}
/*
  결과값으로는
  이순신, 엔지니어
  김유신, 선생님
  이렇게 출력된다.
*/

 

 

레퍼런스

패스트캠퍼스 올인원 패키지 - 자바 객체지향프로그래밍

'JAVA' 카테고리의 다른 글

멀티쓰레드(Multi Thread)  (0) 2021.02.14
쓰레드(Thread)  (0) 2021.02.13
스트림(Stream)  (0) 2021.02.11
람다식(Lambda)  (0) 2021.02.10
제네릭프로그래밍(Generic Programming)  (0) 2021.02.09

+ Recent posts