Java IO 字节流、字符流详解

1. 字节流与字符流

在这里插入图片描述
流是一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。

流的作用: 为数据源和目的地建立一个输送通道。

字节流操作的单元是数据单元是8位的字节,字符流操作的是数据单元为16位的字符。Java中字符是采用Unicode标准,Unicode 编码中,一个英文字母或一个中文汉字为两个字节。在UTF-8编码中,一个中文字符是3个字节。

字符流与字节流的对比:
字节流一般用来处理图像、视频、音频、PPT、Word等类型的文件。
字符流一般用于处理纯文本类型的文件,如TXT文件等,但不能处理图像视频等非文本文件。

总结: 字节流可以处理一切文件,而字符流只能处理纯文本文件。

字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高。而字符流本身就带有缓冲区,缓冲字符流相对于字符流效率提升就不是那么大了。

2. 标准I/O

Java程序可通过命令行参数与外界进行简短的信息交换,同时,也规定了与标准输入、输出设备,如键盘、显示器进行信息交换的方式。而通过文件可以与外界进行任意数据形式的信息交换。

(1)命令行参数

public class TestArgs {
	public static void main(String[] args) {
		for (int i = 0; i < args.length; i++) {
			System.out.println("args[" + i + "] is <" + args[i] + ">");
		}
	}
}

运行命令:java Java C VB
运行结果:

args[0] is <Java>
args[1] is <C>
args[2] is <VB>

(2)标准输入、输出数据流

java系统自带的标准数据流:java.lang.System:

java.lang.System 
public final class System  extends Object{ 
   static  PrintStream  err;//标准错误流(输出)
   static  InputStream  in;//标准输入(键盘输入流)
   static  PrintStream  out;//标准输出流(显示器输出流)
}

注意:
(1)System类不能创建对象,只能直接使用它的三个静态成员。
(2)每当main方法被执行时,就自动生成上述三个对象。

标准输出流 System.out

System.out向标准输出设备输出数据,其数据类型为PrintStream。方法:

Void print( 参数)
Void println( 参数)

标准输入流 System.in

System.in读取标准输入设备数据(从标准输入获取数据,一般是键盘),其数 据类型为InputStream。方法:

int read()   //返回ASCII码。若,返回值=-1,说明没有读取到任何字节读取工作结束。
int read(byte[] b)// 读入多个字节到缓冲区b中返回值是读入的字节数

例如:

import java.io.*;
public class StandardInputOutput {
	public static void main(String args[]) {
		int b;
		try {
			System.out.println("please Input:");
			while ((b = System.in.read()) != -1) {
				System.out.print((char) b);
			}
		} catch (IOException e) {
			System.out.println(e.toString());
		}
	}
}

运行结果:
在这里插入图片描述

3. IO流常用的类与IO流分类

Java IO常用的类
在这里插入图片描述
在整个Java.io包中最重要的就是5个类和一个接口。5个类指的是File、OutputStream、InputStream、Writer、Reader;一个接口指的是Serializable。掌握了这些IO的核心操作那么对于Java中的IO体系也就有了一个初步的认识了。
在这里插入图片描述
Java I/O主要包括如下几个层次,包含三个部分

  • 流式部分――IO的主体部分
    • 字节流
      • InputStream(二进制格式操作):抽象类,基于字节的输入操作,是所有输入流的父类。定义了所有输入流都具有的共同特征。
      • OutputStream(二进制格式操作):抽象类。基于字节的输出操作,是所有输出流的父类。定义了所有输出流都具有的共同特征。
    • 字符流
      • Reader(文件格式操作):抽象类,基于字符的输入操作。
      • Writer(文件格式操作):抽象类,基于字符的输出操作。
  • 非流式部分――主要包含一些辅助流式部分的类,如:File类、RandomAccessFile类和FileDescriptor等类
    • File(文件特征与管理):用于文件或者目录的描述信息,例如生成新目录,修改文件名,删除文件,判断文件所在路径等。
    • RandomAccessFile(随机文件操作):一个独立的类,直接继承至Object。它的功能丰富,可以从文件的任意位置进行存取(输入输出)操作。
  • 其他类–文件读取部分的与安全相关的类,如:SerializablePermission类,以及与本地操作系统相关的文件系统的类,如:FileSystem类和Win32FileSystem类和WinNTFileSystem类。

Java IO分类
1、根据处理数据类型的不同分为:字符流和字节流
2、根据数据流向不同分为:输入流和输出流
3、按数据来源(去向)分类:

  1. File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter
  2. byte[]:ByteArrayInputStream, ByteArrayOutputStream
  3. Char[]: CharArrayReader,CharArrayWriter
  4. String:StringBufferInputStream, StringReader, StringWriter
  5. 网络数据流:InputStream,OutputStream, Reader, Writer

IO流只读或只写,每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
下图是一个描述输入流和输出流的类层次图
在这里插入图片描述

(1)输入字节流InputStream

在这里插入图片描述

  1. InputStream:InputStream是所有字节输入流的抽象基类,前面说过抽象类不能被实例化,实际上是作为模板而存在的,为所有实现类定义了处理输入流的方法。
  2. FileInputSream:文件输入流,一个非常重要的字节输入流,用于对文件进行读取操作。
  3. PipedInputStream:管道字节输入流,能实现多线程间的管道通信。
  4. ByteArrayInputStream:字节数组输入流,从字节数组(byte[])中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去。
  5. FilterInputStream:装饰者类,具体的装饰者继承该类,这些类都是处理类,作用是对节点类进行封装,实现一些特殊功能。
  6. DataInputStream:数据输入流,它是用来装饰其它输入流,作用是“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。
  7. BufferedInputStream:缓冲流,对节点流进行装饰,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送,效率更高。
  8. ObjectInputStream:对象输入流,用来提供对基本数据或对象的持久存储。通俗点说,也就是能直接传输对象,通常应用在反序列化中。它也是一种处理流,构造器的入参是一个InputStream的实例对象。

在这里插入图片描述
InputStream中的三个基本的读方法
public abstract int read() : 读取一个字节数据,并返回读到的数据,如果返回-1,表示读到了输入流的末尾。
public int read(byte[] b) : 将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。
public int read(byte[] b, int off, int len) : 将数据读入一个字节数组,同时返回实际读取的字节数。如果返回-1,表示读到了输入流的末尾。off指定在数组b中存放数据的起始偏移位置;len指定读取的最大字节数。

流结束的判断:方法read()的返回值为-1时;readLine()的返回值为null时。

(2)输出字节流OutputStream

在这里插入图片描述
IO 中输出字节流的继承图可见上图,可以看出:

  1. OutputStream是所有的输出字节流的父类,它是一个抽象类。

  2. ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte数组、和本地文件中写入数据。PipedOutputStream是向与其它线程共用的管道中写入数据。

  3. ObjectOutputStream和所有FilterOutputStream的子类都是装饰流。
    在这里插入图片描述

outputStream中的三个基本的写方法
public abstract void write(int b): 往输出流中写入一个字节。
public void write(byte[] b) : 往输出流中写入数组b中的所有字节。
public void write(byte[] b, int off, int len) : 往输出流中写入数组b中从偏移量off开始的len个字节的数据。

其它方法
public void flush() : 刷新输出流,强制缓冲区中的输出字节被写出。
public void close() : 关闭输出流,释放和这个流相关的系统资源。

(3)字符输入流Reader

在这里插入图片描述
在上面的继承关系图中可以看出:

  1. Reader是所有的输入字符流的父类,它是一个抽象类。

  2. CharReader、StringReader是两种基本的介质流,它们分别将Char数组、String中读取数据。PipedReader是从与其它线程共用的管道中读取数据。

  3. BufferedReader很明显就是一个装饰器,它和其子类负责装饰其它Reader对象。

  4. FilterReader是所有自定义具体装饰流的父类,其子类PushbackReader对Reader对象进行装饰,会增加一个行号。

  5. InputStreamReader是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream转变为Reader的方法。我们可以从这个类中得到一定的技巧。Reader中各个类的用途和使用方法基本和InputStream中的类使用一致。后面会有Reader与InputStream的对应关系。

在这里插入图片描述
Reader主要方法:
public int read() : 读取一个字符,返回值为读取的字符
public int read(char cbuf[]): 读取一系列字符到数组cbuf[]中,返回值为实际读取的字符的数量
public abstract int read:读取len个字符,从数组cbuf[]的下标off处开始存放,返回值为实际读取的字符数量,该方法必须由子类实现

(4)字符输出流Writer

在这里插入图片描述

  1. InputStreamReader:从字节流到字符流的桥梁(InputStreamReader构造器入参是FileInputStream的实例对象),它读取字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给定,或者可以接受平台的默认字符集。
  2. BufferedReader:从字符输入流中读取文本,设置一个缓冲区来提高效率。BufferedReader是对InputStreamReader的封装,前者构造器的入参就是后者的一个实例对象。
  3. FileReader:用于读取字符文件的便利类,new FileReader(File file)等同于new InputStreamReader(new FileInputStream(file, true),“UTF-8”),但FileReader不能指定字符编码和默认字节缓冲区大小。
  4. PipedReader :管道字符输入流。实现多线程间的管道通信。
  5. CharArrayReader:从Char数组中读取数据的介质流。
  6. StringReader :从String中读取数据的介质流。

在这里插入图片描述

Writer的主要方法:
public void write(int c) : 将整型值c的低16位写入输出流
public void write(char cbuf[]) :将字符数组cbuf[]写入输出流
public abstract void write(char cbuf[],int off,int len) :将字符数组cbuf[]中的从索引为off的位置处开始的len个字符写入输出流
public void write(String str) :将字符串str中的字符写入输出流
public void write(String str,int off,int len) :将字符串str 中从索引off开始处的len个字符写入输出流

(5)字节流与输出的对应

在这里插入图片描述
图中蓝色的为主要的对应部分,红色的部分就是不对应部分。从上面的图中可以看出JavaIO中的字节流是极其对称的。

  1. LineNumberInputStream主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。好像更不入流了。

  2. PushbackInputStream的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream几乎实现相近的功能。

  3. StringBufferInputStream已经被Deprecated,本身就不应该出现在InputStream部分,主要因为String应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。

  4. SequenceInputStream可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO包中去除,还完全不影响IO包的结构,却让其更“纯洁”――纯洁的Decorator模式。

  5. PrintStream也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO包!System.out和System.out就是PrintStream的实例!

(6)字符流的输入与输出的对应

在这里插入图片描述

参考资料:
【Java基础-3】吃透Java IO:字节流、字符流、缓冲流

深入理解Java中的IO

Java(2)-Java IO输入输出流

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辰阳星宇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值