Go通过io.Reader和io.Writer两个极简接口抽象读写操作,要求实现Read([]byte)(int,error)或Write([]byte)(int,error),支持任意类型组合;强调不可变性、单向流语义,禁止Seek/Peek等方法,鼓励包装而非继承,典型应用如io.Copy。
Go 把「读」和「写」抽象成两个极简接口,而不是提供一堆带缓冲、超时、加密的封装类。这意味你不需要记住 BufferedReader 或 EncryptedWriter 这类名字——只要类型实现了 Read([]byte) (int, error),它就是 io.Reader;只要实现了 Write([]byte) (int, error),它就是 io.Writer。
这种设计让组合变得自然:你可以把 os.File、bytes.Buffer、net.Conn、甚至自定义的 rot13Reader 全部当成 io.Reader 传给同一函数,不用改签名、不用类型断言。
io.Reader 做 len() 或下标访问 —— 它不保证可回溯、不保证长度已知req.Body(io.ReadCloser),返回响应用 w http.ResponseWriter(隐式实现 io.Writer)Go 不支持继承,但通过结构体字段嵌入(embedding)和包装(wrapping)轻松叠加行为。比如 bufio.Reader 并非继承自某个基类,而是持有一个 io.Reader 字段,并在其上加缓冲逻辑;gzip.NewReader 接收一个 io.Reader,返回一个新的、解压语义的 io.Reader。
type CountingReader struct {
r io.Reader
n int64
}
func (c *CountingReade
r) Read(p []byte) (int, error) {
n, err := c.r.Read(p)
c.n += int64(n)
return n, err
}
io.ReadCloser 时实现 Close() 方法,导致资源泄漏io.Copy 函数只依赖 io.Reader 和 io.Writer,却能处理文件复制、网络代理、内存流转等各种场景。它内部使用固定大小缓冲区(默认 32KB)循环读写,屏蔽了底层细节。
立即学习“go语言免费学习笔记(深入)”;
_, err := io.Copy(dst, src) // dst: io.Writer, src: io.Reader
io.Copy 返回 io.EOF 表示读到流尾,这不是异常,通常无需报错;真正需关注的是其他 error 类型io.CopyN(精确复制 N 字节)、io.CopyBuffer(允许指定缓冲区)供特殊需求io.Writer 实现(如 http.ResponseWriter)不支持多次调用 Write 后再 Flush,此时直接传给 io.Copy 可能出错,需确认目标是否支持流式写入Go 的 I/O 接口不提供 Seek、Peek、Reset 等方法,明确表达“流是一次性的、向前推进的”。这迫使开发者直面数据流动的本质,也避免了因接口膨胀带来的实现负担。
http.Request.Body 读两次;想重读就得用 io.TeeReader + bytes.Buffer 显式缓存io.Reader 的实现者只需专注「按需吐出字节」,不必操心位置管理或并发安全(除非文档特别说明)json.NewDecoder、xml.NewDecoder)接受 io.Reader,但它们内部会读取并消耗流——传同一个实例给多个解码器会导致后一个读不到数据