Java的I/O流

本文深入探讨Java中的输入输出流概念,包括字节流与字符流的区别,节点流和处理流的作用,以及如何使用标准I/O流体系进行数据读写。通过实例展示了处理流的具体用法,如PrintStream包装OutputStream,以及如何解决乱码问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

输入输出流的定义

输入流:只能从中读取数据,而不能向其写入数据.
输出流:只能向其写出数据,而不能从中读取数据.
角度很重要,我们作为Java工程师应该站在程序运行所在内存来考虑的.

字节流和字符流

小技巧

  • 字节流的类一定是以Stream结尾的.
  • 字符流的输入流以Reader结尾,
  • 字符流的输出流以Writer结尾.

区别

字节流操作的最小数据单元是8位(8bit)的字节(byte),而字符流操作的最小数据单元是16位的字符(2byte->char).

节点流和处理流

按照流的角色分,可以分为节点流和处理流.
具体点就是,
可以从或者向一个特定的IO设备读写数据的流(不管你是输入还是输出流,只要与IO设备相关),称为节点流(也被称为低级流).

当一个流对一个已经存在的流进行连接或封装,并通过操作后的流来实现数据读写功能,这个流就可以被称为高级流.

处理流存在的意义

只要使用相同的处理流(字节流或者字符流),程序就可以采用完全相同的输入/输出代码来访问不同的数据源,随着处理流所包装节点流的改变,程序实际所访问的数据源也相应发生改变.

这意味着什么?

我们可以通过使用处理流包装节点流完成装饰器模式的设计,在原节点流的基础上新增一些方法或重写一些方法.

I/O流的基类方法一览(爸爸会的儿子也会)

InputStream和Reader提供的方法一样

区别只是inputStream按字节读取,Reader按字符读取.

方法作用
int read()从输入流中读取单个字节
int read(byte[] b)从输入流中读取最多b.length个字节的数据,并将其存储在字节数组b中,返回实际读取的字节数
int read(byte[] b,int off,int len)从输入流中读取最多len字节数据,并将其存储在数组b中,放入b数组中时,并不是从数组起点开始,而是从offset位置开始,返回实际读取的字节数
void mark(int readAheadLimit)在记录指针当前位置记录一个标记mark
boolean markSupported()判断此输入流是否支持mark()操作,即是否支持记录标记
void reset()将此流的记录指针重新定位到上一次记录标记mark的位置
long skip(long n)记录指针向前移动n个字节/字符

OutputStream和Writer

在这里插入图片描述

读取数据输出后发现乱码的问题

当我们创建较小长度的字节数组时,程序运行时在输出中文注释时可能出现乱码,这是因为本文件保存时采用的是GBK的编码方式.在这种方式下,每个中文字符占2个字节,如果在结束read()后只读到半个中文字符就会发生乱码.

谈谈处理流的具体用法

使用处理流包装节点流,程序通过处理流来执行输入输出.

下面展示一个PrintStream处理流来包装OutputStream节点流的例子

import java.io.FileOutputStream;
import java.io.PrintStream;

public class PrintStreamTest {

	public static void main(String[] args) {
		//声明处理流
		PrintStream pStream=null;
		try {
			//创建一个节点输出流:FileOutputStream
			FileOutputStream fileOutputStream=new FileOutputStream("text.txt");
			//用处理流包装节点流
			pStream=new PrintStream(fileOutputStream);
			pStream.print("普通字符串");
			pStream.println(new PrintStreamTest());
			
		} catch (Exception e) {
			// TODO: handle exception
		}finally {
			pStream.close();
		}
	}
}

标准I/O流体系一览

在这里插入图片描述

关于字节/字符流的转换

java提供了两个转换流:InputStreamReaderOutputStreamWriter
这两个类把字符流转为对应的字节流.

一个使用的例子

将System.in包装成BufferReader.

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class KeyinTest {

	public static void main(String[] args) {
		BufferedReader bReader=null;
		try {
			InputStreamReader reader=new InputStreamReader(System.in);
			bReader=new BufferedReader(reader);
			String buffer=null;
			while((buffer=bReader.readLine())!=null) {
				if (buffer.equals("exit")) {
					System.exit(1);
				}
				System.out.println("输入内容为:"+buffer);
			}
		} catch (Exception e) {
			// TODO: handle exception
		}
	}

神奇的推回输入流

推回字节流:PushbackInputStream
推回字符流:PushbackReader

方法:

方法作用
void unread(byte[]/char[] buf)将一个字节/字符数组内容推回到缓冲区里,从而允许重复读取刚才读取的内容
void unread(byte[] b,int off,int len)将一个字节/字符数组里从off开始,长度为len字节/字符的内容推回到缓冲区里
void unread(int b)将一个字节/字符推回到缓冲区里

这三个方法一一对应Read,怎么读进来的就怎么推回去(参数一样).

重定向标准输入/输出

System类中重定向的方法

方法作用
static void setErr(PrintStream err)重定向"标准"错误输出流
static void setIn(InputStream in)重定向标准输入流
static void setOut(printStream out)重定向标准输出流

操作文件必须会的类:RandomAccessFile

RandomAccessFile可以自由的访问文件的任意位置,所以如果我们希望只访问文件部分内容,而不是把文件从头到尾读取,就使用RandomAccessFile.

优点:可以任意移动记录指针mark,方法如下

方法作用
long getFilePointer返回文件记录指针的当前位置
void seek(long pos)将文件记录指针定位到pos处

关于创建RandomAccessFile的mode参数

r:只读
w:只写
s:内容或元数据每次更新同步到存储设备
d:内容每次更新同步到存储设备

内容插入问题

RandomAccessFile不能向文件的指定位置插入内容,会覆盖后面的内容.
如果要插入内容,则必须先把插入点后面所有的内容读取拷贝(一般放在缓冲区),等把要插入的内容写入后再追加拷贝的内容.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值