IO流和装饰模式

IO流和装饰者设计模式

IO流的概述和分类

解决的问题?

  • 把程序中存储在内存中的数据,写入到文件中
  • 把磁盘文件中存储的数据,读取到内存中

概述:

  • I流:把磁盘文件中存储的数据,读取到内存中(读)
  • O流:把内存中的数据,写入到磁盘文件中(写)

字节输出流

  • 以字节为单位,把内存中数据写入到磁盘文件中

步骤:

  1. 创建
    • 注意事项:如果文件不存在,就创建。如果文件存在就清空
  2. 操作(写或读)
    • 注意事项:写出的整数,实际写出的是整数在码表上对应的字符
  3. 关闭
    • 注意事项:每次使用完流必须要释放资源

字节流写数据的3种方式

方法名说明
void write(int b)一次写一个字节数据
void write(byte[] b)一次写一个字节数组数据
void write(byte[] b, int off, int len)一次写一个字节数组的部分数据

向文件中追加写入数据:(创建字节输出流对象时,不会清空文件中的原有内容)

FileOutputStream fos = new FileOutputStream("关联文件", true)

第二个参数true表示追加写入模式

字节输入流

  • 以字节为单位,把磁盘中数据读取到内存中

步骤:

  1. 创建
    • 注意:如果文件不存在,直接报错
  2. 读数据
    • 注意:读出来的是文件中数据的码表值
  3. 释放资源
    • 注意:每次使用完流必须要释放资源

案例:复制文件

import java.io.*;

public class Demo1 {
    public static void main(String[] args){
        String from = "from/a.jpg";
        String to = "to/a.jpg";
        try(
                InputStream is = new FileInputStream(from);
                OutputStream os = new FileOutputStream(to);
            )
        {
            int data = 0;
            while((data = is.read()) != -1){
                os.write(data);
            }
            System.out.println("从" + from + "-->" + to + ":Done");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Properties

在开发中,通常会使用到配置文件,而配置文件格式通常分为:

  1. XML文件
  2. properties文件

properties类的作用:

  • 读取开发中使用到的.properties配置文件
    • properties配置文件中的数据是以key/value形式体现
    • 把properties配置文件中的key,存储到Properties类中的key元素下
    • 把properties配置文件中的value,存储到Properties类中的value元素下

概述:

  • 是一个Map体系的集合类
  • Properties中有限IO相关的方法
  • 不需要加泛型,默认存储的是Object类型,但是工作中只存字符串

Properties和IO流结合的方法:

方法名说明
void load(InputStream inStream)以字节流形式,把文件中的键值对,读取到集合中
void load(Reader reader)以字符流形式,把文件中的键值对,读取到集合中
void store(OutputStream out, String comments)把集合中的键值对以字节流形式写入到文件中,参数二为注释
void store(Writer writer, String comments)把集合中的键值对以字符流形式写入文件中,参数二为注释

Properties作为集合的特有方法:

方法名说明
Object setProperty(String key, String value)设置结婚的键和值,都是String类型,相当于put方法
String getProperty(String key)使用此属性列表中指定的键搜索属性,相当于get方法
Set< String > stringPropertyNames()从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串,相当于keyset方法

案例:

需求:在properties文件中手动写上姓名和年龄,读取到集合中,将该数据封装成javabean对象

// 配置文件
name=zhangsan
age=18

// Test类
public class Test {
    private String name;
    private int age;

    public Test() {
    }

    public Test(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Test{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

// 测试类
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Properties;

/*
    在properties文件中手动写上姓名和年龄,读取到集合中,将该数据封装成javabean对象
 */
public class Demo3 {
    public static void main(String[] args) throws IOException {
        String path = "file/test.properties";
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream(path));

        Properties p = new Properties();
        p.load(bis);

        String name = p.getProperty("name");
        int age = Integer.parseInt(p.getProperty("age"));

        Test t = new Test(name, age);

        System.out.println(t.toString());
        
        bis.close();
    }
}

ResourceBundle工具类

API介绍

  • java.util.ResourceBundle是一个抽象类
  • 我们可以使用它的子类PropertyResourceBundle来读取以.properties结尾的配置文件

通过静态方法直接获取对象

  • static getBundle(String baseName) 直接获取默认语言环境下的属性资源

  • 参数注意:

    1. 属性集名称不含扩展名
    2. 属性集文件是在src目录中的
  • ResourceBundle中常用方法:

    方法名说明
    String getString(String key)通过建获取对应的值

字符流

字符流写数据的5种方式

方法名说明
void write(int c)写一个字符
void write(char[] cbuf)写入一个字符数组
void write(char[] cbuf, int off, int len)写入字符数组的一部分
void write(String str)写一个字符串
void write(String str, int off, int len)写一个字符串的一部分
flush()刷新流,还可以继续写数据
close()关闭流,释放资源,但是在关闭之前会先刷新流,一旦关闭,就不能再写数据

字符流读数据的2种方式

方法名说明
int read()一次读一个字符数据
int read(char[] cbuf)一次读一个字符数组数据

缓冲区流

字符缓冲流

  • BufferedWriter:可以将数据高效的写出
  • BufferedReader:可以将数据高效的读入到内存
  • 注意:字符缓冲流不具备读写功能,只提供缓冲区,真正读写还是需要依赖与构造接受的基本的字符流

构造方法:

  • BufferedWriter(Writer out)
  • BufferedReader(Reader in)

练习:

  • 需求:使用字符缓冲流复制纯文本文件

    import java.io.*;
    
    public class Demo4 {
        public static void main(String[] args) throws IOException {
            BufferedReader br = new BufferedReader(new FileReader("file/小说.txt"));
            BufferedWriter bw = new BufferedWriter(new FileWriter("file/copy.txt"));
            int len = -1;
            while ((len = br.read()) != -1){
                bw.write(len);
            }
    
    
            br.close();
            bw.close();
        }
    }
    

字符缓冲流特有功能

BufferedWriter:

  • void newLine():写一个分隔符,会根据操作系统的不同,写入不同的行分隔符

BufferedReader:

  • public String readLine():读取文件一行数据,不包含换行符号,读到文件的末尾返回null

案例:

读取文件中的数据排序后再次写到本地

  • 需求:读取文件中的数据,33 22 11 55 44
  • 排序后:11 22 33 44 55 再次写到本地文件
import java.io.*;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/*
   需求:读取文件中的数据:33 22 11 55 44
   排序后进行写入到本地文件
 */
public class Demo2 {
    public static void main(String[] args) {
        try{
            BufferedReader br = new BufferedReader(new FileReader("from/helloworld.txt"));
            String s = br.readLine();
            String[] strArr = s.split(" ");
            List<Integer> list = Arrays.stream(strArr).map(str -> Integer.parseInt(str)).sorted().collect(Collectors.toList());
            String s1 = list.toString();
            s1 = s1.substring(1, s1.length() - 1).replace(",", " ");
            System.out.println(s1);
            BufferedWriter bw = new BufferedWriter(new FileWriter("from/helloworld.txt"));
            bw.write(s1);

            br.close();
            bw.close();
        } catch (IOException e){
            e.printStackTrace();
        }
    }
}

转换流

转换流的作用:读写特定编码表的文件

  • FileReader类默认是UTF-8编码表(无法读取GBK编码表的文件)

输入流:InputStreamReader

  • 自身没有读数据的能力,需要依赖字节输入流

输出流:OutputStreamWriter

  • 自身没有写数据的能力,需要依赖字节输出流

练习

需求1:使用转换流,把数据按照GBK的编码写入文件,在使用GBK的编码读取数据

import java.io.*;

public class Demo5 {
    public static void main(String[] args) throws IOException {
        String path = "file/GBK.txt";
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(path), "gbk");
        osw.write("我是gbk编码的文件");

        osw.close();
        InputStreamReader isr = new InputStreamReader(new FileInputStream(path), "gbk");
        int len = 0;
        while ((len = isr.read()) != -1){
            System.out.print((char)len);
        }

        isr.close();
    }
}

转换流就是来进行字节流和字符流之间转换的

需求2:将模块根目录中GBK编码的文本文件,转换为UTF-8编码的文本文件

import java.io.*;

public class Demo6 {
    public static void main(String[] args) throws IOException {
        String path = "file/GBK.txt";
        String target = "file/utf8.txt";
        InputStreamReader isr = new InputStreamReader(new FileInputStream(path), "gbk");
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(target), "utf8");
        int len = -1;
        while ((len = isr.read()) != -1){
            osw.write(len);
        }

        isr.close();
        osw.close();
    }
}

对象操作流(序列化流)

对象操作流的特点:可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。

对象操作流的应用:

  • 把程序中创建的对象,先写入到文件中(持久化存储)
  • 即使重启计算机后,通过程序读取文件中存储的对象,可以重新把对象加载到内存中

对象操作输入流(ObjectInputStream)(反序列化):从文件中读取存储的对象

对象操作输出流(ObjectOutputStream)(序列化):把内存中创建的对象,写入到文件中或在网络中传输

注意:如果一个类对象想要被序列化,那么此类需要实现Serializable接口

Serializable接口的含义:

  1. 是一个标记型接口,里面没有任何抽象方法
  2. 只要一个类实现了此接口,表示此类的对象可以被序列化

练习:使用对象操作输出流,把一个User对象写入文件中,在使用对象操作输入流读取对象数据

// 将对象写入文件中
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Test1 {
    public static void main(String[] args) throws IOException {
        String path = "file/user.txt";
        User u = new User("张三", "zc123");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(path));
        oos.writeObject(u);

        oos.close();
    }
}

// 将文件反序列化,读入到内存
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class Test2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("file/user.txt"));

        Object user = ois.readObject();

        User u = (User)user;
        System.out.println(u.getName() + " " + u.getPassword());

        ois.close();
    }
}

用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会抛出InvalidClassException异常

解决方法:

  • 给对象所属的类加一个serialVersionUID
    • private static final long serialVersionUID = 42L
  • 如果一个对象中的某个成员变量的值不想被序列化
    • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

打印流

  • 作用:

    1. 在写入数据后可以实现自动换行

    2. 通常用于日志记录

  • 类:PrintStream

    public PrintStream(String filePath)
    
  • 常用方法:

    • public void println(数据) // 打印后换行
    • public void print(数据) // 打印不换行

装饰者设计模式

概述:

  • 装饰模式指的是在不改变原类,不使用继承的基础上,动态的扩展一个对象的功能
  • 不使用继承技术扩展功能,可以降低耦合

使用原则:

  • 装饰类和被装饰类需要有共同的父类型
    • 在之前学习过的BufferedWriter和FileWriter就是装饰设计模式
    • BufferedWriter的父类为Writer
    • FileWriter的父类也是Writer
    • 我们把FileWriter的对象传递到BufferedWriter的构造中,那么可以理解为BufferedWriter是装饰类,FileWriter是被装饰类
    • BufferedWriter对FileWriter的功能做了增强
  • 装饰类的构造要接收被装饰类的对象
    • FileWriter fw = new FileWriter(“路径”);
    • BufferedWriter bw = new BufferedWriter(fw);
  • 在装饰类中把要增强扩展的功能进行扩展
    • BufferedWriter和FileWriter的功能一样,都具备Writer中写数据的功能
    • 但是BufferedWriter提供了缓冲区,相当于对FileWriter功能做了扩展
  • 对于不要增强的功能直接调用
    • 不需要增强的功能直接继承父类的即可
  • 8
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值