一、流
1.流的概念
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
2.流的分类
根据处理数据类型的不同分为:字符流和字节流,字符流处理的单元为 2 个字节的 Unicode 字符,分别操作字符、字符数组或字符串,而字节流处理单元为 1 个字节,操作字节和字节数组。
根据数据流向不同分为:输入流和输出流
3.字符流和字节流
3.1 字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 Java 内用 Unicode 编码存储字符,字符流处理类负责将外部的其他编码的字符流和 java 内 Unicode 字符流之间的转换。
3.2 字节流和字符流的区别:
(1)读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
(2)处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
(3)字节流在操作的时候本身是不会用到缓冲区的,是文件本身的直接操作的;而字符流在操作的时候下后是会用到缓冲区的,是通过缓冲区来操作文件,我们将在下面验证这一点。
结论:优先选用字节流。首先因为硬盘上的所有文件都是以字节的形式进行传输或者保存的,包括图片等内容。但是字符只是在内存中才会形成的,所以在开发中,字节流使用广泛。从效率而言,字符流(一次可以处理一个缓冲区)一次操作比字节流(一次一个字节)效率高。
4.输入流和输出流
对输入流只能进行读操作,对输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。输入输出流的流向是相对于程序而言。
输入流:InputStream 读入 就是从外部文件中读取数据到程序。对应 Reader;
输出流: OutputStream 写出 就是将数据从程序写入到外部文件。对应 Writer;
二、字节流
1.字节流概述
1)字节流和字符流的基本操作是相同的,但是要想操作媒体流就需要用到字节流。
2)字节流因为操作的是字节,所以可以用来操作媒体文件。(媒体文件也是以字节存储的)
3)读写字节流:InputStream 输入流(读)和OutputStream 输出流(写)
4)字节流操作可以不用刷新流操作。
5)InputStream特有方法:
int available(); //返回文件中的字节个数
注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。所以,此方法慎用,当文件不大时,可以使用
2. 字节流缓冲区
1)缓冲区的出现时为了提高流的操作效率而出现的.
2)需要被提高效率的流作为参数传递给缓冲区的构造函数
3)在缓冲区中封装了一个数组,存入数据后一次取出
3.输入字节流InputStream
定义和结构说明:
从输入字节流的继承图可以看出:InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
ObjectInputStream 和所有FilterInputStream的子类都是装饰流(装饰器模式的主角)。意思是FileInputStream类可以通过一个String路径名创建一个对象,FileInputStream(String name)。
DataInputStream必须装饰一个类才能返回一个对象,DataInputStream(InputStream in)。
public static void main(String[] args) throws IOException { File f=new File("aaa.txt"); //定位文件位置 InputStream in=new FileInputStream(f); //创建字节输入流连接到文件 byte[] b=new byte[1024]; //定义一个数组,用来存放读入的数据 byte数组的大小也可以根据文件大小来定 (int)f.length() int count =0; int temp=0; while((temp=in.read())!=(-1)){ //in.read()是逐字节读的。当读到文件末尾时候返回-1 b[count++]=(byte)temp; //将读到的字节存储到byte数组中 } in.close(); //关闭流 System.out.println(new String(b)); //打印读取到的字节 }
注意:当读到文件末尾的时候会返回-1.正常情况下是不会返回-1的。
加入字节缓冲输入流,提高了读取效率
public static void main(String[] args) throws IOException { File f=new File("aaa.txt"); //定位文件位置 InputStream in=new FileInputStream(f); //创建字节输入流连接到文件 BufferedInputStream bis=new BufferedInputStream(in); //创建缓冲字节流 byte[] b=new byte[1024]; //定义一个数组,用来存放读入的数据 byte数组的大小也可以根据文件大小来定 (int)f.length() int count =0; int temp=0; bis.read(); while((temp=bis.read())!=(-1)){ //in.read()是逐字节读的。当读到文件末尾时候返回-1 b[count++]=(byte)temp; //将读到的字节存储到byte数组中 } bis.close(); //关闭缓冲字节流 in.close(); //关闭流 System.out.println(new String(b)); //打印读取到的字节 }
4.输出字节流OutputStream
定义和结构说明:
IO 中输出字节流的继承图可见上图,可以看出:OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
PipedOutputStream 是向与其它线程共用的管道中写入数据,
ObjectOutputStream 和所有FilterOutputStream的子类都是装饰流。具体例子跟InputStream是对应的。
public static void main(String[] args) throws IOException { File f = new File("aaa.txt"); // 定位文件位置 OutputStream out = new FileOutputStream(f); // 创建字节输出流连接到文件 String str = "hhhhhhh"; byte[] b = str.getBytes(); //讲数据存入byte数组 out.write(b); //写数据 out.close(); //关闭流 }
加入字节缓冲输入流,提高了读取效率
public static void main(String[] args) throws IOException { File f = new File("aaa.txt"); // 定位文件位置 OutputStream out = new FileOutputStream(f); // 创建字节输出流连接到文件 BufferedOutputStream bos=new BufferedOutputStream(out); String str = "hhhhhhh"; byte[] b = str.getBytes(); //讲数据存入byte数组 bos.write(b); //写数据 bos.close(); //关闭缓冲流 out.close(); //关闭流 }
5.几个特殊的输入流类分析
LineNumberInputStream
主要完成从流中读取数据时,会得到相应的行号,至于什么时候分行、在哪里分行是由改类主动确定的,并不是在原始中有这样一个行号。在输出部分没有对应的部分,我们完全可以自己建立一个LineNumberOutputStream,在最初写入时会有一个基准的行号,以后每次遇到换行时会在下一行添加一个行号,看起来也是可以的。好像更不入流了。
PushbackInputStream
其功能是查看最后一个字节,不满意就放入缓冲区。主要用在编译器的语法、词法分析部分。输出部分的BufferedOutputStream 几乎实现相近的功能。
StringBufferInputStream
已经被Deprecated,本身就不应该出现在InputStream部分,主要因为String 应该属于字符流的范围。已经被废弃了,当然输出部分也没有必要需要它了!还允许它存在只是为了保持版本的向下兼容而已。
SequenceInputStream
可以认为是一个工具类,将两个或者多个输入流当成一个输入流依次读取。完全可以从IO 包中去除,还完全不影响IO 包的结构,却让其更“纯洁”――纯洁的Decorator 模式。
三、字符流
1.字符输入流Reader
定义和说明:
在上面的继承关系图中可以看出:Reader 是所有的输入字符流的父类,它是一个抽象类。
CharReader、StringReader是两种基本的介质流,它们分别将Char 数组、String中读取数据。
PipedReader 是从与其它线程共用的管道中读取数据。
BufferedReader 很明显就是一个装饰器,它和其子类负责装饰其它Reader 对象。
FilterReader 是所有自定义具体装饰流的父类,其子类PushbackReader 对Reader 对象进行装饰,会增加一个行号。
InputStreamReader 是一个连接字节流和字符流的桥梁,它将字节流转变为字符流。FileReader可以说是一个达到此功能、常用的工具类,在其源代码中明显使用了将FileInputStream 转变为Reader 的方法。我们可以从这个类中得到一定的技巧。Reader 中各个类的用途和使用方法基本和InputStream 中的类使用一致。后面会有Reader 与InputStream 的对应关系。
public static void main(String[] args) throws IOException { File f = new File("aaa.txt"); // 定位文件位置 char[] ch = new char[100]; //定义一个数组,用来存放读入的数据 Reader read = new FileReader(f); //创建字符输入流连接到文件 int count = read.read(ch); //读操作 read.close(); //关闭流 System.out.println("读入的长度为:" + count); System.out.println("内容为" + new String(ch, 0, count)); }
加入缓冲流 提高效率。
public static void main(String[] args) throws IOException { File f = new File("aaa.txt"); // 定位文件位置 char[] ch = new char[100]; //定义一个数组,用来存放读入的数据 Reader read = new FileReader(f); //创建字符输入流连接到文件 BufferedReader bfr=new BufferedReader(read); //创建缓冲流 int count = bfr.read(ch); //读操作 bfr.close(); //关闭缓冲流 read.close(); //关闭流 System.out.println("读入的长度为:" + count); System.out.println("内容为" + new String(ch, 0, count)); }
2.字符输出流Writer
定义和说明:
在上面的关系图中可以看出:Writer 是所有的输出字符流的父类,它是一个抽象类。
CharArrayWriter、StringWriter 是两种基本的介质流,它们分别向Char 数组、String 中写入数据。
PipedWriter 是向与其它线程共用的管道中写入数据,
BufferedWriter 是一个装饰器为Writer 提供缓冲功能。
PrintWriter 和PrintStream 极其类似,功能和使用也非常相似。
OutputStreamWriter 是OutputStream 到Writer 转换的桥梁,它的子类FileWriter 其实就是一个实现此功能的具体类(具体可以研究一SourceCode)。功能和使用和OutputStream 极其类似。
public static void main(String[] args) throws IOException { File f=new File("aaa.txt"); // 定位文件位置 Writer out =new FileWriter(f); //创建字符输出流连接到文件 String str="哈哈哈哈哈哈"; out.write(str); //写操作 out.close(); //关闭流 }
加入缓冲流 提高效率。
public static void main(String[] args) throws IOException { File f=new File("aaa.txt"); // 定位文件位置 Writer out =new FileWriter(f); //创建字符输出流连接到文件 BufferedWriter bfw =new BufferedWriter(out); //创建缓冲流 String str="哈哈哈哈哈哈"; bfw.write(str); //写操作 bfw.close(); //关闭缓冲流 out.close(); //关闭流 }
四、字符流与字节流转换
转换流的特点:
(1)其是字符流和字节流之间的桥梁
(2)可对读取到的字节数据经过指定编码转换成字符
(3)可对读取到的字符数据经过指定编码转换成字节
何时使用转换流?
当字节和字符之间有转换动作时;
流操作的数据需要编码或解码时。
具体的对象体现:
InputStreamReader:字节到字符的桥梁
OutputStreamWriter:字符到字节的桥梁
这两个流对象是字符体系中的成员,它们有转换作用,本身又是字符流,所以在构造的时候需要传入字节流对象进来。
/**
* 将字节输出流转化为字符输出流
* */
public static void main(String[] args) throws IOException { File file=new File("aaa.txt"); Writer out=new OutputStreamWriter(new FileOutputStream(file)); out.write("hello"); out.close(); }
/** * 将字节输入流变为字符输入流 * */ public static void main(String[] args) throws IOException { File file=new File("aaa.txt"); Reader read=new InputStreamReader(new FileInputStream(file)); char[] b=new char[100]; int len=read.read(b); System.out.println(new String(b,0,len)); read.close(); }