一、本章要点
- File
- FilenameFilter
- 字节流
- 字符流
- 代码实例
- 对象序列化与反序列化
- 延伸-装饰者模式
1.File
在Java程序中操作文件或目录,可以使用java.io.File类进行处理。File类提供了很多方法,常用的如下:
func | desc |
---|---|
String getName() | 返回文件名或者路径名 |
String getPath() | 返回路径名 |
boolean renameTo(File newName) | 重命名目录或文件 |
boolean exists() | 判断目录或文件是否存在 |
boolean canWrite() | 是否具有可写权限 |
boolean canRead() | 是否具有可读权限 |
boolean isFile() | 是文件而不是目录返回true |
boolean isDirectory() | 是目录而不是文件返回true |
boolean mkdir()/mkdirs() | 针对file路径创建一个目录,单层 |
File[] listFiles() | 列出file对象下所有子文件 |
2.FilenameFilter
1 | public class FileTest { |
文件过滤器,可以按照要求返回所需的某些文件。见策略模式
3.字节流
InputStream
- int read() 从输入流中读取一个字节的数据,并返回读取的字节数据,到达文件末尾返回-1
- int read(byte[] b) 从输入流中最多读取b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数,到达文件末尾返回-1
- int read(byte[] b,int off,int len) 从输入流中读取最多len个字节的数据,并将其存储在b中。放入数组中时并不是从b[0]开始而是从b[off]开始。到达文件末尾返回-1
OutputStream
- void write(int c) 将指定的字节写入到输出流中
- void write(byte[]/char[] buf) 将字节数组写入输出流中
- void write(byte[]/char[] buf,int off,int len) 将数组buf从off位置开始长度为len的数据写入输出流
- void flush() 刷新输出流并强制写出所有缓冲的输出字节
4. 字符流
Reader
- int read() 从输入流中读取单个字符,并返回读取的字符数据
- int read(char[] cbuf) 从输入流中读取最多cbuf.length个字符数据,并将其存储在cbuf中,返回实际读取的字节数
- int read(char[] cbuf,int off,int len)
Writer
- void write(String str) 将字符串str写入输出流
- void write(String str,int off,int len) 将字符串str从off开始,长度为len的字符输出到指定的输出流
5.代码实例
- 使用FileInputStream读取文件的常用方式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34private static void readFile() {
FileInputStream fis = null;
File f = new File("./src/a.txt");
try {
fis = new FileInputStream(f);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
byte[] buf = new byte[4]; // 一次性读取buf.length个字节的数据
int length = 0;
try {
//将读取的字节数据装入byte数组中,返回的是读取的字节数;读至文件末尾返回 -1
while ((length = fis.read(buf)) != -1) {
/**
* read方法将数据读进byte数组采用覆盖的方式,而并非清空重新赋值的方式。
* 故,在获取输出时要指定byte数组中有效的元素。
*/
System.out.print(new String(buf, 0, length));
}
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
read方法将数据读进字节数组时采用的是覆盖的方式,故在构建字符串时需要指定length,否则会有垃圾数据。
- 使用FileOutputStream写文件
write(int c)方法传入int类型的变量(4个字节),但是该方法只会将最低位的那个字节写入文件,其余的三个字节会被舍弃。二进制数:00000000-00000000-00000001-01100001(353),将353写入,但是文件中却只有低八位(97)。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16private static void writeTest() throws IOException {
FileOutputStream fos = null;
FileInputStream fis = null;
File file = new File("./src/b.txt");
fos = new FileOutputStream(file);
fis = new FileInputStream(file);
fos.write(353); //00000000-00000000-00000001-01100001
byte[] bytes = new byte[4];
fis.read(bytes);
System.out.println(Arrays.toString(bytes)); //输出:[97, 0, 0, 0]
fis.close();
fos.close();
}
6.对象序列化与反序列化
并不是所有对象都可以写入到输出流,可以写入输出流中的对象称为可序列化的(serializable)。可序列化对象的类必须实现java.io.Serializable接口,它是一种标记性接口。1
2
3
4
5
6
7
8
9
10
11
12
13public static void main(String[] args) throws Exception{
String str = "Java程序设计";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./str.dat",true));
oos.writeObject(str);//序列化
oos.close();
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./str.dat"));
String resStr = (String) ois.readObject(); //对象的反序列化
System.out.println(resStr);
}
对象序列化的机制写入的内容是:对象的类、类签名、以及非瞬态和非静态的值(静态的值在方法区)。序列化的目标是将对象保存在磁盘中,或允许在网络中直接传输对象。这在很多框架中很常见,例如web开发中服务器会对客户端对象进行临时存储,短时间内即使宕机也能恢复数据。
如果一个对象是Serializable的实例,但它包含一个不能被序列化的数据域,那么整个对象都不可被序列化。为了使该对象是可序列化的,需要给这些数据域加上关键字transient,告诉Java虚拟机将对象写入对象流时忽略这些数据域。
7. 延伸-装饰者模式
装饰模式指的是在不必改变原类文件和继承关系的情况下,动态地扩展一个对象的功能。它是通过创建一个包装对象,也就是装饰者来包裹真实的对象,以达到装饰的目的。
Java I/O 系统的设计是典型的装饰者模式:
例如java.io.LineNumberReader通过装饰java.io.BufferedReader,实现可以按行读取文本,jdk中LineNumberReader的read()方法代码大致如下:1
2
3
4
5
6
7
8
9
10
11//......
public String readLine() throws IOException {
synchronized (lock) {
String l = super.readLine(skipLF);
skipLF = false;
if (l != null)
lineNumber++;
return l;
}
}
//......
更多关于装饰者模式的细节见:我的博客-装饰者模式
本文链接: http://www.xiaopeng.pro/articles/f3647a9e.html
版权声明: 本原创文章采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。转载请注明出处!