File类、流操作、重点难点:文件字节字符等流的相关知识全解析
导读

主要内容
文件字节输入、输出流
文件字符输入、输出流
缓冲流
随机流
数组流
数据流
对象流
序列化与对象可隆
使用Scanner解析文件
读写图像文件
文件锁
重点和难点
重点在于通过文件流来读写数据,同时要了解各种数据流之间的区别,并且要掌握流的连接思想。
难点:流的连接思想及各种数据流的使用。
概述
输入流提供一条通道程序,能够利用此通道读取源中的数据,也能将数据传送到目的地。输入流的指向被称作源,程序从指向源的输入流里读取源内的数据;输出流的指向是数据要前往的一个目的地,程序通过向输出流写入数据,从而把数据传送到目的地。
10.1File类
File 对象的主要作用是获取文件本身的相关信息,并且不会涉及到对文件的读写操作。
创建一个File对象的构造方法有3个:
File(String filename);
创建一个名为 File 的类,该类接收两个字符串参数,分别是目录路径(directoryPath)和文件名(filename)。
定义一个函数 File,该函数接收一个 File 类型的参数 f 和一个字符串类型的参数 filename 作为输入。
创建 File 对象时,并不一定需要磁盘上存在该 File 对象所要描述的文件。比如说,当创建 File 对象 f 时,即 File f = new File("perrty.txt"),这并不意味着磁盘上就一定有 perrty.txt 文件。并且,在创建了 File 对象 f 之后,也不会在磁盘上生成真实的 perrty.txt 文件。但 File 对象 f 依然是一个非空对象,f 调用方法能够描述 perrty.hello 文件的相关信息,比如 f.getName()的值为 perrty.txt,f.length()的值为 0,f.canRead()的值为 false。
10.1.1文件的属性
File类的下列方法获取文件本身的一些信息。
获取文件的名字的操作是通过 public String getName() 来实现的。
判断文件是否具有可读的属性。
判断文件是否可被写入的是 public boolean canWrite() 。
判断文件是否存在的是 public boolean exits() 。
获取文件的长度,单位是字节,此长度通过 public long length() 方法获取。
获取文件的绝对路径的方法是 public String getAbsolutePath()。
获取文件的父目录的方法是 public String getParent()。
判断文件是否为普通文件而非目录,此为 public boolean isFile() 的功能。
判断文件是否为一个目录的方法是 public boolean isDirectroy()。
判断文件是否为隐藏文件的方法是 public boolean isHidden()。
获取文件最后修改时间。
例子1(例子中使用上述的一些方法,获取某些文件的信息 )
创建了一个名为 f 的 File 对象,其路径为"tom\\cat\\Example10_1.java"。
其中的 tom\cat\Example10_1.java 是一种相对路径的表示方式。这种方式不包含有根目录的路径。如果 Java 主类是在 C:\chapter10 下运行,也就是 Java 程序,那么 Java 会认为 tom\cat\Example10_1.java 的父目录是 C:\chapter10;如果 Java 程序是在 D:\1000 下运行,那么 Java 会认为 tom\cat\Example10_1.java 的父目录是 D:\1000。编写程序时,要尽量避免使用那种带有根目录路径的绝对路径,像 C:\chapter10\tom\cat\Example10_1.java 这样的。这样做能让代码更易于维护。
10.1.2目录
创建目录 public boolean mkdir()
2.列出目录中的文件
此方法会以字符串形式把目录下的所有文件进行返回。
public File[] 以 File 对象的方式把目录下的所有文件进行返回。
公共的 String 数组[]通过 obj(一个文件名过滤器)来列出目录下的指定类型的所有文件,并以字符串形式返回。
public File[] 在目录下以 File 对象形式返回指定类型的所有文件。
上述两方法的参数中,FilenameFilter 是一个接口,这个接口具备一个方法:
这个方法接收两个参数,一个是文件目录`File dir`,另一个是文件名`String name`,并且返回一个布尔值`boolean`。
例子 2 位于 Example10_2.java 和 FileAccept.java 中,它列出了当前目录(也就是应用程序所在的目录)下所有 java 文件的名字。
10.1.3文件的创建与删除
当使用File类创建一个文件对象后,例如
创建一个 File 对象,其路径为 "c:\\myletter" 且文件名是 "letter.txt",即 File file = new File("c:\\myletter", "letter.txt");
如果在 c:\myletter 目录中不存在名字为 letter.txt 的文件,那么文件对象 file 会调用 public boolean createNewFile(); 这个方法。
文件对象的 public boolean delete()方法能够删除当前的文件。
例如:file.delete();
10.1.4运行可执行文件
声明一个对象,该对象使用 Runtime 类(Runtime 类位于 java.lang 包中)。
Runtime ec;
接着通过使用该类的 getRuntime() 这个静态方法来创建这个对象。
ec=Runtime.getRuntime();
ec 具备调用 exec(String command) 这个方法的能力,通过该方法能够打开本地机的可执行文件,也可以利用它来执行一个操作。
在例子 3 中,Runtime 对象能够打开位于 Windows 平台上的记事本程序以及电子表格,并且需要 Win10 操作系统。
10.2 文件字节输入流
Java 的 io 包提供了众多的流类。Java 将 InputStream 抽象类的子类所创建的流对象称作字节输入流。Java 将 OutputStream 抽象类的子类所创建的流对象称作字节输出流。Java 将 Reader 抽象类的子类所创建的流对象称作字符输入流。Java 将 Writer 抽象类的子类所创建的流对象称作字符输出流。
对于不同的源或者目的地,java.io 包给程序提供了对应的输入流或者输出流。这些输入流和输出流,大部分都是 InputStream、OutputStream、Reader 或者 Writer 的子类。
使用输入流通常包括4个基本步骤:
(1)设定输入流的源
(2)创建指向源的输入流
(3)让输入流读取源中的数据
(4)关闭输入流。
1.构造方法:设定输入流源
使用 FileInputStream 类的构造方法来创建指向文件的输入流,以下是其构造方法的相关内容。
FileInputStream(String name);
FileInputStream(File file);
其中:参数name和file指定的文件称为输入流的源。
2.使用输入流读取字节
文件字节流能够调用从父类继承而来的 read 方法,以此来顺序地读取文件。只要文件字节流没有被关闭,那么每次调用 read 方法,就会顺序地读取文件中的其余内容。这种读取会一直持续到文件的末尾,或者文件字节输入流被关闭为止。
int read() 用于读取单个字节的数据,它会返回一个字节值,这个值是 0 到 255 之间的整数。如果没有读出字节,那么就会返回 -1。
读取字节数组 b 中的 b.length 个字节到该数组中,然后返回实际读取到的字节数。若到达文件末尾,就返回 -1。
int 函数用于读取字节到字节数组中。它读取 len 个字节并将其放入字节数组 b 里。此函数会返回实际读取的字节数目。如果读取到文件末尾,就会返回 -1。参数 off 用于指定从字节数组的某个位置开始存放读取的数据。
例子4使用文件字节流读取文件的内容 。
10.3文件字节输出流
使用输出流通常包括4个基本步骤:
(1)给出输出流的目的地
(2)创建指向目的地的输出流
(3)让输出流把数据写入到目的地
(4)关闭输出流。
1.构造方法
使用 FileOutputStream 类的构造方法来创建指向文件的输出流,这些构造方法具有刷新功能。
创建一个文件输出流对象,参数为一个字符串类型的文件名。
FileOutputStream(File file);
其中:参数name和file指定的文件称为输出流的目的地
2.使用输出流写字节
输出流的wirie方法以字节单位向目的地写数据。
void write(int n) 向目的地写入单个字节。
向目的地写入一个字节数组,这个字节数组为 b[]。
将取出的 len 个字节写到目的地。
FileOutputStream 流会顺序地写文件。只要该流没有被关闭,那么每次调用 write 方法,就会顺序地向目的地写入内容。直到流被关闭为止。
3.关闭流
调用 close()方法,能保证操作系统将流缓冲区的内容写入其目的地。也就是说,关闭输出流可以冲洗掉该流所用缓冲区的内容,通常会冲洗到磁盘文件上。
例子5使用文件字节输出流写文件a.txt。
首先使用具有刷新功能的构造方法创建一个指向文件 a.txt 的输出流,接着向 a.txt 文件写入“新年快乐”。之后,选择使用不刷新文件的构造方法指向 a.txt,再向文件写入(即尾加)“Happy New Year”。
10.4 文件字符输入、输出流
Reader 类提供的 read 方法会以字符为单位逐个地把源中的数据读取出来。
int read():
int read(char b[]):
以及要读取的长度 len。
void close():
long skip(long numBytes):
Writer 流按照字符的方式依次写入文件,每当调用 write 方法时,就会依次向目的地写入内容。Writer 类具备以下常用的方法。
void write(int n):向输出流写入一个字符。
将一个字节数组写入输出流。
将这些取出的字符写到输出流中。
void close():关闭输出流。
使用文件字符输入流读取文件 a.txt 的内容,再使用文件字符输出流将读取到的内容尾加到文件 b.txt 中
10.5缓冲流
BufferedReader 类创建的对象被称作缓冲输入流,BufferedWriter 类创建的对象被称作缓冲输出流。并且这二者的源必须是字符输入流,目的地必须是字符输出流。
构造方法:
BufferedReader(Reader in);
BufferedWriter (Writer out);
- 拥有特定的写入机制。
readLine() 读取文本行
将字符串 s 从索引 off 开始,长度为 len 的部分写入文件中。
newLine(); 向文件写入一个回行符
由英语句子构成的文件english.txt(每句占一行):
The arrow missed the target.
他们拒绝了工会的要求。
Where does this road go to?
读取 english.txt 按行进行,然后在每行后面加上该行英语句子中含有的单词数目,接着将该行写入到名为 englishCount.txt 的文件中。程序运行效果呈现为图 10.5。
10.6随机流
使用 RandomAccessFile 类去创建一个随机访问文件流。RandomAccessFile 类所创建的流,其指向既能够当作源,又能够当作目的地。
构造方法:
创建一个 RandomAccessFile 对象,参数为字符串 name 和字符串 mode 。
利用 File 对象和字符串模式来构造 RandomAccessFile 实例。
相关方法:
定位 RandomAccessFile 流的读写位置,需要进行 seek(long a)操作
getFilePointer() 获取流的当前读写位置
将几个整数以 int 类型写入到名为 tom.dat 的文件中。
3. 对读取到的内容进行解码处理,以避免“乱码”。
1.读取String str=in.readLine();
使用“iso-8859-1”对 byte b[] 进行重新编码,具体是将 str 通过“iso-8859-1”编码得到的结果赋给 byte b[] ;即 byte b[] = str.getBytes("iso-8859-1") ; 这里是用“iso-8859-1”来重新编码 byte b[] 。
3.使用当前机器的默认编码将字节数组转化为字符串
String content=new String(b);
例子9(使用readLine()读取文件 )
10.7数组流
1.字节数组流
字节数组输出流 ByteArrayOutputStream 以字节数组作为流的目标。
ByteArrayInputStream构造方法及常用方法
创建一个 ByteArrayInputStream,其参数为一个字节数组 buf。
创建一个 ByteArrayInputStream 对象,传入字节数组 buf、起始偏移量 offset 和长度 length 。
从源中顺序地读出一个字节。
从源中顺序地读出指定的字节数,这些字节数由参数 len 来指定,并且将读出的字节存储到字节数组 b 中,起始位置为 off 。
同时它还有一些常用的方法。
ByteArrayOutputStream();
创建一个字节数组输出流,其初始大小为指定的 size 。
向缓冲区顺序地写入一个字节;将一个字节按顺序写入缓冲区;在缓冲区中顺序地写入一个字节;把一个字节依次写入缓冲区;按顺序往缓冲区里写入一个字节
将参数 b 中的字节进行指定,指定的字节个数为 len,然后把这些指定的字节按照顺序写入缓冲区
返回的是输出流写入到缓冲区的所有字节,以字节数组形式呈现,即 public byte[] 形式。
2.字符数组
CharArrayReader 类和 CharArrayWriter 类属于字符数组流,它们以字符数组作为流的来源以及目标。在例子 10 中,会向内存(也就是输出流的缓冲区)写入字符串。
10.8数据流
DataInputStream 类创建的对象被称作数据输入流。DataOutputStream 类创建的对象被称作数据输出流。
构造方法:
DataInputStream(InputStream in)创建的数据输入流,其指向的是一个由参数 in 所指定的底层输入流。
DataOutputStream(OutnputStream out)创建的数据输出流,其指向的是一个由参数 out 所指定的底层输出流。
例子11写几个Java类型的数据到一个文件。
例子12将字符串加密后写入文件,如图10.6所示。
10.9对象流
对象输入流和对象输出流是由 ObjectInputStream 和 ObjectOutputStream 类创建的对象。
它的构造方法是:
创建一个 ObjectInputStream 对象,该对象的输入流为给定的 InputStream in 。
创建一个 ObjectOutputStream 对象,其参数为 OutputStream 类型的 out 。
相关方法:
将一个对象 obj 写入到一个文件的操作是 writeObject(Object obj)
readObject() 读取一个对象到程序中
一个类实现了 Serializable 接口,那么这个类创建的对象就是序列化的对象,这就是所谓的序列化。
例子13
Example10_13.java 和 TV.java 这两个文件,通过使用对象流来读写由 TV 类创建的对象,其效果呈现为图 10.7 所示的那样。
10.10序列化与对象克隆
如果一个“复制品”实体发生变化,原对象实体不会随之发生变化;反之,如果原对象实体发生变化,这个“复制品”实体也不会随之发生变化。这样的复制品被称为原对象的一个克隆对象,或者简称为克隆。
一个对象调用clone()方法就可以获取该对象的克隆对象。
对象输入流通过对象的序列化信息来得到当前对象的一个克隆。
单击“写出对象”按钮,可将标签写入到内存;单击“读入对象”按钮,能读入标签的克隆对象。
10.11使用Scanner解析文件
使用Scanner类和正则表达式来解析文件。
1.使用默认分隔标记解析文件
创建Scanner对象,并指向要解析的文件,例如:
创建了一个名为 file 的 File 对象,该对象指向名为 "hello.java" 的文件。
创建了一个 Scanner 对象,名为 sc,它用于读取指定的文件 file。
sc将空白作为分隔标记
相关方法next()依次返回file中的单词
hasNext()用于判断 file 的最后一个单词是否已经被 next()方法所返回。
例题15解析文件cost.txt中的全部消费
创建 Scanner 对象,使其指向要解析的文件。然后使用 useDelimiter 方法,指定正则表达式作为分隔标记来解析文件,例如:可以使用正则表达式作为分隔标记来创建 Scanner 对象并指向要解析的文件。
File file = new File("hello.java");
Scanner sc = new Scanner(file);
sc.useDelimiter(正则表达式);
sc将正则表达式作为分隔标记
相关方法 next()依次返回file中的单词
hasNext()用于判断 file 的最后一个单词是否已被 next()方法所返回。
例子16解析student.txt文件中的学生成绩
10.12读写图像文件
javax.imageio 包中的 ImageIO 类所提供的 static 方法,能够使程序便捷地读取图像文件(进行解码操作),以及保存图像文件(进行编码操作)。ImageIO 类的常用 static 方法具体如下:
读取图像文件
用合适的图像解码算法来读取参数所指定的文件。若解码成功,就会返回一个 BufferedImage 对象;若解码失败,则返回 null。
写图像到文件
使用参数 formatName 所给出的编码,把参数 im 所指定的内存中的渲染图像(BufferedImage 实现了 RenderedImage 接口)写入到参数 output 所指定的文件中。如果不能用指定的编码把渲染图像写到 output 所指定的文件里,那么这个方法就会返回 false。
在例子 17 中,通过 ImageIO 读取当前应用程序所在目录下的 image 子目录中的 pic.jpg 文件,从而得到 BufferedImage 图像。接着,在该 BufferedImage 图像上绘制了一行文字“我喜欢 Java ”。接着再将 BufferedImage 对象写入到名为 java.jpg 的文件中。
10.13 文件对话框
构造方法 JFileChooser() 会创建一个初始不可见的有模式的文件对话框。接着,这个文件对话框会调用下面的 2 个方法:
showSaveDialog(Component a);
showOpenDialog(Component a);
showSaveDialog 方法可使对话框可见并提供保存文件的界面,showOpenDialog 方法可使对话框可见并提供打开文件的界面,二者呈现的外观不同。上述两个方法中的参数 a 用于指定对话框可见时的位置,若 a 为 null,文件对话框出现在屏幕中央;若 a 不为空,文件对话框在组件 a 的正前面居中显示。
java 中的 WindowReader.java 利用文件对话框来打开和保存文件。
10.14 带进度条的输入流
如果在读取文件时希望看到文件的读取进度,那么可以使用 javax.swing 包所提供的输入流类。
ProgressMonitorInputStream。
它的构造方法是:
ProgressMonitorInputStream 有三个参数,一个是 Conmponent 类型的 c,一个是 String 类型的 s,还有一个是 InputStream 类型的输入流。
例子19
10.15文件锁
FileLock 和 FileChannel 类能够处理 Java 所提供的文件锁功能。这两个类分别位于 java.nio 包和 java.nio.channels 包中。
输入、输出流读写文件时可以使用文件锁。
最后在读写文件操作中使用获取到的文件锁。
使用 RondomAccessFile 流来建立指向文件的流对象,此对象的读写属性需为 rw,例如:
RandomAccessFile input=new
创建一个名为“Example.java”的文件,并以读写模式打开它,使用 RandomAccessFile 类来进行操作。
input 流调用 getChannel()方法,从而获得一个连接到地层文件的 FileChannel 对象,也就是信道,例如:
FileChannel 是 input 获取到的通道。
信道调用 tryLock() 方法或者 lock() 方法,从而获得一个 FileLock(文件锁)对象,这个过程被称作对文件加锁。
使用 channel 尝试获取锁并将其赋值给 FileLock 类型的变量 lock 。
示例 20 涉及到 (Example10_20.java 和 WindowFileLock.java) 这两个文件。
10.16 应用举例
1.标准化考试
标准化试题文件的格式要求如下:
每道题目提供A、B、C、D四个选择(单项选择)。
请提供需要改写的句子呀,你没有给出具体的内容呢。
1. 北京奥运是什么时间开幕的?