java使用IO流来处理不同设备之间数据的交互;所有的IO操作实际上都是对 Stream 的操作

从功能上划分:

  • 输入流: 当数据从源进入的编写的程序时,称它为输入流;
  • 输出流: 从程序输出回另一个源成为输出流;

输入与输出是有参照物的,而这个参照物就是应用程序本身

从结构上划分:

总体的大纲分为两部分,字节流字符流的封装类体系

首先说:

  • 什么是字节流?
  1. 按照字节读取的流对象,一次读取一个字节
  2. 1字节 占八位
  3. 一个数字占四个字节, (前三个八位补0)
  • 什么是字符流?
  1. 按照字符读取的流对象,一次读取一个字符
  2. 一个字,占两到三个字节
  3. 一个英文单词1字节

什么是字符? 所有的英文字符,特殊符号,世界上所有的国家的文字,统称为字符

Java默认一次性处理8位,也就是一个字节

从第三个维度上的划分:

  • 节点流 : 从特定的地方读写的流类 , 比如, 从磁盘或者内存区域

例: 我们想从文件中读取数据,于是我们创建 FileInputStream() , 这个流称为节点流

  • 过滤流 : 这种流会使用节点流作为输入输出(把节点流作为构造方法的参数) FilterInputStream

过滤流也会包装过滤流---> 装饰者设计模式

输入流 InStream

  • 读数据的逻辑
open a stream
while more information
read information
close the stream

例:

//创建一个文件输入流对象,并关联上src下的xanadu
FileInputStream fis = new FileInputStream("src/xanadu");
int b; //定义变量,记录每次读到的字节
while((b = fis.read()) != -1) { //将每次读到的字节赋值给b并判断是否是-1
System.out.println(b); //打印每一个字节
}
fis.close();

文件中内容是: a312a

输出的结果是: 97 51 49 50 97

按照字节读取 a312a 得到的结果 分别是他们的 ASCII表的值

为什么按照字节读,返回的结果不是byte而是整形呢?

因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte,有可能在读到中间的时候遇到111111111

那么这11111111是byte类型的-1,我们的程序是遇到-1就会停止不读了,后面的数据就读不到了,所以在读取的时候用int类型接收,如果11111111会在其前面补上

24个0凑足4个字节,那么byte类型的-1就变成int类型的255了这样可以保证整个数据读完,而结束标记的-1就是int类型

所以,当我们使用二进制文件输出流往文件中 write(97) 就会写进去a

节点流和过滤流的搭配使用, 当我们想从 file中读取数据, 于是我们创建FileInputStream() 关联磁盘上的文件, 然后我们又想提升的效率让我们的 inputStream 拥有缓存的能力,于是为我们为他包装上了BufferedInputStream,然后我们想把流里面的字节数据转换成我们看的懂的基本数据类型,于是我们又包装上了一层DataInputStream 最终用户使用的就是DataInputStream的实例

输出流 OutputStream

  • 写数据的逻辑
open a stream
while more information
write information
close the stream
  • 常见的方法
abstract void write(int b); // 往输出流中写入一个字节
void write(byte[]b) ; // 往输出流中写入 字节数组b中全部的字节
void write(byte[]b,int off,int len); // 往字节数组中写入从off 开始 len个长度的字节 void flush(); //刷新输出流
void close(); // 关闭

所以当我们进行文件的拷贝的时候我们一般会这样写:

FileInputStream fis = new FileInputStream("1.txt");	//创建输入流对象,关联1.txt
FileOutputStream fos = new FileOutputStream("2.txt");//创建输出流对象,关联2.txt
int b;
while((b = fis.read()) != -1) {
fos.write(b);
} fis.close();
fos.close()

如图是Output链

字符流FileReader

字符流先读取到的是字节,进而把字节转换为字符,如果要写出字符的话,需要先把字符转换为字节再写出去, 字符流的出现是对字节流的一层包装

如下:

FileReader fr = new FileReader("aaa.txt");				//创建输入流对象,关联aaa.txt
int ch;
while((ch = fr.read()) != -1) { //将读到的字符赋值给ch
System.out.println((char)ch); //将读到的字符强转后打印
}

因为他按照字符读取,所以我们可以尝试强制转换为 char类型的符号

同理,它的write方法可以往文件里面 写入 字符串

什么情况下使用字符流?

  • 不推荐用它进行拷贝纯文本文件,因为字节->字符 字符->字节 需要转换两次
  • 推荐仅读取一段纯文本 或者仅写出一段纯文本

装饰者设计模式

像这样的代码:

new DataInputStream(new BufferedInputStream(new FileInputStream()));

它是java IO库中提供的一个称为链接的机制, 可以将流首尾相连,形成一个管道, 称为 Decorator 装饰者设计模式

装饰设计模式的组件

  1. 抽象的组件(Component) : 给出一个抽象的接口,规范整套体系
  2. 具体的构建角色(Concrete Component): 实现了抽象组件的具体类
  3. 装饰角色(Decorator) : 介于装饰者和被装饰者之间的中间层 . 持有Component的引用,同时实现了Component这个顶级接口,使用Component的引用复写Component的所有抽象方法
  4. 具体的装饰角色 : 负责给 具体的构建角色(Concrete Component) 添加添加上自己的特定标签

在java的IO体系中各部分所属的组件如下图

特点:

  • 通过上面的图可以看到,Decorator对象,和其他Concrete Component对象在同一级,拥有相同的接口,这样客户端就可以像使用真实的对象一样使用装饰对象
  • 装饰对象,包含了一个对真实对象的引用,使他可以调用真实对象的方法, 并在真实对象方法调用的结果上进一步的操作
  • 由装饰对象接受客户端的请求,并把这些请求转发给真实的对象

装饰者的任务是什么? :

装饰者接收到给定的对象后,把用户使用装饰者的方法转发给被装饰者,并在这个转发前后添加自己特定的功能, 从而可以在代码运行时,不用修改给定对象的结构就添加了新的功能. (而在面向对象的设计中,通常是被设计成 继承给定对象所属的类) , 一句话总结: 装饰者模式是在扩展给定的对象

下面模拟JavaIO,写一个例子

首先是顶级接口Component:

public interface InputStreamComponent {
void read();
}

Concrete Component 具体的实现组件

public class FileInputStreamConcreteComponent implements InputStreamComponent {
@Override
public void read() {
System.out.println("FileInputStreamConcreteComponent 读取字节流 ...");
}
}

Decorator 和具体的实现组件位于同一级

    // todo 1. 实现了 顶级接口
public class FilterInputStreamDecorator implements InputStreamComponent { // 2. 持有顶级接口的引用
private InputStreamComponent inputStreamComponent; // 3. 在构造函数中初始化
public FilterInputStreamDecorator(InputStreamComponent inputStreamComponent) {
this.inputStreamComponent=inputStreamComponent;
} // todo 4. 重写的抽象方法, 交由顶级接口的引用完成方法的调用
@Override
public void read() {
inputStreamComponent.read();
}
}

带缓存的装饰者

// todo 当前的装饰者,继承中间层的 Decorator
public class BufferedInputStreamConcreateDecrator extends FilterInputStreamDecorator {
// todo 同样持有顶级接口的引用
private InputStreamComponent inputStreamComponent; public BufferedInputStreamConcreateDecrator(InputStreamComponent inputStreamComponent) {
// todo 把传递进来的需要的装饰的对象,传递给Decorator中间层, 让它去执行 需要被装饰对象原有的方法
super(inputStreamComponent);
} @Override
public void read() {
// todo 调用父类的 read
super.read();
this.ARead();
}
private void ARead() {
System.out.println("BufferedInputStreamConcreateDecrator A... 增加缓存的功能...");
}
}

读取基本数据类型的装饰者

/ todo 当前的装饰者,继承中间层的 Decorator
public class DataInputStreamConcreateDecrator extends FilterInputStreamDecorator {
// todo 同样持有顶级接口的引用
private InputStreamComponent inputStreamComponent; public DataInputStreamConcreateDecrator(InputStreamComponent inputStreamComponent) {
// todo 把传递进来的需要的装饰的对象,传递给Decorator中间层, 让它去执行 需要被装饰对象原有的方法
super(inputStreamComponent);
} @Override
public void read() {
// todo 调用父类的 read
super.read();
this.ARead();
} private void ARead() {
System.out.println("BufferedInputStreamConcreateDecrator 进一步把流转换成基本数据类型 ...");
}
}

下面是测试

public class text {
public static void main(String[] args) {
InputStreamComponent DataInputStreamConcreateDecrator =
new DataInputStreamConcreateDecrator(
new BufferedInputStreamConcreateDecrator(
new FileInputStreamConcreteComponent())); DataInputStreamConcreateDecrator.read(); System.out.println("");
InputStreamComponent DataInputStreamConcreateDecrator2 =
new DataInputStreamConcreateDecrator(
new FileInputStreamConcreteComponent()); DataInputStreamConcreateDecrator2.read();
}
}

结果:

FileInputStreamConcreteComponent 读取字节流 ...
BufferedInputStreamConcreateDecrator A... 增加缓存的功能...
BufferedInputStreamConcreateDecrator 进一步把流转换成基本数据类型 ... FileInputStreamConcreteComponent 读取字节流 ...
BufferedInputStreamConcreateDecrator 进一步把流转换成基本数据类型 ...

依然是,根据源的不同,选择不同的 具体实现的组件Concrete Compent 得先把数据读取到InputStream里面,进而选择不同的装饰者对输入流进行包装

中间层设计的很巧妙,1. 中间层实现了顶级接口,而所有的装饰者又继承于中间层, 达到了规范装饰者的目的 2.对于要去重写的顶级接口的方法来说,中层其实他本身啥活也没干,相反他使用自己的成员变量 (顶级接口的引用去完成重写的工作) 而这个引用不是别人,就是用户传递进去的直接关联数据源的对象

装饰模式 VS 继承

装饰者:

  • 装饰者模式用来拓展一个对象的功能,不需要子类
  • 它是动态的,运行时的装饰

继承:

  • 继承是用来拓展一类对象的功能
  • 静态的,编译时任务就分配好了
  • 继承会出现类爆炸的情况, 比如一个顶级接口有5个具体的实现组件,同时每一个组件都需要三种不同的装饰 如果是继承去实现 就是 5*3 = 15 个类 而装饰者模式则是 5+3=8个类

装饰模式 VS 静态代理设计模式

这是一个静态代理的例子:

interface Logger {
void writeLog();
} // 被代理类
class LoggerSubject implements Logger{
@Override
public void writeLog(){
System.out.println("writeLog by LoggerSubject");
}
} // 代理类
class Proxy implements Logger{
Logger logger;
// 与装饰者模式的主要区别位置
// 代理模式一般要求和原来的类行为一致,因此构造函数不传入对象
Proxy(){
this.logger = new LoggerSubject();
}
@Override
public void writeLog(){
System.out.println("logger write before");
logger.writeLog();
System.out.println("logger write after");
}
} public class StaticProxy {
private static void write(Logger logger){
logger.writeLog();
}
public static void main(String []argvs){
Logger logger = new Proxy();
write(logger);
}
}

相同点:

  • 这两种设计模式的最终目标都是目标对象进行增强,所以不是很好区分

不同点:

  • 在使用的时候最直观的不同点就是: 静态代理把被代理的对象隐藏起来了,客户端使用代理对象的时候,是感觉不出有被代理的对象存在的; 而装饰者模式的层级关系很明确

  • 但是它们的侧重点不同, 代理设计模式的侧重点是增加对代理对象方法的控制,比如我想在目标方法执行前后都打印一下日志,而 javaIO的装饰者模式侧重于添加功能,比如先得到 输入流--> 添加缓存 --> 把输入流转换成基本数据类型 类似的这样的功能的添加

IO流与装饰者模式的更多相关文章

  1. Java IO流以及装饰器模式在其上的运用

    流概述 Java中,流是一种有序的字节序列,可以有任意的长度.从应用流向目的地称为输出流,从目的地流向应用称为输入流. Java的流族谱 Java的 java.io 包中囊括了整个流的家族,输出流和输 ...

  2. 设计模式--装饰者模式(io流中使用的模式)

    重点: 1.动态扩展对象,替换之前需要继承才能实现的功能. 2.具体工作的,仍然是被包装的对象,(有点锦上添花的意思,顾名思义仅仅起到装饰的作用,主体不变). 对比继承: 1.减少类的数量. 如果使用 ...

  3. [javaSE] IO流(装饰设计模式)

    装饰设计模式:当想要对已有的对象进行功能增强时,可以自定义类将已有的对象传入,并提供加强功能,自定义的该类称为装饰类 典型的: Reader--FileReader --BufferedReader ...

  4. java IO流的继承体系和装饰类应用

    java IO流的设计是基于装饰者模式&适配模式,面对IO流庞大的包装类体系,核心是要抓住其功能所对应的装饰类. 装饰模式又名包装(Wrapper)模式.装饰模式以对客户端透明的方式扩展对象的 ...

  5. 设计模式 - 装饰者模式(Decorator Pattern) Java的IO类 用法

    装饰者模式(Decorator Pattern) Java的IO类 用法 本文地址: http://blog.csdn.net/caroline_wendy/article/details/26716 ...

  6. Java IO 流 -- 设计模式:装饰设计模式

    在java IO 流中我们经常看到这样的写法: ObjectOutputStream oos = new ObjectOutputStream( new BufferedOutputStream(ne ...

  7. java IO流详解

    流的概念和作用 学习Java IO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

  8. IO流

    流的概念和作用 学习JavaIO,不得不提到的就是JavaIO流. 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输特 ...

  9. Java IO流学习总结

    Java流操作有关的类或接口: Java流类图结构: 流的概念和作用 流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象.即数据在两设备间的传输称为流,流的本质是数据传输,根据数据传输 ...

随机推荐

  1. 牛客NOIP暑期七天营-提高组2

    第一题:ACGT 题目链接:https://ac.nowcoder.com/acm/contest/931/A trie树.hash.map遍历  ①.trie树上的节点多记一个rest值表示还有多少 ...

  2. [译]C# 7系列,Part 2: Async Main 异步Main方法

    原文:https://blogs.msdn.microsoft.com/mazhou/2017/05/30/c-7-series-part-2-async-main/ 你大概知道,C#语言可以构建两种 ...

  3. JS---DOM---案例:模拟百度搜索框

    模拟百度搜索框 我的思路整理: 1. 注册文本框抬起事件(onkeyup) 2. 处理函数: --->创建临时数组,循环遍历文本框键入的文字内容和keywords数组,用keyWords[i]. ...

  4. Mapbox轨迹回放

        轨迹回放是webgis中的常见功能,是一种被客户喜闻乐见的GIS动画.     动画是一种短时间内不停重绘达到不断运动的效果.本文中轨迹回放就是事先计算好所需要的点,后面再进行播放.      ...

  5. CentOS7自动化安装PXE方案

    目的 无人值守批量安装CentOS7 安装条件 一台带有PXE协议支持NIC的待安装主机 一台存放安装文件的服务器,如NFS,HTTP或FTP服务器 Kickstart 生成的配置文件(ks.cfg) ...

  6. Silky-CTF: 0x02 Vulhub Walkthrough

    靶机地址: https://www.vulnhub.com/entry/silky-ctf-0x02,307/ 主机扫描: HTTP进行目录爆破 尝试SQL注入会被封掉IP 经过尝试发现usernam ...

  7. 计划任务cron

    cron 计划任务 作用: 计划任务主要是做一些周期性的任务,目前最主要的用途是定期备份数据 Schedule one-time tasks with at. 一次性调度执行 atSchedule r ...

  8. Django的Form验证(2)

    Django的Form验证(2) Form的含义及作用: 用于验证用户请求数据合法性的一个组件(校验数据的合法性) Django的Form实现步骤: 创建一个验证用户请求的模板 from django ...

  9. AudioFormat.Encoding

    https://docs.oracle.com/javase/7/docs/api/javax/sound/sampled/AudioFormat.Encoding.html

  10. macbook无法下载软件问题解决

    今天新买了一台MacBook Pro,但是发现无法下载软件,在App Store中一直转圈圈. 方法:修改网络DNS为114.114.114.114和8.8.8.8,即可解决.