Java面向对象系列[v1.0.0][输入输出流体系]

输入输出流体系

处理流的用法

处理流可以隐藏底层设备上节点流的差异,并对外提供更加方便的输入/输出方法,代码层面只需要关注高级流的操作,使用处理流的典型思路就是用处理流来包装节点流,节点流与底层的IO设备、文件进行交互,处理流来执行输入/输出
识别处理流和节点流非长简单,流的构造器参数如果不是一个物理节点,而是已经存在的流,那么它一定是处理流,所有节点流都是直接以物理IO节点作为构造器参数的

import java.io.*;

public class PrintStreamTest
{
	public static void main(String[] args)
	{
		try (
			// 创建节点输出流
			var fos = new FileOutputStream("test.txt");
			// 使用PrintStream包装该节点流
			var ps = new PrintStream(fos))
		{
			// 使用PrintStream执行输出
			ps.println("普通字符串");
			// 直接使用PrintStream输出对象
			ps.println(new PrintStreamTest());
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
}

在使用处理流包装了底层节点流后,关闭输入/输出流资源时,只要关闭最上层的处理流即可,关闭了处理流系统会自动关闭被处理流包装的节点流

输入/输出流体系

Java的输入/输出流体系提供了近40个类,如图所示,粗体字标识的是节点流,斜体字标识的是抽象类
在这里插入图片描述
还有一些特殊的输入输出流体系不在表内,他们不属于java.io包下,例如AudioInputStream、CipherInputStream、DeflaterInputStream、ZipInputStream等等用于访问音频、加解密、压缩解压缩等等的字节流,位于JDK的其他包里。

import java.io.*;

public class StringNodeTest
{
	public static void main(String[] args)
	{
		var src = "从明天起,做一个幸福的人\n"
			+ "喂马,劈柴,周游世界\n"
			+ "从明天起,关心粮食和蔬菜\n"
			+ "我有一所房子,面朝大海,春暖花开\n"
			+ "从明天起,和每一个亲人通信\n"
			+ "告诉他们我的幸福\n";
		var buffer = new char[32];
		var hasRead = 0;
		try (
			var sr = new StringReader(src))
		{
			// 采用循环读取的访问读取字符串
			while ((hasRead = sr.read(buffer)) > 0)
			{
				System.out.print(new String(buffer, 0, hasRead));
			}
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
		try (
			// 创建StringWriter时,实际上以一个StringBuffer作为输出节点
			// 下面指定的20就是StringBuffer的初始长度
			var sw = new StringWriter(20))
		{
			// 调用StringWriter的方法执行输出
			sw.write("有一个美丽的新世界,\n");
			sw.write("她在远方等我,\n");
			sw.write("那里有天真的孩子,\n");
			sw.write("还有姑娘的酒窝\n");
			System.out.println("----下面是sw的字符串节点里的内容----");
			// 使用toString()方法返回StringWriter的字符串节点的内容
			System.out.println(sw.toString());
		}
		catch (IOException ex)
		{
			ex.printStackTrace();
		}
	}
}

转换流

转换流用于将字节流转换成字符流,其中InputStreamReader将字节输入流转换成字符输入流,OutputStreamWriter将字节输出流转换成字符输出流

import java.io.*;

public class KeyinTest
{
	public static void main(String[] args)
	{
		try (
			// 将Sytem.in对象转换成Reader对象
			var reader = new InputStreamReader(System.in);
			// 将普通Reader包装成BufferedReader
			var br = new BufferedReader(reader))
		{
			String line = null;
			// 采用循环方式来一行一行的读取
			while ((line = br.readLine()) != null)
			{
				// 如果读取的字符串为"exit",程序退出
				if (line.equals("exit"))
				{
					System.exit(1);
				}
				// 打印读取的内容
				System.out.println("输入内容为:" + line);
			}
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
}

Java使用System.in代表标准输入,也就是键盘的输入,但标准输入流是InputStream类的实例,并且键盘输入都是文本内容,所以可以使用InputStreamReader将其转换成字符输入流
普通的Reader也可以包装成BufferedReader,BufferedReader流具有缓冲功能,利用BufferedReader的readLine()方法可以一次性读取一行文本,以换行符为标志,如果没有读到换行符,则程序阻塞,直到读到换行符为止

推回输入流

PushbackInputStream和PushbackReader,这是两个特殊的流,提供了3个方法:

  • void unread(byte[]/char[] buf): 将一个字节/字符数组内容推回到推回缓冲区里,从而允许重复读取刚刚读取的内容
  • void unread(byte[]/char[] b, int off, int len):将一个字节/字符数组里从off开始,长度为len字节/字符的内容推回到推回缓冲区里,从而允许重复读取刚刚读取的内容
  • void unread(int b):将一个字节/字符推回到推回缓冲区里,从而允许重复读取刚刚读取的内容
    推回输入流带有一个推回缓冲区,当程序调用unread()方法时,系统将会把指定数组的内容推回到该缓冲区里,之后推回输入流每次调用read()方法时,总是先从推回缓冲区读取,只有完全读取了推回缓冲区中的内容后,但还没有装满read()所需的数组时,才会从原输入流中读取。
    当程序创建一个PushbackInputStream和PushbackReader时需要指定推回缓冲区的大小,默认为1,如果推回的内容大于推回缓冲区大小,则引发Pushback buffer overflow的IOException异常
import java.io.*;

public class PushbackTest
{
	public static void main(String[] args)
	{
		try (
			// 创建一个PushbackReader对象,指定推回缓冲区的长度为64
			var pr = new PushbackReader(new FileReader(
				"PushbackTest.java"), 64))
		{
			var buf = new char[32];
			// 用以保存上次读取的字符串内容
			var lastContent = "";
			var hasRead = 0;
			// 循环读取文件内容
			while ((hasRead = pr.read(buf)) > 0)
			{
				// 将读取的内容转换成字符串
				var content = new String(buf, 0, hasRead);
				var targetIndex = 0;
				// 将上次读取的字符串和本次读取的字符串拼起来,
				// 查看是否包含目标字符串, 如果包含目标字符串
				if ((targetIndex = (lastContent + content)
					.indexOf("new PushbackReader")) > 0)
				{
					// 将本次内容和上次内容一起推回缓冲区
					pr.unread((lastContent + content).toCharArray());
					// 重新定义一个长度为targetIndex的char数组
					if (targetIndex > 32)
					{
						buf = new char[targetIndex];
					}
					// 再次读取指定长度的内容(就是目标字符串之前的内容)
					pr.read(buf, 0, targetIndex);
					// 打印读取的内容
					System.out.print(new String(buf, 0, targetIndex));
					System.exit(0);
				}
				else
				{
					// 打印上次读取的内容
					System.out.print(lastContent);
					// 将本次内容设为上次读取的内容
					lastContent = content;
				}
			}
		}
		catch (IOException ioe)
		{
			ioe.printStackTrace();
		}
	}
}

猜你喜欢

转载自blog.csdn.net/dawei_yang000000/article/details/106184167
今日推荐