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、按数据来源(去向)分类:
- File(文件): FileInputStream, FileOutputStream, FileReader, FileWriter
- byte[]:ByteArrayInputStream, ByteArrayOutputStream
- Char[]: CharArrayReader,CharArrayWriter
- String:StringBufferInputStream, StringReader, StringWriter
- 网络数据流:InputStream,OutputStream, Reader, Writer
IO流只读或只写,每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。
下图是一个描述输入流和输出流的类层次图
(1)输入字节流InputStream
- InputStream:InputStream是所有字节输入流的抽象基类,前面说过抽象类不能被实例化,实际上是作为模板而存在的,为所有实现类定义了处理输入流的方法。
- FileInputSream:文件输入流,一个非常重要的字节输入流,用于对文件进行读取操作。
- PipedInputStream:管道字节输入流,能实现多线程间的管道通信。
- ByteArrayInputStream:字节数组输入流,从字节数组(byte[])中进行以字节为单位的读取,也就是将资源文件都以字节的形式存入到该类中的字节数组中去。
- FilterInputStream:装饰者类,具体的装饰者继承该类,这些类都是处理类,作用是对节点类进行封装,实现一些特殊功能。
- DataInputStream:数据输入流,它是用来装饰其它输入流,作用是“允许应用程序以与机器无关方式从底层输入流中读取基本 Java 数据类型”。
- BufferedInputStream:缓冲流,对节点流进行装饰,内部会有一个缓存区,用来存放字节,每次都是将缓存区存满然后发送,而不是一个字节或两个字节这样发送,效率更高。
- 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 中输出字节流的继承图可见上图,可以看出:
-
OutputStream是所有的输出字节流的父类,它是一个抽象类。
-
ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte数组、和本地文件中写入数据。PipedOutputStream是向与其它线程共用的管道中写入数据。
-
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
在上面的继承关系图中可以看出:
-
Reader是所有的输入字符流的父类,它是一个抽象类。
-
CharReader、StringReader是两种基本的介质流,它们分别将Char数组、String中读取数据。PipedReader是从与其它线程共用的管道中读取数据。
-
BufferedReader很明显就是一个装饰器,它和其子类负责装饰其它Reader对象。
-
FilterReader是所有自定义具体装饰流的父类,其子类PushbackReader对Reader对象进行装饰,会增加一个行号。
-
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
- InputStreamReader:从字节流到字符流的桥梁(InputStreamReader构造器入参是FileInputStream的实例对象),它读取字节并使用指定的字符集将其解码为字符。它使用的字符集可以通过名称指定,也可以显式给定,或者可以接受平台的默认字符集。
- BufferedReader:从字符输入流中读取文本,设置一个缓冲区来提高效率。BufferedReader是对InputStreamReader的封装,前者构造器的入参就是后者的一个实例对象。
- FileReader:用于读取字符文件的便利类,new FileReader(File file)等同于new InputStreamReader(new FileInputStream(file, true),“UTF-8”),但FileReader不能指定字符编码和默认字节缓冲区大小。
- PipedReader :管道字符输入流。实现多线程间的管道通信。
- CharArrayReader:从Char数组中读取数据的介质流。
- 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中的字节流是极其对称的。
-
LineNumberInputStream主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。好像更不入流了。
-
PushbackInputStream的功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream几乎实现相近的功能。
-
StringBufferInputStream已经被Deprecated,本身就不应该出现在InputStream部分,主要因为String应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。
-
SequenceInputStream可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO包中去除,还完全不影响IO包的结构,却让其更“纯洁”――纯洁的Decorator模式。
-
PrintStream也可以认为是一个辅助工具。主要可以向其他输出流,或者FileInputStream写入数据,本身内部实现还是带缓冲的。本质上是对其它流的综合运用的一个工具而已。一样可以踢出IO包!System.out和System.out就是PrintStream的实例!