Java之I/O处理

1.流的概念

Java的I/O系统涉及流的概念。一个读取字节序列的对象被称为输入流,一个可以写入字节序列的对象称为输出流。输入流和输出流是相对于程序本身而言的。程序读取数据称为打开输入流,程序向其他源写入数据称为打开输出流


程序读入数据,首先打开一个输入流,流以流对象的形式出现。数据文件或者网络链接信息包装在流对象内,流对象一旦启动,程序可以从输入流依次读入数据。

当程序需要输出数据时,就打开一个输出流对象,该对象知道把数据写到什么地点(一个文件或通过网络传输到其他机器上的文件),数据是依次写入,通过输出流对象把数据写入目标文件。

在java.io包中有各种I/O流类,这里按照输入输出流处理的不同数据类型对流进行分类,即字符流和字节流

2.字符流

在Java的I/O系统提供了InputStream和OutputStream两个抽象类实现字节(8位)数据的输入/输出,其中InputStream是输入流的抽象类,提供了read()方法,每个实现了该类的子类都要实现该方法,

(1)输入流类InputStream

抽象类InputStream表示从不同的输入源输入数据的类,这些数据源的数据类型多样,可以是字节数组、String对象、类的序列化对象,文件、管道或网络链接。对于多样的数据类型有相应的输入流类与其对应。

InputStream是个抽象类,提供了抽象read()方法,下面几个类是继承自InputStream的子类:
ByteArrayInputStream(字节数组输入流)

FileInputStream(文件输入流)

PipedInputStream(管道输入流)

SequenceInputStream(序列化输入流)

StringBufferInputStream(字符串缓冲输入流)

ObjectInputStream(对象输入流)

FilterInputStream(过滤器输入流)

以下的类继承自FilterInputStream(过滤器输入流),同时实现了DataInput接口

LineNumberInputStream(行号输入流)

DataInputStream(数据输入流)

BufferedInputStream(缓冲输入流)

PushbackInputStream(推回输入流)

下面分别介绍上述各种输入流类的功能

1》ByteArrayInputStream:允许带缓冲区的输入流,显然该类的功能就是允许将内存缓冲区作为输入流使用,它包装了filterInputStream对象,把该对象的输入文件作为实际数据输入到ByteArrayInputStream流对象缓冲区中。FilterInputStream包含几个子类,分别是LineNumInputStream、DataInputStream、BufferedInputStream和PushbackInputStream.

2》FileInputSteam:从文件中读取数据,其构造函数参数可以是文件对象、字符串或FileDescriptor对象。通过FileInputStream流类的包装对程序提供读数据的接口

3》PipedInputStream:该类产生输入管道流,该流又产生写入输出管道流的数据,这样就实现了管道化通信,通常在多线程编程中作为线程间通信的实现方式

4》SequeceInputStream:该流把多个输入流对象链接成一个输入流。该类的构造函数参数是两个InputStream对象和一个包含Inpustream对象的枚举器对象

5》StringBufferInputStream:该类的功能是把String转换成输入流。应用程序创建一个该类的输入流,把构造函数的参数中的String数据作为程序的输入。构造函数为StringBufferInputStream(String str)

6》ObjectInputStream:对象输入流读取输入流对象中的各种对象类型数据,用来处理序列化对象的传输。

7》FilterInputStream:该类继承了抽象方法InputStream,实现了抽象方法read()

8》LineNumberInputStream:该类实现对输入流中的行数的计数,对输入流增加了行号,该类的构造函数是InputStream流对象

9》DataInputStream:数据输入流允许程序从底层的输入流中读取基本类型的数据,如int型,float型和byte型等。该类包含了读取基本类型的所有方法,如readByte()、readBoolean()、readChar()、readDouble()和readFloat()等。其构造函数参数为一个InputStream流对象

10》BufferedInputStream:带缓冲区的输入流,可以把数据先放入缓冲区,防止每次读取时进行实际的读写操作,减少了数据实际访问的时间开销。数据以字节数组的形式存放在缓冲区中,该类提供了read()方法,每次从输入流读取一个字节数据。构造函数的参数为InputStream对象

11》PushbackInputStream:该类一般不被程序员使用,是为java编译器而设计的。

(2)输出流类OutputStream

抽象类OutputStream是表示输出数据流的抽象类,与抽象输入流对应,提供各种流对象的数据输出。

OutputStream是个抽象类,提供了抽象write方法,下面几个类是继承自OutputStream的子类,这些类都实现了write()方法

ByteArrayOutputStream(字节数组输出流)

FileOutputStream(文件输出流类)

ObjectOutputStream(对象输出流类)

PipedOutputStream(管道输出流类)

FilterOutputStream(过滤器输出流类)

下面3个类继承自FilterOutputStream类并实现了DataOut接口

DataOutputStream(数据输出流类)

BufferedOutputStream(缓冲输出流类)

printStream(打印输出流类)

这些流是类OutputStream下的子类,这些流类都可以实现Byte(8bit)类型的数据

1》ByteArrayOutputStream:在向输出流写入数据前先将数据缓冲处理,其缓冲区大小通过构造函数的参数设置

2》FileOutputStream:通过该输出流把数据写入文件,构造函数的参数可以是字符串、文件对象、文件或FileDescriptor对象。

3》ObjectOutputStream:对象输出流把各种对象类型数据写入输出流文件中,用来处理序列化对象的传输

4》PipedOutputStream:与管道输入流(PipedInputStream)对应,任何管道输入流都通过输出流写出。两者搭配使用实现管道通信

5》FilterOutputStream:该类继承了抽象类OutputStream,实现了抽象方法write()

下面3个类继承自FilterOutputStream(过滤器输出流):

DataOutputStream:数据输出流同数据输入流(DataInputStream)对应,实现把基本数据类型写入输出流。该类提供了把数据写入输出流的所有方法,如writerBoolean(Boolean v)、writeByte(int v)、writeBytes(String s)、writeChar(int v) 和writeChars(String s)等

BufferedOutputStream:该类实现输出数据时首先进行数据缓冲,该类提供了flush()方法实现清空数据缓冲区。该类的构造函数参数为输出流对象或输出流对象和输出缓冲区大小。即:BufferedOutputStream(OutputStream out)和BufferedOutputStream(OutputStream out,int size)

PrintStream:该类的目的是实现Java基本数据类型的格式化输出。

3.字节流

Java在设计其I/O系统时,把输入输出的系统分为两类:一类是字符流(8bit),另一类是字节流(16bit)。字节流也分为读流数据和写流数据,即Reader和Writer类及其子类。

(1)Writer类

Writer类是一个抽象类,所有继承自该类的子类都必须实现抽象方法write()。

下面列出继承自Writer类的子类:

BufferedWriter(带缓冲Writer)

CharArrayWriter(字符数组Writer)

FilterWriter(带过滤器Writer)

PrintWriter(打印Writer)

PipedWriter(管道Writer)

StringWriter(字符串Writer)

OutputStreamWriter(输出流Writer)

其中下面的类继承自类OutputStreamWriter(输出流Writer)

FileWriter(文件Writer)

下面分别介绍上述各输出Writer类的功能

《1》BufferedWriter:该类将文本写入字符流,将字节转换为字符同时缓冲读取每个字符,提供单个字符、数据和字符串的写入。该类提供两种构造函数指定缓冲区的大小,一种是指定大小,另一种是采用默认值。一般情况下采用默认值就足够使用了。在使用Writer类向文件写入数据时最好使用BufferedWriter包装开销大的writer操作,如FileWriter类

这样可以缓冲字节或字符流,而不是把字符转换成字节后立即写入到文件(这样会造成不断地读写数据,显然效率低)

《2》CharArrayWriter:该类作为Writer的字符缓冲区。该缓冲区随着向流中写入数据而自动增长。该类的构造函数提供两种形式,一种是默认的形式,另一种是有整型参数的形式。第二种构造函数构造指定初始容量的CharArrayWriter.

《3》FileWriter:该类把字符写入文件,文件位置在构造函数中指定,其构造函数接受默认的字符编码和默认的字符缓冲区大小。该类用于写入字符流,如果要写的是原始字节流,则参考FileOutputStream流来实现

《4》FilterWriter:该类是一个抽象类,用于写入已经过滤的字符流。该类提供了写字符流的方法write,其子类必须重写这些方法,也可以提供其他的方法或属性。

《5》OutputStreamWriter:该类提供了字符流向字节流的转换功能。并向流中写入字符。每次调用该类的write()方法都会导致向输出流写入一个或多个字节。为了提高字节到字符的转换效率,可以使用缓冲机制。如可以把OutputStreamReader类包装在Bufferedeader中以提高字符读取的效率。

《6》PrintWriter:该类完成向文本输出流打印格式化数据的形式,它实现了在PrintStream中的所有print()方法,但是不包含写入原始字节的方法。

《7》PipedWriter:管道Writer类创建管道通信的输出流,通过该输出流把数据写入文件,和管道Reader想对应建立起管道通信。

《8》StringWriter:显然该类是一个字符流,利用其字符串缓冲区中的输出字符构造字符串。该类的构造函数有两种,一种是创建默认初始化字符串缓冲区大小的新字符串Writer,另一种是创建具有指定初始字符串缓冲区大小的新字符串Witer.

(2)Reader类

所有继承自该类的子类都必须实现抽象方法read()和close().

继承自Reader类的子类:

BufferedReader(带缓冲Reader)

CharArrayReader(字符数组Reader)

FileReader(文件Reader)

FilterReader(过滤器Reader)

InputStreamReader(输入流Reader)

LineNumberReader(带行号Reader)

Pipedeader(管道Reader)

PushbackReader(推回Reader)

StringReader(字符串Reader)

下面介绍具体的类含义和注意事项:

《1》BufferedReader:该类完成从字符输入流中读取文本,且缓冲读到的字符。用户可以指定缓冲区的大小,或者使用默认的缓冲区大小。为了实现高效的读取文本建议read()操作开销高的Reader,如FileReader采用BufferedReader包装,将缓冲FileReader指定文本的输入。如果不使用缓冲机制,则每次调用read()或readLine()都会导致从文件中读取字节而后直接转换成字符返回退出方法,显然这样的效率很低

《2》CharArrayReader:该类继承了Reader抽象类,实现用做字符输入流的字符缓冲区读取字符。

《3》FileReader:该类继承自InputStreamReader类,读取字符流,其构造函数默认采用了合适的字符编码和字符缓冲区大小。该类通过指定一个File对象、一个文件名或者指定FileDescriptor的条件下创建一个新FileReader对象。

《4》InputStreamReader:该类提供了字节流向字符流的转换功能,并读取字符流。每次调用该类的read()方法都会从输入流中读取一个或多个字节。为了提高字节到字符的转换效率,可以使用缓冲机制从输入流中读取更多的字节后再进行转换,如可以把InputStreamReader类包装在BufferedReader中以提高字符读取的效率。

《5》LineNumberReader:跟踪行号的缓冲字符输入流。该类提供了两个方法void setLineNumber(int)和int getLineNumber()分别用于设置和获取当前行号。

《6》PipedReader:字符输入流,通过管道的方式从PipedWriter流读字符流

《7》PushbackReader:该类读取字符流,允许字符退回到流中的字符流

《8》StringReader:从源数据流为字符串流的源读取字符串。

3.File类

File类可以表示特定文件名(带绝对路径),也可以是某个目录下一组文件,该类提供了方法可以用来访问多个文件。File类提供了丰富的方法来处理和文件或目录相关的操作,如创建和删除文件。创建和删除文件夹以及通过和其他类配合使用实现文件的复制和移动等。

(1)创建文件夹(目录)

建立目录的方法是调用mkdir()方法

创建文件夹程序示例:

import java.io.File;
public class CreateNewFolder {
//参数newFolder表示新建目录的名称,该方法在创建新目录时首先判断该目录文件是否存在,如存在,则程序跳到异常处理代码,打印一行
//错误提示;如果不存在,则建立该目录
private void newFolder(String newfolder){
try{
String filepath=newfolder;
File myPath=new File(filepath);
if(!myPath.exists()){
myPath.mkdirs();
}
}catch(Exception e){
System.out.println("新建目录存在");
e.printStackTrace();
}
}
public static void main(String[]args){
//创建该public类的对象,以调用其函数来建立目录
CreateNewFolder createNewFolder = new CreateNewFolder();
//获得执行程序时的参数,该参数在执行程序的代码后直接给出,该参数放在临时变量mynewpath中
String mynewpath="one/two.three";
//对象调用其函数来创建新目录
createNewFolder.newFolder(mynewpath);

}
}

(2)创建文件

在Java的File类中创建新文件只需要调用该类的一个方法createNewFile().

创建文件程序示例:

import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.io.Writer;


public class CreateNewFile {
//创建一个方法完成创建文件的目的,文件的第一个参数是文件路径和文件名称,第二个参数是文件内容
public void createNewFile(String fileDerectoryAndName,String fileContent){
try{
String fileName=fileDerectoryAndName;
//创建File对象,参数为String类型,表示目录名
File myFile = new File(fileName);
//判断目录是否存在,如果不存在则调用createNewFile()方法创建新目录,否则跳至异常处理代码
if(!myFile.exists()){
myFile.createNewFile();
}
//下面把数据写入创建的文件,首先新建文件名为参数创建FileWriter对象
FileWriter resltFile = new FileWriter(myFile);
//把该对象包装进PrintWriter对象
//Writer resultFile;
PrintWriter myNewFile = new PrintWriter(resltFile);
//再通过PrintWriter对象的println()方法把字符串数据写入新建文件。
myNewFile.println(fileContent);
resltFile.close();//关闭文件写入流
}catch(Exception ex){
System.out.println("无法新建文件");
ex.printStackTrace();
}
}

public static void main(String[] args){
//创建类的对象并调用该对象的createNewFile()方法,创建新文件并写入数据
CreateNewFile createFile = new CreateNewFile();
createFile.createNewFile("D:/111.doc", "liujinqing");
}
}

(3)复制文件

文件的复制涉及文件流的概念,为了实现文件操作,这里使用了FileInputStream和FileOutputStream两个流类,通过文件输入流读取源文件,通过文件输入流把读入缓冲区的字节数据写入新文件。如果新文件已经存在,则覆盖该文件;如果不存在,则新建一个文件。

复制文件程序示例:

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;


public class CopyFileTest {
//参数oldFile如:D:/old.txt,参数newFile 如D:/new.txt
public void copyFile(String oldFile,String newFile){
try{
int bytesum=0;
int byteread=0;
File oldfile = new File(oldFile);
//判断文件是否存在,如果文件存在则实现该文件向新文件的复制
if(oldfile.exists()){
//读取原文件
InputStream ins=new FileInputStream(oldFile);
//创建文件输出流,写入文件
FileOutputStream outs= new FileOutputStream(newFile);
//创建缓冲区,大小为500字节
byte[] buffer = new byte[500];
//每次从文件输入流中读取500字节数据,计算当前为止读取的数据总数
while((byteread=ins.read(buffer))!=-1){
bytesum+=byteread;
System.out.println(bytesum);
//把当前缓冲区中的数据写入新文件
outs.write(buffer, 0, byteread);
}
ins.close();//关闭文件输入流
}
}catch(Exception ex){
System.out.println("原文件不存在");
ex.printStackTrace();
}
}
public static void main(String[] args){
//创建类对象实例,并调用copyFile()函数
//第一个参数表示老文件,第二个参数表示新文件
CopyFileTest copyfile = new CopyFileTest();
copyfile.copyFile("D:/oldFile.txt", "D:/newFile.txt");
}
}

(4)删除文件

在Java的File类中删除文件只需要调用该类的一个方法delete()。该方法可以删除指定的文件

在程序执行时,用户给出要删除的文件的目录和文件名或文件夹,就可以完成删除操作。

import java.io.File;


public class DeleteFile {
public void delFile(String fileDerecatorAndName){
try{
//以要删除的文件或文件夹名为参数,创建File对象
File deletedFile = new File(fileDerecatorAndName);
//调用File类的delete()方法删除文件
deletedFile.delete();
}catch(Exception ex){
System.out.println("删除文件错误");
ex.printStackTrace();
}
}
public static void main(String[]args){
//创建类DeleteFile的对象
DeleteFile deleteFile=new DeleteFile();
//调用类的delFile()方法,参数为要删除的文件或文件夹
deleteFile.delFile("D:/newFile.txt");
}
}

(5)删除文件夹

在Java的File类中删除文件夹,需要首先删除掉文件夹中的文件,再删除空文件夹。删除空文件夹的方法与删除文件的方法相同,所以关键是如何实现删除文件夹下的所有文件,可以想象欲删除一个目录下的所有文件只要获得该文件的目录和文件名,使用一个循环调用来依次删除文件夹中的文件即可。

范例:下面的类提供了两个方法,一个方法是删除文件夹,另一个是删除文件夹下的文件。如果在删除文件夹时即有目录又有文件,则删除文件,再继续删除文件夹;如果该文件夹下还是既有文件又有文件夹则继续上面的操作,直到把目录下的文件和子目录全部删除。

import java.io.File;


public class DeleteFolder {
//定义删除文件夹函数,参数为文件路径
public void delFolder(String folderPath){
try{
//调用删除所有文件函数,删除该目录下的所有文件
delAllFile(folderPath);
//创建文件对象,参数为欲删除的目录
File myFilePath =new File(folderPath);
myFilePath.delete();//调用删除目录函数
}catch(Exception ex){
System.out.println("删除文件夹错误");
ex.printStackTrace();
}
}
public void delAllFile(String path){
//定义并创建删除所有文件方法,参数为文件路径
File file =new File(path);
if(!file.exists()){//如果文件不存在则跳出函数
return;
}
if(!file.isDirectory()){//如果该file对象不是目录也跳出函数
return;
}
String[] tempList = file.list();//取出文件目录下的文件名或目录名
File temp=null;
for(int i=0;i<tempList.length;i++){
//列出当前文件夹下的文件或目录,可以方便的观察删除了哪些文件。
System.out.println(tempList[i].toString());
if(path.endsWith(File.separator)){
temp=new File(path+tempList[i]);
}
else{
//为欲删除目录下的每一个文件或目录创建临时File对象,参数为全路径
temp=new File(path+File.separator+tempList[i]);
}
//如果temp是文件则删除该文件
if(temp.isFile()){
temp.delete();
}
//如果temp是目录则调用删除所有文件的方法,此时出现了delAllFile()方法的迭代调用
if(temp.isDirectory()){
delAllFile(path+"/"+tempList[i]);
delFolder(path+"/"+tempList[i]);
}
}
}
public static void main(String[]args){
DeleteFolder deleteFoler = new DeleteFolder();
deleteFoler.delFolder("C:/Users/Administrator/workspace/LianXi/one");
}
}

4.I/O流的典型运用

(1)文件流

文件流操作的目的是实现文件之间的数据传输,把数据从一个文件复制到另一个文件。文件的输入流可以是流类的对象,如FileReader、FileInputStream。文件的输出流是一个流类的对象,如FileWriter、FileOutputStream.通过在文件上建立流来实现文件间的数据传输。

文件流操作示例:

import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;


//读文件流和写文件流
public class FileStream {
public static void main(String []args) throws IOException{
/*在两个文件上建立输入和输出流,该文件是两个File实例,从输入流对象filein读数据,源文件是FileStream.java,通过输出流对象
* fileout写数据到指定文件目录文件
* */
FileReader filein = new FileReader(new File("C:/Users/Administrator/workspace/LianXi/src/LianXi/FileStream.java"));
FileWriter fileout = new FileWriter(new File("D:/copyFileStream.txt"));
int c;
//依次读取输入流中的数据,先存储在变量c中,然后并把数据写入输出流
//写入文件copyFileStream.txt中
      while((c= filein.read())!=-1){
     fileout.write(c);
     //关闭输入流和输出流,释放流占用的资源
      }
      filein.close();
      fileout.close();
}
}

代码说明:

该程序首先建立一个输入流对象filein,该流类FileReader的参数是一个文件对象,输入流从该对象文件读数据。同时建立了一个输出流类fileout,该类FileWriter的参数也是一个文件对象,表示写入数据到指定的文件(copyFileStream.txt)。一旦执行该程序,在同一目录下会生成一个名为copyFileStream.txt的文本文件。该文件会保存文件Filetream.java的全部内容。对于保存的文件类型有多种选择,如可以保存为Word文件或Excel文件,只要把文件输出流的文件对象的参数String(D:/copyFileStream.txt)的文件后缀修改为.doc或.xls即可。再编译执行该程序时会输出名为FileStream.doc或FileStream.xls文件。

(2)读取内存数据

在Java的输入输出流中提供了读取内存数据的类,这些类包括StringReader和StringWriter、CharArrayReader和CharArrayWriter、ByteArrayInputStream和ByteArrayOutputStream。在内存中读写数据,通常在已经 存在的数据中创建I/O流。

读内存数据示例:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.StringReader;


public class ReadMemoryTest {
public static void main(String[]args) throws IOException{
//创建一个文件reader对象,并用BufferedReader封装
BufferedReader inReader = new BufferedReader(new FileReader("C:/Users/Administrator/workspace/LianXi/src/LianXi/ReadMemoryTest.java"));
String s1,s2=new String();
//每次读一行数据赋予字符串变量s1
while((s1=inReader.readLine())!=null){
//把每次读到的一行数据添加到字符串变量s2,该变量的值存储在内存中
s2+=s1+"\n";
}
inReader.close();
//使用StringReader从内存读出数据,并在控制台打印输出
StringReader inReader2=new StringReader(s2);
int c;
//把读到的字符值赋予int型变量c,如果对读到字符串的末尾会返回-1值,这也是循环结束标志
while((c=inReader2.read())!=-1)
//打印读到的数据,注意必须将int型数据上转换成char型,不然看到的就是一系列数字,这些数字是字符的编码
System.out.print((char)c);
}
}

代码说明:

程序中ReadMemoryTest.java文件存放在程序运行的当前目录下,就是该程序自己的源文件。为了使读者更清楚的了解读到的数据,在使用StringReader读到内存中s2的数据后,把读到的数据打印到控制台输出,一次读取一个字符,如果直接输出变量c,则屏幕上的内容都是数字,这些数字是字符的编码

(3)链接文件

Java提供SequenceInputStream类把多个输入流链接起来放在一个输入流中。多个输入流可以存入Enumeration对象,将依次读取每个流对象内的数据,直到最后一个流对象的结尾;也可以直接链接两个输入流,通过构造函数实现。

示例:首先创建两个输入流,之后再直接链接这两个输入流,并打印到控制台输出。

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.SequenceInputStream;


public class LinkedFileTest {
//定义两个输入流,作为链接文件的输入
private InputStream firstin;
private InputStream secondin;
//定义方法对两个输入流参数初始化
private void getInputStream(String fileone,String filetwo){
try{
firstin=new FileInputStream(fileone);
secondin=new FileInputStream(filetwo);
}catch(Exception ex){
ex.printStackTrace();
}
}
//定义链接函数,调用SequenceInputStream类,创建链接文件对象,并打印链接文件的内容
private void linkFile(){
try{
SequenceInputStream s=new SequenceInputStream(firstin,secondin);
int c;
//一次读取链接流的一个字符,直到流的结尾,输出读到的字符,最后关闭链接文件流
while((c=s.read())!=-1){
System.out.write(c);
}
s.close();
}catch(Exception ex){
System.out.println("输入流异常");
ex.printStackTrace();
}
}
public static void main(String[] args){
LinkedFileTest lftest = new LinkedFileTest();
lftest.getInputStream("D:/oldFile.txt","D:/111.doc");
lftest.linkFile();
}
}

(4)管道流

有两种管道流,一种是通过PipedInputStream和PipedOutputStream实现,另一种是通过PipedReader和PipedWriter实现。

管道流是对应多线程的概念,实现线程间通信,它建立在两个线程之上,它的实现原理是在管道的一端读入数据,而在管道的另一端读出数据。实现管道流时,关键是在线程中建立管道,体现在下面两行关键语句中:

PipedWriter pipeOut = new PipedWriter();

PipedReader pipIn = new PipedReader(pipeOut);

上述语句的作用就是建立一个通信管道,管道的一端是输出流(PipedWriter),另一端是输入流(PipedReader)。数据从输出端写入,从输入端读出,这样形成一个管道流,实现多线程之间的通信。

(5)随机访问文件

类RandomAccessFile实现文件的随机访问,可以在文件的任意位置读取或写入数据。该类与InputStream和OutputStream不同。它把输入输出放在同一个类中,通过构造函数的参数确定是输入还是输出或输入/输出操作可同时实现。该类的构造函数有两个参数,第一个参数是文件目录,第二个参数指定相应的操作,“r”表示读。“rw”表示读写。

RandomAccessFile in =new RandomAccessFile("readme.txt","rw");

RandomAccessFile in =new RandomAccessFile("readme.txt","r");

随机存取文件对任意位置的数据读取和写入全依赖于文件指针,文件指针指向的位置是紧接着要读或写数据的位置,但进行读或写数据时,指针就异动到下一个数据单位(Byte)。其中seek(long)方法来移动指针到文件内的任意字节处,该方法参数的大小从0到文件长度(按字节计算),而getFilePointer()获取文件指针的当前位置。

由于RandomAccessFile类实现了DataOutput和DataInput接口,所以该类具有读、写各种基本类型数据的各种方法,如readLong()、readFloat()、readByte()、wirteLong()、writeFloat()、writeByte().

随机访问文件示例:

import java.io.IOException;
import java.io.RandomAccessFile;


public class RandomAccessTest {
public static void main(String[]args) throws IOException{
//创建随机访问文件对象,对文件的权限为可读可写,若程序所在目录下没有randomfile.dat文件,则在该目录下创建该文件
RandomAccessFile rf=new RandomAccessFile("D:/randomfile.dat","rw");
//向文件中写入10个Double类型的数据
for(int i=0;i<10;i++){
rf.writeDouble(i*1.414);
}
//关闭对文件的随机访问
rf.close();
//创建随机访问文件对象,对文件有读写权限
rf=new RandomAccessFile("D:/randomfile.dat","rw");
//对象rf调用seek(long)函数,把当前的文件指针指向第40个字节位置,即第6个数据的开始位置,接着调用writeDouble()函数
//修改第6个数据为47.99991
rf.seek(5*8);
rf.writeDouble(47.99991);
rf.close();
//创建随机访问文件对象,读文件randomfile中的Double类型数据
rf=new RandomAccessFile("D:/randomfile.dat","r");
for(int i=0;i<10;i++){
System.out.println("Value"+i+":"+rf.readDouble());
}
//关闭对文件randomfile.dat的读访问
rf.close();
}
}

运行结果:

Value0:0.0
Value1:1.414
Value2:2.828
Value3:4.242
Value4:5.656
Value5:47.99991
Value6:8.484
Value7:9.898
Value8:11.312
Value9:12.725999999999999

代码说明:

类RandomAccessFile提供了对文件数据的非顺序读写,在程序中rf.seek(5*8)将文件指针指向文件中第40个字节处,接下来对该文件的写操作将从第40个字节开始,写入double类型数据,此时该数据覆盖掉文件中原来该位置的数据。而在读数据时调用了rf.readDouble()方法,这里是顺序访问文件,随着for语句中变量的递增而输出文件中的double类型的数据。

(6)从标准输入读取

本节介绍如何使用InputStreamReader读取用户的输入,然后再进行包装,并输出到界面

示例:从标准输入流读取数据

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;


public class ReadStanInput {
public static void main(String[]args) throws IOException{
//把输入数据包装成一个BufferedReader
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
String s;
//把BufferedReader读到的数据赋予变量s,并判断是否读到字符串的末尾
while((s=in.readLine())!=null &&s.length()!=0)
//输出从标准输入读到的数据
System.out.println("输出:"+s);
}
}

代码说明:

在代码中用InputStreamReader把System.in包装成Reader,再包装成BufferedReader,从标准输入读数据放入缓存,再从缓存中读出数据赋予变量s,最后从变量s中读取数据并在屏幕上输出。

(7)I/O重定向

I/O重定向是指把标准输入定向到一个文件,把这个文件作为程序输入源,而把数据输出到一个指定的文件。因为I/O操纵的是字节流,所以采用InputStream和OutputStream流类族实现输入输出重定向。

实现I/O重定向示例程序:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;


//I/O重定向,从一个文件(Redirecting.java)读出,写入另一个文件(test.out)
public class Redirecting {
public static void main(String[]args) throws IOException{
//设置输入对象,用FileInputStream对文件Redirecting.java进行包装,创建一个文件输入流对象
//接着创建一个缓冲输入流对象in,重定向标准输入
BufferedInputStream in = new BufferedInputStream(new FileInputStream("C:/Users/Administrator/workspace/LianXi/src/LianXi/Redirecting.java"));
//创建输出标准输出重定向,创建文件按输出流对象,并依次用BufferedOutputStream和PrintStream进行包装,最后得到一个PrintStream类
//对象out
 PrintStream out=new PrintStream(new BufferedOutputStream(new FileOutputStream("D:/test.txt")));
 //设置输出对象
 //设置输入流为in对象,输入数据是Redirecting.java文件
 System.setIn(in);
 System.setOut(out);//把输出定向到test.out文件
 //从标准输入读数据,此时是读重定向后的文件中的数据
 BufferedReader breader = new BufferedReader(new InputStreamReader(System.in));
 String s;
 //通过中间变量s把文件Redirecting.java中的数据输出,此时是输出到重定向的文件test.txt中
  while((s=breader.readLine())!=null)
 System.out.println(s);
   out.close();//关闭输出流,释放流占用的资源
 
}
}

代码说明:

I/O重定向是指把标准输入定向到一个文件,把这个文件作为程序输入源,而把数据输出到一个指定的文件。因为I/O操纵的是字节流,所以采用InputStream和OutputStream流类族实现输入/输出重定向。

(8)过滤流

按照标准I/O模型,Java提供了标准的输入/输出方法,而System.out是经过包装的流对象,可以将数据写到标准输出,但是System.in无法直接实现数据输入,因为System.in是

没有包装的流,所以在读取标准输入前必须对System.in进行包装

范例:用InputStreamReader来包装System.in成Reader,再包装成BufferedReader使用

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;


public class FilterIOTest {
public static void main(String[] args)throws IOException{
//创建一个过滤输出流,过滤处理后的数据输入到文件FilterOut.xls
DataOutputStream filterout=new DataOutputStream(new FileOutputStream("D:/FilterOut.xls"));
//创建3个不同数据类型的数组
double[]prices={11,22,21,41,23,8,9,10,29,12};
int[]counts={1,2,3,4,5,6,7,9,3,4};
String[] desc={"JavaT-shirt","JavaDoc","Javapin","Javaapp","Hello","aa","bb","cc","dd","ee"};
//将不同类型的数据写入DataOutputStream,结束标志为换行符'\n'
for(int i=0;i<prices.length;i++){
filterout.writeDouble(prices[i]);
filterout.writeChar('\t');
filterout.writeInt(counts[i]);
filterout.writeChar('\t');
filterout.writeChars(desc[i]);
filterout.writeChar('\n');
}
//关闭过滤输出流,不再向文件FilterOut.xls写入数据
filterout.close();
//在一个FileInputStream流上建立一个DataInputStream流,从文件FilterOut.xls中读出数据
DataInputStream in =new DataInputStream(new FileInputStream("D:/FilterOut.xls"));
double price;
int unit;
StringBuffer descd;
double total=0.0;
//下面try区块循环且顺序读出存入文件的double型数据,int型数据,char型数据,每一次循环则打印结果
try{
while(true){
price=in.readDouble();
in.readChar();
unit=in.readInt();
in.readChar();
char chr;
descd=new StringBuffer(20);
//判断如果没有到文件末尾,则继续执行,把读到的Char数据放入StringBuffer对象
while  ((chr=in.readChar())!='\n')
descd.append(chr);
System.out.println("您定制了"+unit+"个"+descd+"单价是"+price+"$");
total=total+unit*price;
}
}catch(EOFException e){}
//关闭过滤输入流,不再从过滤输入文件读数据
in.close();
}
}

运行结果:

您定制了1个JavaT-shirt单价是11.0$
您定制了2个JavaDoc单价是22.0$
您定制了3个Javapin单价是21.0$
您定制了4个Javaapp单价是41.0$
您定制了5个Hello单价是23.0$
您定制了6个aa单价是8.0$
您定制了7个bb单价是9.0$
您定制了9个cc单价是10.0$
您定制了3个dd单价是29.0$
您定制了4个ee单价是12.0$

(9)序列化对象

对象的序列化后的输入/输出通过ObjectInputStream和ObjectOutputStream类实现。序列化的本质是把具有一定结构的Java对象进行打包,而后通过特定的输入输出流来处理。

类ObjectInputStream和ObjectOutputStream不能单独使用,必须附加在其他流之上,对其他输入/输出流进行包装,因为对象的输入输出必须对应一个存储对象的文件。这里采用FileInputStream/FileOutputStream对象作为参数来构造ObjectInputStream/ObjectOutputStream对象,实现对象在文件之间的传输。ObjectInputStream类提供了各种read()方法读取特定类型的数据,如readObject()、readInt()、readBoolean()等方法读取流中的对象。ObjectOutputStream类提供了各种write()方法读取特定类型的数据,如writeObject()、writeInt()、writeBoolean()等方法向流中写入数据。

对象序列化示例:

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Date;


public class ObjectSeri {
public static void main(String []args){
try{
//创建文件输出流对象,该对象指明把对象数据 写入参数所指目录中的ObjectSeriOut.txt文件中
FileOutputStream out = new FileOutputStream("D:/ObjectSeriOut.txt");
//创建对象输出流,该流对象的参数是文件输出流对象
ObjectOutputStream sout = new ObjectOutputStream(out);
sout.writeObject(new String("current time is:"));//向对象输出流写对象
sout.writeObject(new Date());//向对象输出流写对象,该对象是int类型
sout.writeInt(1000);//向对象输出流写对象,该对象是Int类型
//将对象输出流中的对象全部推出缓冲区写入文件
sout.flush();
sout.close();//关闭对象输出流,释放流占用的资源
//创建文件输入流对象,对象流文件从ObjectSeri.txt中读取对象数据
FileInputStream in = new FileInputStream("D:/ObjectSeriOut.txt");
//创建对象输入流,通过该流的各种方法读取对象数据
ObjectInputStream sIn=new ObjectInputStream(in);
//通过对象输入流从文件ObjectSeriOut.txt读各种对象,并赋予对应类型的变量
String flag=(String)sIn.readObject();
Date date=(Date)sIn.readObject();
int i=(int)sIn.readInt();
System.out.println(flag+date);//输出读入的对象数据
System.out.println("int型数据:"+i);
in.close();//关闭对象输入流,释放输入流占用资源
//捕获IOException异常和ClassNotFoundException异常
}catch(IOException ex){
System.out.println("IOException happend");
}catch(ClassNotFoundException f){
System.out.println("ClassNotFoundException happend");
}
}
}

运行结果:

current time is:Wed Dec 13 16:16:21 CST 2017
int型数据:1000

代码说明:

在该程序中,在FileInputStream和FileOutputStream对象的基础上创建了ObjectInputStream和ObjectOutputStream对象sln和sout。通过对象输入、输出实例调用相应的写对象方法(writeObject())和读对象方法(readObject())向文件写入和读出对象数据。类ObjectInputStream提供了多个读入简单对象的方法,如writeInt()、writeFloat()和writeBoolean()。类ObjectOutputStream提供了多个写入简单对象的方法,如readInt()、readFloat()和readBoolean()。



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值