사실 책 없이 (책 요약본만 잇음ㅋ) 찾으면서 글을 작성하는 거라 흐름(오 stream?ㅋ)이 일목요연하지 않을 수 있다. 주의하라고 하는 말이에요....
= 입력+출력을 합쳐 부르는 말
= 두 대상간의 데이터를 주고 받기 위해 사용됨
java.io.~
이 패키지를 코테를 풀며 자주 import 했었는데 io가 도대체 뭐지? 하고 그땐 찾아볼 생각을 안했는데 이 단원을 배워보며 생각하니 입출력의 io였다. (코테에서 왜 import 했나면 BufferedReader를 사용해 사용자의 입력, 즉 터미널 창의 내 입력을 받아오기 위해 사용했다.)
왜 입출력에 대한 설명이 나오고 스트림이 등장하냐면, 사실 별 의민 없다. 두 데이터를 주고 받기 위해 만들어진 입출력, 즉 java.io 패키지에서 Stream 관련한 클래스들이 제공되기 때문에 두 개념이 연관된다.
그리고 간단히 생각하면 두 대상간의 데이터를 주고받으려면(입출력) 그 사이의 징검다리(스트림)가 필요하겠쥬?
= 데이터가 단방향으로 흘러가는 개념
= Stream은 단방향으로 데이터를 전달하기에 물의 흐름에 비유
= 데이터의 입출력에 사용되는 연결통로
외부자원
인데, 이는 반납이 필수이다. 이때 한번에 파일의 내용을 모두 메모리에 올리면 부담이 가기 때문에, 스트림을 연결하여 언제든지 원하는 부분의 원하는 만큼의 내용을 읽어들이도록 한다)try-with-resource
를 사용해도 좋다!close()
가 호출된다. (다만 사용조건이 AutoCloseable
을 구현한 객체여야 함 - 이 클래스가 close 메서드를 포함하는 인터페이스거든)try (FileInputStream fis = new FileInputStream("file.txt")){
...
} catch(IOException e) {
...
}
외부 자원: 파일, 네트워크 연결, 데이터베이스 연결, 그래픽 리소스 등 다양한 것들을 포함
아래에서는 두 종류에 대한 간단한 설명과, 두 종류에 해당되는 자주 사용하는 클래스에 대해서 알아볼 것이다.
= 문자, 영상 등의 여러 데이터 형태를 다루는 스트림
= 데이터를 바이트 단위
로 주고받는 스트림
InputStream, OutputStream
의 메서드를 (모두 Closeable을 구현)read()
read(byte[] b)
read(byte[] b, int off, int len)
write(int b)
write(byte[] b)
write(byte[] b, int off, int len)
= 정말 말 그대로 문자만 주고받기 위해 사용되는 스트림
Reader, Writer
의 메서드들read()
read(char[] buf)
, abstract int read(char[] buf, int off, int len)
등이 있당.write(int c)
write(String str)
, write(String str, int off, int len)
등이 있당.public static Writer nullWriter()
= 스트림 기능 향상 or 새로운 기능 추가를 위해 사용되는 스트림
//기반 스트림 생성
FileInputStream file = new FileInputStream("test.txt");
//기반 스트림 -> 보조 스트림 생성
BufferedInputStream bis = new BufferedInputStream(file);
//보조 스트림으로부터 데이터를 읽음
bis.read();
FilterInputStream, FilterOutputStream
BufferedInputStream, BufferedOutputStream
DataInputStream, DataOutputStream
SequenceInputStream, SequenceOutputStream
ObjectInputStream
객체 직렬화
와 관련!)= 객체 -> 데이터 스트림
= 자바 객체를 byte의 정적 스트림(sequence)으로 변환한 뒤 DB에 저장 or 네트워크로 전송
( 시퀀스: 데이터 소스(컬렉션, 배열, 파일)에서 가져온 연속된 요소들의 순서**
-> 중간 연산을 통해 시퀀스를 필터링하거나 변환하고, 최종 연산을 통해 시퀀스의 결과를 얻을 수 있음)
java.io.Serializable
을 구현해야 함NotSerializableException
이 발생)ex. ObjectOutputStream
의 final void writeObject(Object o) throws IOException
메서드는 직렬화가 가능한 객체를 가져와 byte stream(sequence)로 변환해준다.
public class Person implements Serializable {
private static final long serialVersionUID = 1L;
static String country = "ITALY";
private int age;
private String name;
transient int height; //직렬화 대상에서 제외
}
@Test
public void whenSerializingAndDeserializing_ThenObjectIsTheSame() ()
throws IOException, ClassNotFoundException {
Person person = new Person();
person.setAge(20);
person.setName("Joe");
FileOutputStream fileOutputStream
= new FileOutputStream("yourfile.txt");
ObjectOutputStream objectOutputStream
= new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(person); //요기!!!!
objectOutputStream.flush();
objectOutputStream.close();
FileInputStream fileInputStream
= new FileInputStream("yourfile.txt");
ObjectInputStream objectInputStream
= new ObjectInputStream(fileInputStream);
Person p2 = (Person) objectInputStream.readObject(); //요기!!
objectInputStream.close();
assertTrue(p2.getAge() == person.getAge());
assertTrue(p2.getName().equals(person.getName()));
}
Lombok이나 JUnit 처리를 위해 오랜만에 IntelliJ를 켜봤는데, 역시나 작동이 잘 안된다ㅠㅠ 테스트가 잘 동작하는지 확인하고 싶었다만.. 추후에 성공하면 추가하도록 하겠다.
class IOEx3 {
public static void main(String[] args) {
byte[] inSrc = {0,1,2,3,4,5,6,7,8,9};
byte[] outSrc = null;
byte[] temp = new byte[4];
ByteArrayInputStream input = null;
ByteArrayOutputStream output = null;
try {
while(input.available() > 0) {
input.read(temp);
output.write(temp);
}
} catch(IOException e) {}
outSrc = output.toByteArray();
System.out.println("Input Source: "+Arrays.toString(inSrc));
System.out.println("temp: "+Arrays.toString(temp));
System.out.println("Output Source: "+Arrays.toString(outSrc));
}
}
FileInputStream(String name)
FileInputStream(File file)
class FileCopy {
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream(args[0]);
FileOutputStream fos = new FileOutputStream(args[1]);
int data = 0;
while((data = fis.read()) != -1) {
fos.write(data);
}
fis.close();
fos.close();
} catch(IOException e) {
e.printStackTrace();
}
}
}
// java FileCopy FileCopy.java FileCopy.bak
read(), write()
를 원하는 기능대로 오버라이딩 해야함SequenceInputStream
= 여러 입력스트림을 연결해서 하나의 스트림처럼 다룰 수 있도록 함
= SequenceInputStream(Enumeration e)
: Enumeration에 저장된 순서대로 입력 스트림 하나로 연결
PrintStream
= 데이터를 다양한 형식의 문자로 출력하는 기능 제공
System.out, System.err
FileReader, FilerWriter
= 문자 기반의 파일 입출력, 텍스트 파일의 입출력에 사용
PipedReader, PipedWriter
= 프로세스(스레드) 간 데이터를 주고받는데 사용
StringReader, StringWriter
= 메모리의 입출력에 사용 (ex. CharArrayReader, CharArrayWriter)
String readLine()
: 한 라인, void newLine()
: 개행문자 출력= 화면(console)을 통한 데이터의 입출력
File(String fileName)
File(String pathName, String fileName)
String getName()/getPath()/getAbsolutePath()/getParent()/getCanonicalPath()
//예시 코드
class FileEx2 {
public static void main(String[] args) {
if(args.length != 1) {
System.out.println("USAGE: java FileEx2 DIR");
System.exit(0);
}
File f = new File(args[0]);
if(!f.exists() || !f.isDirectory()) {
System.out.println("유효하지 않은 디렉토리입니다.");
System.exit(0);
}
File[] files = f.listFiles();
for(int i = 0; i < files.length; i++) {
String fileName = files[i].getName();
System.out.println(files[i].isDirectory() ? "["+fileName+"]" : fileName);
}
}
}
= 객체를 '연속적인 데이터' 형태로 변환하는 것
= 객체의 인스턴스 변수 값을 일렬로 나열하는 것
= (시스템) JVM의 메모리에 힙 or 스택 되어있는 객체 데이터를 바이트 형태로 변환하는 기술 + 직렬화된 byte 형태의 데이터를 객체로 변환해서 JVM으로 상주시키는 형태
객체를 저장하려면 객체를 직렬화해야 함
객체 저장 = 객체의 모든 인스턴스 변수 값 저장
기본 자료형은 byte 변수가 정해져있기 때문에 byte 단위의 변화에 문제가 X
but 객체를 구성하는 자료형들의 종류, 수에 따라 객체의 크기가 변화할 수 있어 java.io.Serializable
을 구현해야만 직렬화 가능
제어자 transient
나 Serializable
을 구현하지 않은 클래스의 인스턴스도 직렬화 대상에서 제외
Serializable
을 구현하지 않은 조상 멤버들도 직렬화 대상 제외
readObject()
와 writeObject()
를 오버라이딩 하면 직렬화 맘대로
ObjectInputStream, ObjectOutputStream
= 객체를 직렬화하여 입출력할 수 있게 해주는 '보조스트림'
//사용법
//객체를 파일에 저장하는 방법
FileOutputStream fos = new FileOutputStream("objectfile.ser");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(new UserInfo());
//파일에 저장된 객체를 다시 읽어오는 방법
FileInputStream fis = new FileInputStream(objectfile.ser");
ObjectInputStream in = new ObjectInputStream(fis);
UserInfo info = (UserInfo) in.readObject();
JVM의 메모리에서만 상주돼있는 객체 데이터를 그대로 영속화 해야할 때 사용
ex. 서블릿 세션, 캐시, 자바 RMI(Remote Method Invocation)
서로 다른 메모리 공간 사이의 데이터 전달을 위해
(OS의 프로세스는 서로 다른 가상 메모리 주소 공간을 갖기 때문에 Object 타입의 참조값 데이터 인스턴스를 전달할 수 없다)
※ 버퍼
= 특정 원시 타입의 데이터를 위한 컨테이너
그러나...
버퍼는 추가적인 메모리가 사용되기 때문에 사용량이 증가한다. 따라서 실시간 통신이나 대화형 상호작용과 같이 즉각적인 응답이 필요한 상황에서는 즉시 입출력을 처리하는 것이 유리하다!
자바의 정석, 2nd Edition.
https://hudi.blog/java-inputstream-outputstream/
https://docs.oracle.com/en/java/javase/13/docs/api/java.base/java/io/OutputStream.html
https://www.baeldung.com/java-serialization
https://techblog.woowahan.com/2550/
https://ryan-han.com/post/java/serialization/