IO流结构
流的概念和作用
流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特性将流抽象为各种类,方便更直观的进行数据操作。
IO流的分类
根据处理数据类型的不同分为:字符流和字节流
根据数据流向不同分为:输入流和输出流
字符流和字节流
字符流的由来: 因为数据编码的不同,而有了对字符进行高效操作的流对象。本质其实就是基于字节流读取时,去查了指定的码表。 字节流和字符流的区别:
读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。
字节流:一次读入或读出是8位二进制。
字符流:一次读入或读出是16位二进制。
设备上的数据无论是图片或者视频,文字,它们都以二进制存储的。二进制的最终都是以一个8位为数据单元进行体现,所以计算机中的最小数据单元就是字节。意味着,字节流可以处理设备上的所有数据,所以字节流一样可以处理字符数据。
结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。
输入流和输出流
输入流只能进行读操作,输出流只能进行写操作,程序中需要根据待传输数据的不同特性而使用不同的流。
输入字节流 InputStream
InputStream 是所有的输入字节流的父类,它是一个抽象类。
ByteArrayInputStream、StringBufferInputStream、FileInputStream 是三种基本的介质流,它们分别从Byte 数组、StringBuffer、和本地文件中读取数据。
PipedInputStream 是从与其它线程共用的管道中读取数据,与Piped 相关的知识后续单独介绍。
ObjectInputStream 和所有FilterInputStream 的子类都是装饰流(装饰器模式的主角)。
输出字节流 OutputStream
OutputStream 是所有的输出字节流的父类,它是一个抽象类。
ByteArrayOutputStream、FileOutputStream 是两种基本的介质流,它们分别向Byte 数组、和本地文件中写入数据。
PipedOutputStream 是向与其它线程共用的管道中写入数据。
ObjectOutputStream 和所有FilterOutputStream 的子类都是装饰流。
总结:
输入流:InputStream或者Reader:从文件中读到程序中;
输出流:OutputStream或者Writer:从程序中输出到文件中;
节点流
节点流:直接与数据源相连,读入或读出。
直接使用节点流,读写不方便,为了更快的读写文件,才有了处理流。
常用的节点流
父 类 :InputStream 、OutputStream、 Reader、 Writer
文 件 :FileInputStream 、 FileOutputStrean 、FileReader 、FileWriter 文件进行处理的节点流
数 组 :ByteArrayInputStream、 ByteArrayOutputStream、 CharArrayReader 、CharArrayWriter 对数组进行处理的节点流(对应的不再是文件,而是内存中的一个数组)
字符串 :StringReader、 StringWriter 对字符串进行处理的节点流
管 道 :PipedInputStream 、PipedOutputStream 、PipedReader 、PipedWriter 对管道进行处理的节点流
处理流
处理流和节点流一块使用,在节点流的基础上,再套接一层,套接在节点流上的就是处理流。如BufferedReader.处理流的构造方法总是要带一个其他的流对象做参数。一个流对象经过其他流的多次包装,称为流的链接。
常用的处理流
缓冲流:BufferedInputStream 、BufferedOutputStream、 BufferedReader、 BufferedWriter 增加缓冲功能,避免频繁读写硬盘。
转换流:InputStreamReader 、OutputStreamReader实现字节流和字符流之间的转换。
数据流: DataInputStream 、DataOutputStream 等-提供将基础数据类型写入到文件中,或者读取出来。
三种复制文件的方法:
下面代码为了能尽量清晰展示每种方法就把异常抛出去了,实际生产环境能处理的尽量处理一下。
public class Test {
public static void main(String[] args) throws Exception {
// copyFile1();
copyFile2();
// copyFileUsingFileChannels();
}
/**
* 简单的文件复制,不使用处理流
*
* @throws Exception
*/
public static void copyFile1() throws Exception {
// 文件复制,b预先不存在,所以new了下
File file1 = new File("d:a.jpg");
File file2 = new File("d:b.jpg");
file2.createNewFile();
FileInputStream ins = new FileInputStream(file1);
FileOutputStream out = new FileOutputStream(file2);
byte[] b = new byte[1024];
int n = 0;
while ((n = ins.read(b)) != -1) {
out.write(b, 0, n);
}
ins.close();
out.close();
System.out.println("copy over!");
}
/**
* 使用缓冲流
* @throws Exception
*/
public static void copyFile2() throws Exception {
// 文件复制,b预先不存在,所以new了下
File file1 = new File("d:a.jpg");
File file2 = new File("d:b.jpg");
file2.createNewFile();
FileInputStream ins = new FileInputStream(file1);
FileOutputStream out = new FileOutputStream(file2);
BufferedInputStream bufferInput = new BufferedInputStream(ins);
BufferedOutputStream bufferOutput = new BufferedOutputStream(out);
byte[] bb = new byte[1024];// 用来存储每次读取到的字节数组
int n;// 每次读取到的字节数组的长度
while ((n = bufferInput.read(bb)) != -1) {
bufferOutput.write(bb, 0, n);// 写入到输出流
}
bufferInput.close();
bufferOutput.close();
ins.close();
out.close();
System.out.println("copy over!");
}
/**
* 使用nio的filechannel
* @throws Exception
*/
private static void copyFileUsingFileChannels() throws Exception {
// 文件复制,d预先不存在,所以new了下
File file1 = new File("d:c.jpg");
File file2 = new File("d:d.jpg");
file2.createNewFile();
FileChannel inputChannel = null;
FileChannel outputChannel = null;
try {
inputChannel = new FileInputStream(file1).getChannel();
outputChannel = new FileOutputStream(file2).getChannel();
outputChannel.transferFrom(inputChannel, 0, inputChannel.size());
} finally {
inputChannel.close();
outputChannel.close();
}
System.out.println("copy over!");
}
}
对象流:
/**
* 对象流使用
* @throws Exception
*/
private static void copyObject() throws Exception {
User user1 = new User();
user1.setName("我是对象1");
// 保存的地址
File file2 = new File("d:a.obj");
file2.createNewFile();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(file2));
oos.writeObject(user1);
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file2));
User user2 = (User) ois.readObject();
System.out.println(user2);
oos.close();
ois.close();
}
/**
* 注意这个类必须实现Serializable接口
*
* @author Administrator
*
*/
public static class User implements Serializable {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User [name=" + name + "]";
}
}
我对我们的复制文件做了性能比较,同时复制一个70m的文件,使用了缓冲流的方法用时是节点流的1/3;
具体就是调用上面的copyFile1()和copyFile2()
总结:
1.上面例子没有使用字符流,是因为字节流可以替代字符流,只是操作文本文件的时候字符流会快;
2.尽量使用处理流,因为节点流每次操作的粒度有限导致性能没有处理流快;
昨日翻看关于io的资料,发现他是使用了设计模式的装饰器模式,我便通过源码画了一个类图
上图结构简单,但是从图上面看我发现一个比较奇怪的现象,FileterInputStream的属性居然有父类InputStream,InputStream居然是一个抽象类不是接口。翻阅资料说io流使用了装饰器模式,于是再研究一下装饰器模式。
装饰器模式:
允许向一个现有的对象添加新的功能,同时又不改变其结构。装饰者可以在所委托被装饰者的行为之前或之后加上自己的行为,以达到特定的目的。
适用场景:
扩展一个类的功能。
动态增加功能,动态撤销。
优点:
装饰类和被装饰类可以独立发展,不会相互耦合
动态的将责任附加到对象身上。
缺点:
多层装饰比较复杂。
装饰器模式由组件和装饰者组成。
抽象组件(Component):需要装饰的抽象对象。
具体组件(ConcreteComponent):是我们需要装饰的对象
抽象装饰类(Decorator):内含指向抽象组件的引用及装饰者共有的方法。
具体装饰类(ConcreteDecorator):被装饰的对象。
例子:
使用一个装饰手机的例子
public class DecoratorTest {
public static void main(String[] args) {
Phone phone = new Phone();
System.out.println("装饰手机开始:");
Decorateable phoneDecoreat = new BeautifulPhone(phone);
phoneDecoreat.des();
phoneDecoreat.decorate();
phoneDecoreat = new MuchAppPhone(phone);
phoneDecoreat.des();
phoneDecoreat.decorate();
}
}
class BeautifulPhone extends AbstractPhone {
public BeautifulPhone(Phone phone) {
super(phone);
}
@Override
public void decorate() {
System.out.println("我开始装饰了:贴了很多贴画在背面,然后买了一些挂坠挂着,然后买了一个卡通套套着。");
}
@Override
public void des() {
System.out.println("我是一个很美的手机。");
}
}
class MuchAppPhone extends AbstractPhone {
public MuchAppPhone(Phone phone) {
super(phone);
}
@Override
public void decorate() {
System.out.println("我开始装饰了:我装饰的是手机内部,我下载了支付宝,淘宝,下载了微信,下载了麻将游戏,还下载了酷狗等等一共100多款手机应用。");
}
@Override
public void des() {
System.out.println("我是一个功能很多的手机");
}
}
class Phone {
}
abstract class AbstractPhone implements Decorateable {
private Phone phone;
public AbstractPhone(Phone phone) {
this.phone = phone;
}
}
/**
* 包装一个物品
*
* @author Administrator
*
*/
interface Decorateable {
/**
* 装饰
*/
void decorate();
/**
* 描述
*/
void des();
}
执行结果:
例子中不直接使用手机这个对象,而是将手机放到一个父类,然后子类去继承父类来操作手手机,这样,想做怎么装饰手机随便你,这样应用多的手机和很美的手机相互无关。