Input과 Output의 약자, 컴퓨터 내부 또는 외부 장치와 프로그램 간의 데이터를 주고 받는 것
장치와 입출력을 위해서는 하드웨어 장치에 직접 접근이 필요한데 다양한 매체에 존재하는 데이터들을 사용하기 위해 입출력 데이터를 처리할 공통적인 방법으로 스트림 이용
자바에서는 파일이나 콘솔의 입출력을 직접 다루지 않고, 스트림(stream)이라는 흐름을 통해 다룹니다.
스트림(stream)이란 실제의 입력이나 출력이 표현된 데이터의 이상화된 흐름을 의미합니다.
즉, 스트림은 운영체제에 의해 생성되는 가상의 연결 고리를 의미하며, 중간 매개자 역할을 합니다.
먼저 들어간 데이터가 먼저 나오는 형태로 데이터의 순서가 바뀌지 않는다
하나의 스트림에서 입출력이 동시에 이루어 지지 않는다
입력과 출력에 각각 스트림을 열어 사용해야 한다
스트림 내의 데이터가 모두 전송되기 전까지 프로그램이 지연 상태에 빠질 수 있다.
바이트 단위로 데이터를 전송
바이트 단위로 구성된 파일(동영상, 이미지, 음악 등)을 처리하기에 적합한 스트림
바이트 스트림의 최상위 클래스는 InputStream과 OutputStream이다.
Output 스트림은 바이트 기반 출력 스트림의 최상위 클래스로 추상 클래스 입니다.
public static void main(String[] args) throws IOException {
OutputStream os = new FileOutputStream("test.db");
byte a = 10;
byte b = 20;
byte c = 30;
os.write(a);
os.write(b);
os.write(c);
os.flush(); // 버퍼 비우기
os.close(); // 자원 반납 후 스트림 닫기
}
public static void main(String[] args) throws IOException {
OutputStream os = new FileOutputStream("test2.db");
byte[] array = {10, 20, 30};
os.write(array);
os.flush();
os.close();
}
public static void main(String[] args) throws IOException {
OutputStream os = new FileOutputStream("test3.db");
byte[] array = {10, 20, 30, 40, 50};
os.write(array, 1, 3); // 배열의 1번 인댁스 부터 3개 출력
os.flush();
os.close();
}
InputStream은 바이트 기반 입력 스트림의 최상위 클래스로 추상 클래스 입니다.
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("test2.db");
while(true) {
int data = is.read(); // 1 바이트씩 읽기
if(data == -1) break; // 파일의 끝에 도달
System.out.println(data);
}
is.close();
}
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("test2.db");
byte[] buffer = new byte[100];
while(true) {
int readByteNum = is.read(buffer); // 배열의 길이 만큼 읽기
if(readByteNum == -1) break; // 파일의 끝에 도달
for(int i = 0; i < readByteNum; i++) {
System.out.println(buffer[i]);
}
}
is.close();
}
public static void main(String[] args) throws IOException {
InputStream is = new FileInputStream("test2.db");
byte[] buffer = new byte[5];
int readByteNum = is.read(buffer, 2, 3);
if(readByteNum != -1) {
for(int i = 0; i < buffer.length; i++) {
System.out.println(buffer[i]);
}
}
is.close();
}
문자 단위로 데이터를 전송하며 사용방법은 바이트 스트림과 동일하다.
영어 이외의 문자에 대한 처리와 인코딩을 내부에서 처리 해준다.
문자 스트림의 최상위 클래스는 Reader와 Writer이다.
InputStream이 Reader로 OutputStream이 Writer에 대응
이름만 다를 뿐 활용 방법은 거의 동일 합니다.
Write는 문자열 기반 출력 스트림의 최상위 클래스로 추상 클래스 입니다.
모든 문자 기반 출력 스트림 클래스는 이 클래스를 상속받아서 만들어 집니다.
기존 파일 내용 끝에 데이터를 추가할 경우 두번 째 매개값에 true를 주면됨 (new FileWriter("test.txt", true);
public static void main(String[] args) throws Exception {
Writer writer = new FileWriter("test.txt");
char[] data = "곰돌이사육사".toCharArray();
for(int i=0; i<data.length; i++) {
writer.write(data[i]);
}
writer.flush();
writer.close();
}
public static void main(String[] args) throws Exception {
Writer writer = new FileWriter("test.txt");
char[] data = "곰돌이사육사".toCharArray();
writer.write(data);
writer.flush();
writer.close();
}
public static void main(String[] args) throws Exception {
Writer writer = new FileWriter("test.txt");
//char[] data = "곰돌이사육사".toCharArray();
String data = "곰돌이사육사";
writer.write(data, 1, 2);
writer.flush();
writer.close();
}
Reader는 문자 기반 입력 최상위 클래스로 추상 클래스 입니다.
모든 문자 기반 입력 스트림은 이 클래스를 상속 받아서 만들어 집니다.
입력 스트림으로 부터 한 개의 문자(2바이트)를 읽고 4바이트 int 타입으로 리턴 합니다.
read() 메소드가 리턴한 int 값을 char형으로 변환하면 읽은 문자열을 확인 할 수 있습니다.
public static void main(String[] args) throws Exception {
Reader reader = new FileReader("src/test.txt");
int readData;
while( true ) {
readData = reader.read();
if(readData == -1) break;
System.out.print((char)readData);
}
reader.close();
}
입력스트림으로 부터 매개변수로 주어진 문자 배열의 길이만큼 문자를 읽고 배열에 저장 합니다.
그리고, 읽은 문자 수를 리턴 합니다. 실제로 읽은 문자 수가 배열 길이 보다 작을 경우 읽은 수만큼만 리턴 합니다.
public static void main(String[] args) throws Exception {
Reader reader = new FileReader("src/test.txt");
int readCharNo;
char[] cbuf = new char[10];
String data = "";
while( true ) {
readCharNo = reader.read(cbuf);
if(readCharNo == -1) break;
data += new String(cbuf, 0, readCharNo);
}
System.out.println(data);
reader.close();
}
파일이나 디렉토리를 추상화한 클래스이며 가장 많이 사용되는 입출력 대상 입니다.
File file = new File("C:\\Temp\\file.txt");
File file = new File("C:/Temp/file.txt");
public static void main(String[] args) throws URISyntaxException, IOException {
File temp = new File("src");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd a HH:mm");
File[] contents = temp.listFiles();
System.out.println("날짜 시간 형태 크기 이름");
System.out.println("-------------------------------------------------------------------");
for(File file : contents) {
System.out.print(sdf.format(new Date(file.lastModified()))); // 파일의 최종 갱신 시간
if(file.isDirectory()) {
System.out.print("\t<DIR>\t\t\t" + file.getName()); // 파일 이름을 스트링으로 반환
} else {
System.out.print("\t\t\t" + file.length() + "\t" + file.getName());
}
System.out.println();
}
}
파일로 부터 바이트 단위로 읽어 들일 때 사용되는 바이트 기반 입력 스트림 입니다.
public static void main(String[] args) {
try {
FileInputStream fis = new FileInputStream("src/파일인풋스트림/FileInputStreamEx.java");
int data;
while ( (data = fis.read() ) != -1 ) {
System.out.write(data);
}
fis.close();
} catch(Exception e) {
e.printStackTrace();
}
}
바이트 단위로 데이터를 파일에 저장 할 때 사용하는 바이트 기반 출력 스트림 입니다.
바이트 단위로 저장하기 때문에 그림, 오디오, 비디오, 텍스트 등 모든 종류의 데이터를 파일로 저장 할 수 있습니다.
public static void main(String[] args) throws IOException {
String originalFileName = "src/파일아웃풋스트림/house.jpg";
String targetFileName = "src/house.jpg";
FileInputStream fis = new FileInputStream(originalFileName);
FileOutputStream fos = new FileOutputStream(targetFileName);
int readByteNo;
byte[] readBytes = new byte[100];
while( (readByteNo = fis.read(readBytes)) != -1 ) {
fos.write(readBytes, 0, readByteNo);
}
fos.flush();
fos.close();
fis.close();
System.out.println("복사가 잘 되었습니다.");
}
package 파일입출력;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class FileInput {
public static void main(String[] args) {
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream("src/파일입출력/chiken.txt");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
Scanner sc = new Scanner(inputStream);
while(sc.hasNextLine()) {
String line = sc.nextLine();
System.out.println(line);
}
}
}
기본 스트림인 InputStream/OutputStream, Reader/Writer를 직접 사용해서 데이터를 입출력 할 수 있지만, 데이터 변환해서 입출력하거나, 데이터의 출력 형식을 지정하고 싶은 경우, 그리고 입출력 성능을 향상시키고 싶을 경우에 기본 스트림에 보조 스트림을 연결하여 사용하면 편리하게 기능들을 수행 할 수 있습니다.
보조스트림 변수 = new 보조스트림(연결스트림);
InputStream is = System.in;
InputStreamReader reader = new InputStreamReader(is);
바이트 스트림과 문자 스트림을 연결할 수 있도록 도와주는 스트림 입니다.
인코딩 방식을 정할 수 있고 플랫폼의 기본 인코딩을 사용하기도 합니다.
public static void main(String[] args) throws Exception {
InputStream is = System.in;
Reader reader = new InputStreamReader(is);
int readCharNo;
char[] cbuf = new char[100];
while ((readCharNo=reader.read(cbuf)) != -1) {
String data = new String(cbuf, 0, readCharNo);
System.out.println(data);
}
reader.close();
}
public static void main(String[] args) throws Exception {
FileOutputStream fos = new FileOutputStream("src/파일로출력하기/file.txt");
Writer writer = new OutputStreamWriter(fos);
String data = "";
writer.write(data);
writer.flush();
writer.close();
System.out.println("파일 저장이 끝났습니다.");
}
프로그램의 실행 성능에서 가능 느린 것이 입출력 장치 입니다. 이를 해결하기 위해 프로그램이 입출력 소스와 직접 작업하지 않고 중간에 메모리 버퍼와 작업해 실행 성능을 향상 할 수 있습니다.
package 버퍼성능비교;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
public class EnhancedBuffMain {
public static void main(String[] args) throws Exception {
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
int data = -1;
long start = 0;
long end = 0;
fis = new FileInputStream("src/버퍼성능비교/forest.jpg");
bis = new BufferedInputStream(fis);
fos = new FileOutputStream("src/forest.jpg");
start = System.currentTimeMillis();
while((data = bis.read()) != -1) {
fos.write(data);
}
fos.flush();
end = System.currentTimeMillis();
fos.close(); bis.close(); fis.close();
System.out.println("사용하지 않았을 때: " + (end-start) + "ms");
fis = new FileInputStream("src/버퍼성능비교/forest.jpg");
bis = new BufferedInputStream(fis);
fos = new FileOutputStream("src/forest.jpg");
bos = new BufferedOutputStream(fos);
start = System.currentTimeMillis();
while((data = bis.read()) != -1) {
bos.write(data);
}
bos.flush();
end = System.currentTimeMillis();
bos.close(); fos.close(); bis.close(); fis.close();
System.out.println("사용했을 때: " + (end-start) + "ms");
}
}
바이트 스트림은 바이트 단위로 입출력하기 때문에 자바의 기본 데이터 타입인 boolean, char, short, int, long, float, double 단위로 입출력 할 수 없습니다. 그러나 DataInputStream과 DataOutputStream 보조 스트림을 연결하면 기본 데이터 타입으로 입출력이 가능 합니다.
DataInputStream dis = new DataInputStream(바이트입력스트림)
DataOutputStream dos = new DataOutputStream(바이트출력스트림)
public class DataInPutStream {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("primitive.db");
DataOutputStream dos = new DataOutputStream(fos);
dos.writeUTF("우영우");
dos.writeDouble(95.5);
dos.writeInt(1);
dos.writeUTF("동그라미");
dos.writeDouble(90.3);
dos.writeInt(2);
dos.flush();
dos.close();
FileInputStream fis = new FileInputStream("primitive.db");
DataInputStream dis = new DataInputStream(fis);
for(int i = 0; i < 2; i++) {
String name = dis.readUTF();
double score = dis.readDouble();
int order = dis.readInt();
System.out.println(name + " : " + score + " : " + order);
}
dis.close();
}
}
자바는 메모리에 생성된 객체를 파일 또는 네트워크로 출력 할 수 있습니다. 객체는 문자가 아니기 때문에 바이트 기반 스트림으로 출력해야 합니다. 객체를 출력하기 위해서는 일렬로 연속적인 바이트로 변경해야 하는데, 이것을 객체 직렬화라고 합니다. 반대로 파일에 저장되어 있거나 네트워크에서 전송된 객체를 읽을 수도 있는데, 입력 스트림으로 부터 읽어들인 연속적인 바이트를 객체로 복원하는 것을 역직렬화라고 합니다.
package 객체입력보조스트림;
import java.io.Serializable;
import java.util.Date;
public class Board implements Serializable {
private int bno;
private String title;
private String content;
private String writer;
private Date date;
public Board(int bno, String title, String content, String writer, Date date) {
this.bno = bno;
this.title = title;
this.content = content;
this.writer = writer;
this.date = date;
}
public int getBno() {
return bno;
}
public void setBno(int bno) {
this.bno = bno;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public String getWriter() {
return writer;
}
public void setWriter(String writer) {
this.writer = writer;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
}
package 객체입력보조스트림;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
public class ObjectStreamEx {
public static void main(String[] args) throws IOException, ClassNotFoundException {
writeList();
List<Board> list = readList();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
for(Board board : list) {
System.out.println(
board.getBno() + "\t" + board.getTitle() + "\t" + board.getContent() + "\t" +
board.getWriter() + "\t" + sdf.format(board.getDate()));
}
}
public static void writeList() throws IOException {
List<Board> list = new ArrayList<>(); // List 생성
list.add(new Board(1, "C++", "고성능언어", "우영우", new Date()));
list.add(new Board(2, "java", "객체지향언어", "동그라미", new Date()));
list.add(new Board(3, "Python", "간결한언어", "이준호", new Date()));
FileOutputStream fos = new FileOutputStream("board.db");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(list); // 객체 출력 스트림을 이용해서 list 출력
oos.flush();
oos.close();
}
public static List<Board> readList() throws IOException, ClassNotFoundException {
FileInputStream fis = new FileInputStream("board.db");
ObjectInputStream ois = new ObjectInputStream(fis);
List<Board> list = (List<Board>) ois.readObject();
return list;
}
}
Serializable 인터페이스를 implements하여 구현 합니다. 객체 직렬화 시 private 필드를 포함한 모든 필드를 바이트로 변환하지만 transient키워드를 사용한 필드는 직렬화에서 제외 됩니다.
직렬화된 객체를 역직렬화할 때는 직렬화 했을 때와 같은 클래스를 사용 합니다.
단, 클래스 이름이 같더라도 클래스 내용이 변경된 경우 역직렬화가 실패하게 됩니다.
직렬화한 클래스와 같은 클래스임을 알려주는 식별자 역할로 컴파일 시 JVM이 자동으로 serialViersionUID 정적 필드를 추가해줘 별로도 작성하지 않아도 오류는 나지 않지만 자동 생성 시 역직렬화에서 예상하지 못한 InvalidClassException을 유발할 수 있어 명시 권장
private static final long serialVersionUID = -6423919775137290062L;
콘솔은 시스템을 사용하기 위해 키보드로 입력을 받고 화면으로 출력하는 소프트웨어를 말합니다.
자바는 프로그램이 콘솔로 부터 데이터 입력을 받을 수 있도록 System 클래스의 in 정적 필드를 제공하도 있습니다. System.in은 InputStream 타입의 필드 이므로 다음과 같이 InputStream 변수로 참조 가능 합니다.
InputStream is = System.in;
public class SystemInEx {
public static void main(String[] args) throws IOException {
System.out.println("=== 메뉴 ===");
System.out.println("1. 예금 조회");
System.out.println("2. 예금 출금");
System.out.println("3. 예금 입금");
System.out.println("4. 종료 하기");
System.out.print("메뉴를 선택 하세요 : ");
InputStream is = System.in; // 키보드 입력 스트림 얻기
char inputChar = (char) is.read(); // ASCII 코드를 읽어 문자로 리턴
switch(inputChar) {
case '1' :
System.out.println("예금 조회를 선택 하셨습니다.");
break;
case '2' :
System.out.println("예금 출금을 선택 하셨습니다.");
break;
case '3' :
System.out.println("예금 입금을 선택 하셨습니다.");
break;
case '4' :
System.out.println("종료 하기를 선택하셨습니다.");
break;
}
}
}
콘솔로 데이터를 출력하기 위해서 System 클래스의 out 정적 필드를 사용 합니다.
OutputStream os = System.out
콘솔로 1개의 바이트를 출력하려면 OutputStream의 write(int b)메소드를 이용하면 됩니다.
이 때 바이트 값은 ASCII 코드 인데, write() 메소드는 ASCII 코드를 문자로 콘솔에 출력 합니다.
public static void main(String[] args) throws IOException {
OutputStream os = System.out;
for(byte b = 48; b < 58; b++) {
os.write(b);
}
os.write(10);
for(byte b= 97; b < 123; b++) {
os.write(b);
}
os.write(10);
String hangul = "가나다라마바사아자타카타파하";
byte[] hangulBytes = hangul.getBytes();
os.write(hangulBytes);
os.flush();
}