文件操作的字节流和字符流
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了文件操作的字节流和字符流相关的知识,希望对你有一定的参考价值。
在java.io包中流的操作主要有字节流、字符流两大类,两类都有输入和输出操作。在字节流中输出数据主要使用OutputStream类完成,输入使用的是InputStream类。
在字符流中输出主要是使用Writer类完成,输入主要是使用Reader类完成。
这四个类都是抽象类,使用中需通过子类进行实例化(向上转型),或者直接使用子类进行io操作。
文件操作的字节流 FileOutputStream,FileInputStream。文件操作的字符流 FileWriter,FileReader。
一.字节流
字节流主要操作byte类型数据。
OutputStream是整个IO包中字节输出流的最大父类,定义如下:
public abstract class OutputStream implements Closeable, Flushable
InputStream是整个IO包中字节输入流的最大父类,定义如下:
public abstract class InputStream implements Closeable
1.文件字节输出流
定义如下:
public class FileOutputStream extends OutputStream
核心方法:
(1)核心构造方法:从源码分析,构造函数的核心作用对文件执行open操作。
1 public FileOutputStream(File file, boolean append)
2 throws FileNotFoundException
3 {
4 String name = (file != null ? file.getPath() : null);
5 SecurityManager security = System.getSecurityManager();
6 if (security != null) {
7 security.checkWrite(name);
8 }
9 if (name == null) {
10 throw new NullPointerException();
11 }
12 if (file.isInvalid()) {
13 throw new FileNotFoundException("Invalid file path");
14 }
15 this.fd = new FileDescriptor();
16 fd.attach(this);
17 this.append = append;
18 this.path = name;
19 open(name, append);//继续向下看
20 }
1 private void open(String name, boolean append)
2 throws FileNotFoundException {
3 open0(name, append);//继续
4 }
1 private native void open0(String name, boolean append)
2 throws FileNotFoundException;
3 //到此在往下就是调用native层的open0函数了。
(2)write和writeBytes函数最终调用的也是navite的write和writeBytes函数
1 private native void write(int b, boolean append) throws IOException;
2
3 private native void writeBytes(byte b[], int off, int len, boolean append)
4 throws IOException;
2.文件字节输入流
定义如下:
(1)核心构造方法,从源码分析,构造函数的核心作用对文件执行open操作。
1 public FileInputStream(File file) throws FileNotFoundException {
2 String name = (file != null ? file.getPath() : null);
3 SecurityManager security = System.getSecurityManager();
4 if (security != null) {
5 security.checkRead(name);
6 }
7 if (name == null) {
8 throw new NullPointerException();
9 }
10 if (file.isInvalid()) {
11 throw new FileNotFoundException("Invalid file path");
12 }
13 fd = new FileDescriptor();
14 fd.attach(this);
15 path = name;
16 open(name);//继续
17 }
1 private void open(String name) throws FileNotFoundException {
2 open0(name);
3 }
1 private native void open0(String name) throws FileNotFoundException;
(2)read和readBytes函数最终调用的也是navite的read0和readBytes函数
1 private native int read0() throws IOException;
2
3 private native int readBytes(byte b[], int off, int len) throws IOException;
二.字符流
程序中一个字符代表两个字节。
Writer是整个IO包中字符输出流的最大父类,定义如下:
public abstract class Writer implements Appendable, Closeable, Flushable
Reader是整个IO包中字符输入流的最大父类,定义如下:
public abstract class Reader implements Readable, Closeable
1.文件字符输出流
定义如下:
public class FileWriter extends OutputStreamWriter // OutputStreamWriter是Writer的子类
(1)源码:
1 public class FileWriter extends OutputStreamWriter {
2
3 public FileWriter(String fileName) throws IOException {
4 super(new FileOutputStream(fileName));
5 }
6
7 public FileWriter(String fileName, boolean append) throws IOException {
8 super(new FileOutputStream(fileName, append));
9 }
10
11 public FileWriter(File file) throws IOException {
12 super(new FileOutputStream(file));
13 }
14
15 public FileWriter(File file, boolean append) throws IOException {
16 super(new FileOutputStream(file, append));
17 }
18
19 public FileWriter(FileDescriptor fd) {
20 super(new FileOutputStream(fd));
21 }
22
23 }
可以看到它所有的构造方法调用的都是其父类的构造方法,并且传递一个FileOutputStream类的实例进去。
随便举一个父类构造方法的例子 (其他最终也是调用StreamEncoder.forOutputStreamWriter)
1 public OutputStreamWriter(OutputStream out, Charset cs) {
2 super(out);
3 if (cs == null)
4 throw new NullPointerException("charset");
5 se = StreamEncoder.forOutputStreamWriter(out, this, cs);//从字符到字节的编码过程,将字符流转换为字节流。
6 }
(2)其write方法是继承其父类OutputStreamWriter的write方法
随便举一个例子(其他最终也是调用se.write)
public void write(String str, int off, int len) throws IOException {
se.write(str, off, len);
}
/*其中se定义如下*/
private final StreamEncoder se;//它由前面介绍的构造函数实例化
2.文件字符输入流
定义如下:
public class FileReader extends InputStreamReader // InputStreamReader 是 Reader的子类
(1)源码
1 public FileReader(String fileName) throws FileNotFoundException {
2 super(new FileInputStream(fileName));
3 }
4
5 public FileReader(File file) throws FileNotFoundException {
6 super(new FileInputStream(file));
7 }
8
9 public FileReader(FileDescriptor fd) {
10 super(new FileInputStream(fd));
11 }
可以看到它所有的构造方法调用的都是其父类的构造方法,并且传递一个FileIntputStream类的实例进去。
随便举一个例子(其他最终调用也是StreamDecoder.forInputStreamReader)
1 public InputStreamReader(InputStream in, String charsetName)
2 throws UnsupportedEncodingException
3 {
4 super(in);
5 if (charsetName == null)
6 throw new NullPointerException("charsetName");
7 sd = StreamDecoder.forInputStreamReader(in, this, charsetName);//从字节到字符的解码过程,将字节流转换为字符流
8 }
来看一下StreamDecode的源码
1 public class StreamDecoder extends Reader{
2 private static final int MIN_BYTE_BUFFER_SIZE = 32;
3 private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
4 private Charset cs;
5 private CharsetDecoder decoder;
6 private ByteBuffer bb;
7
8 // 由上述的 forInputStreamReader方法的参数可知用的是下面这个方法
9 public static StreamDecoder forInputStreamReader(InputStream in,Object lock,String charsetName) throws UnsupportedEncodingException {
10 String csn = charsetName;
11 if (csn == null) // 由于用的是默认编码,会执行这句
12 csn = Charset.defaultCharset().name();
13 try {
14 if (Charset.isSupported(csn)) // 检测JVM是否支持该编码集
15
16 return new StreamDecoder(in, lock, Charset.forName(csn)); //调用其构造方法实例化一个对象
17 } catch (IllegalCharsetNameException x) { }
18 throw new UnsupportedEncodingException (csn);
19 }
20
21 StreamDecoder(InputStream in, Object lock, Charset cs) {
22 this(in, lock, cs.newDecoder().onMalformedInput(CodingErrorAction
23 .REPLACE).onUnmappableCharacter(CodingErrorAction.REPLACE));
24 // 额,说明它是在用Charset对象产生CharsetDecoder对象,目的是为了执行另一个构造函数
25 }
26
27 StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
28 // CharsetDecoder:是一个引擎,可以将一个字节序列按照特定的字符集转换成一个16位的Unicode序列
29 super(lock);
30 this.cs = dec.charset();
31 this.decoder = dec;
32 // 下面的代码先不用管,我们这里用不上
33 // This path disabled until direct buffers are faster
34 if (false && in instanceof FileInputStream) {
35 ch = getChannel((FileInputStream)in);
36 if (ch != null)
37 bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
38 }
39 if (ch == null) {
40 this.in = in;
41 this.ch = null;
42 bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
43 }
44 bb.flip(); // So that bb is initially empty
45 }
46 // 调用的就是这个函数吧
47 public int read() throws IOException {
48 return read0(); //额,又是假的;继续看
49 }
50 private int read0() throws IOException {
51 synchronized (lock) {
52 // Return the leftover char, if there is one
53 if (haveLeftoverChar) {
54 haveLeftoverChar = false;
55 return leftoverChar;
56 }
57 // Convert more bytessz
58 char cb[] = new char[2]; //一次读两个字节
59 int n = read(cb, 0, 2);
60 switch (n) {
61 case -1:
62 return -1;
63 case 2:
64 leftoverChar = cb[1];
65 haveLeftoverChar = true;
66 // FALL THROUGH
67 case 1:
68 return cb[0];
69 default:
70 assert false : n;
71 return -1;
72 }// end of catch
73 }// end of synchronized
74 }
75
76 }
(2) read操作见上面的源码
以上是关于文件操作的字节流和字符流的主要内容,如果未能解决你的问题,请参考以下文章