JAVA中的输入流和输出流是装载和运送信息的管道,管道一端接到信息来源,另一端连接信息的目的地。文中分别介绍输入(InputStream)和输出流(OutputStream)
1、InputStream抽象类,是所有字节输入流的超类,直接继承Object,不能用构造器创建对象,只能通过子类创建对象。
字节输入流相关类的继承图
2、FileInputStream类,文件输入流,构造器创建对象时要传入文件对象或文件路径,即把输入流信息源端接到文件上,可以把文件内容读取到流中。常用的方法是read()和read(byte[] b),前者是依次读取每个字节,后者一次性把信息读取到byte数组中。
3、ByteArrayInputStream类,byte数组输入流,构造器参数是byte数组,与FileInputStream类似,把输入流的信息源端连接到byte数组上,读取byte数组中的内容。常用方法与FileInputStream相同。
实例1,用FileInputStream/ByteArrayInputStream的两个方法读取信息。
InputStream is = new FileInputStream(File file)//ByteArrayInputStream(byte[] b);
//依次读取每个字节,read()
int i;
while((i=is.read())!=-1){
System.out.println(i);//打印出读取内容
}
//一并读取到字节数组中,read(byte[])
byte[] b = new byte[is.available];
is.read(b);
String s = new String(b);//将字节数组转换成String
System.out.println(s);
上述代码分别用read()和read(byte[] b)读取文件//字节数组,是最基础的实例.
4、FilterInputStream,构造器是protected,故不能通过构造器创建对象,一般使
用其子类对象,嵌套在基本输入流(InputStream)上使用。
5、BufferedInputStream类是FilterInputStream的子类,为添加了mark,reset功 能。创建对象时会创建一个内部缓冲区数组,在缓冲区中可以操作输入流的内容。
6、DataInputStream类,FilterInputStream的子类,使用率非常高的输入流嵌套 这个高级管道后,输入流可以读取任何基本类型的数据,readInt()—读取4个字节组成Int返回。其他数据类型以此类推。
7、ObjectInputStream类,InputStream的子类,重构序列化内容的输入流, 继承DataInput接口,和DataInputStream一样可以读取基本数据类型。另外Object obj = readObject();方法返回obj对象,即返回重构的序列化对象(要先知道序列化文件中对象存储顺序)。一般和ObjectOutputStream一起使用。不能读取普通输入的txt文件,因为ObjectInputStream读取文件要先读取一Header,会导致文件编码出错。(随后的ObjectOutputStream中有实例)
8、 PipedInputStream类,必须要和PipedOutputStream类一同使用对接两个管道,Piped要在线程中使用,否则会造成单线程堵塞。连接的方法1,构造器中传入参数PipedInputStream pis = new PipedInputStream(PipedOutputStream pos),Out类与此相同,2,连接方法pis.connect(pos)或pos.connect(pis),其中pis和pos分别是PipedInputStream和PipedOutputStream的对象。需要注意的是不能重复连接,即构造器连接了就不要再用connect方法连接,不然会抛出异常。读取/写入方法分别是pis.read(byte[] b)和pos.write(byte[] b)。
9、SequenceInputStream类,串联多个输入流,依次读取输入流中内容,构造器参数有两种(InputStream s1,InputStream s2)和(Enumeration<InputStream> e),前者连接两个输入流,后者连接多个输入流。需要特别注意的是,虽然Sequence类提供了read(byte[] b)方法,但是该方法每次只能读取一个InputStream中的内容,方法available()返回的是第一个InputStream内容长度。所以,在Sequence中,只能用read()方法依次读取每个字节,再拼接起来。
实例2:串联两个输入流
//SequenceInputStream串联两个输入流
public void SequenceInputStream(){
try{
InputStream is = new FileInputStream(File file);//文件路径也可做参数
InputStream is1 = new FileInputStream(File file);
SequenceInputStream sis = new SequenceInputStream(is,is1);
//如果用sis.available结果与is.available相同
int len = is.available()+is1.available();
byte[] b = new byte[len];
int c = 0,count=0;
while((c=sis.read())!=-1){//依次读取每个字节,并存放在byte[]中
b[count] = (byte)c;
count++;
}
System.out.println("String是: "+new String(b));
}catch(Exception e){e.printStackTrace();}
}
实例3:串联多个输入流,只需对实例2稍作修改
//串联多个输入流
public static void SequenceEnumeration(){
try{
Vector<InputStream> v = new Vector();
v.addElement(new FileInputStream(File file);
v.addElement(new FileInputStream(File file);
v.addElement(new FileInputStream(File file);
Enumeration<InputStream> e = v.elements();
SequenceInputStream sis = new SequenceInputStream(e);
int len=0;
//长度仍需要依次累加
for(int i=0;i<v.size();i++){
len = len+ v.get(i).available();
}
byte[] b = new byte[len];
int c = 0,count=0;
while((c=sis.read())!=-1){
b[count] = (byte)c;
count++;
}
System.out.println("String是: "+new String(b));
}catch(Exception e){e.printStackTrace();}
}
下面介绍输出流,有联合了对应输入流的实例
1、OutputStream抽象类,是所有字节输出流的超类,有最基本的写入方法write(byte[] b),大多使用其子类。
字节输出流相关类的继承图
2、FileOutputStream类,文件输出流,将输出流中的内容输出到文件中,故构造器参数是文件对象或文件路径,构造器创建对象时就把流管道的输出端连到文件了。输出方法也是write(byte[] b)。但是,写入的文件路径不存在怎么办,路径存在恰好有同名文件夹怎么办?在附件FileOut.rar中有文件环境检测程序(很简单的程序)。
3、ByteArrayOutputStream类,字节数组输出流,把流中的内容输出到字节数组中。一般与DataOutputStream或ObjectOutputStream联合使用,作用就是把信息(基本数据类型或对象)转换成byte[],因为任何信息本质上说都是由字节组成,所以这样是返璞归真,把信息转化成了最本质的字节,信息经过传递,到另外一台主机上后,在用对应的输入流读取,还原信息原貌。
ByteArrayInputStream和ByteArrayOutputStream处理信息流程图
实例4:ByteArrayOutputStream和ObjectOutputStream的联合使用
代码中用上述两个输出流把对象序列化,并转化成字节数组,在用对应的输入流重构被序列化的对象。
节点对象代码如下:需要注意的是该节点对象必须连接Serializable接口,不然运行ObjectOutputStream会报错。
public class Node implements Serializable{
private String name;//节点名
private int weight;//权值
private int d;//度
public Node(String name,int weight,int d){
this.name = name;
this.weight = weight;
this.d = d;
}
//get(),set()方法略
}
主体代码:
public class ByteArrayOut{
public static byte[] b;
//创建一个ArrayList,里面放Node
public static ArrayList<Node> getList(){
ArrayList<Node> list = new ArrayList<Node>();
for(int i=0;i<10;i++){
Node node = new Node("N"+i,i*2,i+3);
list.add(node);
}
return list;
}
//把List中的Node对象放入输出流并转换成了byte[]
public static byte[] WriteObj(){
try{
ArrayList<Node> list = getList();
ByteArrayOutputStream os = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeInt(list.size());
for(int i=0;i<list.size();i++){
oos.writeObject(list.get(i));
}
byte[] b = os.toByteArray();
os.flush();
os.close();
System.out.println(b.length);
return b;
}catch(Exception e){e.printStackTrace();}
return null;
}
public static void AyaByteArray(byte[] b){
try{
ByteArrayInputStream bis = new ByteArrayInputStream(b);
ObjectInputStream ois = new ObjectInputStream(bis);
int len = ois.readInt();
System.out.println("读取出来的len:"+len);
ArrayList<Node> li = new ArrayList<Node>();
for(int i=0;i<len;i++){
Node node = (Node)ois.readObject();
li.add(node);
}
ois.close();
//遍历list
for(int i=0;i<li.size();i++){
Node node = li.get(i);
System.out.println("第"+i+"个node,name:"+node.getName()
+" 度:"+node.getD()+" 权重"+node.getWeight());
}
}catch(Exception ef){ef.printStackTrace();}
}
public static void main(String[] args){
byte[] b = ByteArrayOut.WriteObj();
ByteArrayOut.AyaByteArray(b);
}
}
上述实例完成了对象的序列化,把对象转化成一个一个的字节保存,随后又用保存的字节重构原对象,成功传递
对象。
4、FilterOutputStream类,所有缓冲输出流的父类,构造器是protected,不能创建对象,一般嵌套基本输出流使用。
5、BufferedOutputStream类,FilterOutputStream的子类,创建对象时提供缓冲区,要嵌套在基本输出流上使用。使用方法write(byte[] b)把字节数组的内容读到输出流的缓冲区。可以实现文件复制。
6、DataOutputStream类,FilterOutputStream的子类,也要嵌套在基本数据流上使用,可以把JAVA中的基本数据类型写入输出流中,如writeInt(Int i),writeUTF(String str)分别将Int,String到写到输出流中,到网络另一端再用对应输入流读取。DataOutputStream经常和ByteArrayOutputStream联合使用,以字节数组形式储存并传输基本数据类型。
7、ObjectOutputStream类,用特有的writeObj(Object obj)方法把对象读入输入流中,即序列化对象,将其保存。需要时调用ObjectInputStream重构被序列化的对象,与DataOutputStream相似,可以读基本数据类型到数据流。但对于方法writeBytes(String s),在ObjectInputStream中没有对应的读取方法(只有readByte(byte b),没有readBytes()),所以使用writeBytes(String s)时要先写入字符串长度,再写入字符串,读取时readByte(byte b)依次读取每个自己,或用writeUTF(String s)方法写入,对应的readUTF(String s)读取。
实例5:ObjectInputStream和ObjectOutputStream的写入和读取。
public class ObjectOut {
public static void writeObject(){
try{
FileOutputStream fos = new FileOutputStream("C:\\obj.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeInt(10);
oos.writeUTF("UTF_str");
Node node = new Node("name",12,12);
oos.writeObject(node);
oos.flush();
oos.close();
}catch(Exception e){e.printStackTrace();}
}
public static void readObject(){
try{
FileInputStream fis = new FileInputStream("C:\\obj.obj");
ObjectInputStream ois = new ObjectInputStream(fis);
int i = ois.readInt();
String str1 = ois.readUTF();
Node node = (Node)ois.readObject();
System.out.println("int是:"+i+" str1是:"+str1);
System.out.println("node的name:"+node.getName()
+" 度:"+node.getD()+" 权重"+node.getWeight());
}catch(Exception e){e.printStackTrace();}
}
public static void main(String[] args){
ObjectOut.writeObject();
ObjectOut.readObject();
}
}
8、
PipedOutputStream类,与PipedInputStream连接使用的专属类,构造器参数就是PipedInputStream。或者用方法connect()连接,不能多次连接。另外,Piped输入输出流绝大多数情况下用于多线程,一个线程写入信息,另一个线程读取。如果用单线程,则可能造成通道锁死。
至此,JAVA中字节输入流和输出流经常使用的类介绍完毕。流是传输信息的管道,两端分别连接信息源和目的地,例如,需要读取文件中内容时,用FileInputStream从文件中把信息读出来就行了,需要写入信息到文件中时,就用FileOutputStream把信息写入文件。更多时候,把流管道的一端连接到网络,在网络另一端用对应流接收,就实现网络之间的信息传递。
写得不好的地方,敬请批评指正。