目录
一、IO流概述
概述:
IO流简单来说就是Input和Output流,IO流主要是用来处理设备之间的数据传输,Java对于数据的操作都是通过流实现,而java用于操作流的对象都在IO包中。
分类:
按操作数据分为:字节流和字符流。 如:InputStream和Reader
按流向分:输入流和输出流。如:InputStream和OutputStream
IO流常用的基类:
InputStream , OutputStream
字符流的抽象基类:
Reader , Writer
由上面四个类派生的子类名称都是以其父类名作为子类的后缀:
如:FileReader和FileInputStream
二、IO流的分类
1.Java IO类图
2.下面用一张表格详细讲解IO流的分类:
分类 |
字节输入流 |
字节输出流 |
字符输入流 |
字符输出流 |
抽象基类 |
InputStream |
OutputStream |
Reader |
Writer |
访问文件 |
FileInputStream |
FileOutputStream |
FileReader |
FileWriter |
访问数组 |
ByteArrayInputStream |
ByteArrayOutputStream |
CharArrayReader |
CharArrayWriter |
访问管道 |
PipedInputStream |
PipedOutputStream |
PipedReader |
PipedWriter |
缓冲流 |
BufferedInputStream |
BufferedOutputStream |
BufferedReader |
BufferedWriter |
转换流 |
InputStreamReader |
OutputStreamWriter |
||
对象流 |
ObjectInputStream |
ObjectOutputStream |
||
抽象基类 |
FilterInputStream |
FilterOutputStream |
FilterReader |
FilterWriter |
打印流 |
PrintStream |
PrintWriter |
||
特殊流 |
DataInputStream |
DataOutputStream |
以上流都是继承四个抽象基类:
字节输入流:InputStream 字符输入流:Reader
字节输出流:OutputStream 字符输出流:Writer
为了更好的学习Java流体系,下面主要先从四个抽象基类开始分析。
三.四个抽象基类
1.1.字节流
①概述:
1、字节流和字符流的基本操作是相同的,但是要想操作媒体流就需要用到字节流。
2、字节流因为操作的是字节,所以可以用来操作媒体文件。(媒体文件也是以字节存储的)
3、读写字节流:InputStream 输入流(读)和OutputStream 输出流(写)
4、字节流操作可以不用刷新流操作。
5、InputStream特有方法:
int available();//返回文件中的字节个数
注:可以利用此方法来指定读取方式中传入数组的长度,从而省去循环判断。但是如果文件较大,
而虚拟机启动分配的默认内存一般为64M。当文件过大时,此数组长度所占内存空间就会溢出。
所以,此方法慎用,当文件不大时,可以使用。
②练习:
复制一张图片D:\\Program app.zip到F:/Ba.rar
实现方式有两种:单字节输入输出流(int),多字节输入输出流(byte[]),具体代码如下
package com.bdsw.wxl.day9.homework;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
*
* @author 王雪亮
* @date 2018年7月24日
* @Description 测试FileInputStream,FileOutputStream,BufferedInputStream
* BufferedOutputStream两个类的实现文件的复制
*
* ①singleBStream: 单字节实现文件的复制
* ②mulCharTest: 多字节实现文件的复制
* ③bufReader: 单字节缓冲区实现文件的复制
*/
import java.io.InputStream;
public class FileTest {
/**
*@function 单字节实现文件的复制
*@param s1 将复制的文件的路径
*/
// 17.4M需要12s
static void singleBStream(String s1) throws Exception {
try {
long l1 = System.currentTimeMillis();
//1.定义文件输入输出流
FileInputStream fis = new FileInputStream(s1);
FileOutputStream fos = new FileOutputStream("F:\\aaa.zip");
int l = 0;
//2.循环从fis中读一个字节并复制到fos中,直到完成复制
while ((l = fis.read()) != -1) {
fos.write(l);
}
long l2 = System.currentTimeMillis();
//3.打印所需时间
System.out.println("需要" + (l2 - l1) / 1000 + "s");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/**
*@function 多字符实现文件的复制
*@param s1 将复制的文件的路径
*/
//17.4M 需要0s
static void mulBStream(String s1) throws Exception {
try {
long l1 = System.currentTimeMillis();
//1.定义文件输入输出流
FileInputStream fis = new FileInputStream(s1);
FileOutputStream fos = new FileOutputStream("F:\\aaa.rar");
int len = 0;
byte[] bt = new byte[1000];
//2.循环将fis中的数据读入字节数组bt,直到fis中的元素读完
while ((len = fis.read(bt)) != -1) {
//3.将字节数组bt中的内容写到fos中
fos.write(bt, 0, len);
}
long l2 = System.currentTimeMillis();
//4.打印所需时间
System.out.println("需要" + (l2 - l1) / 1000 + "s");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
String s1 = "D:\\Program app.zip";
singleBStream(s1);//单字节复制文件
mulBStream(s1);//多字节复制
}
}
1.2.字节流缓冲区
* 字节流缓冲区跟字符流缓冲区一样,也是为了提高效率。
注意事项:
1. read():会将字节byte()提升为int型值
2. write():会将int类型转换为byte()类型,保留最后的8位。
练习:使用缓冲区方式,实现上述功能
代码如下:
package com.bdsw.wxl.day9.homework;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
/**
*
* @author 王雪亮
* @date 2018年7月24日
* @Description BufferedInputStream,BufferedOutputStream两个类的实现文件的复制
*
*
* ③bufReader: 单字节缓冲区实现文件的复制
*/
import java.io.InputStream;
public class FileTest {
//**
*@function 多字节缓冲区实现文件的复制
*@param s1 将复制的文件的路径
*//*
static void buffStream(String s1) throws Exception {
//1.定义文件输入输出流in,out
FileInputStream in;
FileOutputStream out;
try {
long l1 = System.currentTimeMillis();
//2.为文件输入输出流赋值,
in = new FileInputStream(s1);
out=new FileOutputStream("F:/Ba.rar");
//3.用文件输入输出流构造文件输入输出流缓冲区
BufferedInputStream bis=new BufferedInputStream(in);
BufferedOutputStream bos=new BufferedOutputStream(out);
int len;
byte []bt=new byte[100];
//4.循环将bis中的数据读入字符数组bt,直到bis中的元素读完
while((len=bis.read(bt))!=-1) {
//5.将字节数组bt中的内容写到bos中
bos.write(bt,0,len);
}
long l2 = System.currentTimeMillis();
//6.打印复制所需时间
System.out.println("缓冲区需要" + (l2 - l1) / 1000 + "s");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
String s1 = "D:\\Program app.zip";
buffStream(s1); //多字节缓冲区复制文件
}
}
2.1字符流
①. 字符流简介:
* 字符流中的对象融合了编码表,也就是系统默认的编码表。我们的系统一般都是GBK编码。
* 字符流只用来处理文本数据,字节流用来处理媒体数据。
* 数据最常见的表现方式是文件,字符流用于操作文件的子类一般是FileReader和FileWriter。
②.字符流读写:
注意事项:
* 写入文件后必须要用flush()刷新。
* 用完流后记得要关闭流
* 使用流对象要抛出IO异常
* 定义文件路径时,可以用“/”或者“\\”。(具体区别已在上一篇博客中说明)
* 在创建一个文件时,如果目录下有同名文件将被覆盖。
* 在读取文件时,必须保证该文件已存在,否则出异常
思考:
通过字符流的方式,实现字节流中的例题(通过int和char[]两种形式分别自己实现)
2.2.字符缓冲区
字符流的缓冲区:BufferedReader和BufferedWreiter
* 缓冲区的出现时为了提高流的操作效率而出现的.
* 需要被提高效率的流作为参数传递给缓冲区的构造函数
* 在缓冲区中封装了一个数组,存入数据后一次取出
综合练习
分别使用字节流和字符流两种方式,实现对F:\\a.jpg图像进行等份切割,再将等份切割的图像合并成一张新的图像
*思考两种切割方式结果不同的原因
package com.bdsw.wxl.day9.homework;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
/**
*
* @author 王雪亮
* @date 2018年7月24日
* @Description 字节流和字符流分别切割并合并文件(图像为例)
*
* ①byteDepart: 字节流切割文件
* ②byteFit: 将字节流分割图像合并
* ③charDepart: 用字符流分割图像
* ④charFit: 将字符流分割图像合并
*/
public class DepartFile {
/**
*@function 用多字节流分割图像
*@param s1 需要切割文件的路径
*/
static void byteDepart(String s1) throws Exception {
try {
//1.字节流方式定义被切割文件对象
FileInputStream fis=new FileInputStream(s1);
int i=0; //用于构造切割图像的文件名
int len=0; //存放字节数组返回的大小
byte[] bt=new byte[50*1024]; //存放读的多个字节数据
//2.循环读fis对象文件中的数据,一次读取len大小个字节
while((len=fis.read(bt))!=-1) {
//3.定义切割成的文件对象
FileOutputStream fos=new FileOutputStream("F:\\a"+i++ +".jpg");
//4.将字节流中的数据写入切割成的对象fos中
fos.write(bt, 0, len);
//5.关闭文件
fis.close();
fos.close();
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/**
*@function 将多字节流分割的图像合并
*/
static void byteFit() throws Exception {
//1.定义合并成的文件 对象
FileOutputStream fos=new FileOutputStream("F:/aa.jpg");
try {
int i;//用于构造切割图像的文件名
for(i=0;i<7;i++) {
//2.定义切割的图像名字,并生成相应对象
String src="F:\\a"+i+".jpg";
FileInputStream fis=new FileInputStream(src);
int len=0; //存放字节数组返回的大小
byte[] b=new byte[1024*50];//存放切割图像的字节数组
//3.将字节数组的内容循环写入合并成的文件
while((len=fis.read(b))!=-1) {
fos.write(b, 0, len);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
*@function 用多字符流分割图像
*@param s2 需要切割文件的路径
*/
static void charDepart(String s2) throws Exception {
try {
//1.字符流方式定义被切割文件对象
FileReader fr=new FileReader(s2);
int i=0; //用于构造切割图像的文件名
int len=0; //存放字符数组返回的大小
char bf[]=new char[1024*50]; //存放读的多个字符数据
//2.循环读fr对象文件中的数据,一次读取len大小个字符
while((len=fr.read(bf))!=-1) {
//3.定义切割成的文件对象
FileWriter fw=new FileWriter("F:/b"+i+++".jpg");
fw.write(bf, 0, len);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
/**
*@function 将多字符流分割的图像合并
*/
static void charFit() throws Exception {
//1.定义合并成的文件 对象
FileWriter fw=new FileWriter("F:/bb.jpg");
try {
int i; //用于构造切割图像的文件名
for(i=0;i<6;i++) {
//2.定义切割的图像名字,并生成相应对象
String src="F:\\b"+i+".jpg";
FileReader fis=new FileReader(src);
int len=0;//存放字符数组返回的大小
char[] b=new char[1024*50]; //存放切割图像的字符数组
//3.将字符数组的内容循环写入合并成的文件
while((len=fis.read(b))!=-1) {
fw.write(b,0,len);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
//1.定义需要切割图片的路径
String s1 = "F:\\a.jpg";
String s2 = "F:\\b.jpg";
try {
byteDepart(s1);//2.1字节方式切割图片
byteFit();//3.1将字节切割的图片合并
charDepart(s2);//2.2字符方式切割图片
charFit();//3.2将字符切割的图片合并
} catch (Exception e) {
e.printStackTrace();
}
}
}
扩展:了解装饰设计模式
四.流操作的基本规律
/*
流操作的基本规律。
一、两个明确:(明确体系)
1. 明确源和目的
源:输入流 InputStream Reader
目的:输出流 OutputStream Writer
2. 操作的数据是否是纯文本
是: 字符流
否: 字节流
二、明确体系后要明确具体使用的对象
通过设备区分:内存,硬盘,键盘
目的设备:内存,硬盘,控制台
示例1:将一个文本文件中的数据存储到另一个文件中: 复制文件
一、明确体系
源:文件-->读取流-->(InputStream和Reader)
是否是文本:是-->Reader
目的:文件-->写入流-->(OutputStream Writer)
是否纯文本:是-->Writer
二、 明确设备
源:Reader
设备:硬盘上一个文本文件 --> 子类对象为:FileReader
FileReader fr = new FileReader("Goods.txt");
是否提高效率:是-->加入Reader中的缓冲区:BufferedReader
BufferedReader bufr = new BufferedReader(fr);
目的:Writer
设备:键盘上一个文本文件 --> 子类对象:FileWriter
FileWriter fw = new FileWriter("goods1.txt");
是否提高效率:是-->加入Writer的缓冲区:BufferedWriter
BufferedWriter bufw = new BufferedWriter(fw);
示例2:将一个图片文件数据复制到另一个文件中:复制文件
一、明确体系
源:文件-->读取流-->(InputStream和Reader)
是否是文本:否-->InputStream
目的:文件-->写入流-->(OutputStream Writer)
是否纯文本:否-->OutputStream
二、 明确设备
源:InputStream
设备:硬盘上一个媒体文件 --> 子类对象为:FileInputStream
FileInputStream fis = new FileInputStream("Goods.txt");
是否提高效率:是-->加入InputStream中的缓冲区:BufferedInputStream
BufferedInputStream bufi = new BufferedInputStream(fis);
目的:OutputStream
设备:键盘上一个媒体文件 --> 子类对象:FileOutputStream
FileOutputStream fos = new FileOutputStream("goods1.txt");
是否提高效率:是-->加入OutputStream的缓冲区:BufferedOutputStream
BufferedOutputStream bufo = new BufferedOutputStream(fw);
示例3:将键盘录入的数据保存到一个文本文件中
一、明确体系
源:键盘-->读取流-->(InputStream和Reader)
是否是文本:是-->Reader
目的:文件-->写入流-->(OutputStream Writer)
是否纯文本:是-->Writer
二、 明确设备
源:InputStream
设备:键盘 --> 对用对象为:System.in --> InputStream
为了操作方便,转成字符流Reader --> 使用Reader中的转换流:InputStreamReader
InputStreamReader isr = new InputStreamReader(System.in);
是否提高效率:是-->加入Reader中的缓冲区:BufferedReader
BufferedReader bufr = new BufferedReader(isr);
目的:Writer
设备:键盘上一个文本文件 --> 子类对象:FileWriter
FileWriter fw = new FileWriter("goods1.txt");
是否提高效率:是-->加入Writer的缓冲区:BufferedWriter
BufferedWriter bufw = new BufferedWriter(fw);
五.指定编码表(转换流可以指定编码表)
要求:用UTF-8编码存储一个文本文件
import java.io.*;
public class IOStreamLaw {
/**
* @param args
*/
public static void main(String[] args) throws IOException {
//键盘的最常见写法
BufferedReader bufr = new BufferedReader(new InputStreamReader(System.in));
BufferedWriter bufw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("goods1.txt"),"UTF-8"));
String line = null;
while((line=bufr.readLine())!=null){
if("over".equals(line)) break;
bufw.write(line.toUpperCase());
bufw.newLine();
bufw.flush();
}
bufr.close();
}
}
文章主要参考:JAVA IO流最详解