4输入输出流对象应用IO体系结构.docx
《4输入输出流对象应用IO体系结构.docx》由会员分享,可在线阅读,更多相关《4输入输出流对象应用IO体系结构.docx(17页珍藏版)》请在冰豆网上搜索。
4输入输出流对象应用IO体系结构
第四节:
IO体系结构和基础应用
目标:
1.理解流的概念和分类;
2.掌握阻文件复制的实现;理解缓冲原理;
3.掌握对象序列化技术;
4.掌握原始字节流的读写特点。
1.流的基础概念:
2
1.stream(流)的概念:
2
2.流的继承体系:
2
3.流的分类:
2
2.InputStream/Outpustream子类:
文件读写3
1.InputStream抽像类:
3
2.InputStream的继承树3
3.使用FileInputStream从文件读取数据4
4.OutPustStream抽像类及基子类5
5.使用FileOutputStream写数据到文件6
3.缓冲流的使用8
1.缓冲流的概念8
2.输入输出缓冲流的使用8
4.对象的串行化:
10
1.对象序列化的用途:
10
2.将对象保存到文件:
10
5.原始数据类型读写流:
11
总结和任务:
12
1.流的基础概念:
1.stream(流)的概念:
java中输入输出相关的类都是java.io包中,java将输入和输出抽像为叫做流的概念,并提供了相应的实现类;流是程序和外界进行数据交换的通道在OOP中的表现,可以将流对象理解从一个水管,它从一个地方输入流据,向另外一个地方输出数据:
输入流是我们在程序中从某个地方(文件、网络)读取数据时使用;输出流是我们将数据发送到某个地方(文件、网络、控制台)时使用。
2.流的继承体系:
在java中,为了对不同来源和性质的流对象调用统一的方法,java中的流首先定义了顶层输入/输出流的接口或抽像类,这样不同性质具体的流对象就会有一个统一的调用方法以便与使用,在使用流对象时,尽量的按照在具体流所实现的接口(抽象类)中定义的方法使用。
3.流的分类:
流按方向分为输入流(InputStream)和输出流(OutputStream):
程序可以使用输入流对象从数据源读取数据,使用输出流对象向目的地写出数据,对应的流类名中一般有Input和Output词;
按性质可以分为:
基础字节流(原始流):
InputStream和OutputStream是java中可以按最小数据单位读取的流,即每次读写一个字节,基础流是直接连结到输入源的流。
过滤流(节点流):
过滤流是用来包装基础流以提供更好的特性,如提供缓冲功能的BufferedInputStream和BufferedOutputStream;过滤流是用来包装基础流或其它流(以其它流对象为构造参数)---它并不直接连结到数据源。
基与具体数据类型的流:
如果要从流中读取指定的数据类型的数据,如int,long型的数值,则要使用DataInput/DataOutput接口的子类如DataInputStream和DataOutputStream;
基与对象读写:
JDK提供了一种强大的功能流,即对象的输入输出流,即ObjectInput/ObjectOutput接口的子类,如我们使用ObjectOutputstream将一个java对象写入到文件中;对象流的读取就是常说的java对象序列化技术。
2.InputStream/Outpustream子类:
文件读写
1.InputStream抽像类:
InputStream基与字节(一个byte一个byte的读取)读取的输入流,它是java.io包中的一个抽像类,在JDK文档中有如下说明(后面流的介绍将不再展示JDK文档,但使用前一定要先查看文档):
我们可以看到它实现了Closeable接口,有9个具体的实现子类---每一个子类对象都可以按照InputStream中定义的方法调用来得到从对应的源得到的数据;
2.InputStream的继承树
InputStream是一个抽像类,它有多种适用与不同用途的具体实现类,InputStream的继承如下图示:
InputStream中定义了如下几个重要的方法:
intavailable():
流中可读取的有效字节长度(以多少个byte计),如具体的InputStream对象源是一个文件,则表示文件中可读取的字节长度;
voidclose():
流对象使用完后要关闭,就像水龙头,用完了要关,否则会占用一些系统资源;
intread():
这个方法调用会返回流中的下一个字节做为一个byte值,如果流己读到末尾,则会返回-1,即表示流中数据己读完;注意,此方法返回虽为int型,实际上是从流中读取的一个byte,即8bit,如果要从流中读取一个int型返回,则需要用后面所讲的DataInput对象的readInt()方法,才是读取四个byte,即32位长度;
intread(byte[] b):
用从流中读到的byte
3.使用FileInputStream从文件读取数据
FileInputStream中InputStream的一个直接字类,可用于构造从文件中读到数据的流,创建一个FileInputStream对象可以使用如下两个常用的构造器:
FileInputStream(File file):
通过一个文件对象做参数构造输入流对象;
FileInputStream(String name):
传入一个字符串(文件路径名)构造连结到指定文件的输入流;
如下代码示例:
创建一个从文件得到的输入流对象,从文件中读取出所有内容做为字符串返回:
packagejava.iotest;
importjava.io.*;
/**
*输入输出流测试
*@authorwww.NetJ
*/
publicclassBaseIO{
//程序主方法
publicstaticvoidmain(Stringargs[])throwsException{
BaseIObi=newBaseIO();
//读取我们当前正在编写的这个java源文件
StringfileName="src\\cn\\netjava\\iotest\\BaseIO.java";
Stringresult=bi.readFile2String(fileName);
System.out.println(result);
}
/**
*读取指定文件名的内容,做为字行串返回
*@paramfileName:
文件名
*@return:
读到的内容做为字符串返回
*@throwsIOException:
可能会抛出IO异常
*/
publicStringreadFile2String(StringfileName)throwsjava.io.IOException{
//构造输入流对象,做为一个Inpustream对象使用
//因为创建的对象是Inpustream的子类的对象,我们用父类型变量引用,方便统一使用
java.io.InputStreamins=newjava.io.FileInputStream(fileName);
//通过文件对象创建输入流
//FilesrcFile=newFile(fileName);
//java.io.InputStreamins=newjava.io.FileInputStream(srcFile);
//根据流中的字节长度,创建一个byte数组,保存读到的数据
byte[]contentByte=newbyte[ins.available()];
//将流中的数据读到数组中
ins.read(contentByte);
//将byte数组转换为字符串
Strings=newString(contentByte);
returns;
}
}
执行程序,将会看到,这个源文件中的内容被输出到的控制台;在InputStream中还应义有一个read()方法,每次调用会读取流源中的一个字节,如果使用read()方法读取,我们可以编写如下方法:
/**
*一次一个字节的读取指定文件名的内容,做为字行串返回
*@paramfileName:
文件名
*@return:
读到的内容做为字符串返回
*@throwsIOException:
可能会抛出IO异常
*/
publicStringreadFileOneByOne(StringfileName)throwsjava.io.IOException{
java.io.InputStreamins=newjava.io.FileInputStream(fileName);
inti=-1;
byte[]contentByte=newbyte[ins.available()];
//读取到第几个byte
intcount=0;
//每次读取一个字节,如返回为-1则表示读完了
while((i=ins.read())!
=-1){
//将读到的1个byte数字入到数组中
contentByte[count]=(byte)i;
count++;
}
//将byte数组转换为字符串
Strings=newString(contentByte);
returns;
}
这两种读法并无高下之分,需要根据具体情况使用。
4.OutPustStream抽像类及基子类
有了InputStream的经验,与其相对的OutputStream就容易掌握了,OutputStream同样是一个抽像象,它只定义了字节输入流统一的几个输出方法;具体的实现则有6个不同的子类,如文档中所示:
OutputStream的继承如下图示:
OutputStream定义了如下方法调用:
voidclose():
和输入流中的作同一样,输出完了,调用这个方法关闭流
voidflush():
将输出流有可还还保存在(JVM)内存中的数据强制输出到目标中(文件或网络上)
voidwrite(byte[] b):
将byte数组中的内容输出到流中;
voidwrite(byte[] b,int off,int len):
将数组中的一部分写出到流中
voidwrite(int b):
向流中写入一个byte值(注意,此处虽定义为int型,但是写入是做为一个字节,即8位写入的),如果要写入一个int型,则要用后面的DataOutput对象的writeInt()方法将一个int做为4个byte,即32位写入。
5.使用FileOutputStream写数据到文件
FileOutputStream是OutputStream的子类,我们可以利用它将数据写入到文件中,与FileInputStream相对应,它也有如下几个常用构造器:
FileOutputStream(File file):
构造输出到指定文件file对象的输出流.FileOutputStream(File file,boolean append):
append表示输出到文件中是否要覆盖文件中原有的数据;
FileOutputStream(String name)和FileOutputStream(String name,boolean append)与前面两个构造器性质相同,只是用一个字符串文件路径指向的文件构造流对象;
如下示例:
我们可以将从FileInputStream中读取到的数据写入到一个文件中,即简单的文件复制程序:
importjava.io.*;
/**
*输入输出流测试
*@authorwww.NetJ
*/
publicclassBaseIO{
//程序主方法
publicstaticvoidmain(Stringargs[])throwsException{
BaseIObi=newBaseIO();
//读取我们当前正在编写的这个java源文件
StringsrcName="src\\cn\\netjava\\iotest\\BaseIO.java";
//要复制到的目标文件
StringbakName="src\\cn\\netjava\\iotest\\BaseIO.java.bak";
booleanresult=bi.copyFile(srcName,bakName);
System.out.println("复制结果:
"+result);
}
/**
*简单文件复制方法
*@paramsrcFile:
源文件名
*@paramdestFile:
目标文件名
*@throwsIOExcepton:
IO异常
*@return:
是否cp成功
*/
publicbooleancopyFile(StringsrcFile,StringdestFile)throwsIOException{
//创建从源文件来的输入流
InputStreamins=newFileInputStream(srcFile);
//缓冲输出流对象:
如果文件中己有内容则覆盖原来内容
OutputStreamout=newFileOutputStream(destFile);
inti=0;
//从输入流中读取一个字节
while((i=ins.read())!
=-1){
//将这个字节写入到输出流
out.write(i);
}
ins.close();
//清空输出流的缓存并关闭
out.flush();
out.close();
returntrue;
}
}
注意:
当使用输出流将数据输出到文件时,如果目标文件己存在,且FileOutputStream构造器中append为true,则输出的内容会附加到己存在文件的末尾;否则或使用没有这个参数的构造器,都将覆盖己存在的文件;如果目标文件不存在,不论是否有append参数,都将自动创建一个文件;如果程序中的目标文件名字符串中有多级目录,但磁般上并不存在这个目录,程序运行将会报“找不到路径的错误“。
可以看到,流的使用就是这么简单,短短几行代码就完成了文件的复制(读入,写出);但问题不是这么简单,我们复制的这个文件长度很小,请你测试一下用以上方法复制一个大与20M的文件,你会发现速度是简直是相当的慢---该我们介绍缓冲流了:
3.缓冲流的使用
1.缓冲流的概念
缓冲流如我们前面所是,是一种过滤流,常用的是BufferedInputStream/BufferedOutputStream,这两个类分别是InputStream和OutputStream的子类,可用来为InputStream和OutputStream的其它子类流提供高性能的读写包装;
关于“缓冲”简单的理解为:
使用流读写数据时,一般是一个字节(字符流则是一个字符)的读写,这一个字节的读写过程以文件为例,其实需要经过以下几个过程:
输入文件操作系统内存JVM内存代码变量JVM内存操作系统内存输出文件。
经过这么多环节,却仅是传送了一个字节!
这是上面试验复制大文件是速度爆慢的原因,如下图示:
缓冲流的机制是在能过缓冲流对象在VM开避了一定大小的缓存区---每次传送缓存区大小的数据,而不是一个一个字节的送,这样效率就有了明显的提高,请看下面的代码示例:
2.输入输出缓冲流的使用
输入缓冲流:
BufferedInputStream本身是InputStream的子类,具体方法就不再介绍,它有以下常用的两种构造器:
BufferedInputStream(InputStream in):
使用一个InputStream类型的输入流对象创建一个默认缓冲区大小的缓冲输入对象;默认缓冲区大小在jdk1.6中为8192K。
BufferedInputStream(InputStream in,int size):
可以指定缓冲区大小构造缓冲输入流对象;
输出缓冲流:
BufferedOutputstream同样是对应的OutputStream的子类,它有如下两个构造器
BufferedOutputStream(OutputStream out):
使用默认大小(8192K)的缓冲区构造缓冲输出流对象。
BufferedOutputStream(OutputStream out,int size):
可指定缓冲大小。
使用缓冲进行文件copy示例:
/**
*使用缓冲流拷文件测试
*@paramsrcFile:
源文件名
*@paramdestFile:
目标文件名
*@throwsIOExcepton:
IO异常
*@return:
是否cp成功
*/
publicbooleanbufferedCopyFile(StringsrcFile,StringdestFile)throwsIOException{
Filesrc=newFile(srcFile);
Filedest=newFile(destFile);
//创建从源文件来的输入流
InputStreamins=newFileInputStream(src);
//构造输入缓冲流
BufferedInputStreambis=newBufferedInputStream(ins);
//构造缓冲输出流对象
OutputStreamout=newFileOutputStream(dest);
BufferedOutputStreambos=newBufferedOutputStream(out);
inti=0;
while((i=bis.read())!
=-1){
//从缓冲输入中读,写出到缓冲输出
bos.write(i);
}
ins.close();
out.flush();
out.close();
returntrue;
}
可以将其和不使用缓冲时复制文件所用的时间做下对比,在我的机器上复制一个30M的文件,两种方法相差有十倍之巨!
4.对象的串行化:
1.对象序列化的用途:
串行化又叫做对象序列化,是可以通过流对象保存java对象的一种方法;对象序列化是java语言的一种强大功能;这种技术是一些软件工具所必须实现的功能:
比如你在编辑一张图片,现在要存盘但下次又要取出修改,程序中的图片不是一张一般的jpeg或gif图片,而是附加了许多属性信息(如phoshop中的图层)的一个程序内部的对象!
通过对象的串行化,这个问题就非常容易解决;再如游戏玩家在休息时需要保存游戏中角色的状态,这也需要用到对象序列化技术。
2.将对象保存到文件:
在java中要实现序列化的对象的类,必须实现java.io.serializable接口;通过查看源代码可以发现这个接口中没有任何方法,实现它的类仅仅是给jVM提供了一个可序列化的标记而己。
当一个对象被串行化时,只有对象的数据被保存;方法和构造函数不属于串行化流。
如果一个数据变量是一个对象引用,那么这个对象的数据成员也会被串行化。
树或者对象数据的结构,包括这些子对象,构成了对象图。
对象类所表示的数据在不断地改变,所以它们不会被串行化;例如,java.io.FileInputStream、java.io.FileOutputStream和java.lang.Thread等流。
如果一个可串行化对象包含某个不可串行化元素的引用,那么整个串行化操作就会失败,而且会抛出一个NotSerializableException。
在有些情况下,为了保密需要,类不允许自己对象的某个属性被序列化,就可以在这个属性前面加上transient关键字,加上transient关键字的属性在对象的保存时不会生效,读取时也不会得到数据;
3.对象读写示例:
对象读写一般需使用java.io.ObjectInputStream类的 voidwriteObject(Object obj)方法向流中写入一个对象和java.io.ObjectOutputStream类的ObjectreadObject()读取一个对象,这两个类的构造器分别使用一个InputStream和OutputStream类型的对象做参数:
ObjectInputStream(InputStream in)
ObjectOutputStream(OutputStream out)
这样,我们可用文件输入输出流构造对象输出输出流对象,就可将某个对象保存到文件中。
如下代码示例:
/**
*将一个对象写入到流中,再从流中读取出画
*@paramfileName:
绿写入的文件名
*@return;是否写入成功
*@throwsException
*/
publicbooleanwriteReadObject(StringfileName)throwsException{
//Customer类是一个实现Serializable接口的类
//我们在此保存并读取它的一个对象
Customercustomer=newCustomer();
customer.setName("这个对象保存是设置属性的名字");
FileOutputStreamfos=newFileOutputStream(fileName);
//构造对象输出流
ObjectOutputStreamout=newObjectOutputStream(fos);
//保存对象
out.writeObject(customer);
out.flush();
out.close();
//读取对象
FileInputStreamfis=newFileInputStream(fileName);
ObjectInputStreaminput=newObjectInputStream(fis);
//需要强制转型,从流中读到的是一个Object类型的
CustomerinCus=(Customer)input.readObject();
System.out.println("读取对象:
"+inCus.getName());
returntrue;
}
5.原始数据类型读写流:
DataInputStream和DataOutputStream这两种流主要用来读写指定的数据类型,比如我们要从流中读取一个8个字节的Long型值,再读一个4个字节的int型值,用这两种流就非常方便;在应用中,许多基与TCP/IP的通信协议都是调计为按指写数据类型读取的,有了前面对流的使用经验,这两种流就容易掌握了,请看代码:
/**
*向文件中按数据类型的长度写入数据并读取
*@throwsException
*/
publicvoiddataTypeStream()throwsExcep