IO流详解

IO流

先决条件:掌握File类
File类是让Java虚拟机知道文件的抽象路径,包括对文件或文件夹本身的操作。IO流则是对文件中数据的读写操作,这篇文章则是详细介绍IO流

  • I表示input,是数据从硬盘进内存的过程,称之为读
  • O表示output,是数据从内存到硬盘的过程,称之为写
  • 在数据传输的过程中,是谁在读?是谁在写?这个参照是谁
    • IO的数据传输,可以看作是一条数据的流动,按照流动的方向,以内存为参照物进行读写操作
    • 简单来说:内存在读,内存在写

IO流的分类:
什么是纯文本文件,用windows记事本打开能读的懂,那么这样的文件就是纯文本文件

  • 1.按流向分
    • 输入流
    • 输出流
  • 2.按数据类型分
    • 字节流(操作非纯文本文件,如音频、视频和图片
    • 字符流 (操作纯文本文件,如java文件,txt文件等

1 字节流

1.1 字节流写数据

创建字节输出流对象,

  • 1.如果文件存在,会帮我们创建
  • 2.如果文件不存在,会把文件清空

1.11 字节流的流程

  • 创建字节输出流对象
  • 调用字节输出流对象的写数据方法
  • 释放资源

1.12 字节流写数据的三种方式

方法名说明
void write(int b)将指定的字节写入此文件输出流 一次写一个字节数据
void write(byte[] b)将 b.length字节从指定的字节数组写入此文件输出流 一次写一个字节数组数据
void write(byte[] b, int off, int len)将 len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流 一次写一个字节数组的部分数据

1.13 字节流写数据的常见问题

字节流写数据如何实现换行

  • windows:\r\n
  • linux:\n
  • mac:\r

字节流写数据如何实现追加写入

  • public FileOutputStream(String name,boolean append)
  • 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头

1.14 字节流写数据加异常处理

异常处理格式

  • try-catch-finally

    try{
    	可能出现异常的代码;
    }catch(异常类名 变量名){
    	异常的处理代码;
    }finally{
    	执行所有清除操作;
    }
    
  • finally特点

    • 被finally控制的语句一定会执行,除非JVM退出

1.2 字节流读数据

字节输入流

  • FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名

字节输入流读取数据的步骤

  • 创建字节输入流对象
  • 调用字节输入流对象的读数据方法
  • 释放资源

一次读一个字节数组的方法

  • public int read(byte[] b):从输入流读取最多b.length个字节的数据
  • 返回的是读入缓冲区的总字节数,也就是实际的读取字节个数

2 字节缓冲流

2.1 字节缓冲流构造方法

字节缓冲流介绍

  • BufferOutputStream:该类实现缓冲输出流.通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用(减少硬盘到内存的次数)
  • BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组.当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节

2.2 字节缓冲流例子

public class BufferStreamDemo1 {
    public static void main(String[] args) throws IOException {

        //字节缓冲输出流
        BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(new FileOutputStream("C:\\b.txt"));
        //写数据
        bufferedOutputStream.write("hello\r\n".getBytes());
        bufferedOutputStream.write("world\r\n".getBytes());
        //释放资源
        bufferedOutputStream.close();

        //字节缓冲输入流
        BufferedInputStream bufferedInputStream = new BufferedInputStream(new FileInputStream("C:\\b.txt"));
        //一次读取一个字节数组数据
        byte[] bys = new byte[1024];
        int len;
        while ((len = bufferedInputStream.read(bys))!=-1){
            System.out.println(new String(bys,0,len));
        }

    }
}

3 字符流

3.1 编码表

什么是字符集

  • 是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
  • 计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套编码。常见字符集有ASCII字符集、GBK字符集、Unicode字符集等
  • GBK 一个文字两个字节
  • Unicode码表中的UTF-8 编码格式一个文字三个字节

3.2 字符串中的编码问题

  • 相关方法
方法名说明
byte[] getBytes()使用平台的默认字符集将该 String编码为一系列字节
byte[] getBytes(String charsetName)使用指定的字符集将该 String编码为一系列字节
String(byte[] bytes)使用平台的默认字符集解码指定的字节数组来创建字符串
String(byte[] bytes, String charsetName)通过指定的字符集解码指定的字节数组来创建字符串
  • 代码
public class FileWriterDemo1 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        //定义一个字符串
        String s = "中国";
        byte[] bytes = s.getBytes();
        System.out.println(Arrays.toString(bytes));
        byte[] bytes1 = s.getBytes("gbk");
        System.out.println(Arrays.toString(bytes1));
        //IDE默认的使用是Unicode码表的UTF-8编码格式
        String ss = new String(bytes1);
        System.out.println(ss);
        String ss2 = new String(bytes1,"UTF-8");
        System.out.println(ss2);
        //出现乱码的原因是编码和解码格式不一致
        String ss3 = new String(bytes1,"GBK");
        System.out.println(ss3);
    }
}

3.3 字符流写数据

字节流可以操作所有的文件,那么为什么还要使用字符流呢?

  • 介绍
    Writer: 用于写入字符流的抽象父类
    FileWriter: 用于写入字符流的常用子类
    简单理解:字符流=字节流+编码
  • 构造方法
方法名说明
FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象
FileWriter(File file, boolean append)根据给定的 File 对象构造一个 FileWriter 对象
FileWriter(String fileName)根据给定的文件名构造一个 FileWriter 对象
FileWriter(String fileName, boolean append)根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象
  • 成员方法
方法名说明
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()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

3.4 字符读数据

  • 介绍
    Reader: 用于读取字符流的抽象父类
    FileReader: 用于读取字符流的常用子类
    构造方法
方法名说明
FileReader(File file)在给定从中读取数据的 File 的情况下创建一个新 FileReader
FileReader(String fileName)在给定从中读取数据的文件名的情况下创建一个新 FileReader
  • 成员方法
方法名说明
int read()一次读一个字符数据
int read(char[] cbuf)一次读一个字符数组数据
  • 代码演示
public class FileReaderDemo1 {
    public static void main(String[] args) throws IOException {
        FileReader fileReader = new FileReader("C:\\a.txt");
        int len;
        //一次读一个字符数据
        while ((len = fileReader.read())!=-1){
            System.out.println((char)len);
        }
        //一次读一个字符数组数据
        char[] chs = new char[1024];
        int len2;
        while ((len2 = fileReader.read(chs))!=-1){
            System.out.println(new String(chs,0,len));
        }
        //释放资源前,自动刷新流
        fileReader.close();
    }
}

3.5 字符流用户注册案例

  • 案例需求

    将键盘录入的用户名和密码保存到本地实现永久化存储

  • 代码演示

public class FileWriteDemo2 {
    public static void main(String[] args) throws IOException {
        //需求: 将键盘录入的用户名和密码保存到本地实现永久化存储
        //要求:用户名独占一行,密码独占一行
        //分析:
        //1,实现键盘录入,把用户名和密码录入进来
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名");
        String username = sc.next();
        System.out.println("请输入用户名");
        String password = sc.next();
        //2.分别把用户名和密码写到本地文件。
        FileWriter fileWriter = new FileWriter("C:\\yonghu.txt");
        //将用户名和密码写到文件中
        fileWriter.write(username);
        //表示写出一个回车换行符 windows \r\n
        fileWriter.write("\r\n");
        fileWriter.write(password);
        //刷新流
        fileWriter.flush();
        //3.关流,释放资源
        fileWriter.close();
    }
}

4 字符缓冲流

4.1字节缓冲流介绍

  • BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值(8192个字符)足够大,可用于大多数用途
  • BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值(8192个字符)足够大,可用于大多数用途。

构造方法

方法名说明
BufferedWriter(Writer out)创建字符缓冲输出流对象
BufferedReader(Reader in)创建字符缓冲输入流对象
  • 代码演示
public class BufferedWriterDemo1 {
    public static void main(String[] args) throws IOException {
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter("C:\\a.txt"));
        bufferedWriter.write("hello\r\n");
        bufferedWriter.write("world\r\n");
        bufferedWriter.close();

       BufferedReader reader = new BufferedReader(new FileReader("C:\\a.txt"));
       //一次读一个字符数据
    /*    int ch;
        while((ch = reader.read())!=-1){
            System.out.println((char)ch);
        }*/
        //一次读一个字符数组
        char[] chs = new char[1024];
        int len;
        while((len = reader.read(chs))!=-1){
            System.out.println(new String(chs,0,len));
        }
        reader.close();
    }
}

4.2 字符缓冲流特有功能

  • 特有方法
方法名说明
void newLine()写一行行分隔符,行分隔符字符串由系统属性定义
String readLine()读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null
  • 代码演示
public class BufferedReaderDemo2 {
    public static void main(String[] args) throws IOException {
        //创建字符串缓冲流
        BufferedWriter bw = new BufferedWriter(new FileWriter("C:\\a.txt"));
        //写数据
        for(int i=0;i<10;i++){
            bw.write("hello" + i);
            //字符缓冲流特有方法  换行
            bw.newLine();
           // bw.flush();
        }
        //释放资源
        bw.close();
        //创建字符缓冲输入流
        BufferedReader br = new BufferedReader(new FileReader("C:\\a.txt"));

        String line;
        while ((line= br.readLine())!=null){
            System.out.println(line);
        }
        br.close();
    }
}

4.3 字符缓冲流案例

  • 案例需求
    使用字符缓冲流读取文件中的数据,排序后再次写到本地文件。
  • 代码演示
public class BufferedDemo3 {
    public static void main(String[] args) throws IOException {
        //需求:读取文件中的数据,排序后再次写到本地文件
        //分析:
        //1.要把文件中的数据读取进来。
        BufferedReader br = new BufferedReader(new FileReader("C:\\sort.txt"));
        String line = br.readLine();
        System.out.println("读到的数据为:" + line);
        br.close();
        //2.按照空格进行切割
        String[] split = line.split(" ");
        int[] arr = new int[split.length];
        for (int i = 0; i <split.length ; i++) {
            String tem = split[i];
            //类型转化
            //3.把字符串类型的数组变成int类型
            arr[i] = Integer.parseInt(tem);
        }
        //4。排序
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));
        BufferedWriter bw2 = new BufferedWriter(new FileWriter("C:\\sort.txt"));
        //5.把排序之后结果写回到本地 1 2 3 4...
        for (int i = 0; i < arr.length; i++) {
            bw2.write(arr[i]+ " ");
        }
        //释放资源
        bw2.close();

    }
}

5 转化流

5.1 字符流中和编码解码问题相关的两个类

  • InputStreamReader:是从字节流到字符流的桥梁,父类是Reader

    • 它读取字节,并使用指定的编码将其解码为字符

    • 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集-

  • OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer

    • 是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节

    • 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

5.2 转化流读写数据

构造方法

方法名说明
InputStreamReader(InputStream in)使用默认字符编码创建InputStreamReader对象
InputStreamReader(InputStream in,String chatset)使用指定的字符编码创建InputStreamReader对象
OutputStreamWriter(OutputStream out)使用默认字符编码创建OutputStreamWriter对象
OutputStreamWriter(OutputStream out,String charset)使用指定的字符编码创建OutputStreamWriter对象
  • 代码演示
public class InputStreamDemo1 {
    public static void main(String[] args) throws IOException {
        //FileReader fileReader = new FileReader("C:\\a.txt");
        //OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("C:\\a.txt"));
        OutputStreamWriter outputStreamWriter = new OutputStreamWriter(new FileOutputStream("C:\\a.txt"));
        outputStreamWriter.write("中国");
        outputStreamWriter.close();
        InputStreamReader inputStreamReader = new InputStreamReader(new FileInputStream("C:\\a.txt"),"UTF-8");
        int ch;
        while((ch = inputStreamReader.read())!=-1){
            System.out.println((char)ch);
        }
        inputStreamReader.close();

    }

}

JDK11后推出字符流构造方法可以指定编码表,就不需要使用转化流让字符流与字节的之间进行转化了,减少了代码量。其中第二个参数为Charset对象,需要Charset.forName(String)方法来获取Charset对象。

6 对象操作流

6.1 对象序列化流

  • 对象序列化介绍

    • 对象序列化就是将对象保存到磁盘中,或者在网络中传输对象
    • 这种机制就是使用一个字节序列表示一个对象,该字节序列化包含:对象的类型、对象的数据,对象的属性等信息
    • 字节序列化写到文件之后,相当于文件中持久保存了一个对象的信息
    • 反之,该字节序列化还可以从文件中读取回来,重构对象,对它进行反序列化
  • 对象序列化流:ObjectOutputStream

    • 将Java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream读取(重构)对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象。
    • 构造方法
方法名说明
ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的ObjectOutputStream
  • 序列化对象的方法
方法名说明
void writeObject(Object obj)将指定的对象写入ObjectOutputStream

测试类

public static void main(String[] args) throws IOException {
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C:\\a.txt"));
        Student student = new Student("张三",50);
        objectOutputStream.writeObject(student);
        objectOutputStream.close();
    }

注意事项

  • 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
  • Serializable是一个标记接口,实现该接口,不需要重写任何方法

6.2 对象反序列化

  • 对象反序列化流:ObjectInputStream
    • ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
  • 构造方法
方法名说明
Object readObject()从ObjectInputStream读取一个对象
  • 代码演示
public class ObjectInputStreamDemo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //ObjectInputStream(InputStream in):创建从指定的InputStream读取的ObjectInputStream
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myOtherStream\\oos.txt"));

        //Object readObject():从ObjectInputStream读取一个对象
        Object obj = ois.readObject();

        Student s = (Student) obj;
        System.out.println(s.getName() + "," + s.getAge());

        ois.close();
    }
}

6.3 serialVersionUID&transient

serialVersionUID

  • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?

    • 会出问题,会抛出InvalidClassException异常
  • 如果出问题了,如何解决呢?

    • 重新序列化
    • 给对象所属的类加一个serialVersionUID
      • private static final long serialVersionUID = 42L;
  • transient

    • 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?

      • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程

6.4 对象操作流练习

  • 代码演示
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //序列化
        //1.创建序列化流对象
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C:\\c.txt"));
        ArrayList<User> arrayList = new ArrayList<>();
        //2.创建多个用户对象
        User u1 = new User("张三",20);
        User u2 = new User("李四",21);
        User u3 = new User("王五",22);
        //3.将学生对象添加到集合中
        arrayList.add(u1);
        arrayList.add(u2);
        arrayList.add(u3);
        //4.将集合对象序列化到文件中
        objectOutputStream.writeObject(arrayList);
        objectOutputStream.close();

        //反序列化
        //5.创建反序列化对象数据,读取到内存中
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("C:\\c.txt"));
        Object obj = objectInputStream.readObject();
        ArrayList<User> arrayList1 = (ArrayList<User>) obj;
        objectInputStream.close();
        for (User user:arrayList1
             ) {
            System.out.println(user.getName()+","+user.getAge());
        }
    }
}

7 Properties集合

7.1 Properties作为Map集合的使用

Properties介绍

  • 是一个Map体系的集合类

  • Properties可以保存到流中或从流中加载

  • 属性列表中的每个键及其对应的值都是一个字符串

  • Properties基本使用

public class PPDemo1 {
    public static void main(String[] args) {
        //创建集合对象
        Properties prop = new Properties();
        prop.put("ditian001","陈先生");
        prop.put("ditian002",";刘先生");
        prop.put("ditian003",";和先生");
        //遍历集合
        Set<Object> keySet = prop.keySet();
        for (Object key :
             keySet) {
            Object value = prop.get(key);
            System.out.println(key+","+value);
        }
    }
}

7.2 Properties作为Map集合的特有方法

特有方法

方法名说明
Object setProperty(String key, String value)设置集合的键和值,都是String类型,底层调用 Hashtable方法 put
String getProperty(String key)使用此属性列表中指定的键搜索属性
Set stringPropertyNames()从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
  • 代码演示
public static void main(String[] args) {
        Properties prop = new Properties();
        prop.setProperty("ditian001","陈先生");
        prop.setProperty("ditian002","刘先生");
        prop.setProperty("ditian003","和先生");
        System.out.println(prop.getProperty("ditian001"));
        System.out.println(prop.getProperty("ditian002"));

        Set<String> setkey = prop.stringPropertyNames();
        for (String key:setkey
             ) {
            String value = prop.getProperty(key);
            System.out.println(key + "," + value);
        }
        
    }
}

7.3Properties和IO流相结合的方法

  • 和IO流结合的方法
方法名说明
void load(Reader reader)从输入字符流读取属性列表(键和元素对)
void store(Writer writer, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流

7.4 Properties集合练习

  • 案例需求
    生成一个a.txt文件中手动写上姓名和年龄将该数据封装成学生对象,写到本地文件、读取到集合中
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Properties prop = new Properties();
        prop.setProperty("刘志忠","22");
        prop.setProperty("高勤","23");
        prop.setProperty("王五","24");
        Set<String> names = prop.stringPropertyNames();
        //int age = Integer.parseInt(prop.getProperty("age"));
        ArrayList<Teacher> teachers = new ArrayList<>();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("C:\\a.txt"));
        //对象操作流,在操作的时候,如果是多个对象,需要先存储在集合中,否则取出会抛异常
        for (String name:names) {
            String Sage = prop.getProperty(name);
            int age = Integer.parseInt(Sage);
            Teacher t = new Teacher(name,age);
           teachers.add(t);
        }
        objectOutputStream.writeObject(teachers);
        objectOutputStream.close();
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("C:\\a.txt"));
        ArrayList<Teacher >tea = (ArrayList<Teacher>)objectInputStream.readObject();
        objectInputStream.close();
        for (Teacher t:tea) {
            System.out.println(t);
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值