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

时间:2023-01-12 21:01:49
     在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操作见上面的源码