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