IO流中的设计模式

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了IO流中的设计模式相关的知识,希望对你有一定的参考价值。

参考技术A 1.IO中用到的适配器模式
在IO中,如将字符串数据转变成字节数据保存到文件中,将字节数据转变成流数据等都用到了适配器模式,下面以InputStreamReader和OutputStreamWriter类为例介绍适配器模式。
InputStreamReader和OutputStreamWriter类分别继承了Reader和Writer接口,但要创建它们必须在构造函数中传入一个InputStream和OutputStream的实例,InputStreamReader和OutputStreamWriter的作用也就是将InputStream和OutputStream适配到Reader和Writer。
InputStreamReader实现了Reader接口,并且持有了InputStream的引用,这是通过StreamDecoder类间接持有的,因为byte到char要经过编码。
这里,适配器就是InputStreamReader类,而源角色就是InputStream代表的实例对象,目标接口就是Reader类,OutputStreamWriter类也是类似的方式。
在IO中类似的还有,如StringReader将一个String类适配到Reader接口,ByteArrayInputStream适配器将byte数组适配到InputStream流处理接口。

2.IO中用到的装饰模式
装饰模式就是对一个类进行装饰,增强其方法行为,在装饰模式中,作为原来的这个类使用者还不应该感受到装饰前与装饰后有什么不同,否则就破坏了原有类的结构了,所以装饰器模式要做到对被装饰类的使用者透明,这是对装饰器模式的一个要求。总之装饰器设计模式就是对于原有功能的扩展
在IO中有许多不同的功能组合情况,这些不同的功能组合都是使用装饰器模式实现的,下面以FilterInputStream为例介绍装饰器模式的使用。
InputStream类就是以抽象组件存在的,而FileInputStream就是具体组件,它实现了抽象组件的所有接口,FilterInputStream类就是装饰角色,它实现了InputStream类的所有接口,并持有InputStream的对象实例的引用,BufferedInputStream是具体的装饰器实现者,这个装饰器类的作用就是使得InputStream读取的数据保存在内存中,而提高读取的性能。类似的还有LineNumberInputStream类,它的作用是提高按行读取数据的功能。

总结
这两种设计模式看起来都是起到包装一个类或对象的作用,但是使用它 们的目的却不尽相同。适配器模式主要在于将一个接口转变成另一个接口,它的目的是通过改变接口来达到重复使用的目的;而装饰器模式不是要改变被装饰对象的接口,而是保持原有的接口,但是增强原有对象的功能,或改变原有对象的方法而提高性能。

补充:高性能的IO体系。
首先得明白什么是同步,异步,阻塞,非阻塞.
1,同步和异步是针对应用程序和内核的交互而言的
2,阻塞和非阻塞是针对于进程在访问数据的时候,根据IO操作的就绪状态来采取的不同方式
总结一句简短的话,同步和异步是目的,阻塞和非阻塞是实现方式。
名词解释
同步 指的是用户进程触发IO操作并等待或者轮询的去查看IO操作是否就绪 自己上街买衣服,自己亲自干这件事,别的事干不了。

IO流中的字符输入输出流及try...catch处理流处理中的异常

使用字节流读取中文的问题

import java.io.FileInputStream;
import java.io.IOException;

/*
使用字节流读取中文文件
1个中文
    GBK:占用两个字节
    UTF-8:占用3个字节
 */
public class Demo01InputStream {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("D:\\\\environment\\\\java_project\\\\javase\\\\c.txt");
        int len = 0;
        while ((len = fis.read())!=-1){
            System.out.println((char)len);//出现错误
        }
        fis.close();
    }
}

字符输入流读取字符数据

import java.io.FileReader;
import java.io.IOException;

/*
java.io.Reader:字符输入流,是字符输入流的最顶层的父类,定义了一些共性的成员方法,是一个抽象类

共性的成员方法:
    int read() 读取单个字符并返回
    int read(char[] cbuf) 一次读取多个字符,将字符读入数组
    void close() 关闭该流并释放与之关联的所有资源

java.io.FileReader extends InputStreamReader extends Reader
FileReader:文件字符输入流
作用:把硬盘文件中的数据以字符的方式读取到内存中

构造方法:
    FileReader(String fileName)
    FileReader(File file)
    参数:读取文件的数据源
        String fileName:文件的路径
        File file:一个文件
    FileReader构造方法的作用:
        1.创建一个FileReader对象
        2.会把FileReader对象只想要读取的文件
 */
public class Demo02Reader {
    public static void main(String[] args) throws IOException {
        //1.创建FileReader对象,构造方法中绑定要读取的数据源
        FileReader fr = new FileReader("D:\\\\environment\\\\java_project\\\\javase\\\\c.txt");
        //2.使用FileReader对象中的方法read读取文件
        //int read() 读取单个字符并返回
        /*int len = 0;
        while ((len = fr.read())!=-1){
            System.out.println((char)len);
        }*/

        //int read(char[] cbuf) 一次读取多个字符,将字符读入数组
        char[] cs = new char[1024];//存储读取到的多个字符
        int len = 0;//记录的是每次读取的有效字符个数
        while((len = fr.read(cs))!=-1){
            /*
            String类的构造方法
            String(char[] value) 把字符数组转换为字符串
            String(char[] value, int offset, int count) 把字符数组的一部分转换为字符串 offset数组的开始索引 count转换的个数
             */
            System.out.println(new String(cs,0,len));
        }

        //3.释放资源
        fr.close();
    }
}

字符输出流FileWriter

import java.io.FileWriter;
import java.io.IOException;

/*
java.io.Writer:字符输出流,是所有字符输出流的最顶层的父类,是一个抽象类

共性的成员方法:
    - void write(int c) 写入单个字符
    - void write(char[] cbuf) 写入字符数组
    - abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数
    - void write(String str) 写入字符串
    - void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数
    - void flush() 刷新该流的缓冲
    - void close() 关闭此流,但要先刷新它

java.io.FileWriter extends OutputStreamWriter extends Writer
FileWriter:文件字符输出流
作用:把内存中字符数据写入到文件中

构造方法:
    FileWriter(File file) 根据给定的File对象构造一个FileWriter对象
    FileWriter(String fileName) 根据给定的文件名构造一个FileWriter对象
    参数:写入数据的目的地
        String fileName:文件的路径
        File file:是一个文件
    构造方法的作用:
        1.会创建一个FileWriter对象
        2.会根据构造方法中传递的文件/文件的路径,创建文件
        3.会把FileWriter对象指向创建好的文件

字符输出流的使用步骤(重点):
    1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
    2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
    3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
    4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
 */
public class Demo01Writer {
    public static void main(String[] args) throws IOException {
        //1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
        FileWriter fw = new FileWriter("D:\\\\environment\\\\java_project\\\\javase\\\\d.txt");
        //2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        //void write(int c) 写入单个字符
        fw.write(97);
        //3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        //fw.flush();
        //4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();
    }
}

FileWriter中flush方法与close方法的区别

import java.io.FileWriter;
import java.io.IOException;

/*
flush方法和close方法的区别
    - flush:刷新缓冲区,流对象可以继续使用
    - close:先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了
 */
public class Demo02CloseAndFlush {
    public static void main(String[] args) throws IOException {
        //1.创建FileWriter对象,构造方法中绑定要写入数据的目的地
        FileWriter fw = new FileWriter("D:\\\\environment\\\\java_project\\\\javase\\\\e.txt");
        //2.使用FileWriter中的方法write,把数据写入到内存缓冲区中(字符转换为字节的过程)
        //void write(int c) 写入单个字符
        fw.write(97);
        //3.使用FileWriter中的方法flush,把内存缓冲区中的数据,刷新到文件中
        fw.flush();
        //刷新之后流可以继续使用
        fw.write(98);

        //4.释放资源(会先把内存缓冲区中的数据刷新到文件中)
        fw.close();

        //close方法之后流已经关闭了,已经从内存中消失了,流就不能再使用了
        fw.write(99);//IOException: Stream closed
    }
}

字符输出流写数据的其他方法

/*
字符输出流写数据的其他方法:
    - void write(char[] cbuf) 写入字符数组
    - abstract void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数
    - void write(String str) 写入字符串
    - void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数
 */
public class Demo03Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:\\\\environment\\\\java_project\\\\javase\\\\f.txt");
        char[] cs = {\'a\',\'b\',\'c\',\'d\',\'e\'};
        //void write(char[] cbuf)写入字符数组
        fw.write(cs);//abcde

        //void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数
        fw.write(cs,1,3);//bcd

        //void write(String str)写入字符串
        fw.write("传智播客");//传智播客

        //void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数
        fw.write("天生我材必有用",0,4);//天生我材

        fw.close();
    }
}

字符输出流的续写和换行

import java.io.FileWriter;
import java.io.IOException;

/*
续写和换行
续写,追加写:使用两个参数的构造方法
    FileWriter(String fileName, boolean append)
    FileWriter(File file, boolean append)
    参数:
        String fileName,File file:写入数据的目的地
        boolean append:续写开关 true:不会创建新的文件覆盖源文件,可以续写;false:创建新的文件覆盖源文件
换行:换行符号
    windows:\\r\\n
    linux:/n
    mac:/r
 */
public class Demo04Writer {
    public static void main(String[] args) throws IOException {
        FileWriter fw = new FileWriter("D:\\\\environment\\\\java_project\\\\javase\\\\g.txt",true);
        for (int i = 0; i < 10; i++) {
            fw.write("HelloWorld"+i+"\\r\\n");
        }
        fw.close();
    }
}

使用try...catch处理流中的异常

  • try...catch...finally
import java.io.FileWriter;
import java.io.IOException;

/*
在jdk1.7之前使用try  catch   finally 处理流中的异常
格式:
    try{
        可能会产出异常的代码
    }catch(异常类变量 变量名){
        异常的处理逻辑
    }finally{
        一定会执行的代码
        资源释放
    }
 */
public class Demo01TryCatch {
    public static void main(String[] args) {
        //提高变量fw的作用域,让finall可以使用
        //变量在定义的时候,可以没有值,但是使用的时候必须有值
        //fw = new FileWriter("D:\\\\environment\\\\java_project\\\\javase\\\\g.txt",true);执行失败,fw没有值,fw.close会报错
        FileWriter fw = null;
        try {
            //可能会产生异常的代码
            fw = new FileWriter("W:\\\\environment\\\\java_project\\\\javase\\\\g.txt",true);
            for (int i = 0; i < 10; i++) {
                fw.write("HelloWorld"+i+"\\r\\n");
            }

        }catch (IOException e){
            //异常的处理逻辑
            System.out.println(e);
        }finally {
            //一定会执行的代码
            //创建对象失败了,fw的默认值是null,null不能调用方法,会抛出NullPointerException,需要增加一个判断,不是null再把资源释放
            if (fw!=null){
                try {
                    //fw.close方法声明抛出了IOException异常对象,所以我们就得处理这个异常对象,要么throws,要么try...catch
                    fw.close();//Alt+Enter,使用try...catch
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
  • JDK7的新特性
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/*
JDK7的新特性
在try的后边可以增加一个(),在括号中可以定义流对象
那么这个流对象的作用域就在try中有效
try中的代码执行完毕,会自动把流对象释放,不用写finally
格式:
    try(定义流对象;定义流对象...){
        可能会产出异常的代码
    }catch(异常类变量 变量名){
        异常的处理逻辑
    }
 */
public class Demo02JDK7 {
    public static void main(String[] args) {
        try (//1.创建一个字节输入流对象,构造方法中绑定要读取的数据流
             FileInputStream fis = new FileInputStream("D:\\\\environment\\\\java_project\\\\javase\\\\c.txt");
             //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
             FileOutputStream fos = new FileOutputStream("D:\\\\environment\\\\java_project\\\\javase\\\\d.txt");){

            //可能会产生异常的代码
            //一次读取一个字节写入一个字节的方式
            //3.使用字节输入流对象中的方法read读取文件
            int len = 0;
            while ((len = fis.read())!=-1){
                //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
                fos.write(len);
            }
        }catch (IOException e){
            //异常的处理逻辑
            System.out.println(e);
        }
    }
}
  • JDK9的新特性(使用不便)
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

/*
JDK9新特性
try的前边定义流对象
在try后边的()中可以直接引入流对象的名称(变量名)
在try代码执行完毕之后,流对象也可以释放掉,不用写finally
格式:
    A a = new A();
    B b = new B();
    try(a,b){
        可能会产出异常的代码
    }catch(异常类变量 变量名){
        异常的处理逻辑
    }
 */
public class Demo03JDK9 {
    public static void main(String[] args) throws IOException {
        //1.创建一个字节输入流对象,构造方法中绑定要读取的数据流
        FileInputStream fis = new FileInputStream("D:\\\\environment\\\\java_project\\\\javase\\\\c.txt");
        //2.创建一个字节输出流对象,构造方法中绑定要写入的目的地
        FileOutputStream fos = new FileOutputStream("D:\\\\environment\\\\java_project\\\\javase\\\\d.txt");

        try (fis;fos){
            //一次读取一个字节写入一个字节的方式
            //3.使用字节输入流对象中的方法read读取文件
            int len = 0;
            while ((len = fis.read())!=-1){
                //4.使用字节输出流中的方法write,把读取到的字节写入到目的地的文件中
                fos.write(len);
            }
        }catch (IOException e){
            System.out.println(e);
        }
        fos.write(1);//Stream closed
    }
}
作者:wangyudong
本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。

以上是关于IO流中的设计模式的主要内容,如果未能解决你的问题,请参考以下文章

IO流中「线程」模型总结

IO流的设计模式

二进制流中`open`和`io.BytesIO`之间的区别

设计模式------装饰者设计模式(IO流的应用)

javaEE面试宝典

设计模式之装饰模式