第 10章 Java数据流
Java数据流
本章主要介绍 Java语言如何利用数据流的思想处理字节和字符的输入输出(包括 stdin、
stdout和 stderr)。 本章后面的几个小节还将介绍一些对文件和文件中的数据进行处理的具体方法。
数据流的基本概念
数据流是指一组有顺序的、有起点和终点的字节集合文件程序网络端点数据流文件程序终端网络端点文件、字符串存储区终点起点流的不同层次
流被组织成不同的层次。最粗略的,数据流可以分为输入数据流( input stream) 和输出数据流( output stream)。 输入数据流只能读不能写,而输出数据流只能写不能读
Java开发环境数据流
java.io包提供数据流
在 JDK1.1之前,java.io包中的流只有以 byte为单位的流
对于以 16位的 Unicode码 表示的字符流处理很不方便
JDK1.1开始,java.io包中加入了专门用于字符流处理的类,这是以 Reader和 Writer为基础派生的一系列的类,还有用于对象的永久化保存状态的机制,它们通过实现 ObjectInput和 ObjectOutput
接口来达到要求
import java.io.*
输入数据流
输入数据流( Input Stream) 是指只能读不能写的数据流,用于向计算机内输入信息而用
java.io包中所有输入数据流都是由抽象类
InputStream继承而来
从数据流中读取数据时,必须有一个数据源与该数据流相连主要数据操作方法
int read()
从输入流中读一个字节的二进制数据,然后以此数据为低位字节,配上一个全零字节,形成一个 0~ 255之间的整数返回。它是一个抽象方法,需要在子类中具体实现。读取结束时,它会得到 -1
int read(byte[] b)
将多个字节读到数组中,填满整个数组
int read(byte[] b,int off,int len)
从输入流中读取长度为 len的数据,从数组 b中索引为 off
的位置开始放置读入的数据,读毕返回读取的字节数主要数据操作方法
void close()
数据流关闭,同时释放与该数据流相关的资源
也可以由运行时系统自动关闭
int available()
返回目前可以从数据流中读取的字节数(实际的读操作所读得的字节数可能大于该返回值)
long skip(long l)
跳过数据流中指定数量的字节不读,返回值表示实际跳过的字节数主要数据操作方法
boolean markSupported()
用于指示数据流是否支持回推操作,当数据流支持 mark()和 reset()方法时返回 true,反之返回
false
void mark(int markarea)
标记数据流的当前位置,并划出一个缓冲区,
其大小至少为指定参数的大小
void reset()
执行完 read()操作后,调用方法 reset()将回到输入数据流中被标记的位置输出数据流
输出数据流( Output Stream) 是指只能写不能读的流,用于从计算机中输出数据
java.io包中所有输出数据流大多是从抽象类
OutputStream继承而来主要数据操作方法
void write(int i)
将字节 i写入到数据流中,它只输出低位字节。
该方法是抽象方法,需要在其输出流子类中加以实现,然后才能使用
void write(byte b[])
将数组 b[]中的全部 b.length个字节写入数据流
void write(byte b[],int off,int len)
将数组 b[]中从第 off个字节开始的 len个字节写入数据流主要数据操作方法
void close()
当结束对输出数据流的操作时将其关闭
void flush()
输出数据流会在提交数据之前把所要输出的数据先锁定在内存缓冲区中,然后成批地进行输出,每次传输过程都以某特定数据长度为单位进行传输。这种方式下,在数据的末尾一般都会有一部分数据由于数量不够一个批次,而存留在缓冲区里,flush()可以将留在缓冲区里的数据强制提交缓冲存储

缓冲区单位的容量 末尾剩余部分基本字节数据流类
文件数据流
包括 FileImputStream和 FileOutputStream,
用来进行文件的 I/O处理,其数据源都应当是文件
对本机上的文件进行操作,但是它们不支持方法 mark()和 reset()
在构造文件数据流时,可以直接给出文件名
FileInputStream fis = new FileInputStream("myFile");
例 10-2
在,myFile.dat”文件中会保存字符串
HELLO!
FileOutputStream out = new FileOutputStream("myFile.dat");
out.write('H');
out.write(69);
out.write(76);
out.write('L');
out.write('O');
out.write('!');
out.close();
文件 I/O操作异常
类 FileInputStream的实例对象,如果所指定的文件不存在,产生 FileNotFoundException
异常
类 FileOutputStream的实例对象,如果所指定的文件不存在,则创建一个新文件;如果存在,那么新写入的内容将会覆盖原有数据
如果在读、写或生成新文件时发生错误,
则会产生 IOException异常文件数据流 I/O操作
程序 10-1异常捕获并处理
FileInputStream来读取 FileOutputStream
输出的数据 程序 10-2
过滤流
过滤器数据流在创建时与一个已经存在的数据流相连,这样在从这样的数据流中读取数据时,它提供的是对一个原始输入数据流的内容进行了特定处理的数据。
缓冲区数据流
BufferedInputStream和 BufferedOutputStream
都属于过滤器数据流,都是在数据流上增加了一个缓冲区
读写数据时,数据以块为单位先进入缓冲区(块的大小可以进行设置),其后的读写操作则作用于缓冲区
降低了不同硬件设备之间速度的差异,提高了 I/O操作的效率
提供了对 mark(),reset(),skip()等方法的支持创建该类的实例对象
取缺省缓冲区的大小
自行设置缓冲区的大小
关闭一个缓冲区输出流之前,使用 flush()方法
FileInputStream fis = new FileInputStream("myFile");
InputStream is = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("myFile");
OutputStream os = new BufferedOutputStream(fos);
FileInputStream fis = new FileInputStream("myFile");
InputStream is = new BufferedInputStream(fis,1024);
FileOutputStream fos = new FileOutputStream("myFile");
OutputStream os = new BufferedOutputStream(fos,1024);
数据数据流
DataInputStream和 DataOutputStream
允许通过数据流来读写 Java原始类型
布尔型( Boolean),浮点型( float) 等等
创建方式,
DataInputStream dis = new DataInputStream(is);
DataOutputStream dos = new DataOutputStream(os);
DataInputStream类方法
byte readByte()
long readLong()
double readDouble()
boolean readBoolean()
String readUTF()
int readInt()
float readFloat()
short readShort()
char readChar()
DataOutputStream类方法
void writeByte(int aByte)
void writeLong(long aLong)
void writeDouble(double aDouble)
void writeBoolean(boolean aBool)
void writeUTF(String aString)
void writeInt(int anInt)
void writeFloat(float aFloat)
void writeShort(short aShort)
void writeChar(char aChar)
DataInputStream的方法与 DataOutputStream的方法都是成对出现的管道数据流
管道数据流主要用于线程间的通信
一个线程中的 PipedInputStream对象从另一个线程中互补的 PipedOutputStream对象中接收输入
类 PipedInputStream必须和类 PipedOutputStream
一起使用,来建立一个通信通道
管道数据流必须同时具备可用的输入端和输出端创建一个通信通道
1
建立输入数据流:
PipedInputStream pis = new
PipedInputStream()
2
建立输出数据流:
PipedInputStream pis = new
PipedInputStream()
3
输入数据流和输出数据流连起来
pis.connect(pos)或者 pos.connect(pis)
创建一个通信通道 2
直接将输入流与输出流连接起来
PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);
管道通信程序
管道的两端建立连接以后就可以进行数据的通信了,见 程序 10-3
对象流
能够输入输出对象的流称为对象流
通过 java.io包中 ObjectInputStream和
ObjectOutputStream两个类实现的写对象数据流
将一个 java.util.Date对象实例送入文件
Date d = new Date();
FileOutputStream f = new FileOutputStream("date.ser");
ObjectOutputStream s = new ObjectOutputStream(f);
try{
s.writeObject(d);
s.close();
}catch(IOException e){
e.printStackTrace();
}
读对象数据流
readObject()把数据流以 Object类型返回,应该在转换为正确的类名之后再执行
Date d = null;
FileInputStream f = new FileInputStream("date.ser");
ObjectInputStream s = new ObjectInputStream(f);
try{
d = (Date)s.readObject(d);
s.close();
}catch(IOException e){
e.printStackTrace();
}
System.out.println("Date serialized at" "+d);
可持续性概念
能够记录自己的状态以便将来再生的能力,叫对象的持续性( Persistence)
一个对象是可持续的,意味着可以把这个对象存入磁盘、磁带,或传入另一台机器保存在它的内存或磁盘中。也就是说把对象存为某种永久存储类型。
对象通过写出描述自己状态的数值来记录自己的过程叫持续化(或串行化,Serialization)
持续化的主要任务是写出对象实例变量的数值,如果变量是另一个对象的引用,则引用的对象也要串行化。
这个过程是递归的。
Java的可持续性
一个类声明实现 Serializable接口 时,表明该类加入了对象串行化协议
Serializable接口 中没有定义任何方法,只是作为一个标记来指示实现该接口的类可以被持续化
Java中,允许可串行化的对象通过对象流进行传输
例 10-5
串行化对象
要串行化一个对象,必须与一定的对象输出 /输入流联系起来
通过对象输出流将对象状态保存下来
通过对象输入流将对象状态恢复。
通过 java.io包中的 ObjectOutputStream和
ObjectInputStream两个类实现
前者用 writeObject()方法可以直接将对象保存到输出流中,而后者用 readObject()方法可以直接从输入流中读取一个对象串行化对象例
对象的存储 程序 10-4
对象的恢复 程序 10-5
对象结构表
串行化只能保存对象的非静态成员变量,而不能保存任何成员方法和静态成员变量,并且保存的只是变量的值,对于变量的任何修饰符都不能保存,访问权限( public,protected,private) 对于数据域的持续化没有影响
数据会经常变化,状态只是瞬时的对象无法保存其状态,不具有可持续性。如 Thread对象或流对象。对于这样的成员变量,必须用 transient关键字标明,否则编译器将报错。 任何用 transient关键字标明的成员变量,都不会被保存对象结构表
串行化可能涉及将对象存放到磁盘上或在网络上发送数据,这时会产生安全问题。对于一些需要保密的数据,不应保存在永久介质中(或者不应简单地不加处理地保存下来),为了保证安全,
应在这些变量前加上 transient关键字
数据变量是一个对象时,该对象的数据成员也可以被持续化。对象的数据结构或结构树,包括其子对象树在内,构成了这个对象的结构表
如果一个对象图表中包含了一个对不可持续化对象的引用,而这个引用已用关键字 transient加以标记,则这个对象仍可以被持续化例 10-6
于 myThread域有 transient修饰,所以尽管它为不可持续化元素,但其整个对象仍可持续化
public class MyClass implements Serializable{
public transient Thread myThread;
private String customerID;
private int total;
}
transient例
尽管变量 customerID是可持续化元素,但由于有 transient修饰,所以,整个对象在持续化时不会对它进行持续化
public class MyClass implements Serializeble{
public transient Thread myThread;
private transient String customerID;
private int total;
}
基本字符流
从 JDK1.1开始,java.io包中加入了专门用于字符流处理的类
以 Reader和 Writer为基础派生的一系列类
Reader和 Writer也是抽象类,只提供了一系列用于字符流处理的接口。它们的方法与类 InputStream和 OutputStream类似,只不过其中的参数换成字符或字符数组
字节流中类 DataInputStream的 readLine方法,可以以字节形式读入,以 Unicode形式输出( String readLine())
读者和写者
读者( Readers) 和写者( Writers) 提供对不同平台之间数据流数据进行转换的功能
同其他程序设计语言使用 ASCII字符集不同,
Java使用 Unicode来表示字符串和字符
ASCII字符集是以一个字节( 8bit) 来表示一个字符,所以可以认为一个字符就是一个字节
( byte),Unicode是一种大字符集,要用两个字节( 16bit) 来表示一个字符
InputStreamReader
OutputStreamWriter
这两个类是字节流和读者、写者的接口,
用来在字节流和字符流之间作为中介
构造方法
InputStreamReader(InputStream in); //缺省规范
InputStreamReader(InputStream in,String enc);//指定规范 enc
OutputStreamWriter(OutputStream out); //缺省规范
OutputStreamWriter(OutputStream out,String enc);//指定规范 enc
构造映射到 ASCII码的标准的 InputStreamReader的方法如下
ir = new InputStreamReader(System.in,"8859_1");
读者提供的方法
void close()
void mark(int readAheadLimit)
boolean markSupported(),
int read()
int read(char[] cbuf)
int read(char[] cbuf,int off,int len)
boolean ready()
void reset()
long skip(long n)
写者提供的方法
void close()
void flush()
void write(char[] cbuf)
void write(char[] cbuf,int off,int len)
void write(int c)
void write(String str)
void write(String str,int off,int len)
缓冲区读者和写者
BufferedReader和 BufferedWriter,
构造方法与 BufferedInputStream,BufferedOutputStream
相类似
整行字符处理方法
public String readLine(),BufferedReader的方法,
从输入流中读取一行字符,行结束标志为 ‘ \n’、
‘\r’或两者一起
public void newLine(),BufferedWriter的方法,
向输出流中写入一个行结束标志程序 10-6
程序 10-6 每从文件,file1.txt”中读出一行,
就将其显示出来,并在行首加上字符串
,read:”
程序 10-7
程序 10-7 从标准输入通道读取字符串信息进行输出。将标准输入流( System.in) 串接到一个 InputStreamReader上,而后又将其串接到一个 BufferedReader上,把键盘输入的内容经过处理显示在屏幕上程序 10-8
程序 10-8中使用 PrintWriter类中的 print()或
println()方法,输出文本格式的内容
程序 10-9使用 readLine()方法从文本文件中缓冲读取内容文件的处理
File类提供了获得文件基本信息及操作文件的一些工具
创建一个新的 File对象
File myFile;
myFile = new File("mymotd");
myFile = new File("/","mymotd");
File myDir = new File("/");
myFile = new Fle(myDir,"mymotd");
与文件名相关的方法
String getName()
获取文件名
String getPath()
获取文件路径
String getAbsolutePath()
获取文件绝对路径
String getParent()
获取文件父目录名称
boolean renameTo(File newName)
更改文件名,成功则返回 true,否则返回 false
文件测定方法
boolean exists()
文件对象是否存在
boolean canWrite()
文件对象是否可写
boolean canRead()
文件对象是否可读
boolean isFile()
文件对象是否是文件
boolean isDirectory()
文件对象是否是目录
boolean isAbsolute()
文件对象是否是绝对路径常用文件信息和方法
long lastModified()
获取文件最后修改时间
long length()
获取文件长度
boolean delete()
删除文件对象指向的文件,成功则返回 true,
否则返回 false
程序 10-10
以上方法的使用见 程序 10-10
目录工具
boolean mkdir() 创建新目录
boolean mkdirs() 创建新目录
mkdir()和 mkdirs()区别在于用 mkdirs()可以一次生成多个层次的子目录
String[] list() 列出符合模式的文件名
类 File同样可以用来描述一个目录,对其进行的操作也与文件相同,只是对目录不能改变目录名,也不能进行删除,但是可以按模式匹配要求列出目录中所有的文件或子目录随机访问文件
类 RandomAccessFile来处理这种输入输出
创建一个随机访问文件
使用文件名
使用文件对象
myRAFile=new RandomAccessFile(String name,String mode)
myRAFile=new RandomAccessFile(File file,String mode);
决定是以只读方式( "r " )
还是以读写方式 ("rw")访问文件随机访问文件
对象 RandomAccessFile读写信息的方法同数据输入输出对象的方法相同,它可以访问类 DataInputStream和 DataOutputStream中的所有 read(),write()方法
移动文件读写指针的方法,
long getFilePointer()返回文件指针的当前位置
void seek(long pos);将文件指针置于指定的绝对位置。位置值以从文件开始处的字节偏移量 pos
来计算,pos为 0代表文件的开始
long length() 返回文件的长度。位置值为
length(),代表文件的结尾