黑马程序员-IO(管道流、切割流)

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

 

合并流

 

SequenceInputStream:表示其他输入流的逻辑串联。它从输入流的有序集合开始,并从第一个输入流开始读取,直到到达文件末尾,接着从第二个输入流读取,依次类推,直到到达包含的最后一个输入流的文件末尾为止。

SequenceInputStream(InputStreams1,InputStream s2)

将按顺序读取这两个参数,先读取s1,然后读取s2

读取完毕后,直接关闭SequenceInputStream即可,内部会关闭两个参数的输入流

 

如果有多个文件需要合并为一个文件,构造方法也提供了

SequenceInputStream(Enumeration<?extends InputStream> e)

附:Enumeration是Vector的迭代器

步骤与思路:

(1)创建Vector集合,把多个输入流添加到Vector集合中

Vector<FileInputStream> v =new Vector<FileInputStream>()

(2)获取Vector的迭代器对象

Enumeration<FileInputStream> en =v.elements()

(2)把迭代器对象传入

SequenceInputStream sis = newSequenceInputStream(en);

(4)创建输出流FileOutputStream fos = new FileOutputStream(file);

(5)创建缓冲区byte[] bt = new byte[1024];

合并流使用范例

 

                  

Vector<FileInputStream>v = new Vector<FileInputStream>();//创建一个Vector集合,也可以创建ArrayList集合

 

                   v.add(newFileInputStream("D:\\test\\Day19\\1.txt"));//把流添加到集合当中

                   v.add(newFileInputStream("D:\\test\\Day19\\2.txt"));

                   v.add(newFileInputStream("D:\\test\\Day19\\3.txt"));

 

                   Enumeration<FileInputStream>en = v.elements();返回一个集合内容的枚举

                   SequenceInputStreamsis = new SequenceInputStream(en);

                   FileOutputStreamfos = new FileOutputStream("4.txt");

                   intlen = 0;

                   byte[]bt = new byte[1024];

                   while((len=sis.read(bt))!=-1)

                   {

                            fos.write(bt,0,len);

                   }

                   fos.close();

                   sis.close();

 

拓展:使用Vector可能会导致效率低下,因为Vector是线程安全,如果想使用ArrayList存储输入流,并实现合并流,应该如何实现?

 

观察构造方法

SequenceInputStream(Enumeration<?extends InputStream> e)

参数必须是Enumeration类型的(附:Enumeration是一个接口类

),也就是说可以是Enumeration及其子类对象,但是ArrayList的迭代器类型是Itorator

思路如下:

以匿名内部类的形式传入Enumeration的子类,然后覆写接口内部的抽象方法

Iterator<E> it =arraylist.iterator();

SequenceInputStream(new Enumeration()

{

boolean hasNextelement()

{

   return it.hasNext();

}

E next()

{

   return it.next();

}

})


 

 

切割流

示例:

FileInputStream fis = newFileInputStream("d:\\test\\Day19\\IMG_0461.JPG");//创建读取流,关联文件

                   FileOutputStreamfos = null;

                   intcount = 1;

                   intlen;

                   byte[]buf = new byte[1024];

                   while((len=fis.read(buf))!=-1)

                   {

                            fos= new FileOutputStream("d:\\test\\jpg\\" + (count++) +"part"); 

                            fos.write(buf,0,len);

                            fos.close();

                   }

                   fis.close();

 

对象的序列化

ObjectInputStream

ObjectOutputStream

两者都是操作对象的,必须成对使用

对象本身存在于堆内存中的,这两个类的功能就是存取对象的

 

如果要序列化一个类,必须要实现一个接口:Serializable否则编译时会报错

这个Serializable接口没有任何的方法,只用于标记,该类能够被序列化

该接口虽然没有方法,但是给实现该接口的类添加了一个ID标识号语句,在编译时期为每一个对象产生一个特有的ID号,标记着类与对象的所属关系,该ID是根据对象的属性和方法名计算出来

如果修改了对象类的属性或者方法,那么在反序列化的时候,两者的ID不一致,会导致反序列化失败

解决方法如下:

此ID可以手动设置,如果手动添加ID,java虚拟机则不会再计算ID,而是直接使用用户自定义的ID,这种方式可以解决修改了类对象的属性导致ID不一致再导致反序列化失败

static final long serialVersionUID = 42L;

 

被static修饰的属性不能被序列化

如果对于某个非静态的属性也不想被序列化,那么可以加上一个transient关键字修饰,告诉编译器不要把该属性写到文件中

或者在反序列化的时候,告诉编译器忽略被transient关键字修饰的属性,告诉编译器不要读取这个属性的值

用法示例:

class WriteObjectDemo

{

         publicstatic void main(String[] args) throws Exception

         {

                   writeObj();

                   readObj();

         }

public staticvoid writeObj()throws IOException

         {//对象序列化,把对象信息存储进文件中

       ObjectOutputStreamoos = new ObjectOutputStream(new FileOutputStream("obj.txt"));

         oos.writeObject(newPerson999("liangjian",23));

         oos.close();

         }

 

         publicstatic void readObj()throws Exception

         {//对象反序列化,把对象的内容从文件中读取出来

                   ObjectInputStream ois = new ObjectInputStream(newFileInputStream("obj.txt"));

                   Object o = ois.readObject();

                   System.out.println(o);

                   ois.close();

         }

         

}


 

 

拓展:创建对象一定会调用构造方法吗?

不一定,有特殊的情况

在对象的反序列化中,不会调用构造方法

ObjectInputStream ois =new ObjectInputStream(new FileInputStream("obj.txt"));

Object o =ois.readObject();

内部的实现方式如下:

执行ois.readObject();会创建一个Object对象,把从obj.txt文件中读取到的对象信息赋予该对象,Object o再指向该对象

 

注意的地方:

如果一个类内部维护着另外一个类的对象,那么另外一个类也必须实现Serializable接口,否则,序列化失败

示例:

class Address

{

String addr;

}

Class Person implementsSerializable

{

String name;

int age;

Address addr;

}

此时如果要序列化Person的对象,序列化失败,因为person对象内部维护着Address类的对象,但是Address类没有实现Serializable接口

 

管道流

 

RandomAccessFile

 

该类不是算是IO体系中子类。

而是直接继承自Object。

 

但是它是IO包中成员。因为它具备读和写功能。

内部封装了一个数组,而且通过指针对数组的元素进行操作。

可以通过getFilePointer获取指针位置,

同时可以通过seek改变指针的位置。

 

其实完成读写的原理就是内部封装了字节输入流和输出流。

 

通过构造函数可以看出,该类只能操作文件。

而且操作文件还有模式:只读r,,读写rw等。

 

如果模式为只读 r。不会创建文件。会去读取一个已存在文件,如果该文件不存在,则会出现异常。

如果模式rw。操作的文件不存在,会自动创建。如果存则不会覆盖。

 

用于操作字节数组的流对象。

ByteArrayInputStream:在构造的时候,需要接收数据源,。而且数据源是一个字节数组。

 

ByteArrayOutputStream:在构造的时候,不用定义数据目的,因为该对象中已经内部封装了可变长度的字节数组。

这就是数据目的地。

 

因为这两个流对象都操作的数组,并没有使用系统资源。不会发生任何的IO异常(但是有可能有别的异常)

所以,不用进行close关闭。

 

在流操作规律讲解时:

源设备,

       键盘 System.in,硬盘 FileStream,内存 ArrayStream。

目的设备:

       控制台 System.out,硬盘FileStream,内存 ArrayStream。

用流的读写思想来操作数据。

 

 

 

---------------------- ASP.Net+Android+IOS开发.Net培训、期待与您交流! ----------------------

详细请查看:http://edu.youkuaiyun.com 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值