# 字节流和字符流
[TOC]
## 流-再述
* 流是个抽象的概念,是对输入输出设备的抽象,输入流可以看作一个输入通道,输出流可以看作一个输出通道。
* 输入流是相对程序而言的,外部传入数据给程序需要借助输入流。
* 输出流是相对程序而言的,程序把数据传输到外部需要借助输出流。
## 字节流
字节流常用于读取图像,音频,视频之类的二进制字节数据。
### 字节输入流`InputStream`
`InputStream`是字节输入流的父类,以下是`Inputstream`的子类结构图
![](https://img.kancloud.cn/65/7c/657ca9420478d84add13fe24f4b7b9c7_894x526.png)
>[info]常用的输入流有`FileInputStream`,`ObjectInputStream`,`BufferedInputStream`
#### 文件输入流`FileInputStream`
该类用于从文件系统中的某个文件中获得输入字节,用于读取诸如图像数据之类的原始字节流。
**常用方法:**
| 方法名 | 描述 |
| :--- | :--- |
| public int read() | 从输入流中读取一个数据字节 |
| public int read(byte[] b) | 从输入流中将最多b.length个字节的数据读入到一个byte数组中 |
| public int read(byte[] b,int off,int len) | 从此输入流中将最多`len`个字节的数据读入一个 byte 数组中。 |
| public void close() | 关闭此文件输入流并释放与此流有关的所有系统资源。 |
>[warning] 除了第一个方法可以得到读取到的下一个数据字节,这三个read()方法,如果返回值为-1,则表示已经到达文件末尾。可以用来作为文件是否读完的一个标志。
~~~java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
public class FileInputDemo {
public static void main(String[] args) {
//创建一个FileInputStream对象
try {
FileInputStream fis = new FileInputStream("d:"+File.separator + "fileTest" + File.separator + "abcNew.txt");
//********************无参read()方法使用**********************
/*int n = fis.read();
System.out.println((char)n);//得到H
*/
/*int n = fis.read();
while(n != -1) {
System.out.println((char)n);
n = fis.read();
}*/
//为了提升代码的简洁性
/*int n = 0;
while((n = fis.read()) != -1) {
System.out.println((char)n);
}*/
//*******************read(byte[] b)和read(byte[] b,int off,int len)方法的使用*****************
/*byte[] b = new byte[100];
fis.read(b);
System.out.println(new String(b));*/
byte[] b = new byte[100];
fis.read(b,0,5);
System.out.println(new String(b));
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
### 字节输出流`OutputStream`
`OutputStream`是字节输出流的父类,以下是`Outputstream`的子类结构图
![](https://img.kancloud.cn/59/a4/59a4a42318b59f71300d0cfee52dcfe0_942x412.png)
>[info]常用的输入流有`FileOutputStream`,`ObjectOutputStream`,`BufferedOutputStream`
#### 文件输出流`FileOutputStream`
该类用来讲数据写入到文件系统的文件中
**常用方法:**
| 方法名 | 描述 |
| :--- | :--- |
| public void write(int b) | 将指定字节写入此文件输出流。 |
| public void write(byte[] b) | 将`b.length`个字节从指定 byte 数组写入此文件输出流中。 |
| public void write(byte[] b,int off,int len) | 将指定 byte 数组中从偏移量`off`开始的`len`个字节写入此文件输出流。 |
| public void close() | 关闭此文件输出流并释放与此流有关的所有系统资源。 |
~~~java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputDemo {
public static void main(String[] args) {
String path = "d:"+File.separator + "fileTest" + File.separator + "abcNew.txt";
FileOutputStream fos;
FileInputStream fis;
try {
fos = new FileOutputStream(path,true);
fis = new FileInputStream(path);
/*fos.write(50);
fos.write('e');
//编码问题导致
System.out.println(fis.read());
System.out.println((char)fis.read());*/
String content = "\nsay Hello World again!";
fos.write(content.getBytes());
fos.close();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
**文件拷贝**
~~~java
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputDemo {
public static void main(String[] args) {
String path = "d:"+File.separator + "picture" + File.separator + "20170315inHome.jpg";
String copyPath = "d:"+File.separator + "fileTest" + File.separator + "flower.jpg";
FileInputStream fis;
FileOutputStream fos;
try {
//创建输入流
fis = new FileInputStream(path);
File file = new File(copyPath);
//判断文件的父路径是否存在,如果不存在则新建目录
if(!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
//创建新文件
file.createNewFile();
//创建输出流
fos = new FileOutputStream(copyPath);
int n = 0;
byte[] b = new byte[1024];
while((n = fis.read(b)) != -1) {
//一边读取输入流,一边写入到新建的文件中去
//fos.write(b);
//为了保证最后复制文件和源文件同样大小
fos.write(b, 0, n);
}
fos.close();
fis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
### 缓冲流
* 缓冲输入流 `BufferedInputStream`
* 缓冲输出流 `BufferedOutputStream`
相较于之前直接从硬盘中读取数据,利用缓冲输入输出流,可以从内存中读取数据,可以极大的提高读写速度。
以缓冲输入流为例,它是不能直接读取文件系统中的文件的,需要和文件输入流结合。读取原理是,文件输入流首先从文件系统中读取数据,读取到数据后不是直接到程序中,而是给缓冲流读取到字节数组中。输出流也是一样。
>[warning]针对于缓冲输出流,如果缓冲区已满,则执行文件写入操作,如果缓冲区不满需要先执行`flush()`方法刷新缓冲区。
~~~java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
public class BufferedIODemo {
public final static String PATH = "d:"+File.separator + "fileTest" + File.separator + "abcNew.txt";
public static void main(String[] args) {
setBufferedIO();
}
public static void setBufferedIO() {
try {
//首先利用文件输出流,执行文件的写操作
FileOutputStream fos = new FileOutputStream(PATH);
BufferedOutputStream bos = new BufferedOutputStream(fos);
//读操作
FileInputStream fis = new FileInputStream(PATH);
BufferedInputStream bis = new BufferedInputStream(fis);
//利用缓冲输出流进行写操作,需要注意的是此时数据是写在缓冲区的,如果缓冲未满,且没有调用flush()方法,则不会写入文件
bos.write(50);
bos.write('a');
//一般会要求同时写flush和close
bos.flush();
System.out.println(bis.read());
System.out.println((char)bis.read());
fos.close();
//close()方法会释放有关bos的一切资源,所以调用close()方法,也会执行写入操作
bos.close();
fis.close();
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
## 字符流
Java提供了字符流便于针对字符串的输出,尤其是针对中文的数据处理,都会采用字符流的形式。
### 字符输入流`Reader`
`Reader`是字符输入流的父类,以下是字符输入流的结构图
![](https://img.kancloud.cn/55/d9/55d903f08d754e7d1308ab41641462ba_944x474.png)
>[info]常用的字符输入流有`BufferedReader`,`InputStreamReader`,`FileReader`
#### 字节字符转换输入流
转换流,可以将一个字节流转换为字符流,也可以将一个字符流转换为字节流。
`InputStreamReader`:**将输入的字节流转换为字符流输入形式。**
~~~java
public class ReadDemo {
public final static String PATH = "d:"+File.separator + "fileTest" + File.separator + "abcNew.txt";
public static void main(String[] args) {
FileInputStream fis;
InputStreamReader isr;
try {
fis = new FileInputStream(PATH);
//这里其实是一个流的连接
isr = new InputStreamReader(fis);
int n = 0;
/*while((n = isr.read()) != -1) {
System.out.println((char)n);
}*/
char[] charArr = new char[15];
while((n = isr.read(charArr)) != -1) {
//System.out.println(new String(charArr));
//根据操作系统和字符编码的不同,可能最后的读取的效果不经如人意
System.out.println(new String(charArr,0,n));
}
fis.close();
isr.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
### 字符输出流`Writer`
Writer是字符输出流的父类,以下是字符输出流的结构图
![](https://img.kancloud.cn/10/7d/107d638d78f77e370f1f18a60e98b888_891x444.png)
>[info]常用的字符输出流有`BufferedWriter`,`InputStreamWriter`,`FileWriter`
#### 字节字符转换输出流
转换流,可以将一个字节流转换为字符流,也可以将一个字符流转换为字节流。
`OutputStreamWriter`:**可以将输出的字符流转换为字节流的输出形式。**
~~~java
public class WriterDemo {
public final static String PATH = "d:"+File.separator + "fileTest" + File.separator + "abcNew.txt";
public final static String PATH2 = "d:"+File.separator + "fileTest" + File.separator + "abcNew2.txt";
public static void main(String[] args) {
FileInputStream fis;
InputStreamReader isr;
FileOutputStream fos;
OutputStreamWriter ost;
try {
fis = new FileInputStream(PATH);
isr = new InputStreamReader(fis,"UTF-8");
//其实这个文件并没有但是文件输出流会帮我们自动创建一个
fos = new FileOutputStream(PATH2);
ost = new OutputStreamWriter(fos,"UTF-8");
int n = 0;
char[] charArr = new char[13];
while((n = isr.read(charArr)) != -1) {
ost.write(charArr, 0, n);
ost.flush();
}
fis.close();
isr.close();
fos.close();
ost.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
>[danger]读写的时候需要保持编码的一致
### 字节字符转换缓冲流
`BufferedReader`和`BufferedWriter`
~~~java
public class BufferedRWDemo {
public final static String PATH = "d:"+File.separator + "fileTest" + File.separator + "abcNew.txt";
public final static String PATH2 = "d:"+File.separator + "fileTest" + File.separator + "abcNew2.txt";
public static void main(String[] args) {
FileInputStream fis;
InputStreamReader isr;
FileOutputStream fos;
OutputStreamWriter ost;
try {
fis = new FileInputStream(PATH);
isr = new InputStreamReader(fis,"UTF-8");
//其实与之前的缓冲输入输出流相比,这里出现的是一个三层的连接
BufferedReader br = new BufferedReader(isr);
//其实这个文件并没有但是文件输出流会帮我们自动创建一个
fos = new FileOutputStream(PATH2);
ost = new OutputStreamWriter(fos,"UTF-8");
BufferedWriter bw = new BufferedWriter(ost);
int n = 0;
char[] charArr = new char[13];
while((n = br.read(charArr)) != -1) {
bw.write(charArr, 0, n);
bw.flush();
}
fis.close();
isr.close();
fos.close();
ost.close();
br.close();
bw.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
~~~
## 总结
字符流与字节流中的类多种多样,不可能全部讲完,但是各个类的使用大多是大同小异。
在字符流中使用字节流去读写文件,其实只是为了模拟网络文件的读写操作。字符流有着自己的读写文件的类`FileReader`和`FileWriter`