Java的IO流是实现输入、输出的基础,它可以方便的实现数据的输入输出操作。在Java中把不同的输入输出源(键盘、文件、网络连接)抽象表述为流(stream)。通过流可以使java程序使用相同的方式访问不同的输入输出源。
一、流的分类
1、按照流的流向分:输入流和输出流
输入流:只能从中读取数据,不能向其写入数据
InputStream/Reader:所有输入流的基类,前者是字节输入流,后者是字符输入流
输出流:只能向其写入数据,不能从中读取数据
OutputStream/Writer:所有输出流的基类,前者是字节输出流,后者是字符输出流
2、按照流的内容分:字节流和字符流
字节流主要是有InputStream/OutputStream作为基类
字符流主要是有Reader/Writer作为基类
3、按照流的角色分:节点流和处理流
从一个特定的IO设备读/写数据的流称为节点流,在已经存在的流进行连接或封装,封装后的流称为处理流。
二、代码测试
import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.io.PrintStream; import java.io.PushbackReader; import java.io.RandomAccessFile; import java.io.Reader; import org.junit.Test; public class StreamTest { /** * 字节输入流读取文件内容 * */ @Test public void inputStreamTest() { File f = new File("G://test//a1.txt"); InputStream input = null; try { input = new FileInputStream(f); byte[] bbuf = new byte[1024]; int hasRead = 0; while((hasRead =input.read(bbuf)) > 0) { System.out.println(new String(bbuf,0,hasRead)); } } catch (Exception e) { e.printStackTrace(); }finally { try { input.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 字符输入流读取文件内容 */ @Test public void fileReaderTest() { File f = new File("G://test//a1.txt"); Reader reader = null; try { reader = new FileReader(f); char[] cbuf = new char[32]; int hasRead = 0; while((hasRead = reader.read(cbuf))>0) { System.out.println(new String(cbuf,0,hasRead)); } } catch (Exception e) { e.printStackTrace(); }finally { try { reader.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 使用FileInputStream输入文件,FileOutPutStream输出文件,实现文件复制 */ @Test public void fileOutputStreamTest() { //字节输入流 FileInputStream fis = null; byte[] bbuf = new byte[1024]; int hasRead = 0; //字节输出流 FileOutputStream fos = null; try { fis = new FileInputStream("G://test//a1.txt"); fos = new FileOutputStream("G://test//a2.txt"); while((hasRead = fis.read(bbuf))>0) { fos.write(bbuf, 0, hasRead); } } catch (Exception e) { e.printStackTrace(); }finally { try { fos.close(); fis.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 字符输出流 */ @Test public void FileWriterTest() { FileWriter fw =null; try { fw = new FileWriter("G://test//a3.txt"); fw.write("窗前明月光"); } catch (IOException e) { e.printStackTrace(); }finally { try { fw.close(); } catch (IOException e) { e.printStackTrace(); } } } /** * 转换流的使用:InputStreamReader:将字节输入流转换成字符输入流 , * OutputStreamWriter将字节输出流转换成字符输出流 */ public void convertedStreamTest() { try ( InputStreamReader reader = new InputStreamReader(new FileInputStream("G://test//temp.txt"),"UTF-8"); OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("G://test//temp1.txt"),"UTF-8"); ) { char[] cbuf = new char[32]; int hasRead = 0; while((hasRead=reader.read(cbuf))>0) { System.out.print(new String(cbuf, 0, hasRead)); writer.write(cbuf, 0, hasRead);; } } catch (Exception e) { e.printStackTrace(); } } /** * 缓冲流BufferReader:具有缓冲功能,它可以一次读取一行文本(以换行符为标识),如果没有读到换行符,则程序堵塞 * 下面程序键盘输入字符串,只有等到按下回车键后,输入的内容才会打印出来 */ @Test public void bufferReaderTest() { try ( InputStreamReader reader = new InputStreamReader(System.in); //用缓存流包装输入流 BufferedReader br = new BufferedReader(reader); ) { String line = null; while((line = br.readLine()) != null) { System.out.println(line); } } catch (Exception e) { e.printStackTrace(); } } /** * 处理流 * 处理流的优势:1、处理流进行输入输出操作更简单 2、使用处理流的效率更高 * *使用PrintStream 代替 OutputStream *PrintStream类的输出功能非常强大,通常如果我们要输出文本内容,应该将输出流包装成PrintStream */ @Test public void printStreamTest() { try ( FileOutputStream fos = new FileOutputStream("G://test//a4.txt"); PrintStream ps = new PrintStream(fos); ) { //输出字符串 ps.println("使用PrintStream处理流输出文件"); //输出对象 ps.println(new StreamTest()); } catch (Exception e) { e.printStackTrace(); } } /** * 推回输入流PushbackInputStream和PushbackReader * 推回输入流都带有一个推回缓冲区,当程序调用这两个推回输入流的unread()方法,系统会把指定数组的内容推回到该缓冲区里,而推回输入流每次调用read()方法时总是先 * 从推回缓冲区中读取内容,只有完全读取了缓冲区的内容,但还没有装满read()所需的数组时才会从原输入流中读取。 * 下面程序利用推回缓冲区,返回文件中"rdb"字符串之前的字符串 */ @Test public void pushBackTest() { try ( //创建PushbackReader对象,指定缓冲区长度为64 PushbackReader pr = new PushbackReader(new FileReader("G://test//a1.txt"),64); ) { char[] cbuf = new char[32]; //用于保存上次读取字符串的内容。 String lastContent = ""; int hasRead = 0; while((hasRead = pr.read(cbuf))>0) { //将读取内容转换成字符串 String content = new String(cbuf,0,hasRead); //用于记录 ‘rdb’字符串的位置 int targetIndex = 0; if((targetIndex = (content + lastContent).indexOf("rdb"))>0) { //回推包含‘rdb’的字符串 pr.unread((content+lastContent).toCharArray()); int len = targetIndex > 32? 32:targetIndex; pr.read(cbuf, 0, len); System.out.print(new String(cbuf,0,len)); System.exit(0); }else { System.out.println(lastContent); } } } catch (Exception e) { e.printStackTrace(); } } /** * 缓冲流BufferReader:具有缓冲功能,它可以一次读取一行文本(以换行符为标识),如果没有读到换行符,则程序堵塞 * 下面程序键盘输入字符串,只有等到按下回车键后,输入的内容才会打印出来 */ @Test public void bufferReaderTest() { try ( InputStreamReader reader = new InputStreamReader(System.in); //用缓存流包装输入流 BufferedReader br = new BufferedReader(reader); ) { String line = null; while((line = br.readLine()) != null) { System.out.println(line); } } catch (Exception e) { e.printStackTrace(); } } /** * RandomAcessFile类支持"随机访问的方式".程序可以直接跳转到文件的任意地方来读写数据 * 如果我们只想访问文件的部分内容,使用RandomAcessFile更好 * RandomAcessFile允许自由定位文件记录指针,所以RandomAcessFile可以不从开始的地方开始输出,所以RandomAcessFile可以向已经存在的文件 * 后面追加内容。如果程序想在已存在的文件后追加内容,应该使用RandomAcessFile * */ @Test public void randomAcessFile() { try ( //创建RandomAccessFile对象时需要一个mode参数 //r:以只读方式打开文件,rw:以读写方式打开文件 //rws:以读写方式打开文件,还要求对文件内容或元数据的每个更新都同步写入底层存储设备 //rwd:以读写方式打开文件,还要求对文件内容的每个更新都同步写入到底层存储设备 RandomAccessFile raf = new RandomAccessFile(new File("G://test//a1.txt"), "rw"); ) { //RandomAccessFile的文件初始位置,初始是0 System.out.println("文件指针初始位置:"+raf.getFilePointer()); //移动文件指针位置,从移动后的位置开始读取文件内容 raf.seek(32); byte[] bbuf = new byte[32]; int hasRead = 0; while((hasRead = raf.read(bbuf))>0) { System.out.println(new String(bbuf,0,hasRead)); } //指针移动到文件最后 raf.seek(raf.length()); //向文件后追加内容 raf.write("追加内容".getBytes()); } catch (Exception e) { e.printStackTrace(); } } }