操作外设以提供有用输入/输出信息抽象/封装为"流stream"的概念
对I/O的操作被封装在操作系统底层,都在java.io.*包中
1)标准I/O:对操作系统指定的标准设备的输入和输出,即从键盘输入数据,输出到显示器屏幕
2)文件I/O:以外存磁盘文件为对象进行输入和输出,即从磁盘文件输入数据,数据输出到磁盘文件
3)字符串I/O:对内存中指定的空间进行输入和输出,通常指定一个字符数组作为存储空间(实际上可以利用该空间存储任何信息)
1)8位字节流:继承于InputStream和OutputStream基类的“流”以字节为处理单元
通过继承上述抽象类,可以创建与存储介质直接连接的流,如System.in常量、FileInputStream、StringBufferedInputStream、ByteArrayInputStream / System.out常量、FileOutputStream、ByteArrayOutputStream
输入流的通用方法包括:
int read(byte[] b, int off, int len);
注:0~255、字符类型返回ASCⅡ码、返回-1表示读取到头了
* read()方法内部将读取到的所有字节高位补0转为int返回, 这样做所有的数据都会是正数,这时就可以用-1表示流末尾了
* 而改变后的数据只要强转回byte, 就可以得到原有数据
int available();
long skip(long n);
void close();注:及时关闭以节约内存资源
1 public class SystemIn{ 2 public static void main(String[] args) throws IOException { 3 int in = System.in.read();//in使类System的静态变量,即public static final InputStream in, 将键盘输入的数据作为输入流使用 4 while((char)in!=‘ ’) {//也可以将从控制台输入的换行符作为循环终止的条件 5 System.out.print((char)in); 6 in = System.in.read(); 7 } 8 } 9 }
1 private static void main(String[] args) throwa IOException { 2 FileInputStream readFile = 3 new FileInputStream("FileInputStreamDemo.java"); 4 int intTemp = readFile.read(); 5 while (intTemp!=-1) { 6 System.out.print((char)intTemp);//将0~255转化成可读的内容 7 intTemp = readFile.read(); 8 } 9 readFile.close(); 10 }
输出流的通用方法包括:
void write(byte[]b, int off, int len);
flush();特别是向显示屏输出时,可能不会立即显示,那么就刷空输出流,并将缓冲区中左右被缓存的字节强制送出显示到目的地
close();
1 public static void main(String[] args) throws IOException { 2 FileOutputStream writeFile = 3 new FileOutputStream("out.txt"); 4 String s = "happy new year! happy everyday!" 5 +" "+"wish fulfillment!" 6 +" "+"你好 End of file stream "; 7 for (int i=0;i<s.length();i++) { 8 writeFile.write(s.charAt(i)); 9 } 10 writeFile.close(); 11 }
FilterInputStream过滤流与InputStream介质流之间的关系包括:继承关系和关联关系;通过维护一个私有的关联属性-inputStream作为过滤流的加工对象以提供更多更装好的功能(操作);通过FilteredStream构造参数的参数初始化该私有属性
注:缓冲区是如何提高读取效率的
系统调用时,若不用缓冲,CPU会酌情考虑使用 中断。此时CPU是主动地,每个周期中都要花去一部分去询问IO设备是否读完数据,这段时间CPU不能做任何其他的事情(至少负责执行这段模块的核不能)。所以,调用一次读了一个字,通报一次,CPU腾出时间处理一次。
而设置缓冲,CPU通常会使用 DMA 方式去执行 IO 操作。CPU 将这个工作交给DMA控制器来做,自己腾出时间做其他的事,当DMA完成工作时,DMA会主动告诉CPU“操作完成”。这时,CPU接管后续工作。在此,CPU 是被动的。DMA是专门 做 I\O 与 内存 数据交换的,不仅自身效率高,也节约了CPU时间,CPU在DMA开始和结束时做了一些设置罢了。
举个形象的例子,在A地有10000本书需要搬到B地,如果一次搬1本,需要10000次。如果每次取1000本放到一个货车上,运到B地,需要10次完成。货车相当于是缓存区。同样道理,开设一个数据缓存区每次读取一数据块对于提高读取效率有显著提升。下面用一个具体代码示例来表示二者的性能差别。
DataInputStream in = new DataInputStream( new BufferedInputStream( new FileInputStream("Data.txt")));//过滤流与过滤流之间可以层层封装,实现功能的组合,但同时最底层的加工对象一定是一个介质流 //一般将DataInputStream放在最外层,因为相比于BufferedInputStream()提供的方法更实用
(Decorator设计模式:很好的运用了继承和关联机制,因为多了一层关联关系,从而可以将outputStream子类对象作为过滤流的初始化对象来使用。当为满足各种必要的功能组合造成单纯的继承动作产生大量的子类时,往往使用Decorate设计模式,JavaI/O程序往往需要不同的功能组合,故使用了下图显示的Decorator设计模式)
2)16位字符流:继承于Reader和Writer基类的"流"以16位Unicode编码表示的字符位处理单元(汉字就可以正常显示了)
介质流包括:FileReader、StringReader(对比StringBufferInputStream)、CharArrayReader / FileWriter、StringWriter(没有可对比的inputStream的类)、CharArrayWriter
输入流的常用方法包括:
int read(char[] cbuf, int off, int len);
long skip(long n);
void close();
输出流的常用方法包括:
void write(char[] cbuf, int off, int len);
void write(String str, int off, int len);
void flush();
void close();
过滤流包括:BufferedReader(readLine();)、BufferedReader、PrintWriter,若需读取基本数据类型则仍然应用字节流中的DataInputStream和DataOutputStream
public BufferedReader(Reader in), public String readLine();可提升读取数据,一行一行的读取
桥接类InputStreamReader / OutputStreamWriter允许将"byte"继承体系和"char"继承体系中的类搭配运用,可将操作台中的标准字节流转化成字符流一行一行的读取:public class InputStreamReader extends Reader;通过构造函数InputStreamReader(InputStream in);实现字节流转换为字符流Reader读取
BufferedReader stdIn = new BufferedReader( new InputStreamReader( System.in));//实现从控制台读取一行一行的字符串 String input = stdIn.readLine(); --------------------------------------------------------------------------------
BufferedReader fileIn = new BufferedReader( new FileReader("filename"));//实现从磁盘文件中读取一行一行的字符串 String input = stdIn.readLine();
String input = fileIn.readln(); while (input!=null) { System.out.println("read:"+input); }
public class PrintWriter extends Writer;构造函数包括:PrintWriter(Writer out);PrintWriter(Writer out, boolean autoFlush);PrintWriter(OutputStream out);PrintWriter(OutputStream out, boolean autoFlush);
PrintWriter stdOut = new PrintWriter(System.out, true)//调用构造函数时自动刷新缓存,构造函数1 stdOut.println("A line of output");//还可以打印一个对象,自动调用该对象的toString方法 ---------------------------------------------------------------------------------- PrintWriter fileAppend = new PrintWriter(new FileWriter("filename", true)); fileAppend.println("A line of output.");//在源文件中追加内容,构造函数2
----------------------------------------------------------------------------------
PrintWriter fileOut = new PrintWriter(new FileWriter("filename"));
fileOut.println("A line of output.")//覆盖源文件中内容,构造函数3
应用注意事项:
1)所有java.io.*中的类的操作都有可能抛出异常,checked异常必须try-catch或者throws
2)在应用中尽可能先尝试使用Reader和Writer,一旦无成功编译程序,就会发觉似乎非得使用字节流不可;注:java.util.*就是字节流
3)灵活使用BufferedReader和PrintWriter这两个类