转换流:
为什么有它?
之前的里面的FileWriter 和 FileReader 类的时候,我们知道它们只能采用默认字符编码 和 默认字节缓冲区。
所以我们这里介绍 转换流 它可以指定 字符编码,它能帮助我们解决 往后开发中遇到的乱码问题!
转换流---OutputStreamWriter 类:
文档里说它是 字符流通向字节流 的桥梁。它可以将字符流 转为 字节流。 因为文件的本质其实都是以字节存储的。
注:
FileOutputStream是 字节流中的输出流 类! (写文件)
FileInputStream 是 字节流中的输入流 类! (读文件)
FileWriter 是 字符流 中的 文件写入方法!(写文件)
FileReader 是 字符流 中的文件读出方法! (读文件)
OutputStreamWriter : 新学的 转换流!它可以将 字符流 转为字节流!
OutputStreamWriter 的使用:
两个构造函数,
注:编码表 不区分大小写。

1 package cn.zcb.demo03; 2 3 import java.io.FileOutputStream; 4 import java.io.IOException; 5 import java.io.OutputStreamWriter; 6 7 public class Test { 8 /*转换流 OutputStreamWriter 的使用*/ 9 public static void main(String[] args) throws IOException { 10 //1,先创建一个 字节输出流(FileOutputStream ),待会 我们的转换流会 连接上它 11 FileOutputStream fileOutputStream = new FileOutputStream("d:\test\a.txt"); 12 13 //2,创建转换流 并连接字节输出流 14 OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"gbk"); //此时也可以省略不写,因为默认就是gbk 15 16 //3,此时,如果通过转换流写入的 字符就会以转换流指定的编码形式写入到 字节输出流指定的文件中了。 17 outputStreamWriter.write("我爱你中国!"); 18 19 outputStreamWriter.close();//此时,关闭转换流也同样会关闭 字节输出流! 20 } 21 }

package cn.zcb.demo03; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStreamWriter; public class Test { /*转换流 OutputStreamWriter 的使用*/ public static void main(String[] args) throws IOException { //1,先创建一个 字节输出流(FileOutputStream ),待会 我们的转换流会 连接上它 FileOutputStream fileOutputStream = new FileOutputStream("d:\test\a.txt"); //2,创建转换流 并连接字节输出流 OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream,"utf-8"); //3,此时,如果通过转换流写入的 字符就会以转换流指定的编码形式写入到 字节输出流指定的文件中了。 outputStreamWriter.write("我爱你中国!"); outputStreamWriter.close();//此时,关闭转换流也同样会关闭 字节输出流! } }
注意:
OutputStreamWriter 有个子类---FileWriter。(也就是我们的字符写入流)。
二者的区别是后者不能更改编码表,只能使用默认的编码表来转换为字节;前者可以更改编码表!
转换流---OutputStreamReader 类:
它可以将字节流 转为字符流。
和 写入的时候一样,我们前面使用的读字符的类 FileReader 只能用于 默认编码。如果我们想使用其他编码来读文件。就要使用FileReader的父类OutputStreamReader的帮助了。
它类似于上面介绍的过程,它是先通过FileInputStream字节输入流 从文件中读入得到 相应的字节。 然后,它在通过制定的编码表 将字节转换为 字符输出!
它的构造函数,此时是多态!

1 package cn.zcb.demo03; 2 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 import java.io.InputStream; 6 import java.io.InputStreamReader; 7 8 public class Test { 9 /*转换流 OutputStreamWriter 的使用*/ 10 public static void main(String[] args) throws IOException { 11 //1,先创建一个 字节输入流 ,从文件中读取相应的字节 出来, 待会我们的InputStreamReader会连接它。 12 FileInputStream fileInputStream = new FileInputStream("d:\test\a(gbk 编码).txt"); 13 14 //2,创建转换流 InputStreamReader ,并连接 我们的字节输入流 15 InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"gbk"); //gbk可以省略 16 17 //3,此时,如果读取文件,就会以 转换流指定的编码 将文件中的字节 读取到程序中了。 18 char[] arr = new char[1024]; 19 int ret = inputStreamReader.read(arr); //因为我们知道一次一定可以读完,所以没有循环读取,如果不确定是否能读完,要进行循环读取 20 System.out.println(arr); 21 22 inputStreamReader.close(); //同时,用于连接的 字节输入流 也会被关闭 23 } 24 }

1 package cn.zcb.demo03; 2 3 import java.io.FileInputStream; 4 import java.io.IOException; 5 import java.io.InputStreamReader; 6 7 public class Test { 8 9 /*转换流 OutputStreamWriter 的使用*/ 10 public static void main(String[] args) throws IOException { 11 //1,先创建一个 字节输入流 ,从文件中读取相应的字节 出来, 待会我们的InputStreamReader会连接它。 12 FileInputStream fileInputStream = new FileInputStream("d:\test\a(utf8 编码).txt"); 13 14 //2,创建转换流 InputStreamReader ,并连接 我们的字节输入流 15 InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream,"utf8"); //gbk可以省略 16 17 //3,此时,如果读取文件,就会以 转换流指定的编码 将文件中的字节 读取到程序中了。 18 char[] arr = new char[1024]; 19 int ret = inputStreamReader.read(arr); //因为我们知道一次一定可以读完,所以没有循环读取,如果不确定是否能读完,要进行循环读取 20 System.out.println(arr); 21 22 inputStreamReader.close(); //同时,用于连接的 字节输入流 也会被关闭 23 } 24 }
转换流 的子类 父类的区别:
转换流 OutputStreamWriter 和 InputStreamReader 两个可以指定编码表;
它们的子类 FileWriter 和 FileReader 两个只能是默认的编码表。
缓冲流:
在流对象中,效率是个头疼的事情,我们上次的时候使用数组 提高了效率(不至于一次读一个,一次读一个数组 )。
其实,系统中已经提供好了一套缓冲流,它们设计的目的就是为了提高IO操作速度。
缓冲流都是以 Buffered开头。
四大缓冲流!
字节缓冲流:
BufferedOutputStream 字节缓冲 输出流
它继承自OutputStream ,也是字节输出流。 起到缓冲作用。

1 package cn.zcb.demo03; 2 3 4 import java.io.BufferedOutputStream; 5 import java.io.FileOutputStream; 6 import java.io.IOException; 7 8 public class Test { 9 /*字节缓冲输出流的 的使用*/ 10 public static void main(String[] args) throws IOException { 11 //1, 创建一个 字节输出流 12 FileOutputStream fileOutputStream = new FileOutputStream("d:\test\c.txt"); 13 //2,创建一个字节缓冲输出流 它 可以提高 字节输出流写入文件的效率! 14 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); 15 16 //3,使用字节缓冲输出流 提高字节输出流的 效率。 17 bufferedOutputStream.write("Hello 我爱你呀".getBytes()); 18 19 //4,关闭 20 bufferedOutputStream.close(); //此时的 缓冲流也会被关闭 ! 21 } 22 }
BufferedInputStream字节缓冲 输入流

1 package cn.zcb.demo03; 2 3 4 import java.io.BufferedInputStream; 5 import java.io.FileInputStream; 6 import java.io.IOException; 7 8 public class Test { 9 /*字节缓冲输出流的 的使用*/ 10 public static void main(String[] args) throws IOException { 11 //1, 创建一个 字节输入流 12 FileInputStream fileInputStream = new FileInputStream("d:\test\c.txt"); 13 //2,创建一个字节缓冲输入流 它 可以提高 字节输入流 读取文件的效率! 14 BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); //里面是被提高效率的流。 15 16 //3,使用buffer 提高 效率。 17 byte[] b = new byte[1024]; 18 bufferedInputStream.read(b); //这里我们知道一次可以读完! 19 System.out.println(new String(b)); 20 21 //4,关闭 22 bufferedInputStream.close(); //此时的 缓冲流也会被关闭 ! 23 } 24 25 }

1 package cn.zcb.demo03; 2 3 4 public class Test { 5 6 public static void main(String[] args) { 7 //我们一直使用的 System.in 我们发现 它是抽象类 InputStream 的子类的对象 。其实这个子类就是BufferedInputStream 类 8 //证明如下: 9 System.out.println(System.in); //java.io.BufferedInputStream@2d98a335 10 //所以,我们在键盘输入的时候,传入 System.in 也是为了提高,字节输入流的效率的。 11 } 12 }
四种文件复制 效率比较:

1 package cn.zcb.demo03; 2 3 import java.io.*; 4 import java.util.Arrays; 5 6 public class Test { 7 public static void main(String[] args) throws IOException{ 8 long begin_time = System.currentTimeMillis(); //毫秒值! 9 function01(new File("d:\a.txt"),new File("e:\a.txt")); //文件大小为 2Mb 10 long end_time1 = System.currentTimeMillis(); 11 System.out.println(end_time1 - begin_time); //11388ms 12 13 function02(new File("d:\a.txt"),new File("e:\a.txt")); //文件大小为 2Mb 14 long end_time2 = System.currentTimeMillis(); 15 System.out.println(end_time2 - end_time1); //31ms 16 17 function03(new File("d:\a.txt"),new File("e:\a.txt")); //文件大小为 2Mb 18 long end_time3 = System.currentTimeMillis(); 19 System.out.println(end_time3 - end_time2); //32ms 20 21 function04(new File("d:\a.txt"),new File("e:\a.txt")); //文件大小为 2Mb 22 long end_time4 = System.currentTimeMillis(); 23 System.out.println(end_time4 - end_time3); //15ms 24 25 } 26 public static void function01(File src,File dest) throws IOException { 27 /*第一种 字节流读写 单个字节*/ 28 FileInputStream fileInputStream = new FileInputStream(src); //读入 29 FileOutputStream fileOutputStream = new FileOutputStream(dest); //写出 30 int ret; 31 while ((ret = fileInputStream.read()) != -1){ 32 fileOutputStream.write(ret); 33 } 34 fileInputStream.close(); 35 fileOutputStream.close(); 36 } 37 public static void function02(File src,File dest) throws IOException{ 38 /*第二种 字节流读写 字节数组*/ 39 FileInputStream fileInputStream = new FileInputStream(src); //读入 40 FileOutputStream fileOutputStream = new FileOutputStream(dest); //写出 41 int ret; 42 byte[] arr = new byte[1024]; 43 while ((ret = fileInputStream.read(arr)) != -1){ 44 fileOutputStream.write(arr); 45 //清空 arr 46 Arrays.fill(arr,(byte)0); 47 } 48 fileInputStream.close(); 49 fileOutputStream.close(); 50 } 51 public static void function03(File src,File dest) throws IOException{ 52 /*第三种 字节缓冲流读写 单个字节 */ 53 FileInputStream fileInputStream = new FileInputStream(src); 54 FileOutputStream fileOutputStream = new FileOutputStream(dest); 55 BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); //读入 56 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); //写出 57 58 int ret; 59 while ((ret = bufferedInputStream.read()) != -1){ 60 bufferedOutputStream.write(ret); 61 } 62 bufferedInputStream.close(); 63 bufferedOutputStream.close(); 64 65 } 66 public static void function04(File src,File dest) throws IOException{ 67 /*第四种 字节缓冲流读写 字节数组 */ 68 FileInputStream fileInputStream = new FileInputStream(src); //读入 69 FileOutputStream fileOutputStream = new FileOutputStream(dest); //写出 70 BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream); //读入 71 BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream); //写出 72 73 int ret; 74 byte[] arr = new byte[1024]; 75 while ((ret = bufferedInputStream.read(arr)) != -1){ 76 bufferedOutputStream.write(arr); 77 //清空 arr 78 Arrays.fill(arr,(byte)0); 79 } 80 bufferedInputStream.close(); 81 bufferedOutputStream.close(); 82 } 83 84 85 } 86 /*输出: 87 11388 88 31 89 32 90 15 91 * */
所以,结果很明显,推荐使用缓冲流来 进行IO操作!
字符缓冲流:
完成文本数据的高效的写入 与 读取的操作。
BufferedWriter 字符缓冲输出流

1 package cn.zcb.demo03; 2 3 import java.io.BufferedWriter; 4 import java.io.FileWriter; 5 import java.io.IOException; 6 7 public class Test { 8 /*字符缓冲输出流 的使用*/ 9 public static void main(String[] args) throws IOException { 10 FileWriter fileWriter = new FileWriter("d:\d.txt"); 11 BufferedWriter bufferedWriter =new BufferedWriter(fileWriter); //使用buffered 提高了 fileWriter流的效率 12 13 bufferedWriter.write(97); //写字符 a 14 bufferedWriter.write("hello world".toCharArray());//写字符数组 15 bufferedWriter.write("我爱你中国!"); //写字符串 16 17 bufferedWriter.close();//此时,fileWriter也跟着 被关闭了 。 18 } 19 } 20 /*输出: 21 * */
不过,这个BufferedWriter字符缓冲输出流,它具有一个自己特有的方法:void newLine() .它的意思是写换行。
我们之前换行是使用 ,
这个方法直接就是换行,而且关键的是它与平台无关。所以,建议使用newLine() 方法。

1 package cn.zcb.demo03; 2 3 import java.io.BufferedWriter; 4 import java.io.FileWriter; 5 import java.io.IOException; 6 7 public class Test { 8 /*字符缓冲输出流 的使用*/ 9 public static void main(String[] args) throws IOException { 10 FileWriter fileWriter = new FileWriter("d:\d.txt"); 11 BufferedWriter bufferedWriter =new BufferedWriter(fileWriter); //使用buffered 提高了 fileWriter流的效率 12 13 bufferedWriter.write(97); //写字符 a 14 bufferedWriter.newLine(); 15 bufferedWriter.write("hello world".toCharArray());//写字符数组 16 bufferedWriter.newLine(); 17 bufferedWriter.write("我爱你中国!"); //写字符串 18 19 bufferedWriter.close();//此时,fileWriter也跟着 被关闭了 。 20 } 21 } 22 /*输出: 23 * */
BufferedReader 字符缓冲输入流
对于 读取一个字符 和 读取 字符数组,这里不说了。
它也有自己的一个特殊方法,就是可以一下读一个整行,并返回一个字符串。 String readLine(); 它依靠的是 当遇到终止符( ),停止读取;返回的字符串中不包含终止符。
如果读到文件末尾,返回null 。

1 package cn.zcb.demo03; 2 3 import java.io.BufferedReader; 4 import java.io.FileReader; 5 import java.io.IOException; 6 7 public class Test { 8 /*字符缓冲输入流 的使用*/ 9 public static void main(String[] args) throws IOException { 10 FileReader fileReader = new FileReader("d:\d.txt"); 11 12 //创建字符缓冲输入流 BufferedReader 13 BufferedReader bufferedReader = new BufferedReader(fileReader); 14 15 // 1, bufferedReader.read(); //读到 一个字符 16 // 2, bufferedReader.read(arr); //读到 一个字符数组中 17 18 // 3, BufferedReader 的特有方法: readLine(); 读取行。 19 // String ret = bufferedReader.readLine(); 20 // System.out.println(ret); 21 //循环 行读取 22 String ret; 23 while ((ret = bufferedReader.readLine()) != null){ 24 System.out.println(ret); //ret 为读取到 行 ,它是个字符串,而且它里面没有 任何终止符 存在! 25 } 26 27 } 28 } 29 /*输出: 30 * */
补充:
注: BufferedReader 有个已知子类 LineNumberReader 它可以帮我们读取的内容实现 带有行号。 其实行号就是个计数器,我们也可以自己手动实现。

1 package cn.zcb.demo03; 2 3 import java.io.FileReader; 4 import java.io.IOException; 5 import java.io.LineNumberReader; 6 7 public class Test { 8 /*字符缓冲输入流BufferedReader 的子类 LineNumberReader(带有行号) 的使用*/ 9 public static void main(String[] args) throws IOException { 10 FileReader fileReader = new FileReader("d:\d.txt"); 11 LineNumberReader lineNumberReader = new LineNumberReader(fileReader ); 12 13 lineNumberReader.getLineNumber(); 14 String ret; 15 while ((ret = lineNumberReader.readLine()) != null){ 16 System.out.println(lineNumberReader.getLineNumber()+" "+ret); 17 } 18 } 19 } 20 /*输出: 21 1 a 22 2 hello world 23 3 我爱你中国! 24 * */
但是,其实我们用一个变量做计数器就能实现这个功能。
字符缓冲流 复制文本文件:

1 package cn.zcb.demo03; 2 3 import java.io.*; 4 5 public class Test { 6 /*字符缓冲流 复制文件*/ 7 public static void main(String[] args) throws IOException { 8 BufferedReader bufferedReader = new BufferedReader(new FileReader("d:\d.txt")); 9 BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("e:\e.txt")); 10 11 //循环 读取文本行,写文本行,最后再写换行。 12 String ret; 13 while ((ret = bufferedReader.readLine()) != null){ 14 bufferedWriter.write(ret); //写文本行。 15 bufferedWriter.newLine(); //写 换行(因为读取到的是没有 终止符的) 16 } 17 18 bufferedReader.close(); 19 bufferedWriter.close(); 20 } 21 } 22 /*输出: 23 1 a 24 2 hello world 25 3 我爱你中国! 26 * */
但是,此时的问题是 复制后的文件会多两个字节。原因是多复制了一个空行。
多了个空行对于文本文件来说,不算什么,
但是对于图片,视频等文件来说,可能就打不开了。 这是个问题!
流 的操作规律:
IO 流的 四大抽象 基类:
1, InputStream 2, Reader
3,OutputStream 4, Writer
所有的流都是它们的孩子。
四个明确:
1,明确操作的是读取文件(1,2基类),还是写入文件(3,4基类)。
2,明确操作的数据 是 字节还是字符。 字节(非文本)用(1,3基类),字符(文本文件)用(2,4基类)。
3,明确数据所在的具体设备,
源设备:
硬盘上文件, File开头的流。
内存里数组,字符串,需要用到内存流,例如ByteArrayInputStream,等等,这里是从内存中读取数据,或写入到内存中。
键盘:System.in 流。
网络:Socket 流
目的设备:
硬盘上文件,File开头的 流
内存,数组,字符串,例如ByteArrayOutputStream
屏幕:System.out 流。
网络 :Socket 流。
4,明确是否需要额外功能,额外功能指
需要进行转换吗?转换流。 InputStreamReader 和 OutputStreamWriter。
需要提高 效率吗? 缓冲流 。BufferedXXX.
IO流的继承图:
操作字节的流:
InputStream <-- FileInputStream(操作文件的字节输入流) <--BufferedInputStream(提高 字节输入流的效率)
OutputStream <--FileOutputStream(操作文件的字节输出流) <--BufferedOutputStream(提高 字节输出流的效率)
操作字符的流:
Writer <-- OutputStreamWriter(可以指定编码表) <--FileWriter(简便的 字符输出流)
<-- BufferedWriter(提高 字符输出流的 效率)
Reader <-- InputStreamReader (可以指定编码表) <--FileReader(简便的 字符输入流)
<-- BufferedReader(提高 字符输入流的 效率)