文件操作的字节流和字符流

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操作见上面的源码

 

以上是关于文件操作的字节流和字符流的主要内容,如果未能解决你的问题,请参考以下文章

Java中的字节流和字符流区别

Java IO流字节流和字符流详解

转换流

IO流系统总结

Java中常用的字节流和字符流

java IO的字节流和字符流及其区别