概述
从14年10月份开始工作,到今天做Android已经两年半了。可是到现在也没搞清楚Java的I/O机制,痛定思痛,觉得好好整理一下。古人云“格物致知”,今天就好好格一格I/O机制吧!
常见问题
什么是流
“流”是一连串流动的字符,同时也说明了数据传输的一种状态:“均匀与连续”。java使用“流”进行数据传输。而传输的双方一般是“你的程序”和“设备(手机)”。
输入和输出流
根据流的方向,可以分为输入流和输出流。流的输入(Input)和输出(Output)是以应用程序为参考中心的。输入流就是从设备流入到应用程序,也即常说的读(Read);输出流就是从应用程序流到设备,也即我们常说的写(Write)。
原理图如下:
注:输入流命名规则是包含Input或Reader,如InputStream、BufferedReader;输出流命名规则是包含Output或Writer,如OutputStream、BufferedWriter。
字节流和字符流
数据传输的基本单位是字节(bytes),但1个字节是8位,表示范围是0-255。面对诸多汉字,1个字节很明显是不足的。所以生成了一种新的单位:字符,而字符和字节之间的映射关系就是Unicode。在java中1个字符等于2个字节。根据流的数据类型,可分为字节流和和字符流。
字节流和字符流的区别
1. 读写单位:字节流处理的基本单元是字节;字符流处理的基本单元是Unicode码,2个字节
2. 处理对象:字节流能处理所有类型的数据(图片,视频,音频等),字符流只能处理字符型数据(字符串,纯文本文档)
3. 是否缓存:字符流传输之前要先经过Unicode转换成字节流,批量转换后的字节流存储到缓存当中(提示效率),然后再进行读写。字节流则不用缓存(字节流本身是不使用字节流的,但是如果想提升效率,可以自定义缓存区)。
注:字节流一般命名规则是以Stream结尾,如InputStream或OutputStream;字符流一般命名规则是以Reader或Writer结尾。如BufferedReader、BufferedWriter。
节点和处理流
根据流是否直接与数据源相连,可以划分为节点流和处理流。节点流是可以直接从/向数据源(设备,硬盘,内存等)读/写数据;处理流是对一个已存在的流的连接、封装和处理。
注:处理流命名规则是包含buffered,如BufferedInputStream、BufferedReader。
介绍
I/O家族谱系
根据操作的数据类型,分为字节流和字符流;根据数据的流向,又分为输入流和输出流;根据功能,分为节点流和处理流。
InputStream和OutputStream
字节流处理的抽象基类是InputStream和OutputStream。InputStream是字节输入流,OutputStream是字节输出流。
Reader和Writer
字符流处理的抽象基类是Reader和Writer。Reader是字符输入流,Writer是字符输出流。
实战演示
介绍的差不多了,接下来就是实战演示了。
1.从本地读取图片
1. 读取说明数据流是从设备到应用,因此使用输入流,那就是InputStream或Reader
2. 操作的数据对象是图片,使用字节流的话效率较高(不用经由Unicode转换)
3. 所以排除Reader,选择InputStream
public void readImg(ImageView iv) {
File file = new File("/sdcard/temp.jpg");
if (file.exists()) {
FileInputStream fileInputStream = null;
try {
fileInputStream = new FileInputStream(file);
byte[] img = new byte[(int) file.length()];// 根据文件长度创建字节数组
fileInputStream.read(img); // 将图片字节信息写入到字节数组
Bitmap bitmap = BitmapFactory.decodeByteArray(img,0,img.length);
iv.setImageBitmap(bitmap); // 显示图片
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
2.保存图片到本地
1. 保存到本地说明数据流是从应用到设备,因此使用输出流,所以选择OutputStream或Writer
2. 由于操作的数据对象是图片,使用字节流的话效率较高(不用经由Unicode转换)
3. 所以排除Writer,选择OutputStream
public void saveImg() {
InputStream inputStream = null;
FileOutputStream fileOutputStream = null;
try {
URL url = new URL("https://ss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/image/h%3D360/sign=e572f27e9058d109dbe3afb4e158ccd0/b7fd5266d0160924933e331bd60735fae6cd3492.jpg");
URLConnection urlConnect = url.openConnection();
urlConnect.setConnectTimeout(5000);
inputStream = urlConnect.getInputStream();
File tempFile = new File("/sdcard/temp.jpg");// 保存到临时文件中
if(tempFile.isDirectory()){
tempFile.delete();
}
if (!tempFile.exists()) {
tempFile.createNewFile();
}
fileOutputStream = new FileOutputStream(tempFile);// 创建输出流,参数tempFile为写入的目标
byte[] buffer = new byte[1024];
int length = -1;
while ((length = inputStream.read(buffer)) != -1) { // 读取字节,先存储到缓存区
fileOutputStream.write(buffer, 0, length); // 从缓存区提取数据,可以提升效率
}
inputStream.close();
fileOutputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
3.从本地读取纯文档
1. 由于是读取,说明数据流是从设备流到应用,因此使用输入流,所以选择InputStream或Reader
2. 操作的数据对象是纯文档,需要使用字符流(纯文档需要经由Unicode转码)
3. 所以排除InputStream,选择Reader
public String readWold() {
try {
FileReader fileReader = new FileReader("/sdcard/test.txt");
BufferedReader bufferedReader = new BufferedReader(fileReader);
String line = null;
StringBuilder builder = new StringBuilder();
while ((line = bufferedReader.readLine()) != null) {
builder.append(line + "\n");
}
bufferedReader.close();
return builder.toString();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
4.写入到本地纯文档
1.写入说明数据流是从应用到设备,因此使用输出流,所以选择OutputStream或Writer
2. 操作的数据对象是纯文档,需要使用字符流(纯文档或字符串需要经由Unicode转码)
3. 所以排除OutputStream,选择Writer
public void saveWord() {
try {
File file = new File("/sdcard/test.txt");
if (file.isDirectory()){
file.delete();
}
if (!file.exists()){
file.createNewFile();
}
FileWriter fileWriter = new FileWriter(file);
BufferedWriter bufferedWriter = new BufferedWriter(fileWriter);
bufferedWriter.write("Hi,I am Chaos!");
bufferedWriter.newLine();
bufferedWriter.write("I am come from China!");
bufferedWriter.newLine();
bufferedWriter.flush();
bufferedWriter.close();
} catch (IOException e) {
e.printStackTrace();
}
}
参考资料
字节流和字符流 http://www.jianshu.com/p/4edb6b99204e
Java中的字节流、缓冲流 http://blog.youkuaiyun.com/sinat_34669892/article/details/52738643
java 下载网络上的图片并保存到本地目录 http://takeme.iteye.com/blog/1683380
IO结构图源于百度图片