1. 字节流和字符流的基类各是什么?
2. 什么是对象的串行化?对象串行化的作用是什么?
第八章 输入输出流
【课前思考】
1. 字节流和字符流的基类各是什么?
2. 什么是对象的串行化?对象串行化的作用是什么?
【学习目标】
本讲主要讲述了java语言中的输入/输出的处理,通过本讲的学习,同学们可以编写更为完善的java程序。
【学习指南】
仔细阅读本章各知识点的内容, 深刻理解 java 语言中输入/输出流的处理方法,掌握处理问题的方法,多练习,多上机。
【难 重 点】
遇到实际问题时,要根据需要正确使用各种输入/输出流,特别是对中文使用适当的字符输入流。
正确使用对象串行化的方法。
处理字符流时,其构造方法的参数是一个字节流。
对象串行化的概念。
【知 识 点】
I/O 流概述
文件处理
过滤流
字符流的处理
对象的串行化
其它常用的流
【内 容】
第一节 数据流的基本概念
理解数据流
流一般分为输入流(Input Stream)和输出流(Output Stream)两类,但这种划分并不是绝对的。比如一个文件,当向其中写数据时,它就是一个输出流;当从其中读取数据时,它就是一个输入流。当然,键盘只是一个数人流,而屏幕则只是一个输出流。
Java的标准数据流
标准输入输出指在字符方式下(如DOS),程序与系统进行交互的方式,分为三种:
标准输入studin,对象是键盘。
标准输出stdout,对象是屏幕。
标准错误输出stderr,对象也是屏幕。
例 8.1 从键盘输入字符。
本例用System.in.read(buffer)从键盘输入一行字符,存储在缓冲区buffer中,count保存实际读入的字节个数,再以整数和字符两种方式输出buffer中的值。Read方法在java.io包中,而且要抛出IOException异常。程序如下:
import java.io.*;
public class Input1
{
public static void main(String args[]) throws IOException
{
System.out.println("Input: ");
byte buffer[] = new byte[512]; //输入缓冲区
int count = System.in.read(buffer); //读取标准输入流
System.out.println("Output: ");
for (int i=0;i<COUNT;I++) 输出buffer元素值
{
System.out.print(" "+buffer[i]);
}
System.out.println();
for (int i=0;i<COUNT;I++) 按字符方式输出buffer
{
System.out.print((char) buffer[i]);
}
System.out.println("count = "+ count); //buffer实际长度
}
}
程序中,main方法采用throws子句抛出IOException异常交由系统处理。
Java.io包中的数据流及文件类
字节流:
从InputStream和OutputStream派生出来的一系列类。这类流以字节(byte)为基本处理单位。
InputStream、OutputStream
◇ FileInputStream、FileOutputStream
◇ PipedInputStream、PipedOutputStream
◇ ByteArrayInputStream、ByteArrayOutputStream
◇ FilterInputStream、FilterOutputStream
◇ DataInputStream、DataOutputStream
◇ BufferedInputStream、BufferedOutputStream
字符流:
从Reader和Writer派生出的一系列类,这类流以16位的Unicode码表示的字符为基本处理单位
Reader、Writer
◇ InputStreamReader、OutputStreamWriter
◇ FileReader、FileWriter
◇ CharArrayReader、CharArrayWriter
◇ PipedReader、PipedWriter
◇ FilterReader、FilterWriter
◇ BufferedReader、BufferedWriter
◇ StringReader、StringWriter
第二节 字节流初步
InputStream 和OutputStream
read():从流中读入数据
skip():跳过流中若干字节数
available():返回流中可用字节数
mark():在流中标记一个位置
reset():返回标记过得位置
markSupport():是否支持标记和复位操作
close():关闭流
int read()
从输入流中读一个字节,形成一个0~255之间的整数返回(是一个抽象方法)。
int read(byte b[])
读多个字节到数组中。
int read(byte b[], int off, int len)
write(int b)
将一个整数输出到流中(只输出低位字节,抽象)
write(byte b[])
将字节数组中的数据输出到流中
write(byte b[], int off, int len)
将数组b中从off指定的位置开始,长度为len的数据输出到流中
flush():刷空输出流,并将缓冲区中的数据强制送出
close():关闭流
从输入流中读取长度为len的数据,写入数组b中从索引off开始的位置,并返回读取得字节数。
进行I/O操作时可能会产生I/O例外,属于非运行时例外,应该在程序中处理。如:型FileNotFoundException, EOFException, IOException
例 8.2 打开文件。
本例以FileInputStream的read(buffer)方法,每次从源程序文件OpenFile.java中读取512个字节,存储在缓冲区buffer中,再将以buffer中的值构造的字符串new String(buffer)显示在屏幕上。程序如下:
import java.io.*;
public class OpenFile
{
public static void main(String args[]) throws IOException
{
try
{ //创建文件输入流对象
FileInputStream rf = new FileInputStream("OpenFile.java");
int n=512;
byte buffer[] = new byte[n];
while ((rf.read(buffer,0,n)!=-1) && (n>0)) //读取输入流
{
System.out.print(new String(buffer));
}
System.out.println();
rf.close(); //关闭输入流
}
catch (IOException ioe)
{
System.out.println(ioe);
}
catch (Exception e)
{
System.out.println(e);
}
}
}
例 8.3 写入文件。
本例用System.in.read(buffer)从键盘输入一行字符,存储在缓冲区buffer中,再以FileOutStream的write(buffer)方法,将buffer中内容写入文件Write1.txt中,程序如下:
import java.io.*;
public class Write1
{
public static void main(String args[])
{
try
{
System.out.print("Input: ");
int count,n=512;
byte buffer[] = new byte[n];
count = System.in.read(buffer); //读取标准输入流
FileOutputStream wf = new FileOutputStream("Write1.txt");
//创建文件输出流对象
wf.write(buffer,0,count); //写入输出流
wf.close(); //关闭输出流
System.out.println("Save to Write1.txt!");
}
catch (IOException ioe)
{
System.out.println(ioe);
}
catch (Exception e)
{
System.out.println(e);
}
}
}
第三节 文件操作
File类
File类声明如下:
public class File ectends Object implements Serializable,Comparable
构造方法:
public File(String pathname)
public File(File patent,String chile)
public File(String patent,String child)
文件名的处理
String getName( ); //得到一个文件的名称(不包括路径)
String getPath( ); //得到一个文件的路径名
String getAbsolutePath( );//得到一个文件的绝对路径名
String getParent( ); //得到一个文件的上一级目录名
String renameTo(File newName); //将当前文件名更名为给定文件的完整路径
文件属性测试
boolean exists( ); //测试当前File对象所指示的文件是否存在
boolean canWrite( );//测试当前文件是否可写
boolean canRead( );//测试当前文件是否可读
boolean isFile( ); //测试当前文件是否是文件(不是目录)
boolean isDirectory( ); //测试当前文件是否是目录
普通文件信息和工具
long lastModified( );//得到文件最近一次修改的时间
long length( ); //得到文件的长度,以字节为单位
boolean delete( ); //删除当前文件
目录操作
boolean mkdir( ); //根据当前对象生成一个由该对象指定的路径
String list( ); //列出当前目录下的文件
例 8.4 自动更新文件。
本例使用File类对象对指定文件进行自动更新的操作。程序如下:
import java.io.*;
import java.util.Date;
import java.text.SimpleDateFormat;
public class UpdateFile
{
public static void main(String args[]) throws IOException
{
String fname = "Write1.txt"; //待复制的文件名
String childdir = "backup"; //子目录名
new UpdateFile().update(fname,childdir);
}
public void update(String fname,String childdir) throws IOException
{
File f1,f2,child;
f1 = new File(fname); //当前目录中创建文件对象f1
child = new File(childdir); //当前目录中创建文件对象child
if (f1.exists())
{
if (!child.exists()) //child不存在时创建子目录
child.mkdir();
f2 = new File(child,fname); //在子目录child中创建文件f2
if (!f2.exists() || //f2不存在时或存在但日期较早时
f2.exists()&&(f1.lastModified() > f2.lastModified()))
copy(f1,f2); //复制
getinfo(f1);
getinfo(child);
}
else
System.out.println(f1.getName()+" file not found!");
}
public void copy(File f1,File f2) throws IOException
{ //创建文件输入流对象
FileInputStream rf = new FileInputStream(f1);
FileOutputStream wf = new FileOutputStream(f2);
//创建文件输出流对象
int count,n=512;
byte buffer[] = new byte[n];
count = rf.read(buffer,0,n); //读取输入流
while (count != -1)
{
wf.write(buffer,0,count); //写入输出流
count = rf.read(buffer,0,n);
}
System.out.println("CopyFile "+f2.getName()+" !");
rf.close(); //关闭输入流
wf.close(); //关闭输出流
}
public static void getinfo(File f1) throws IOException
{
SimpleDateFormat sdf;
sdf= new SimpleDateFormat("yyyy年MM月dd日hh时mm分");
if (f1.isFile())
System.out.println("<FILE>\t"+f1.getAbsolutePath()+"\t"+
f1.length()+"\t"+sdf.format(new Date(f1.lastModified())));
else
{
System.out.println("
\t"+f1.getAbsolutePath());
File[] files = f1.listFiles();
for (int i=0;i<FILES.LENGTH;I++)
getinfo(files[i]);
}
}
}
f1.lastModified()返回一个表示日期的长整型,值为从1970年1月1日零时开始计算的毫秒数,并以此长整型构造一个日期对象,再按指定格式输出日期。程序运行结果如下:<FILE> D:\myjava\Write1.txt 6 2002年12月11日02时18分
D:\myjava\backup<FILE> D:\myjava\backup\Write1.txt 6 2002年12月31日05时13分
文件过滤器
类FilterInputStream和FilterOutputStream分别对其他输入/输出流进行特殊处理,它们在读/写数据的同时可以对数据进行特殊处理。另外还提供了同步机制,使得某一时刻只有一个线程可以访问一个输入/输出流
类FilterInputStream和FilterOutputStream分别重写了父类InputStream和OutputStream的所有方法,同时,它们的子类也应该重写它们的方法以满足特定的需要
• 要使用过滤流,首先必须把它连接到某个输入/输出流上,通常在构造方法的参数中指定所要连接的流:
– FilterInputStream(InputStream in);
– FilterOutputStream(OutputStream out);
这两个类是抽象类,构造方法也是保护方法
类BufferedInputStream和BufferedOutputStream实现了带缓冲的过滤流,它提供了缓冲机制,把任意的I/O流“捆绑”到缓冲流上,可以提高读写效率
• 在初始化时,除了要指定所连接的I/O流之外,还可以指定缓冲区的大小。缺省大小的缓冲区适合于通常的情形;最优的缓冲区大小常依赖于主机操作系统、可使用的内存空间以及机器的配置等;一般缓冲区的大小为内存页或磁盘块等地整数倍,如8912字节或更小。
– BufferedInputStream(InputStream in[, int size])
– BufferedOutputStream(OutputStream out[, int size])
例 8.5 列出当前目录中带过滤器的文件名清单。
本例实现FilenameFilter接口中的accept方法,在当前目录中列出带过滤器的文件名。
程序如下:
import java.io.*;
public class DirFilter implements FilenameFilter
{
private String prefix="",suffix=""; //文件名的前缀、后缀
public DirFilter(String filterstr)
{
filterstr = filterstr.toLowerCase();
int i = filterstr.indexOf('*');
int j = filterstr.indexOf('.');
if (i>0)
prefix = filterstr.substring(0,i);
if (j>0)
suffix = filterstr.substring(j+1);
}
public static void main(String args[])
{ //创建带通配符的文件名过滤器对象
FilenameFilter filter = new DirFilter("w*abc.txt");
File f1 = new File("");
File curdir = new File(f1.getAbsolutePath(),""); //当前目录
System.out.println(curdir.getAbsolutePath());
String[] str = curdir.list(filter); //列出带过滤器的文件名清单
for (int i=0;i<STR.LENGTH;I++)
System.out.println("\t"+str[i]);
}
public boolean accept(File dir, String filename)
{
boolean yes = true;
try
{
filename = filename.toLowerCase();
yes = (filename.startsWith(prefix)) &
(filename.endsWith(suffix));
}
catch(NullPointerException e)
{
}
return yes;
}
}
程序运行时,列出当前目录中符合过滤条件“w*.txt“的文件名清单。结果如下:
D:\myjava
Write1.txt
Write2.txt
文件对话框
随机文件操作
于InputStream 和OutputStream 来说,它们的实例都是顺序访问流,也就是说,只能对文件进行顺序地读/写。随机访问文件则允许对文件内容进行随机读/写。在java中,类RandomAccessFile 提供了随机访问文件的方法。类RandomAccessFile的声明为:
public class RandomAccessFile extends Object implements DataInput, DataOutput
File:以文件路径名的形式代表一个文件
FileDescriptor:代表一个打开文件的文件描述
FileFilter & FilenameFilter:用于列出满足条件的文件
File.list(FilenameFilter fnf)
File.listFiles(FileFilter ff)
FileDialog.setFilenameFilter(FilenameFilter fnf)
• FileInputStream & FileReader:顺序读文件
• FileOutputStream & FileWriter:顺序写文件
• RandomAccessFile:提供对文件的随机访问支持
类RandomAccessFile则允许对文件内容同时完成读和写操作,它直接继承Object,并且同时实现了接口DataInput和DataOutput,提供了支持随机文件操作的方法
DataInput和DataOutput中的方法
• readInt(), writeDouble()…
int skipBytes(int n):将指针乡下移动若干字节
length():返回文件长度
long getFilePointer():返回指针当前位置
void seek(long pos):将指针调到所需位置
void setLength(long newLength):设定文件长度
构造方法:
RandomAccessFile(File file, String mode)
RandomAccessFile(String name, String mode)
mode 的取值
– “r” 只读. 任何写操作都将抛出IOException。
– “rw” 读写. 文件不存在时会创建该文件,文件存在时,原文件内容不变,通过写操作改变文件内容。
– “rws” 同步读写. 等同于读写,但是任何协操作的内容都被直接写入物理文件,包括文件内容和文件属性。
– “rwd” 数据同步读写. 等同于读写,但任何内容写操作都直接写到物理文件,对文件属性内容的修改不是这样。
例 8.6 随机文件操作。
本例对一个二进制整数文件实现访问操作当以可读写方式“rw“打开一个文件”prinmes.bin“时,如果文件不存在,将创建一个新文件。先将2作为最小素数写入文件,再依次测试100以内的奇数,将每次产生一个素数写入文件尾。
程序如下:
import java.io.*;
public class PrimesFile
{
RandomAccessFile raf;
public static void main(String args[]) throws IOException
{
(new PrimesFile()). createprime(100);
}
public void createprime(int max) throws IOException
{
raf=new RandomAccessFile("primes.bin","rw");//创建文件对象
raf.seek(0); //文件指针为0
raf.writeInt(2); //写入整型
int k=3;
while (k<=max)
{
if (isPrime(k))
raf.writeInt(k);
k = k+2;
}
output(max);
raf.close(); //关闭文件
}
public boolean isPrime(int k) throws IOException
{
int i=0,j;
boolean yes = true;
try
{
raf.seek(0);
int count = (int)(raf.length()/4); //返回文件字节长度
while ((i<=count) && yes)
{
if (k % raf.readInt()==0) //读取整型
yes = false;
else
i++;
raf.seek(i*4); //移动文件指针
}
} catch(EOFException e) { } //捕获到达文件尾异常
return yes;
}
public void output(int max) throws IOException
{
try
{
raf.seek(0);
System.out.println("[2.."+max+"]中有 "+
(raf.length()/4)+" 个素数:");
for (int i=0;i<(int)(raf.length()/4);i++)
{
raf.seek(i*4);
System.out.print(raf.readInt()+" ");
if ((i+1)%10==0) System.out.println();
}
} catch(EOFException e) { }
System.out.println();
}
}
程序运行时创建文件“primes.bin“,并将素数写入其中,结果如下:
[2..100]中有 25 个素数:
2 3 5 7 11 13 17 19 23 29
31 37 41 43 47 53 59 61 67 71
73 79 83 89 97
第四节 字符流
Reader类和Writer类
前面说过,在JDK1.1之前,java.io包中的流只有普通的字节流(以byte为基本处理单位的流),这种流对于以16位的Unicode码表示的字符流处理很不方便。从JDK1.1开始, java.io包中加入了专门用于字符流处理的类,它们是以Reader和Writer为基础派生的一系列类
同类InputStream和OutputStream一样,Reader和Writer也是抽象类,只提供了一系列用于字符流处理的接口。它们的方法与类InputStream和OutputStream类似,只不过其中的参数换成字符或字符数组
Reader类
• 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)
Writer类
• 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)
例 8.7 文件编辑器。
本例实现文件编辑器中的打开、保存文件功能。程序如下:
import java.awt.*;
import java.awt.event.*;
import java.io.*;
public class EditFile1 extends WindowAdapter
implements ActionListener,TextListener
{
Frame f;
TextArea ta1;
Panel p1;
TextField tf1;
Button b1,b2,b3;
FileDialog fd;
File file1 = null;
public static void main(String args[])
{
(new EditFile1()).display();
}
public void display()
{
f = new Frame("EditFile");
f.setSize(680,400);
f.setLocation(200,140);
f.setBackground(Color.lightGray);
f.addWindowListener(this);
tf1 = new TextField();
tf1.setEnabled(false);
tf1.setFont(new Font("Dialog",0,20)); //设置文本行的初始字体
f.add(tf1,"North");
ta1 = new TextArea();
ta1.setFont(new Font("Dialog",0,20)); //设置文本区的初始字体
f.add(ta1);
ta1.addTextListener(this); //注册文本区的事件监听程序
p1 = new Panel();
p1.setLayout(new FlowLayout(FlowLayout.LEFT));
b1 = new Button("Open");
b2 = new Button("Save");
b3 = new Button("Save As");
p1.add(b1);
p1.add(b2);
p1.add(b3);
b2.setEnabled(false);
b3.setEnabled(false);
b1.addActionListener(this); //注册按钮的事件监听程序
b2.addActionListener(this);
b3.addActionListener(this);
f.add(p1,"South");
f.setVisible(true);
}
public void textValueChanged(TextEvent e)
{ //实现TextListener接口中的方法,对文本区操作时触发
b2.setEnabled(true);
b3.setEnabled(true);
}
public void actionPerformed(ActionEvent e)
{
if (e.getSource()==b1) //单击[打开]按钮时
{
fd = new FileDialog(f,"Open",FileDialog.LOAD);
fd.setVisible(true); //创建并显示打开文件对话框
if ((fd.getDirectory()!=null) && (fd.getFile()!=null))
{
tf1.setText(fd.getDirectory()+fd.getFile());
try //以缓冲区方式读取文件内容
{
file1 = new File(fd.getDirectory(),fd.getFile());
FileReader fr = new FileReader(file1);
BufferedReader br = new BufferedReader(fr);
String aline;
while ((aline=br.readLine()) != null)//按行读取文本
ta1.append(aline+"\r\n");
fr.close();
br.close();
}
catch (IOException ioe)
{
System.out.println(ioe);
}
}
}
if ((e.getSource()==b2) || (e.getSource()==b3))
{ //单击[保存]按钮时
if ((e.getSource()==b3) ||(e.getSource()==b2)&&(file1==null))
{ //单击[SaveAs]按钮时,或单击[Save]按钮且文件对象为空时
fd = new FileDialog(f,"Save",FileDialog.SAVE);
if (file1==null)
fd.setFile("Edit1.txt");
else
fd.setFile(file1.getName());
fd.setVisible(true); //创建并显示保存文件对话框
if ((fd.getDirectory()!=null) && (fd.getFile()!=null))
{
tf1.setText(fd.getDirectory()+fd.getFile());
file1 = new File(fd.getDirectory(),fd.getFile());
save(file1);
}
}
else
save(file1);
}
}
public void save(File file1)
{
try //将文本区内容写入字符输出流
{
FileWriter fw = new FileWriter(file1);
fw.write(ta1.getText());
fw.close();
b2.setEnabled(false);
b3.setEnabled(false);
}
catch (IOException ioe)
{
System.out.println(ioe);
}
}
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
}
第五节 字节流的高级应用
管道流
管道用来把一个程序、线程和代码块的输出连接到另一个程序、线程和代码块的输入。java.io中提供了类PipedInputStream和PipedOutputStream作为管道的输入/输出流
管道输入流作为一个通信管道的接收端,管道输出流则作为发送端。管道流必须是输入输出并用,即在使用管道前,两者必须进行连接
管道输入/输出流可以用两种方式进行连接:
– 在构造方法中进行连接
• PipedInputStream(PipedOutputStream pos);
• PipedOutputStream(PipedInputStream pis);
– 通过各自的connect()方法连接
• 在类PipedInputStream中,connect(PipedOutputStream pos);
• 在类PipedOutputStream中,connect(PipedInputStream pis);
例 8.8 管道流。
本例例管道流的使用方法。设输入管道in与输出管道out已连接,Send线程向输出管道out发送数据,Receive线程从输入管道in中接收数据。程序如下:
import java.io.*;
public class Pipedstream
{
public static void main (String args[])
{
PipedInputStream in = new PipedInputStream();
PipedOutputStream out = new PipedOutputStream();
try
{
in.connect(out);
}
catch(IOException ioe) { }
Send s1 = new Send(out,1);
Send s2 = new Send(out,2);
Receive r1 = new Receive(in);
Receive r2 = new Receive(in);
s1.start();
s2.start();
r1.start();
r2.start();
}
}
class Send extends Thread //发送线程
{
PipedOutputStream out;
static int count=0; //记录线程个数
int k=0;
public Send(PipedOutputStream out,int k)
{
this.out= out;
this.k= k;
this.count++; //线程个数加1
}
public void run( )
{
System.out.print("\r\nSend"+this.k+": "+this.getName()+" ");
int i=k;
try
{
while (i<10)
{
out.write(i);
i+=2;
sleep(1);
}
if (Send.count==1) //只剩一个线程时
{
out.close(); //关闭输入管道流
System.out.println(" out closed!");
}
else
this.count--; //线程个数减1
}
catch(InterruptedException e) { }
catch(IOException e) { }
}
}
class Receive extends Thread //接收线程
{
PipedInputStream in;
public Receive(PipedInputStream in)
{
this.in = in;
}
public void run( )
{
System.out.print("\r\nReceive: "+this.getName()+" ");
try
{
int i = in.read();
while (i!=-1) //输入流未结束时
{
System.out.print(i+" ");
i = in.read();
sleep(1);
}
in.close(); //关闭输入管道流
}
catch(InterruptedException e) { }
catch(IOException e)
{
System.out.println(e);
}
}
}
程序运行结果如下:
Send1: Thread-0
Send2: Thread-1
Receive: Thread-2 1
Receive: Thread-3 2 3 4 5 7 out closed!
6 8 9 java.io.IOException: Pipe closed!
数据流
DataInputStream和DataOutputStream
在提供了字节流的读写手段的同时,
以统一的通用的形式向输入流中写入boolean,int,long,double等基本数据类型,并可以在次把基本数据类型的值读取回来。
提供了字符串读写的手段。
分别实现了DataInput和DataOutput接口
声明类:
Public class DataInputStream extends filterInputStream implements DataInput
例 8.9 数据流。
本例演示数据流的使用方法。
程序如下:
import java.io.*;
public class Datastream
{
public static void main(String arg[])
{
String fname = "student1.dat";
new Student1("Wang").save(fname);
new Student1("Li").save(fname);
Student1.display(fname);
}
}
class Student1
{
static int count=0;
int number=1;
String name;
Student1(String n1)
{
this.count++; //编号自动加1
this.number = this.count;
this.name = n1;
}
Student1()
{
this("");
}
void save(String fname)
{
try
{ //添加方式创建文件输出流
FileOutputStream fout = new FileOutputStream(fname,true);
DataOutputStream dout = new DataOutputStream(fout);
dout.writeInt(this.number);
dout.writeChars(this.name+"\n");
dout.close();
}
catch (IOException ioe){}
}
static void display(String fname)
{
try
{
FileInputStream fin = new FileInputStream(fname);
DataInputStream din = new DataInputStream(fin);
int i = din.readInt();
while (i!=-1) //输入流未结束时
{
System.out.print(i+" ");
char ch ;
while ((ch=din.readChar())!='\n') //字符串未结束时
System.out.print(ch);
System.out.println();
i = din.readInt();
}
din.close();
}
catch (IOException ioe){}
}
}
程序运行结果如下:
1 Wang
2 Li
对象流
• 对象的持续性(Persistence)
– 能够纪录自己的状态一边将来再生的能力,叫对象的持续性
• 对象的串行化(Serialization)
– 对象通过写出描述自己状态的的数值来记录自己的过程叫串行化。串行化的主要任务是写出对象实例变量的数值,如果变量是另一个对象的引用,则引用的对象也要串行化。这个过程是递归的
• 对象流
– 能够输入输出对象的流称为对象流。
– 可以将对象串行化后通过对象输入输出流写入文件或传送到其它地方
在java中,允许可串行化的对象在通过对象流进行传输。只有实现Serializable接口的类才能被串行化, Serializable接口中没有任何方法,当一个类声明实现Serializable接口时,只是表明该类加入对象串行化协议
要串行化一个对象,必须与一定的对象输出/输入流联系起来,通过对象输出流将对象状态保存下来(将对象保存到文件中,或者通过网络传送到其他地方) ,再通过对象输入流将对象状态恢复
类ObjectOutputStream和ObjectInputStream分别继承了接口ObjectOutput和ObjectInput,将数据流功能扩展到可以读写对象,前者用writeObject()方法可以直接将对象保存到输出流中,而后者用readObject()方法可以直接从输入流中读取一个对象
例 8.10 对象流。
本例声明Student2为序列化的类。Save方法中,创建对象输出流out,并以添加方式向文件中直接写入当前对象out.writeObject(this);display方法中,创建对象输入流in,从文件中直接读取一个对象in.readObject(),获得该对象的类名、接口名等属性,并显示其中的成员变量。程序如下:
import java.io.*;
public class Student2 implements Serializable //序列化
{
int number=1;
String name;
Student2(int number,String n1)
{
this.number = number;
this.name = n1;
}
Student2()
{
this(0,"");
}
void save(String fname)
{
try
{
FileOutputStream fout = new FileOutputStream(fname);
ObjectOutputStream out = new ObjectOutputStream(fout);
out.writeObject(this); //写入对象
out.close();
}
catch (FileNotFoundException fe){}
catch (IOException ioe){}
}
void display(String fname)
{
try
{
FileInputStream fin = new FileInputStream(fname);
ObjectInputStream in = new ObjectInputStream(fin);
Student2 u1 = (Student2)in.readObject(); //读取对象
System.out.println(u1.getClass().getName()+" "+
u1.getClass().getInterfaces()[0]);
System.out.println(" "+u1.number+" "+u1.name);
in.close();
}
catch (FileNotFoundException fe){}
catch (IOException ioe){}
catch (ClassNotFoundException ioe) {}
}
public static void main(String arg[])
{
String fname = "student2.obj";
Student2 s1 = new Student2(1,"Wang");
s1.save(fname);
s1.display(fname);
}
}
程序运行结果如下:
Student2 interface java.io.Serializable
1 Wang
查看本文来源