객체를 컴퓨터에 저장했다가 꺼내쓰는것과 컴퓨터간에 서로 객체를 주고받을 수 있게 해 주는것을 직렬화를 통해 가능하게한다.

 

직렬화(serialization)란 객체를 데이터 스트림으로 만드는 것을 뜻한다. 객체에 저장된 데이터를 스트림에 쓰기(write)위해 연속적인(serial) 데이터로 변환하는 것을 말한다. 반대로 스트림으로부터 데이터를 읽어서 객체를 만드는 것을 역직렬화(deserialization)이라고 한다.

객체는 클래스에 정의된 인스턴스변수의 집합이다. 객체는 클래스변수나 메서드가 포함되지 않는다. 객체는 오직 인스턴스변수들로만 구성되어 있다. 인스턴스변수는 인스턴스마다 다른 값을 가질 수 있어야하기 때문에 별도의 메모리공간이 필요하지만 메서드는 변하는 것이 아니라 인스턴스마다 같은 내용의 코드를 포함시킬 이유는 없다.

객체를 저장한다는 것은 바로 객체의 모든 인스턴스변수의 값을 저장한다는 것이다. 어떤 객체를 저장하고자 한다면, 현재 객체의 모든 인스턴스 변수의값을 저장하기만 하면 된다. 그리고 저장했던 객체를 다시 생성하려면, 객체를 생성한 후에 저장했던 값을 읽어서 생성한 객체의 인스턴스변수에 저장하면 되는 것이다.

클래스에 정의된 인스턴스변수가 단순히 기본형일 때는 인스턴스변수의 값을 저장하는 일이 간단하지만, 인스턴스변수의 타입이 참조형 일 때는 그리 간단하지 않다. 그러나 우리에게는 직렬화/역직렬화를 할 수 있는 ObjectInputStream/ObjectOutputStream을 사용하는 방법만 알면된다.

두 객체가 동일한지 판단하는 기준이 두 객체의 인스턴스변수의 값들이 같고 다름이라는 것을 기억하자.

 

 

 

ObjectInputStream, ObjectOutputStream

직렬화(스트림에 객체를 출력)에는 ObjectOutputStream을 사용하고 역직렬화(스트림으로부터 객체를 입력)에는 ObjectInputStream을 사용한다.

ObjectInputStream과 ObjectOutputStream은 각각 InputStream과 OutputStream을 직접 상속받지만 기반스트림을 필요로하는 보조스트림이다. 그래서 객체를 생성할 때 입출력(직렬화/역직렬화)할 스트림을 지정해 주어야 한다.

ObjectInputStream(InputStream in)
ObjectOutputStream(OutputStream out)

만일 파일에 객체를 저장(직렬화)하고 싶다면 다음과 같이 하면 된다.

FileOutputStream fos = new FileOutputStream("objectfile.ser");
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(new UserInfo());

위의 코드는 objectfile.ser이라는 파일에 UserInfo객체를 직렬화하여 저장한다. 출력할 스트림(FileOutputStream)을 생성해서 이를 기반스트림으로하는 ObjectOutputStream을 생성한다.

ObjectOutputStream의 writeObject(Object obj)를 사용해서 객체를 출력하면, 객체가 파일에 직렬화되어 저장된다.

역직렬화 방법은 입력스트림을 사용하고 wrieteObject(Object obj)대신 readObject()이기 때문에 객체 원래의 타입으로 형변환 해주어야 한다.

ObjectInputStream과 ObjectOutputStream에는 readObject()와 writeObject()이외에도 여러 가지 타입과 값을 입출력할 수 있는 메서드를 제공한다.

ObjectInputStream ObjectOutputStream
void
int
int
boolean
byte
char
double
float
int
long
short
Object
int
int
Object
string
defaultReadObject()
read()
read(byte[]buf, int off, int len)
readboolean()
readByte()
readChar()
readDouble()
readFloat()
readInt()
readLong()
readShort()
readObject()
readUnsignedByte()
readUnsignedShort()
readUnshared()
readUTF()
void defaultWriteObject
void write(byte[] buf)
void write(byte[] buf, int off, int len)
void write(int val)
void writeBoolean(boolean val)
void writeByte(int val)
void writeBytes(String str)
void writeChar(int val)
void writeChars(String str)
void writeDouble(double val)
void writeFloat(float val)
void writeInt(int val)
void writeLong(long val)
void writeObject(Object obj)
void writeShort(int val)
void writeUnshared(Object obj)
void write UTF(String str)

이 메서드들은 직렬화와 역직렬화를 직접 구현할 때 주로 사용되며, defaultReadObject(0와 defaultWriteObject()는 자동 직렬화를 수행한다. 객체를 직렬화/역직렬화하는 작업은 객체의 모든 인스턴스변수가 참조하고 있는 모든 객체에 대한 것이기 때문에 상당히 복잡하며 시간도 오래 걸린다. readObject()와 writeObject()를 사용한 자동 직렬화가 편리하기는 하지만 직렬화 작업시간을 단축시키려면 직렬화하고자 하는 객체의 클래스에 추가적으로 다음과 같은 2개의 메서드를 직접 구현해주어야 한다. 

private void writeObject(ObjectOutputStream out)
	throws IOException {
	}
private void readObject(ObjectInputStream in)
	throws IOException, ClassNotFoundExceaption{
    }

 

 

 

직렬화가 가능한 클래스 만들기 - Serializable, transient

직렬화가 가능한 클래스를 만드는 방법은 간단하다. 직렬화하고자 하는 클래스가 java.io.Serializable인터페이스를 구현하도록 하면 된다.

 예를 들어 왼쪽과 같이 UserInfo라는 클래스가 있을 때, 이 클래스를 직렬화가 가능하도록 변경하려면 오른쪽과 같이 Serializable인터페이스를 구현하도록 변경하면 된다.

public class UserInfo {
	String name;
	String password;
	int age;
}
public class UserInfo
	implements java.io.Serializable {
		String name;
		String password;
		int age;
}

Serializable 인터페이스는 아무런 내요도 없는 빈 인터페이스이지만, 직렬화를 고려하여 작성한 클래스인지를 판단하는 기준이 된다.

public interface Serialzable{ }

아래와 같이 Serializable을 구현한 클래스를 상속받는다면, Serializable을 구현하지 않아도 된다. UserInfo는 Serializable을 구현하지 않았지만 조상인 SuperUserInfo가 Serializable를 구현하였으므로 UserInfo역시 직렬화가 가능하다.

public class SuperUserInfo implement Serializable {
	String name;
	String password;
}
public class UserInfo extends SuperUserInfo{
	int age;
}

위의 경우 UserInfo객체를 직렬화하면 조상인 SuperUserInfo에 정의된 인스턴스변수name, password도 함께 직렬화된다.

 

그러나 조상클래스가 Serializable을 구현하지 않았다면 자손클래스를 직렬화할 때 조상클래스에 정의된 인스턴스변수 name과 password는 직렬화 대상에서 제외된다.

public class SuperUserInfo {
	String name;
	String password;
}
public class UserInfo extends SuperUserInfo implements Serializable {
	int age;
}

조상클래스에 정의된 인스턴스변수 name과 password를 직렬화 대상에 포함시키기 위해서는 조상클래스가 Serializable을 구현하도록 하던가, UserIInfo에서 조상의 인스턴스변수들이 직렬화되도록 처리하는 코드를 직접 추가해 주어야 한다. 

public class UserInfo implements Serializable {
	String name;
	String password;
	int age;
	Object obj = new Object(); // Object객체는 직렬화할 수 없다.
}

이 경우에는 직렬화 될 수 없다.

public class UserInfo implements Serializable {
	String name;
	String password;
	int age;
	Object obj = new String("abc");	// String은 직렬화될 수 없다.
}

인스턴스변수 obj의 타입이 직렬화가 안 되는 Objct이긴 하지만 실제로 저장된 객체는 직렬화가 가능한 String인스턴스이기 때문에 직렬화가 가능하다. 

 

직렬화하고자 하는 객체의 클래스에 직렬화가 안 되는 객체에 대한 참조를 포함하고 있다면, 제어자 transient를 붙여서 직렬화 대상에서 제외되도록 할 수 있다.

 또는 password와 같이 보안상 직렬화되면 안 되는 값에 대해서 transient를 사용할 수 있다. 다르게 표현하면 transient가 붙은 인스턴스변수의 값은 그 타입의 기본값으로 직렬화된다고 볼 수 있다.

즉, UserInfo객체를 역직렬화하면 참조변수인 obj와 password의 값은 null이 된다.

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

class SuperUserInfo{
	String name;
	String password;
	
	public SuperUserInfo(){
		this("Unknown", "1111");
	}
		
	public SuperUserInfo(String name, String password){
		this.name = name;
		this.password = password;
	}
}

public class UserInfo2 extends SuperUserInfo implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -7739307153548748307L;
	int age;
	String addr;
	
	public UserInfo2() {
		this("Unknown", "1111",0);
	}
	public UserInfo2(String name, String password, int age) {
		super(name, password);
		this.age = age;
	}
	@Override
	public String toString() {
		return "UserInfo2 [age=" + age + ", name=" + name + ", password=" + password + "]";
	}
	
	private void writeObject(ObjectOutputStream oos) throws IOException{
		oos.writeUTF(name);
		oos.writeUTF(password);
		oos.defaultWriteObject();
	}
	private void readObject(ObjectInputStream ois) throws Exception {
		name = ois.readUTF();
		password = ois.readUTF();
		ois.defaultReadObject();
	}
	public static void main(String[] args) throws Exception {
		
		List<UserInfo2> list = new ArrayList<UserInfo2>();
		list.add(new UserInfo2("1길동", "1111", 10));
		list.add(new UserInfo2("2길동", "1212", 20));
		
		new ObjectOutputStream(new FileOutputStream("abcd.ser")).writeObject(list);
		
		List<UserInfo2> list2 = (List<UserInfo2>) new ObjectInputStream(new FileInputStream("abcd.ser")).readObject();
		
		list2.forEach(System.out::println);
	}
}

아래는writeObject()와 readObject()를 추가해서 조상으로부터 상속받은 인스턴스변수인 name과 password가 직접 직렬화되도록 해야 한다. 이 메서드들은 직렬화/역직렬화 작업시에 자동적으로 호출된다.

 

 

 

직렬화가능한 클래스의 버전관리

 

직렬화된 객체를 역직렬화할 때는 직렬화 했을 때와 같은 클래스를 사용해야한다. 그러나 클래스의 이름이 같더라도 클래스의 내용이 변경된 경우 역직렬화는 실패한다.

역직렬화를 이용해 클래스의 버전을 비교함으로써 직렬화할 때의 클래스 버전과 일치하는지 확인할 수 있다. 그러나 static변수나 상수 transient가 붙은 인스턴스변수가 추가되는 경우에는 직렬화에 영향을 미치지 않기 때문에 클래스의 버전을 다르게 인식하도록 할 필요는 없다.

 

표준입출력 - System.in, System.out, System.err

표준입출력은 콘솔(console, 도스창)을 통한 데이터 입력과 콘솔로의 데이터 출력을 의미한다. 자바에서는 표준 입출력(standard I/O)을 위해 3가지 입출력 스트림, System.in, System.out, System.err을 제공하는데, 이 들은 자바 어플리케이션의 실행과 동시에 사용할 수 있게 자동적으로 생성되기 때문에 개발자가 별도의 스트림을 생성하는 코드를 작성하지 않고도 사용이 가능하다.

System.in 콘솔로부터 데이터를 입력받는데 사용
System.out 콘솔로 데이터를 출력하는데 사용
System.err 콘솔로 데이터를 출력하는데 사용

 

System클래스의 소스에서 알 수 있듯이, in, out, err은 System클래스에 선언된 클래스변수(static변수)이다. 선언부분만을 봐서는 out, err, in의 타입은 InputStream과 PrintStream이지만 실제로는 버퍼를 이용하는 BufferedInputStream과 BufferedOutputStream을 사용한다(속도가 빠르다)

public final class System {
	public final static InputStream in = nullInputStream();
	public final static PrintStream out = nullPrintStream();
	public final static PrintStream err = nullPrintStream();
	...
}

Editplus나 이클립스와 같은 에디터에서는 콘솔로의 출력을 중간에 가로채서 에디터에 뿌려주는 것이다. Editplus의 설정화면에서 'Capture Output'옵션을 선택하면 콘솔에 출력하는 내용이 Editplus의 하단에 출력된다.

 

 

public class StandardIOEx1 {
	public static void main(String[] args) {
		try {
			int input = 0;

			while ((input = System.in.read()) != -1) {
				System.out.println("input :" + input + ", (char)input : " + (char) input);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

콘솔입력은 버퍼를 가지고 있기 때문에 Backspace키를 이용해서 편집이 가능하며 한 번에 버퍼의 크기만큼 입력이 가능하다.

콘솔에 데이터를 입력하고 Enter키를 누르면 입력대기상태에서 벗어나 입력된 데이터를 읽기 시작하고 입력된 데이터를 모두 읽으면 다시 입력대기 상태가 된다.

이러한 과정이 반복되다가 사용자가 ctrl+z를 입력하면, read()는 입력이 종료되었음을 인식하고 -1을 반환하여 while문을 벗어나 프로그램이 종료된다.

enter키를 두개 누르는 것은 두 개의 특수문자'\r'과'\n'이 입력된 것으로 간주된다. '\r'은 캐리지리턴(carriage return), 즉 커서를 현재 라인의 첫 번째 컬럼으로 이동시키고'\n'은 커서를 다음 줄로 이동시키는 줄바꿈(new line)을 한다.

그래서 Enter키를 누르면, 캐리지리턴과 줄바꿈이 수행되어 다음 줄의 첫 번째 칼럼으로 커서가 이동하는 것이다.

 

 

 

표준입출력 대상변경 - setOut(), setErr(), setln()

초기에는 System.in, System.out, System.err의 입출력대상이 콘솔화면이지만, setln(), setOut(), setErr()를 사용하면 입출력을 콘솔 이외에 다른 입출력 대상으로 변경하는 것이 가능하다.

 

메서드 설명
static void setOut(PrintStream out) System.out의 출력을 지정된 PrintStream으로 변경
static void setErr(PrintStream err) System.err의 출력을 지정한 PrintStream으로 변경
static void setln(InputStream in) System.in의 입력을 지정한 InputStream으로 변경

 

public class StandardIOEx2 {
	public static void main(String[] args) {
		System.out.println("out : Hello World!");
		System.err.println("err : Hello World!");
	}
}

 

 

 

RandomAccessFile

자바에서는 입력과 출력이 각각 분리되어 별도로 작업을 하도록 설계되어 있는데 RandomAccessFile만은 하나의 클래스로 파일에 대한 입력과 출력을 모두 할 수 있도록 되어 있다.

InputStream이나 OutputStream으로 상속받지 않고, DataInput인터페이스와 DataOutput인터페이스를 모두 구현했기 때문에 읽기와 쓰기가 모두 가능하다.

 

DataInputStream은 DataInput인터페이스를, DataOutputStream은 DataOutput인터페이스를 구현했다. 이 두 클래스의 기본 자료형(primitive data type)을 읽고 쓰기위한 메서드들은 모두 이 2개의 인터페이스에 정의되어있는 것들이다.

따라서, RandomAccessFile클래스도 DataInputStream과 DataOutputStream처럼, 기본자료형 단위로 데이터를 읽고 쓸 수 있다. 

RandomAccessFile클래스의 가장 큰 장점은 파일의 어느 위치에나 읽기/쓰기가 가능하다는 것이다.

이것은 RandomAccessFile클래스 내부의 파일포인터를 사용하는데, 입출력 시에 작업이 수행되는 곳이 바로 파일 포이터가 위치한 곳이 된다.

파일 포인터의 위치는 파일의 제일 첫 부분(0부터 시작)이며, 일기 또는 쓰기를 수행할 때 마다 작업이 수행된 다음 위치로 이동하게 된다. 순차적으로 읽기나 쓰기를 한다면, 파일 포인터를 이동시키기 위해 별도의 작업이 필요하지 않지만, 파일의 임의의 위치에 있는 내용에 대해서 작업하고자 한다면, 먼저 파일 포인터를 원하는 위치로 옮긴 다음 작업을 해야 한다.

현재 작업 중인 파일에서 파일 포인터의 위치를 알고 싶을 때는 getFilePointer()를 사용하면 되고, 파일 포인터의 위치를 옮기기 위해서는 seek(long pos)나 skipBytes(int n)를 사용하면 된다.

모든 입출력에 사용되는 클래스들은 입출력 시 다음 작업이 이루어질 위치를 저장하고 있는 포인터를 내부적으로 갖고 있다. 다만 내부적으로만 사용될 수 있기 때문에 작업자가 포인터의 위치를 마음대로 변경할 수 없다는 것이 RandomAccessFile과 다른 점이다.

 

생성자/메서드 설명
RandomAccessFile(File file, String mode)
RandomAccessFile(String fileName, String mode)
주어진 file에 읽기 또는 읽기+쓰기를 하기 위한
RandomAccessFile인스턴스를 생성한다. mode의 값은 "r", "rw", "rws", "rwd"가 지정가능하다.
"r" - 파일로부터 읽기(r)만 수행할 때
"rw" - 파일로부터 읽기(r)와 쓰기(w)를 수행할 때
"rws - rw+출력내용(파일의 내용 + 메타정보)을 파일에 지연없이 바로 쓰이게 한다. 
"rwd"- rw+출력내용(파일의 내용만)을 파일에 지연없이 바로 쓰이게 한다. 
FileChannel getChannel() 파일의 파일 채널을 반환한다.
FileDescriptor getFD() 파일의 파일 디스크립터를 반환한다.
long getFilePointer() 파일의 포인터의 위치를 알려 준다.
long length() 파일의 크기를 얻을 수 있다.(byte단위)
void seek(long pos) 첫 부분부터 pos크기만큼 떨어진 곳까지 파일 포인터의 위치를 변경한다. 
void setLength(long newLength) 파일의 크기를 지정된 길이로 변경한다.(byte단위)
int skipBytes(int n) 지정된 수만큼 byte를 건너뛴다.

RandomAccessFile의 인스턴스를 "rw" mode로 생성할 때, 지정된 파일이 없으면 새로운 파일을 생성한다.

 

public class RandomAccessFileEx1 {
	public static void main(String[] args) {
		try {
			RandomAccessFile raf = new RandomAccessFile("test.dat", "rw");
			System.out.println("파일 포인터의 위치: " + raf.getFilePointer());
			raf.writeInt(100);
			System.out.println("파일 포인터의 위치: " + raf.getFilePointer());
			raf.writeLong(100L);
			System.out.println("파일 포인터의 위치: " + raf.getFilePointer());

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

 

파일 포인터의 위치: 0
파일 포인터의 위치: 4
파일 포인터의 위치: 12

이 예제는 파일에 출력작업이 수행되었을 때 파일 포인터의 위치가 어떻게 달라지는지에 대해서 보여 준다.

 

 

 

File

파일은 기본적이면서도 가장 많이 사용되는 입출력 대상이기 때문에 중요하다.

그래서 관련된 기본적인 내용뿐 만 아니라 다양한 활용예제들을 실었다. 이 들을 응용해서 다양한 예제들을 만들어 보면 실력향상에 많은 도움이 될 것이다.

자바에서는 File클래스를 통해서 파일과 디렉토리를 다룰 수 있도록 하고 있다. 그래서 File인스턴스는 파일 일 수도 있고 디렉토리일 수도 있다.

생성자/메서드 설명
File(String fileName) 주어진 문자열(fileName)을 이름으로 갖는 파일을 위한 File인스턴스를 생성한다.파일 뿐만 아니라 디렉토리도 같은 방법으로 다룬다.
여기서 fileName은 주로 경로(path)를 포함해서 지정해주지만, 파일 
File(String pathName, String fileName)
File(File pathName, String fileName)
파일의 경로와 이름을 따로 분리해서 지정할 수 있도록 한 생성자. 이 중 두 번째 것은 경로를 문자열이 아닌 File인스턴스인 경우를 위해서 제공된 것이다.
File(URI uri) 지정된 uri로 파일을 생성
String getName() 파일이름을 String으로 반환
String getPath() 파일의 경로(path)를 String으로 반환
String getAbsolutePath()
File getAbsoluteFile()
파일의 절대경로를 String으로 반환
파일의 절대경로를 File로 반환
String getParent()
File getParentFile()
파일의 조상 디렉토리를 String으로 반환
파일의 조상 디렉토리를 File로 반환
String getCanonicalPath()
File getCanonicalFile()
파일의 정규경로를 String으로 반환
파일의 정규경로를 File로 반환

 

멤버변수 설명
static String pathSeparator OS에서 사용하는 경로(path) 구분자. 윈도우";", 유닉스"."
static char pathSeparatorChar OS에서 사용하는 경로(path) 구분자.
윈도우에서는 ';', 유닉스':'
static String separator OS에서 사용하는 이름 구분자. 윈도우 "\", 유닉스"/"
static char separatorChar OS에서 사용하는 이름 구분자. 윈도우"\", 유닉스"/"

파일의 경로(path)와 디렉토리나 파일의 이름을 구분하는 데 사용 되는 구분자가 OS마다 다를 수 있기 때문에, OS독립적으로 프로그램을 작성하기 위해서는 반드시 위의 멤버변수들을 이용해야한다. 만일 윈도우에서 사용하는 구분자를 코드에 직접 적어 놓았다면, 이 코드는 다른 OS에서는 오류를 일으킬 수 있다.

 

1. 이미 존재하는 파일을 참조할 때 :
File f = new File("c:\\jdk1.8\\work\\ch15", "FileEx1.java");
2. 기존에 없는 파일을 새로 생성할 때 :
File f = new File("c:\\jdk1.8\\work\\ch15", "NewFile.java");
f.creadteNewFile();

 

 

 

메서드 설명
boolean canRead() 읽을 수 있는 파일인지 검사한다.
boolean canWrite() 쓸 수 있는 파일인지 검사한다.
boolean canExecute() 실행할 수 있는 파일인지 검사한다.
int compareTo(File pathname) 주어진 파일 또는 디렉토리를 비교한다. 같으면 0을 반환하며, 다르면 1 또는 -1을 반환한다.(Unix시스템에서는 대소문자를 구별하며, Windows에서는 구별하지 않는다)
boolean exists() 파일이 존재하는지 검사한다.
boolean isAbsolute() 파일 또는 디렉토리가 절대경로명으로 지정되었는지 확인한다.
boolean isDirectory() 디렉토리인지 확인한다.
boolean isFile() 파일인지 확인한다.
boolean isHidden() 파일의 속성이 '숨김(Hidden)'인지 확인한다.
또한 파일이 존재하지 않으면 false를 반환한다.
boolean creadteNewFile() 아무런 내용이 없는 새로운 파일을 생성한다.(단, 생성하려는 파일이 이미 존재하면 생성되지 않는다.)
File f = new File("c:\\jdk1.8\\work\\test3.java");
f.createNewFile();
static File createTempFile
(String prefix, String suffix)
임시파일을 시스템의 임시 디렉토리에 생성한다.
System.out.println(File.createTempFile("work",".tmp"));
결과 : c:\windows\TEMP\work14247.tmp
static File createTempFile
(String prefix, String suffix, File directory)
임시파일을 시스템의 지정된 디렉토리에 생성한다.
boolean delete() 파일을 삭제한다.
void deleteOnExite() 응용 프로그램 종료시 파일을 삭제한다. 주로 실행 시 작업에 사용된 임시파일을 삭제하는데 사용된다.
boolean equals(Object obj) 주어진 객체(주로 File인스턴스)가 같은 파일인지 비교한다. (Unix시스템에서는 대소문자를 구별하며, Windows에서는 구별하지 않는다.)
long lastModified() 파일의 마지막으로 수정된 시간을 지정된 시간으로 반환
long length() 파일의 크기를 반환한다.
String[] list() 디렉토리의 파일목록(디렉토리 포함)을 String배열로 반환한다.
String[] list(FilenameFilter filter)
File[] list(FilenameFilter filter)
FilenameFilter인스턴스에 구현된 조건에 맞는 파일을 String배열(File배열)로 반환한다.
File[] listFiles()
File[] listFiles(FileFilter filter)
File[] listFiles(FilenameFilter f)
디렉토리의 파일목록(디렉토리 포함)을 File배열로 반환(filter가 지정된 경우에는 filter의 조건과 일치하는 파일만 반환)
static File[] listRoots()
long getFreeSpace()
long getTotalSpace()
long getUsableSpace()
컴퓨터 파일시스템의 root의 목록(floppy, CD-ROM, HDD drive)을 반환(예: A:\, D:\)
get으로 시작하는 메서드들은 File이 root일 때, 비어있는 공간, 전체 공간, 사용가능한 공간을 바이트 단위로 반환
boolean mkdir()
boolean mkdrs()
파일에 지정된 경로로 디렉토리(폴더)를 생성, 성공하면 true mkdirs는 필요하면 부모 디렉토리까지 생성
boolean renameTo(File dest) 지정된 파일(dest)로 이름을 변경
boolean setExcutable(boolean excutable)
boolean setExcutable(boolean excutable, boolean ownerOnly)
boolean setReadable(boolean readable)
boolean setReadable(boolean readable, boolean wonerOnly)
boolean setReadOnly()
boolean setWritable(boolean writable)
boolean setWritable(boolean writable, boolean ownerOnly)
파일의 속성을 변경한다.
OwnerOnly가 true면, 파일의 소유자만 해당 속성을 변경할 수 있다.
boolean setLastModified(long t) 파일의 마지막으로 수정된 시간을 지정된 시간(t)로 변경
Path toPath() 파일을 Path로 변환해서 반환
URI toRUI() 파일 URI로 변환해서 반환

 

BufferedReader/BufferedWriter는 버퍼를 이용해서 입출력의 효율을 높일 수 있도록 해주는 역할을 한다. 버퍼를 이용하면 입출력의 효율이 비교할 수 없을 정도로 좋아지기 때문에 사용하는 것이 좋다.

BufferedReader의 readLine()을 사용하면 데이터를 라인단위로 읽을 수 있고 BufferedWriter는 newLine()이라는 줄바꿈 해주는 메서드를 가지고 있다.

public class BufferedReaderEx1 {
	public static void main(String[] args) {
		try {
			FileReader fr = new FileReader("src/a210727/BufferedReaderEx1.java");
			BufferedReader br = new BufferedReader(fr);
			
			String line = "";
			for(int i=1;(line = br.readLine())!=null;i++) {
				if(line.indexOf(";")!=-1)
					System.out.println(i+":"+line);
			}
			br.close();
		} catch(Exception e) {}
	}

}
1:package a210727;
3:import java.io.BufferedReader;
4:import java.io.FileReader;
9:			FileReader fr = new FileReader("src/a210727/BufferedReaderEx1.java");
10:			BufferedReader br = new BufferedReader(fr);
12:			String line = "";
13:			for(int i=1;(line = br.readLine())!=null;i++) {
14:				if(line.indexOf(";")!=-1)
15:					System.out.println(i+":"+line);
17:			br.close();

BufferedReader의 readLine()을 이용해서 파일의 라인단위로 읽은 다음 indexOf()를 이ㅛㅇㅇ해서 ':'를 포함하고 있는지 확인하여 출력하는 예제이다. 파일에서 특정 문자 또는 문자열을 포함한 라인을 쉽게 찾아낼 수 있음을 보여 준다.

 

 

 

InputStreamReader와 OutputStreamWriter

InputStreamReader/OutputStreamWriter는 이름에서 알 수 있는 것과 같이 바이트기반 스트림을 문자기반 스트림으로 연결시켜주는 역할을 한다. 그리고 바이트기반 스트림의 데이터를 지정된 인코딩의 문자데이터로 변환하는 작업을 수행한다.

InputStreamReader/OutputStreamWriter는 Reader/Writer의 자손이다.

 

생성자/메서드 설명
InputStreamReader(InputStream in) QS에서 사용하는 기본 인코딩의 문자로 변환하는 InputStreamReader를 생성한다
InputStreamReader(InputSteram in, String encoding) 지정된 인코딩을 사용하는 InputStreamReader를 생성한다.
String getEncoding() InputStreamReader의 인코딩을 알려 준다.

 

생성자/메서드 설명
OutputStream(OutputStream out) OS에서 사용하는 기본 인코딩의 문자로 변환하는 OutputStreamWriter를 생성한다.
OutputStreamWriter(OutputStream out, String encoding) 지정된 인코딩을 사용하는 OutputStreamWriter를 생성한다.
String getEncoding() OutputStreamWriter의 인코딩을 알려 준다.

한글윈도우에서 중국어로 작성된 파일을 읽을 때 InputStreamReader(InputStream in, String encoding)를 이용해서 인코딩이 중국어로 되어 있다는 것을 지정해주어야 파일의 내용이 깨지지 않고 올바르게 보일 것이다. 인코딩을 지정해 주지 않는다면 OS에서 사용하는 인코딩을 사용해서 파일을 해석해서 보여 주기 때문에 원래 작성된 대로 볼 수 없을것이다.

이와 마찬가지로 OutputStreamWriter를 이용해서 파일에 텍스트데이터를 저장할 때 생성자 OutputStreamWriter(OutputStream out, String encoding)를 이용해서 인코딩을 지정하지 않으면 OS에서 사용하는 인코딩으로 데이터를 저장할 것이다.

시스템 속성에서 sun.jnu.encoding의 값을 보면 OS에서 사용하는 인코딩의 종류를 알 수 있다.

FilterInputStream/FilterOutputStream은 InputStream/OutputStream의 자손이면서 모든 보조스트림의 조상이다. 보조스트림은 자체적으로 입출력을 수행할 수 없기 때문에 기반스트림을 필요로 한다.

protected FilterInputStream(InputStream in)
public	filterOutputStream(OutputStream out)

FilterInputStream/FilterOutputStream의 모든 메서드는 단순히 기반스트림의 메서드를 그대로 호출할 뿐이다. FilterInputStream/FilterOutputStream자체로는 아무런 일도 하지 않음을 의미한다. FilterInputStream/FilterOutputStream은 상속을 통해 원하는 작업을 수행하도록 읽고 쓰는 메서드를 오버라이딩해야한다. 생성자 FilterInputStream(InputStream in)는 접근 제어자가 protected이기 때문에 Filter InputStream의 인스턴스를 생성해서 사용할 수 없고 상속을 통해서 오버라이딩되어야 한다. FilterInputStream/FilterOutputStream을 상속받아서 기반스트림에 보조기능을 추가한 보조스트림 클래스는 다음과 같다.

FilterInputStream의 자손	BufferedInputStream, DataInputStream, PushbackInputStream 등
FilterOutputStream의 자손	BufferedOutputStream, DataOutputStream, PrintStream 등

 

 

 

BufferedInputStream과 BufferedOutputStream

BufferedInputStream/BufferedOutputStream은 스트림의 입출력 효율을 높이기 위해 버퍼를 사용하는 보조스트림이다.

한 바이트씩 입출력하는 것 보다는 버퍼(바이트배열)를 이용해서 한 번에 여러 바이트를 입출력하는 것이 빠르기 때문에 대부분의 입출력 작업에 사용된다.

 

생성자 설명
BufferedInputStream(InputStream in, int size) 주어진 InputStream인스턴스를 입력소스(input source)로하며 지정된 크기(byte단위)의 버퍼를 갖는 BufferedInputStream인스턴스를 생성한다.
BufferedInputStream(InputStream in) 주어진 InputStream인스턴스를 입력소스(input source)로 하며 버퍼의 크기를 지정해주지 않으므로 기본적으로 8192byte 크기의 버퍼를 가젝 된다.

bufferedInputStream의 버퍼크기는 입력소스로부터 한 번에 가져올 수 있는 데이터의 크기로 지정하면 좋다.

프로그램에서 입력소스로부터 데이터를 읽기 위해 처음으로 read메서드를 호출하면, BufferedInputStream은 입력소스로 부터 버퍼 크기만큼의 데이터를 읽어다 자신의 내부 버퍼에 저장한다. 이제 프로그램에서는 BufferedInputStream의 버퍼에 저장된 데이터를 읽으면 되는 것이다. 외부의 입력소스로 부터 읽는 것보다 내부의 버퍼로 부터 읽는 것이 훨씬 빠르기 때문에 그만큼 작업 효율이 높아진다.

 

 

메서드/생성자 설명
BufferedOutputStream(OuputStream out,int size) 주어진 OutputStream인스턴스를 출력소스(outputsource)로하며 지정된 크기(단위byte)의 버퍼를 갖는 BufferedOutputStream인스턴스를 생성한다.
BufferedOutputStream(OutputStream out) 주어진 OutputStream인스턴스를 출력소스(output ource)로하며 버퍼의 크기를 지정해주지 않으므로 기본적으로 8192 byte 크기의 버퍼를 갖게 된다.
flush() 버퍼의 모든 내용을 출력소스에 출력한 다음, 버퍼를 비운다.
close() flush()를 호출해서 버퍼의 모든 내용을 출력소스에 출력하고, bufferedOutputStream인스턴스가 사용하던 모든 자원을 반환한다.

BufferedOutputStream 역시 버퍼를 이용해서 출력소스와 작업을 하게 되는데, 입력소스로부터 데이터를 읽을 때와 반대로, 프로그램에서 write메서드를 이용한 출력이 BufferedOutputStream의 버퍼에 저장된다, 버퍼가 가득 차면, 그 때 버퍼의 모든내용을 출력소스에 출력한다. 그리고는 버퍼를 비우고 다시 프로그램으로부터의 출력을 저장할 준비를 한다.

버퍼가 가득 찼을 때문 출력소스에 출력을 하기 때문에, 마지막 출력부분이 출력소스에 쓰이지 못하고 BufferedOutputStream의 버퍼에 남아있는 채로 프로그램이 종료될 수 있다는 점을 주의해야한다.

따라서 프로그램에서 모든 출력작업을 마친 후 BufferedOutputStream에 close()나 flush()를 호출해서 마지막에 버퍼에 있는 모든 내용이 출력소스에 출력되도록 해야 ㅎ나다.

BufferedOutputStream의 close()는 flush()를 호출하여 버퍼의 내용을 출력스트림에 쓰도록 한 후, BufferedOutputSream인스턴스의 참조변수에 null을 지정함으로써 사용하던 자원들이 반환되게 한다.

 

 

public class BufferedOutputStreamEx1 {
	public static void main(String[] args) {
		try {
			FileOutputStream fos = new FileOutputStream("123.txt");
			BufferedOutputStream bos = new BufferedOutputStream(fos, 5);
			for (int i = '1'; i <= '9'; i++) {
				bos.write(i);
			}

			fos.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

이 예제에서 fos.close()를 호출해서 스트림을 닫아주기는 했지만, 이렇게 해서는 BufferedOutputStream의 버퍼에 있는 내용이 출력되지 않는다. bos.close();와 같이 해서 BufferedOutputStream의 close()를 호출해야 주어진 버퍼에 남아있떤 모든 내용이 출력된다.

 

 

 

3.5 PrintStream

PrintStream은 데이터를 기반스트림에 다양한 형태로 출력할 수 있는 print, println, printf와 같은 메서드를 오버로딩하여 제공한다.

 PrintStream은 데이터를 적절한 문자로 출력하는 것이기 때문에 문자기반 스트림의 역할을 수행한다. 그래서 JDK1.1에서 부터 PrintStream보다 향상된 기능의 문자기반 스트림인 PrintWriter가 추가되었으나 그 동안 매우 빈번히 사용되던 System.out이  PrintStream이다 보니 둘 다 사용할 수밖에 없게 되었다.

PrintStream과 PrintWriter는 거의 같은 기능을 가지고 있지만 PrintWriter가 PrintStream에 비해 다양한 언어의 문자를 처리하는데 적합하기 때문에 가능하면 PrintWriter를 사용하는것이 좋다.

PrintStream은 System클래스의 static멤버인 out과 err, 즉 System.out, System.err이 PrintStream이다.

 

생성자/메서드 설명
PrinStream(File file)
PrinStream(File file, String scn)
PrinStream(OutputStream out)
PrinStream(OutputStream out, boolean autoFlush)
PrinStream(OutputStream out, boolean autoFlush, String encoding)
PrinStream(String fileName)
PrinStream(String fileName, String scn)
지정된 출력스트림을 기반으로 하는 PrintStream인 스턴스를 생성한다. autoFlush의 값을 true로 하면 println메서드가 호출되거나 개행문자가 출력될 때 자동으로 flush된다. 기본값은 false이다.
boolean checkError() 스트림을 flush하고 에러가 발생했는지를 알려 준다.
void print
void print
void print
void print
void print
void print
void print
void print
void print
void println
void println
void println
void println
void println
void println
void println
void println
void println
인자로 주어진 값을 출력소스에 문자로 출력한다.
println메서드는 출력 후 줄바꿈을 하고, print메서드는 줄을 바꾸지 않는다.
void println 줄바꿈 문자(line separator)를 출력함으로써 줄을 바꾼다.
PrintStream printf 정형화된(formatted)출력을 가능하게 한다.
protected void setError() 작업 중에 오류가 발생했음을 알린다.
(setError()를 호출한 후에, checkError()를 호출하면 true를 반환한다.)

print()나 println()을 이용해서 출력하는 중에 PrintStream의 기반스트림에서 IOException이 발생하면 checkError()를 통해서 인지할 수 있다. println()이나 print()는 예외를 던지지않고 내부에서 처리하도록 정의하였는데, 그 이유는 println()과 같은 메서드가 매우 자주 사용되는 것이기 때문이다./

만일 println()이 예외를 던지도록 정의되었다면 println()을 사용하는 모든 곳에 try-catch문을 사용해야 할 것이다.

 

public class PrintStream extends FileOutputStream implements Appendable, Closeable {
	...
	private boolean trouble = false;
	public void print(int i) {
		write(String.valueOf(i));
	}
	private void write(String s) {
		try {
			...
		} catch (IOExcepton x) {
			trouble = true;
		}
}
	...
	public boolean checkError() {
		if(out != null) flush();
		return trouble;
	}
}

i+와 String.valueOf(i)는 같은 결과를 얻집만, String.valueOf(i)가 더 성능이 좋다.

 

printf()는 JDK.15부터 추가된 것으로, C언어와 같이 편리한 형식화된 출력을 지원하게 되었다. printf()에 사용될 수 있는 옵션은 꽤나 다양한데 그에 대한 자세한 내용은 JAVAAPI문서에서 Formatter클래스를 참고하면 된다.

 

format 설명 결과(int i=65)
%d 10진수(decimal integer) 65
%o 8진수(ocrtal integer) 101
%x 16진수(hexadecimal integer) 41
%c 문자 A
%s 문자열 65
%5d 5자리 숫자. 빈자리는 공백으로 채운다    65
%-5d 5자리 숫자. 빈자리는 공백으로 채운다(왼쪽 정렬) 65
%05d 5자리 숫자. 빈자리는 0으로 채운다. 00065

 

 

format 설명 결과(String str = "ABC")
%s 문자열(string) ABC
%5s 5자리 문자열. 빈자리는 공백으로 채운다.   ABC
%-5s 5자리 문자열. 빈자리는 공백으로 채운다.(왼쪽 정렬) ABC

 

 

format 설명 결과(float f = 1234.56789f)
%e 지수형태표현(exponent) 1.234568e+03
%f 10진수(decimal float) 1234.56789
%3.1f 출력될 자리수를 최소 3자리(소수점포함). 소수점 이하 1자리(2번째 자리에서 반올림) 1234.6
%8.1f 소수점이상 최소 6자리. 소수점 이하 1자리.
출력될 자리수를 최소 8자리(소수점포함)를 확보한다. 빈자리는 공백으로 채워진다.(오른쪽 정렬)
  1234.6
%08.1f 소수점이상 최소 6자리, 소수점 이하 1자리.
출력될 자리수를 최소 8자리(소수점포함)를 확보한다. 빈자리는 0으로 채워진다.
001234.6
%-8.1f 소수점이상 최소 6자리, 소수점 이하 1자리
출력될 자리수를 최소 8자리(소수점포함)를 확보한다. 빈자리는 공백으로 채워진다.(왼쪽 정렬)
1234.6
1234.6

 

format 설명
\t 탭(tab)
%n 줄바꿈 문자(new line)
%% %

 

 

format 설명 결과
%tR
%tH:%tM
시분(24시간) 21:05
21:05
%tT
%tH:%tM:%tS
시분초(24시간) 21:05:33
21:05:33
%tD
%tm/%td/%ty
월일년 11/16/15
11/16/15
%tF
%tY-%tm-%td
년월일 2015-11-16
2015-11-16

날짜와 시간에 사용될 수 있는 옵션

InputStream과 OutpurStream

앞서 얘기한 바와 같이 InputStream과 OutpurStream은 모든 바이트기반의 스트림의 조상이며 다음과 같은 메서드가 선언되어 있다.

메서드명 설명
int available() 스트림으로부터 읽어 올 수 있는 데이터의 크기를 반환한다.
void close() 스트림을 닫음으로써 사용하고 있던 자원을 반환한다.
void mark(int readlmit) 현재위치를 표시해 놓는다. 후에 reset( )에 의해서 표시해 놓은 위치로 다시 돌아갈 수 있다. readlimit은 되돌아갈 수 있는 byte의 수이다.
boolean markSupported( ) mark( )와 reset( )을 지원하는지를 알려준다. mark( )와 reset( )기능을 지원하는 것은 선택적이므로, mark( )와 reset( )을 사용하기 전에 markSupported( )를 호출해서 지원여부를 확인해야 한다.
abstract int read() 1 byte를 읽어 온다(0~255사이의 값), 더 이상 읽어 올 데이터가 없으면 -1을 반환한다. abstract메서드라서 InputStream의 자손들은 자신의 상황에 알맞게 구현해야한다.
int read(byte[ ] b) 배열 b의 크기만큼 읽어서 배열을 채우고 읽어 온 데이터의 수를 반환한다. 반환하는 값은 항상 배열의 크기보다 작거나 같다.
int read(byte[ ] b,
int off, int len
최대 len개의 byte를 읽어서, 배열 b의 지정된 위치(off)부터 저장한다. 실제로 읽어 올 수 있는 데이터가 len개보다 적을 수 있다.
void reset( ) 스트림에서의 위치를 마지막으로 mark( )이 호출되었던 위치로 되돌린다.
long skip(long n) 스트림에서 주어진 길이(n만큼을 건너뛴다.

 

 

메서드명 설명
void close( ) 입력소스를 닫음으로써 사용하고 있던 자원을 반환한다.
void flush( ) 스트림의 버퍼에 있는 모든 내용을 출력소스에 쓴다.
abstract void write(int b) 주어진 값을 출력소스에 쓴다
void write(byte[ ] b) 주어진 배열 b에 저장된 모든 내용을 출력소스에 쓴다
void write(byte[ ] b,
int off, int len)
주어진 배열 b에 저장된 내용 중에서 off번째부터 len개 만큼만을 읽어서 출력소스에 쓴다.

스트림의 종류에 따라서 mark( )와 reset( )을 사용하여 이미 읽은 데이터를 되돌려서 다시 읽을 수 있다.

markSupported( )를 통해서 사용가능여부를 알 수 있다.

flush( )는 버퍼가 있는 출력스트림의 경우에만 의미가 있으며 OutputStream에 정의된 flush( )는 아무런 일도 하지 않는다. flus( )는 수시로 사용해주어 부담을 덜어주는게 좋다.

또한 스트림을 사용해서 모든 작업을 마치고 난 후에는 close()를 호출해서 반드시 닫아주어야 한다. 그러나 ByteArrayStream과 같이 메모리를 사용하는 스트림과 System.in, System.out과 같은 표준 입출력 스트림은 닫아 주지 않아도 된다.

스트림의 종류가 달라도 읽고 쓰는 방법은 동일하다.

 

 

ByteArrayInputStream과 ByteArrayOutStream

ByteArrayInputStream/ByteArrayOutStream은 메모리, 즉 바이트배열에 데이터를 입출력 하는데 사용되는 스트림이다. 다른 곳에 입출력하기 전에 데이터를 임시로 바이트배열에 담아서 변환 등의 작업을 하는데 사용된다.

class IOEx1 {
	public static void main(String[] args) {
		byte[] inSrc = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
		byte[] outSrc = null;
		
		ByteArrayInputStream input = null;
		ByteArrayOutputStream output = null;
		
		input = new ByteArrayInputStream(inSrc);
		output = new ByteArrayOutputStream();
		
		int data = 0;
		
		while((data = input.read())!=-1) {
			output.write(data);
		}
		
		outSrc = output.toByteArray();
		
		System.out.println("Input Source :"+ Arrays.toString(inSrc));
		System.out.println("Output Source :"+ Arrays.toString(outSrc));
	}
}

여기서 while안의 식이 !=-1인데 이것은 input.read로 읽은값을 data에 저장해서 data가 -1이되면 종료되는 식이다.

 

public class IOEx4 {
	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;

		input = new ByteArrayInputStream(inSrc);
		output = new ByteArrayOutputStream();

		try {
			while (input.available() > 0) {
				int len = input.read(temp);
				output.write(temp, 0, len);
			}
		} catch (Exception 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));
	}
}
Input Source	:[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
temp		:[8, 9, 6, 7]
Output Source   :[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

 

 

FileInputStream과 FileOutpurStream

 

생성자 설명
FileInputStream(String name) 지정된 파일이름(name)을 가진 실제 파일과 연결된 FileInputStream을 생성한다.
FileInputStream(File file) 파일의 이름이 String이 아닌 File인스턴스로 지정해주어야 하는 점을 제외하고 FileInputStream(String name)와 같다.
FileInputStream(FileDescriptor fdObj) 파일 디스크립터(fdObj)로 FileInputStream을 생성한다.
FileOutputStream(String name) 지정된 파일이름(name)을 가진 실제 파일과의 연결된 FileOutputStream을 생성한다.
FileOutputStream(String name, boolean append) 지정된 파일이름(name)을 가진 실제 파일과 연결된 FileOutputStream을 생성한다. 두번째 인자인 append를 true로하면, 출력 시 기존의 파일내용의 마지막에 덧붙인다. false면 기존의 파일내용을 덮어쓰게 된다.
FileOutputStream(File file) 파일의 이름을 String이 아닌 File인스턴스로 지정해주어야 하는점을 제외하고 FileOutputStream(String name)과 같다.
FileOutputStream(File file, bollean append) 파일 이름을 String이 아닌 File인스턴스로 지정해주어야 하는 점을 제외하고 FileOutputStream(String name, boolean append)과 같다.
FileOutputstream(FileDescriptor fdObj) 파일 디스크립터(fdObj)로 FileOutputStream을 생성한다.

 

I/O란  Input과 Output의 약자로 입력과 출력, 간단히 줄여서 입출력이라고 한다. 입출력은 컴퓨터 내부 또는 외부의 장치와 프로그램 간의 데이터를 주고받는 것을 말한다.

 

스트림(stream)

자바에서 어느 한쪽에서 다른 쪽으로 데이터를 전달하려면, 두 대상을 연결하고 데이터를 전송할 수 있는 무언가가 필요한데 이것을 스트림(stream)이라고 정의했다.

스트림이란 데이터를 운반하는데 사용되는 연결통로이다.

스트림은 단방향통신만 가능하기 때문에 하나의 스트림으로 입력과 출력을 동시에 처리할 수 없다.

 

스트림은 먼저 보낸 데이터를 먼저 받게 되어 있으며 중간에 건너뜀 없이 연속적으로 데이터를 주고받는다.

큐(queue)와 같은 FIFO(First In First Out) 구조로 되어 있다고 생각하면 이해하기 쉽다.

 

 

바이트 기반 스트림 - InputStream, OutputStream

스트림은 바이트단위로 데이터를 전송하며 입출력 대상에 따라 다음과 같은 입출력 스트림이 있다.

입력스트림 출력스트림 입출력 대상의 종류
FileInputStream FileOutputStream 파일
ByteArrayInputStream ByteArrayOutputStream 메모리(byte배열)
PipedInputStream PipedOutputStream 프로세서(프로세스간의 통신)
AudioInputStream AudioOutputStream 오디오장치

자바에서는 java.io패키지를 통해서 많은 종류의 입출력관련 클래스들을 제공하고 있으며, 입출력을 처리할 수 있는 표준화된 방법을 제공함으로써 입출력의 대상이 달라져도 동일한 방법으로 입출력이 가능하기 때문에 프로그래밍을 하기에 편리하다.

 

InputStream OutputStream
abstract int read() abstract void write(int b)
int read(byte[ ] b) void write(byte[ ] b)
int read (byte[ ] b, int off, int len) void write(byte[ ] b, int off, int len)

read()의 반환타입이 byte가 아니라 int인 이유는 read()의 반환 값의 범위가 0~255와 -1이기 때문이다.

 

InputStream의 read()와 OutputStream의 write(int b)는 입출력의 대상에 따라 읽고 쓰는 방법이 다를 것이기 때문에 상황에 알맞게 구현하라는 의미에서 추상 메서드로 정의되어 있다.

 

 

 

보조 스트림

스트림의 기능을 보완하기 위한 보조스트림이 제공된다. 보조 스트림은 실제 데이터를 주고받는 스트림이 아니기 때문에 데이터를 입출력할 수 있는 기능은 없지만, 스트림의 기능을 향상하거나 새로운 기능을 추가할 수 있다. 그래서 보조 스트림만으로는 입출력을 처리할 수 없고, 스트림을 먼저 생성한 다음에 이를 이용해서 보조 스트림을 생성해야 한다.

 

// 먼저 기반스트림을 생성한다.
FileInputStream fis = new FileInputStream("test.txt");
// 기반스트림을 이용해서 보조스트림을 생성한다.
BufferedInputStream bis = new BufferedInputStream(fis);
bis.read();

코드 상으로는 보조스트림인 BufferedInputStream이 입력 기능을 수행하는 것처럼 보이지만, 실제 입력 기능은 BufferedInputStream과 연결된 FileInputStream이 수행하고, 보조 스트림인 BufferedInputStream은 버퍼만을 제공한다.

버퍼를 사용한 입출력이 빠르기 때문에 버퍼를 이용한 보조스트림을 사용한다.

 

문자 기반 출력 설명
FilterInputStream FilterOutputStream 필터를 이용한 입출력 처리
BufferedInputStream BufferedOutputStream 버퍼를 이용한 입출력 성능향상
DataInputStream DataOutputStream int, flaoat와 같은 기본형 단위(primitive type)로 데이터를 처리하는 기능
SequenceInputStream 없음 두 개의 스트림을 하나로 연결
LineNumberInputStream 없음 읽어 온 데이터 라인의 번호를 카운트(JDK1.1부터LineNumberReader로 대체)
ObjectInputStream ObjectOutputStream 데이터를 객체단위로 읽고 쓰는데 사용
주로 파일을 이용하며 직렬화와 관련있음
없음 PrintOutputStream 버퍼를 이용하며, 추가적인 print관련 기능
(print, printf, println메서드)
pushbackInputStream 없음 버퍼를 이용해서 읽어 온 데이터를 다시 되돌리는 기능(unread, push back to buffer)

 

 

 

스트림 - Reader, Writer

문자 데이터를 입출력할 때는 바이트 기반 스트림 대신 문자 기반 스트림을 사용하자.

InputStream > Reader
OutputStream > Writer

 

바이트기반 스트림 문자기반 스트림
FileInputStream
FileOutputStream
FileReader
FileWriter
ByteArrayInputStream
ByteArrayOutputStream
CharArrayReader
CharArrayWriter
PipedInputStream
PipedOutputStream
PipedReader
PipedWriter
StringBufferInputStream(deprecated)
StringBufferOutputStream(deprecated)
StringReader
StringWriter

StringBufferInputStream, StringBufferOutputStream은 StringReader와 StringWriter로 대체되어 더 이상 사용되지 않는다.

 

문자기반 스트림의 이름은 바이트 기반 스트림의 이름에서 InputStream은 Reader로 OutputStream은 Writer로만 바꾸면 된다. 단, ByteArrayInputStream에 대응하는 문자 기반 스트림은 char배열을 사용하는 CharArrayReader이다.

Reader와 Writer에서도 역시 추상메서드가 아닌 메서드들은 추상 메서드를 이용해서 작성되었으며, 프로그래밍적인 관점에서 볼 때 read()를 추상 메서드로 하는 것보다 int read(char [] cbuf, int off, int len)를 추상 메서드로 하는 것이 더 바람직하다.

 

InputStream Reader
abstract int read()
int read(byte[] b)
int read(byte[] b, int off, int len)
int read()
int read(char[] cbuf)
abstract int read(char[] chuf, int off, int len)

 

OutputStream Writer
abstract void write(int b)
void write(byte[] b
void write(byte[] b, int off, int len)
void write(int c)
void write(char[] cbuf)
abstract void write(char[] cbuf, int off, int len)
void write(String str)
void write(String str, int off, int len)

 

 

바이트기반 보조스트림 문자기반 보조스트림
BufferedInterStream
BufferedOutputStream
BufferedReader
BufferedWriter
FilterInputStream
FilterOutputStream
FilterReader
FilterWriter
LineNumberInputStream(deprecated) LineNumberReader
PrintStream PrintWriter
PushbackInputStream PushbackReader

 

+ Recent posts