一、流的分类
输入流和输出流
按照流的流向来分,可以分为输入流和输出流
- 输入流:只能从中读取数据,而不能向其写入数据。
- 输出流:只能向其写入数据,而不能从中读取数据。
此处的输入、输出涉及一个方向的问题,数据从内存到硬盘,通常称为输出流。(即这里的
输入、输出都是从程序运行所在内存的角度来划分的)。Java的输入流主要由InputStream和
Reader作为基类,而输出流则要由OutputStream和Writer作为基类。它们都是一些抽象基类
,无法直接创建实例。
字节流和字符流
字节流和字符流的用法几乎一样,只是它们所操作的数据单元不同,字节流操作的数据单元
是8位,而字符流操作的数据单元是16位。字节流要由InputStream和OutputStream作为基类
,而字符流则要由Reader和Writer作为基类。
节点流和处理流
按照流的角色来分,可以分为节点流和处理流。
可以从向一个特定的IO设备读/写数据的流称为节点流,节点流也被称为低级流。当使用节点
流进行输入/输出时,程序直接连接到实际的数据源,和实际的输入/输出节点连接。
处理流则用于对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能。处
理流也被称为高级流和包装流。
使用处理流的一个明显的好处是:只要使用相同的处理流,程序就可以采用完全相同的输入/
输出代码来访问不同的数据源,随着处理流所包装节点流的变化,程序实际所访问的数据源也相应
地发生变化。
二、流的概念模型
- InputStream/Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流。
- OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流。
下面程序示范了使用FileInputStream来读取自身的效果:
1 package IO; 2 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 6 public class FileInputStreamTest { 7 8 public static void main(String[] args) throws IOException { 9 10 //创建字节输入流 11 FileInputStream fis = new FileInputStream("D:\\Java\\adt-bundle-windows-x86_64_20140101\\project\\fsad\\src\\IO\\FileInputStreamTest.java"); 12 13 //创建一个长度为1024的字节数组 14 byte[] buf = new byte[1024]; 15 int len = 0; 16 17 while ((len = fis.read(buf)) > 0 ) { 18 //取出buf中的字节,将字节数组转换成字符串输入 19 System.out.println(new String(buf, 0, len)); 20 } 21 fis.close(); 22 } 23 24 }
运行结果:
package IO; import java.io.FileInputStream; import java.io.IOException; public class FileInputStreamTest { public static void main(String[] args) throws IOException { //创建字节输入流 FileInputStream fis = new FileInputStream("D:\\Java\\adt-bundle-windows-x86_64_20140101\\project\\fsad\\src\\IO\\FileInputStreamTest.java"); //创建一个长度为1024的字节数组 byte[] buf = new byte[1024]; int len = 0; while ((len = fis.read(buf)) > 0 ) { //取出buf中的字节,将字节数组转换成字符串输入 System.out.println(new String(buf, 0, len)); } fis.close(); } }
上面程序创建了一个长度为1024的字节数组来读取该文件,实际上该Java源文件的长度还不到1024
字节,也就是说,程序只需执行一次read()方法可读取全部内容。但如果创建较小长度的字节数组,程序
运行时在输出中文注释时就可能出现乱码,这是因为文件保存时采用GBK编码方式,在这种方式下,每个
中文字符占2个字节,如果read()方法读取时只读到了半个中文字符,这将导致乱码。
Java7改写了所有的IO资源类,它们都实现了AutoCloseable接口,因此都可以通过自动关闭资源
的try语句来关闭这些IO流。下面程序使用了FileReader来读取文件本身。
1 package IO; 2 3 import java.io.FileReader; 4 import java.io.IOException; 5 6 public class FileReaderTest { 7 8 public static void main(String[] args) { 9 10 try ( 11 // 创建字符输入流 12 FileReader fr = new FileReader( 13 "D:\\Java\\adt-bundle-windows-x86_64_20140101\\project\\fsad\\src\\IO\\FileReaderTest.java")) { 14 // 创建一个长度为32的字符数组 15 char[] cbuf = new char[32]; 16 int len = 0; 17 18 while ((len = fr.read(cbuf)) > 0) { 19 System.out.println(new String(cbuf, 0, len)); 20 } 21 } catch (IOException e) { 22 // TODO Auto-generated catch block 23 e.printStackTrace(); 24 } 25 } 26 27 }
运行结果:
package IO; import java.io.Fi leReader; import java.io.IOExce ption; public class FileReade rTest { public static void m ain(String[] args) { try ( // 创建字符输入流 FileReader fr = new FileReader( "D:\\Java\adt-bundle-windows-x86_64_20140 101\\project\\fsad\\src\\IO\\Fil eReaderTest.java")) { // 创建一 个长度为32的字符数组 char[] cbuf = ne w char[32]; int len = 0; while ((len = fr.read(cbuf)) > 0) { System.out.println(n ew String(cbuf, 0, len)); } } catch (IOException e) { // TODO Auto-generated catch bl ock e.printStackTrace(); } } }
下面程序使用FileInputStream来执行输入,并使用FileOutputStream来执行输出。
1 package IO; 2 3 import java.io.FileInputStream; 4 import java.io.FileOutputStream; 5 import java.io.IOException; 6 7 public class FileOutputStreamTest { 8 9 public static void main(String[] args) { 10 11 try { 12 // 创建字节输入流 13 FileInputStream fis = new FileInputStream( 14 "D:\\Java\\adt-bundle-windows-x86_64_20140101\\project\\fsad\\src\\IO\\FileOutputStreamTest.java"); 15 // 创建字节输出流 16 FileOutputStream fos = new FileOutputStream("newFile.txt"); 17 18 byte[] buf = new byte[32]; 19 int len = 0; 20 21 while ((len = fis.read(buf)) > 0) { 22 fos.write(buf, 0, len); 23 } 24 } catch (IOException e) { 25 26 e.printStackTrace(); 27 } 28 System.out.println("写入成功!"); 29 } 30 }
运行结果:
写入成功!
下面以获取键盘输入为例来介绍转换流。Java使用System.in代表标准输入,即键盘输入,但这个标准输
入流是InputStream类的实例,使用不太方便,而且键盘输入的内容都是文本,所以可以使用InputStreamReader
将其转换成字符输入流Reader,再将普通的Reader包装成BufferedReader,利用BufferedReader的readLine()方法
可以一次读取一行内容。程序如下:
1 package IO; 2 3 import java.io.BufferedReader; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 7 public class KeyinTest { 8 9 public static void main(String[] args) { 10 11 // 将System.in对象转换成Reader对象,再包装成BufferedReader 12 BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); 13 14 String buffer = null; 15 16 try { 17 while ((buffer = br.readLine()) != null) { 18 //如果读取到的字符串时“exit”,则程序退出 19 if (buffer.equals("exit")) { 20 System.exit(1); 21 } 22 System.out.println("输入的内容为:" + buffer); 23 } 24 } catch (IOException e) { 25 // TODO Auto-generated catch block 26 e.printStackTrace(); 27 } 28 } 29 30 }
运行结果:
黑马程序员 输入的内容为:黑马程序员 exit
BufferedReader流具有缓冲功能,它可以一次读取一行文本——以换行符为标志,如果没有读到
换行符则程序阻塞,等到读到换行符为止。