Java IO(2)—缓冲字符流以及IO中的装饰者模式

简介: 详细介绍了Java IO中的缓冲字符流BufferedWriter、BufferedReader、LineNumberReader的使用方式,以及Java IO中的装饰者模式。

1 BufferedWriter缓冲区字符输出流
public class BufferedWriter
extends Writer

特点:

自带缓冲区。缓冲区可以实现自动扩容,提高了写的效率。
特有的newLine(); 方法可以写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,并且不一定是单个新行 ('\n') 符,可以实现跨平台。

1.1 构造器

创建一个使用默认大小输出缓冲区的缓冲字符输出流。Writer类,是一个抽象类,应该传递的是该抽象类的实现类对象。

创建一个使用给定大小输出缓冲区的新缓冲字符输出流。在大多数情况下,默认值就足够大了,因此该构造器一般不用。sz:输出缓冲区的大小,是一个正整数

为什么要传递一个流对象而不传递file或者路径呢?

因为缓冲区流仅仅多提供一个缓冲区的功能,是为了高效而设计的。真正的读写还是靠基本的流对象的方法实现!(即装饰设计模式)。并且关闭外层流就相当于关闭了内层的流!

1.2 API方法
大多数方法均继承或重写自直接父类Writer。

特有的方法:

写入一个行分隔符,即提供了一个换行的方法。

2 BufferedReader缓冲区字符输入流
public class BufferedReader
extends Reader
特点:

自带缓冲区。可以实现自动扩容,提高了读的效率。
提供了readLine方法,一次可以读取一行数据。
2.1 构造器

创建一个使用默认大小输入缓冲区的缓冲字符输入流。传递一个实现Reade实现类。

2.2 API方法
大部分方法都继承和重写自父类Reader。

特有的方法:

能够一个读取一行数据,提高了读的效率。返回包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回null。

2.2.1 readLine方法实现原理
BufferedReader构造的时候,包装了一个类:FileReader。

其底层是调用了FileReader#read()方法,一次读取一个字符,将读取的字符放在缓冲区,当读取到换行符号的时候,将一行数据返回到内存当中,“\r’的ASCII码为13,“\n”的ASCII码为10。

3 案例
3.1 写入文件
/**

  • @author lx

*/
public class BufferedWriterDemo01 {

public static void main(String[] args) {
    BufferedWriter bw = null;
    try {
        bw = new BufferedWriter(new FileWriter("C:\\Users\\lx\\Desktop\\test.txt"));
        bw.write(97);
        bw.newLine();
        bw.write("nishuo");
        bw.newLine();
        bw.write("你说啥你", 1, 2);
        bw.newLine();
        char[] ch = new char[]{'1', '-', '\t', ' '};
        bw.write(ch);
        bw.write(ch, 1, 2);
        bw.flush();

    } catch (IOException e) {
        e.printStackTrace();
    } finally {

        if (bw != null) {
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

}
3.2 读取文件
/**

  • @author lx

*/
public class BufferedReaderDemo01 {

public static void main(String[] args) throws IOException {
    BufferedReader br = new BufferedReader(new FileReader("C:\\Users\\lx\\Desktop\\test.txt"));
    String str;
    //一次读取一行
    while ((str = br.readLine()) != null) {
        System.out.println(str);
    }
    br.close();
}

}
3.3 拷贝文件
使用带有缓冲区的流,实现文件的copy。

/**

  • @author lx

*/
public class CopyFile {

public static void main(String[] args) {
    BufferedReader br = null;
    BufferedWriter bw = null;
    try {
        br = new BufferedReader(new FileReader("C:\\Users\\lx\\Desktop\\test.txt"));
        bw = new BufferedWriter(new FileWriter("C:\\Users\\lx\\Desktop\\test2.txt"));
        String str;
        //读取  写入
        while ((str = br.readLine()) != null) {
            bw.write(str);
            bw.newLine();
            bw.flush();
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (br != null) {
            try {
                br.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        if (bw != null) {
            try {
                bw.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

}
4 LinenumberReader跟踪行号缓冲字符输入流
public class LineNumberReader
extends BufferedReader
这是Reader的又一个包装类,其特点是:

是BufferedReader子类,具备缓冲区,具有有一次读取一行的方法。
具有获得行号和设置行号的方法
setLineNumber(int num); 设置行号
getLineNumber(); 获得当前行的行号,返回的是int 类型的值
注意:循环之前设置行号后,循环后获得第一行字符串行号是设置的行号+1。

构造器:

5 装饰设计模式
作用:对某个类的某个方法进行功能性的增强。

装饰模式的实现步骤:

定义一个类:装饰类。实现被装饰类的最上层的接口或类,让装饰类和被装饰类有共同的行为,即同一体系。
引入一个被装饰类,作为全局变量。这里选择reader抽象类,此后reader的子类都能使用。
将被装饰的类作为参数传递给装饰类的构造器,用于初始化变量。
对该被装饰类的某个方法进行功能性的增强。
5.1 Java IO中的装饰模式
BufferedReader类对Reader类的read()方法进行包装,它是基于read方法,并对read方法进行了功能的增强,形成readLine()方法。实际上整个IO流系统用的最多的就是装饰设计模式。

LineNumberReader类对BufferedReader类的readLine()方法进行包装,让其在调用时能够对行号计数,添加了获得行号和设置行号的方法。

5.2 装饰设计模式和继承的异同点
相同点:装饰设计模式和继承都是为了对某个类的行为或者属性的扩展(增强)。

不同点:

当我有2个功能差不多 但是操作数据对象不一样的功能的子类 想要扩展一样的功能的时候,继承需要给每个子类进行扩展,并且需要继承每一个方法,而装饰设计模式,只要写一份,把父类传进来就扩展了制定的方法。

这样如果为了某个功能的扩展而使用继承那么可能产生很多子类,那系统体系是非常臃肿的,并且继承的类之间耦合度高,不利于扩展。

装饰模式使用对象的依赖关系代替继承关系,允许系统动态决定“贴上”一个需要的“装饰”,或者除掉一个不需要的“装饰”。继承关系则不同,继承关系是静态的,它在系统运行前就决定了。因此装饰设计模式更加灵活,同时避免类型体系的快速膨胀降低了类与类之间的关系(即所谓的耦合性,继承的一个缺点就是耦合性太强)。

装饰模式符合程序设计开闭原则就是说对扩展开放,对修改关闭。在对某个类需要进行拓展的时候,最好不要修改原有的代码,而是要扩展原有代码,使用装饰设计模式就可以做到,使程序的扩展性好,易于维护和升级。

建议:通常使用装饰设计模式,少用继承。

5.3 案例
5.3.1 自定义读取一行的方法
自定义MyBufferedReader类:对FileReader类进行了包装并对FileReader 的read()方法进行功能性的增强,完成readLine方法的效果。

/**

  • @author lx

*/
public class MyBufferedReader extends Reader {

private Reader reader;

public MyBufferedReader(Reader reader) {
    this.reader = reader;
}

public String MyReaderLine() throws IOException {
    int read;
    StringBuilder sb = new StringBuilder();

    while ((read = reader.read()) != -1) {
        //判断是否等于13,13是\r的Unicode码,即回车符,回车符不加
        if (read == 13) {
            continue;
        }
        //判断是否是换行符,是换行就返回,不加,否则就添加
        if (read == 10) {
            return sb.toString();
        } else {
            sb.append((char) read);
        }
    }
    //到这说明读到了末尾,但是末尾可能没有\n,因此这里需要额外判断
    if (sb.length() > 0) {
        return sb.toString();
    }
    //走到这里说明,读到了末尾.并且缓冲数组的内容已经全部返回,读取完毕
    return null;
}

@Override
public int read(char[] cbuf, int off, int len) throws IOException {
    return reader.read(cbuf, off, len);
}

@Override
public void close() throws IOException {
    reader.close();
}

}
5.3.2 自定义设置和获得行号的方法
自定义MyLineNumberReader类,使用装饰设计模式,设计设置和获得行号并且读取一行的方法。

/**

  • @author lx

*/
public class MyLineNumberReader extends Reader {


private Reader reader;
private int num;

public MyLineNumberReader(Reader reader) {
    this.reader = reader;
}

public void setMyLineNumber(int num) {
    this.num = num;
}

public int getLineNumber() {
    return num;
}

public String myReadLine() throws IOException {
    StringBuilder sb = new StringBuilder();
    int read;
    while ((read = reader.read()) != -1) {
        if (read == '\r') {
            continue;
        }
        if (read == '\n') {
            num++;
            return sb.toString();
        } else {
            sb.append((char) read);
        }
    }
    if (sb.length() > 0) {
        num++;
        return sb.toString();
    }
    return null;
}

@Override
public int read(char[] cbuf, int off, int len) throws IOException {
    return reader.read(cbuf, off, len);
}

@Override
public void close() throws IOException {
    reader.close();
}

}

作者:刘Java
链接:https://juejin.cn/post/7002042608205692958
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

相关文章
|
2月前
|
存储 缓存 Java
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
这篇文章详细介绍了Java中的IO流,包括字符与字节的概念、编码格式、File类的使用、IO流的分类和原理,以及通过代码示例展示了各种流的应用,如节点流、处理流、缓存流、转换流、对象流和随机访问文件流。同时,还探讨了IDEA中设置项目编码格式的方法,以及如何处理序列化和反序列化问题。
86 1
java基础:IO流 理论与代码示例(详解、idea设置统一utf-8编码问题)
|
2月前
|
Java 数据处理 开发者
揭秘Java IO流:字节流与字符流的神秘面纱!
揭秘Java IO流:字节流与字符流的神秘面纱!
39 1
|
2月前
|
自然语言处理 Java 数据处理
Java IO流全解析:字节流和字符流的区别与联系!
Java IO流全解析:字节流和字符流的区别与联系!
95 1
|
2月前
|
Java
Java 中 IO 流的分类详解
【10月更文挑战第10天】不同类型的 IO 流具有不同的特点和适用场景,我们可以根据具体的需求选择合适的流来进行数据的输入和输出操作。在实际应用中,还可以通过组合使用多种流来实现更复杂的功能。
58 0
|
2月前
|
存储 Java 程序员
【Java】文件IO
【Java】文件IO
38 0
|
4月前
|
存储 Java
【IO面试题 四】、介绍一下Java的序列化与反序列化
Java的序列化与反序列化允许对象通过实现Serializable接口转换成字节序列并存储或传输,之后可以通过ObjectInputStream和ObjectOutputStream的方法将这些字节序列恢复成对象。
|
5月前
|
Java 大数据
解析Java中的NIO与传统IO的区别与应用
解析Java中的NIO与传统IO的区别与应用
|
3月前
|
Java 大数据 API
Java 流(Stream)、文件(File)和IO的区别
Java中的流(Stream)、文件(File)和输入/输出(I/O)是处理数据的关键概念。`File`类用于基本文件操作,如创建、删除和检查文件;流则提供了数据读写的抽象机制,适用于文件、内存和网络等多种数据源;I/O涵盖更广泛的输入输出操作,包括文件I/O、网络通信等,并支持异常处理和缓冲等功能。实际开发中,这三者常结合使用,以实现高效的数据处理。例如,`File`用于管理文件路径,`Stream`用于读写数据,I/O则处理复杂的输入输出需求。
219 12
|
4月前
|
Java 数据处理
Java IO 接口(Input)究竟隐藏着怎样的神秘用法?快来一探究竟,解锁高效编程新境界!
【8月更文挑战第22天】Java的输入输出(IO)操作至关重要,它支持从多种来源读取数据,如文件、网络等。常用输入流包括`FileInputStream`,适用于按字节读取文件;结合`BufferedInputStream`可提升读取效率。此外,通过`Socket`和相关输入流,还能实现网络数据读取。合理选用这些流能有效支持程序的数据处理需求。
50 2
|
4月前
|
XML 存储 JSON
【IO面试题 六】、 除了Java自带的序列化之外,你还了解哪些序列化工具?
除了Java自带的序列化,常见的序列化工具还包括JSON(如jackson、gson、fastjson)、Protobuf、Thrift和Avro,各具特点,适用于不同的应用场景和性能需求。