流式I/O
以byte为单位进行操作
流(Stream)分为两类:
节点流:以节点为单位录入或者输出
过滤流:通过对已经存在的流过滤得到,必须从已经存在的流对象构造。
当然,Stream区分为输入、输出两类,这是一个很容易混淆的概念,必须依照以下的话来区分:
可以从中读出一系列字节的对象称为输入流;能向其中写入一系列字节的对象称为输出流;
我把它简单地称为“输入流读出,输出流写入"
最基本的两个流类是InputStream和OutputStream,由此派生出一系列功能各异的流式类,它们重写或者继承了这两个类的方法,并加入各自独立的新方法,构成了完成不同的功能。
InputStream
常用方法列表:
abstract int read(),读取下一个字节的数据,并返回读到的数据的一个int型的值,如果返回了-1,则表示读到了输入流的结尾。这是一个虚函数,在不同的类中一般有这个类的重写,如system.in,这个类就重写了这个方法,该方法会读出所有字节中的数据直到末尾。
int read(byte[] b),将数据读入一个byte类型的数组,返回实际读取的数据个数,如果返回-1,则表示读到了数据的末尾
int read(byte[] b,int off,int len),将数据读入一个byte类型的数组,返回实际读取的数据个数,如果返回-1,则表示读到了数据的末尾,off指定读取的数据从b数组中的何处开始存放,它的范围是数组的下标的范围;len指定一次读取的数据长度,即读取多少byte位。
Long skip(long n),在输入流中跳过n个字节,并返回实际跳过的字节数
Int available(),在不发生阻塞情况下可以读取的字节数
Void close(),关闭于当前流相关的资源
Void mark(int readlimited),在流的当前位置放置一个标记,如果读取的字节数多于readlimited,则忽略这个标记;
Void reset(),返回到上一个标记;
Boolean markSupported(),测试当前的流是否支持mark和reset方法。
注意:并不是所有的InputStream流都支持mark()和reset()方法,这两个方法是空,需要重写。
OutputStream
常用方法列表
Abstract int write(int b),写入一个字节,一般会重写,如System.out重写了这个方法,是写入所有数据直到末尾
Void write(byte[] b),写入b数组中的所有数据
Void write(byte[] b,int off,int len),从数组b中的off指定位置开始写入长度为len个字节的数据
Void close(),关闭与当前流相关资源
另,在某些OutputStream的子类里,实现了缓冲输入的,有一个flush()方法,强制写入当前缓存中的所有数据而不是等到缓存满。
---------------------------------------------
一个简单的从标准输入设备(键盘)获得数据并显示在标准输出设备(显示器)上的程序
class SystemInuOutTest
{
public static void main(String[] args) throws Exception
{
int data;
while((data=System.in.read())!=-1)
{
System.out.write(data);
}
}
}
这个程序会运行直到你按下ctrl+C才会停止
---------------------------------------------
这里要介绍byte类型和string类型的转换。当我们要将一个String类型的数组写入流中怎么办?答案是java.lang.String类提供了byte[] getBytes()方法,将一个string类型转变为一个byte类型的数组。而byte数组转换为String类型的方法是调用String的构造函数String(byte[] b),类似的构造函数有很多,不一一列举。
---------------------------------------------
InputStream和OutputStream类的几个派生类
1.FileInputStream和FileOutputStream
节点流,用于从文件中读取数据(向文件中写入数据),如果在构造FileOutputStream时,文件已经存在,则这个文件会被覆盖;
2.BufferedInputStream和BufferedOutputStream
过滤流,需要通过已经存在的流来构造,提供带缓存的读写,提高了读写的效率,类BufferedOutputStream会用到前面提到过的flush()方法,强制缓存中的数据写入。
3.DataInputStream和DataOutputStream
过滤流,提供java中基本的数据类型的读出和写入。
4.PipedInputStream和PipedOutputStream
称为“管道流”,一般成对存在才有意义,用于线程之间的通信,如一个线程的PipedInputStream从另一个线程的PipedOutputStream获得数据。
-----------------------------------------------
代码:1.FileInputStream和FileOutputStream
import java.io.*;
class StreamTest
{
public static void main(String[] args) throws Exception
{
FileOutputStream fops=new FileOutputStream("hello.txt");
fops.write("hello,world".getBytes());
//String类型转换为一个bytes数组
FileInputStream fips=new FileInputStream("hello.txt");
byte[] by=new byte[100];
fips.read(by,0,15);
System.out.println(new String(by));
//如何将byte类型作为String输出
}
}
加入.BufferedInputStream和BufferedOutputStream后提供了带缓存的读写
import java.io.*;
class StreamTest
{
public static void main(String[] args) throws Exception
{
FileOutputStream fops=new FileOutputStream("hello.txt");
BufferedOutputStream bops=new BufferedOutputStream(fops);
bops.write("hello,world".getBytes());
//String类型转换为一个bytes数组
bops.flush();
//flush强制缓存中的数据写入
bops.close();
FileInputStream fips=new FileInputStream("hello.txt");
BufferedInputStream bips=new BufferedInputStream(fips);
byte[] by=new byte[100];
bips.read(by,0,15);
System.out.println(new String(by));
//如何将byte类型作为String输出
bips.close(); //只需要关闭最顶端的流
}
}
DataInputStream和DataOutputStream
import java.io.*;
public class IoTest {
public static void main(String[] args) throws IOException
{
FileOutputStream fops=new FileOutputStream("hello.txt");
DataOutputStream dops=new DataOutputStream(fops);
boolean b=true;
byte bb=1;
double d=1.234;
float f=1.23f;
String s="hello,world";
dops.writeBoolean(b);
dops.writeByte(bb);
dops.writeDouble(d);
dops.writeFloat(f);
dops.writeChars(s);
dops.close();
FileInputStream fips=new FileInputStream("hello.txt");
DataInputStream dips=new DataInputStream(fips);
System.out.println(dips.readBoolean());
System.out.println(dips.readByte());
System.out.println(dips.readDouble());
System.out.println(dips.readFloat());
byte[] by=new byte[100];
dips.read(by,0,30);
System.out.println(new String(by));
dips.close();
//读出和写入的顺序必须一直才能得到正确的结果
}
}
PipedInputStream和PipedOutputStream
大体的机制是这样,在某两个线程中,一个定义管道输入对象,另一个定义管道输出对象。在这两个线程类的外部,可以使用某两个成对(connect())的管道对像对线程中对应的管道赋值,然后可以实现这两个线程的通信。简单代码如下:
import java.io.*;
public class ThreadPipedStreamTest
{
public static void main(String[] args) throws Exception
{
PipedOutputStream pos=new PipedOutputStream();
PipedInputStream pis=new PipedInputStream();
pos.connect(pis);
//管道的输入输出流成对存在才有意义,此函数对其进行链接
try
{
new Producer(pos).start();
new Comsumer(pis).start();
}
catch(Exception e)
{
e.printStackTrace();
}
}
}
class Producer extends Thread
{
private PipedOutputStream pos;
Producer(PipedOutputStream pos)
{
this.pos=pos;
}
public void run()
{
try
{
byte[] b="hello,world".getBytes();
pos.write(b,0,b.length);
}
catch(IOException e)
{
e.printStackTrace();
}
}
}
class Comsumer extends Thread
{
private PipedInputStream pis;
private byte[] b=new byte[40];
Comsumer(PipedInputStream pis)
{
this.pis=pis;
}
public void run()
{
try
{
pis.read(b,0,30);
System.out.println(new String(b));
}
catch(IOException e)
{
e.printStackTrace();
}
}
}