IO字节流,字符流Reader 和 Writer 抽象类,转换流,对象流,关于流的总结,方法的多参数,Properties

一、IO字节流

1.1 简介

  1. 以内存为自我角色:对硬盘写、输出–>Ouput和writer,读取硬盘、读、输入–>Input和reader
  2. IO流的分类:流向的划分–>输入流和输出流、类型的划分–>字节流和字符流
  3. 万物皆字节,电脑上的任意文件都可用字节来表示
  4. 字符流底层也是用字节流进行操作的,只是通过编码表将字节转换为字符
  5. 注意点:A.所有的流资源都是Java.io下的 B.使用流资源可能会出现异常 C.所有的流资源使用后都要进行关闭,输出和写需要进行流的刷新

1.2 OutputStream抽象类及子类FileOutputStream

  1. OutputStrea此类是所有的字节输出流的超类
  2. FileOutputStream 用于将图像视频之类的原始字节的流操作
1.2.1 FileOutputStream写入文件代码实现
package qf22020305_IOStream.Demo02;

import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Scanner;

/**
 * 文件输出流 从内存写入到硬盘中
 */
public class Test01 {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        Scanner sc = new Scanner(System.in);
        System.out.println("输入要写的内容:");
        String next = sc.nextLine();
        byte[] b = next.getBytes();//转变为byte数组
        try {
            //true在文件末尾添加
            //实例化对象做的三件事:实例化、如果无这个文件则创建空的文件(路径必须先有这个文件名)、将内存地址指向这个文件
            fos = new FileOutputStream("D:\\IODemo\\test.txt", true);
            //以整个数组写入
            fos.write(b);
            fos.flush();
            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

1.3 InputStream抽象类及子类FileInputStream

  1. InputStream抽象类
  2. FileInputStream 是所有字节输入流的超类,用于读取诸如图像数据之类的原始字节流 用于操作字节
1.3.1 FileInputStream读取文件代码实现
package qf22020305_IOStream.Demo01_File;

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

/**
 * 文件输入流  从硬盘写入到内存中
 */
public class Test01 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("D:\\IODemo\\test.txt");
            //byte[] b = new byte[1024 * 1024];
            byte[] b = new byte[fis.available()];  //根据文件大小定义实际数组大小,优化
            //1024*1024==1,048,576(b字节)/1000==1048kb
            //1048kb/1000 == 每次读取1M大小
            int temp;
            while ((temp = fis.read(b)) != -1) {
                //b存储读取来的内容,temp是有效读取字节的总数
                System.out.println(new String(b, 0, temp));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();  //关闭通道
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

1.4 上诉两个流进行文件复制

package qf22020305_IOStream.Demo03;

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

//文件的复制
public class Test03 {
    public static void main(String[] args) {
        FileInputStream fis = null;
        FileOutputStream fos = null;
        try {
            fis = new FileInputStream("D:\\IODemo\\3.jpg");
            fos = new FileOutputStream("C:\\3.jpg", true);
            byte[] b = new byte[1024 * 1024];
            int temp;
            while ((temp = fis.read(b)) != -1) {
                //temp 代表的是读取到有效字节的个数
                fos.write(b, 0, temp);
            }
            fos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

1.5 写入的原理

在这里插入图片描述

1.6 写入内容说明

  1. 问题:写入的字节是 97 98 99 使用记事本打开就是 a b c
  2. 原因:记事本打开的文件的时候,如果字节的范围是 0-127使用ASCII码来表示,操作其范围则用记事本默认的码来表示

1.7 Buffered Out/In putStream高效流

  1. BufferedOutputStream和BufferedInputStream
  2. 内部创建一个缓冲数组,内置默认长度都为8192
  3. 缓冲流效率高的原因:底层封装一个缓冲的数组,会一次读取8192个字节数组,存入缓冲数组中,避免对次交互,提高效率
  4. 高效率本身没有对文件内容操作的功能,只提供一个缓冲数组,对Out/In putStream的进行了功能的封装
  5. 流的关闭都是从下往上进行关闭(最后的使用先关闭)
1.7.1 高效流对文件的复制
package qf22020305_IOStream.Demo04_Buffer;
import java.io.*;

//对图片的复制转移
public class Demo01 {
    public static void main(String[] args) {
        BufferedInputStream bis = null;
        BufferedOutputStream bos = null;
        int temp;
        try {
            bis = new BufferedInputStream(new FileInputStream("D:\\IODemo\\3.jpg"));
            bos = new BufferedOutputStream(new FileOutputStream("D:\\3.jpg"));
            while ((temp = bis.read()) != -1) {
                bos.write(temp);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bos != null) {
                    bos.close();
                }
                if (bis != null) {
                    bis.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

二、字符流Reader 和 Writer 抽象类

2.1 简介

  1. 用于字符的读取,纯文本文件用的比较多
  2. 使用字符流的原因:使用字节流读取字符,需要将读取的内容来进行转换,转换错误可能会出现乱码 ,使用字节操作字符比较麻烦
  3. 字符流=字节流+编码表
  4. 任何的编码表下,中文的第一个字节都是负数,根据这个规律来进行转换
2.1.1 Writer写入代码实现
package qf22020305_write_read;

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

//写入字符文本
public class Write01 {
    public static void main(String[] args) throws IOException {
        Writer writer = new FileWriter("writer.txt");
        writer.write(new char[]{'1','2'});
        writer.write("你好啊");
        writer.write('b');
        writer.flush();
        writer.close();
    }
}

2.1.2 Reader读取代码实现
package qf22020305_write_read;

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.Reader;

public class Read01 {
    public static void main(String[] args) throws FileNotFoundException {
        Reader reader = new FileReader("writer.txt");
        char[] ch = new char[1024];
        try {
            int temp;
            while ((temp = reader.read(ch)) != -1) {
                new String(ch, 0, temp);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 编码表

  1. 在这里插入图片描述

2.3 字符流不能复制音频文件的原因

  1. 原因:因为使用字符流读取文件的时候,需要依赖于编码表来进行转换,然而音频文件不能使用常规编码表来进行转换
  2. 图:在这里插入图片描述

2.4 字符高效流

  1. 前面字节高效流语法基本一致,略…

三、转换流

3.1 InputStreamReader 字节流转字符流

  1. 是字节流通向字符流的桥梁,因为字节进行存储,然后读取出来用字符进行表述
  2. 它使用指定的charset 读取字节并将其解码为字符,可以设置其编码格式
  3. 为达到最高效率,可考虑BufferedReader
3.1.1 InputStreamReader读取文件
package qf22020307_InputStreamReader_outputStreamWriter;

import java.io.*;

/**
 * 读取文件开发常用,可以设置编码格式
 */
public class Demo01 {
    public static void main(String[] args) {
        try {
            BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\IODemo\\test.txt"), "UTF-8"));
            int temp;
            while ((temp = br.read()) != -1) {
                System.out.println((char) temp);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.2 OutputStreamWriter 字符流转字节流

  1. OutputStreamWriter 是字符流通向字节流的桥梁,因为使用字符写入,转为字节进行存储
  2. 使用指定的 charset 将要写入流中的字符编码成字节 可以设置其编码表(编码格式)
  3. 为了获得最高效率,可考虑将 OutputStreamWriter 包装到 BufferedWriter 中
3.2.1 利用两个转换流复制文件
package qf22020307_InputStreamReader_outputStreamWriter;
import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 一行一行读取,在反转写入文件,需要格式不变
 */
public class Demo02 {
    public static void main(String[] args) {
        BufferedWriter bw = null;
        BufferedReader br = null;
        List<String> list = new ArrayList<>();
        try {
            br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\IODemo\\test.txt"), "UTF-8"));
            String temp;
            while ((temp = br.readLine()) != null) {
                list.add(temp);
            }
            bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:\\test.txt"), "UTF-8"));
            for (int i = list.size() - 1; i >= 0; i--) {
                bw.write(list.get(i));
                bw.newLine();//底层写入一个换行符
            }
            bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
                if (br != null) {
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

3.2 close()与flush()的比较

  1. flush() 将缓冲区的数据刷新到硬盘中
  2. close() 关闭流资源,close() 方法的底层会调用flush()方法
  3. 区别:调用 close() 方法之后,流资源就不能使用,调用 flush() 方法之后 流资源是可以使用

四、对象流

4.1 序列化概念

  1. 使用场景:在读写存储文件的时候或在网络通信传输对象时
  2. 序列化机制:使用一个序列化的字节(唯一标识)来表示文件中的对象(属性值、方法)
  3. 什么是序列化:将对象写入到文件中的过程就是序列化,将对象进行流化,加快对象写入到文件的速度
  4. 什么反序列化:将文件中序列化的对象,读取的过程就是序列化,加快读取的速度
  5. 只支持将实现了Serializable 接口的对象写流中

4.2 ObjectOutputStream和ObjectIntputStream

  1. 将实现了序列化接口的多个对象分别写入文件和读取出来
  2. 定义一个实体类Person
package qf22020307_objectInputStream_objectOutputStream.Demo02;

import java.io.Serializable;

/**
 * 实现序列化对象
 * @author 18480
 */
public class Person implements Serializable {
    private String name;
    private Integer age;

    public Person() {
    }

    ;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

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

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


  1. 测试类
package qf22020307_objectInputStream_objectOutputStream.Demo02;

import java.io.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 自定义对象的序列化和反序列化
 */
public class Demo01 {
    public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("小明", 20));
        list.add(new Person("小李", 21));
        list.add(new Person("小张", 22));
        addObject(list);
        getObject();
    }

    public static void addObject(List<Person> list) {
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("D:\\test.txt"));
            oos.writeObject(list);
            oos.writeObject(null);
            oos.flush();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (oos != null) {
                    oos.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void getObject() {
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream("D:\\test.txt"));
            Object o;
            while ((o = ois.readObject()) != null) {
                System.out.println(o);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            try {
                if (ois != null) {
                    ois.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

五、关于流的总结

5.1 字符流、字节流、缓冲流、对象流总结

  1. 在这里插入图片描述

六、方法的多参数

6.1 简介

  1. 使用场景:在定义方法不确定其参数的个数的时候,就可以使用多参数
  2. 多参数的本质就是一个数组,所以可以直接传入一个数组
  3. 多参数的类型都必须一致
  4. 多参数可以有其他的参数,但多参数必须放在最后面

6.2 案例

package qf22020307_objectInputStream_objectOutputStream;

/**
 * @author 18480
 */
public class Demo03 {
    public static void main(String[] args) {
        add("加法", 10, 10, 10, 10, 10);
    }

    public static Integer add(String s, Integer... n) {
        int sum = 0;
        for (int i = 0; i < n.length; i++) {
            sum += n[i];
        }
        System.out.println(s);
        return sum;
    }
}

七、Properties

7.1 简介

  1. 具有属性集将数值以字符串键值对的方式存储、具有持久化保持到文件中
  2. 继承Hashtable

7.2 案例综合应用

  1. 问题:利用properties方法、反射机制、双重锁设计模式设计属性读取工具类,读取到properties的对象属性,来通过反射创建对象
  2. 创建属性配置文件在这里插入图片描述
  3. 创建一个Student实体类
package qf22020310_properties;

public class Student {

    private Student() {}

    public void add(String name,Integer age){
        System.out.println("姓名:"+name+"\t年龄:"+age);
    }
    public void get(){
        System.out.println("无参数get方法");
    }
}


  1. 封装一个工具类
package qf22020310_properties;

import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

/**
 * 利用反射和双重锁设计模式,实现加载资源文件工具类
 */
public class PropertiesUtils {
    private static PropertiesUtils pu;
    Properties p;

    private PropertiesUtils() {
        try {
            p = new Properties();
            //利用本身类对象方法加载资源文件
            InputStream ras = PropertiesUtils.class.getResourceAsStream("properties.properties");
            //加载到Properties
            p.load(ras);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //双重锁设计模式
    public static synchronized PropertiesUtils getInstance() {
        if (pu == null) {
            synchronized (PropertiesUtils.class) {
                if (pu == null) {
                    pu = new PropertiesUtils();
                }
            }
        }
        return pu;
    }

    public String getValue(String name) {
        return p.getProperty(name);
    }
}

  1. 通过反射利用类属性值构造对象
package qf22020310_properties;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.List;

/**
 * @author 18480
 */
public class ReflectInstance {

    public void createInstance() throws ClassNotFoundException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
        PropertiesUtils instance = PropertiesUtils.getInstance();
        //工具类获取参数
        String classname = instance.getValue("classname");
        String methodname = instance.getValue("methodname");
        String par = instance.getValue("par");

        //获取aClass类操作对象
        Class<?> aClass = Class.forName(classname);
        //获取单个构造方法对象
        Constructor<?> constructor = aClass.getDeclaredConstructor();
        //暴力去除私有,实例化对象
        constructor.setAccessible(true);
        //利用构造对象实例化类对象
        Object o = constructor.newInstance();
        //返回要调用方法的参数类型集
        Class[] classes = classes(par);

        if (classes == null || classes.length <= 0) {
            //创建一个方法对象,调用invoke启动方法
            aClass.getDeclaredMethod(methodname).invoke(o);
        } else {
            aClass.getMethod(methodname, classes).invoke(o, "小明", 22);
        }
    }

    public static Class[] classes(String par) {

        if (par == null || "".equals(par)) {
            return null;
        }
        String[] split = par.split(",");
        List<Class> list = new ArrayList<>();
        for (String s : split) {
            if (s.equals("String")) {
                list.add(String.class);
            } else if (s.equals("Integer")) {
                list.add(Integer.class);
            } else {
                list.add(Object.class);
            }
        }
        return list.toArray(new Class[list.size()]);
    }
}

  1. 测试类
package qf22020310_properties;

import org.junit.Test;

import java.lang.reflect.InvocationTargetException;

/**
 * @author 18480
 */
public class Test01 {

    @Test
    public void test() throws ClassNotFoundException, InvocationTargetException, InstantiationException, NoSuchMethodException, IllegalAccessException {
        ReflectInstance ri = new ReflectInstance();
        ri.createInstance();
    }
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT阿生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值