DAY13_IO-字节流、字节缓冲流、字符流、字符缓冲流&转换流&对象操作流&Properties集合&IO与集合强化练习

1 字节流

1.1 IO流概述和分类

  • 学习IO流的目的?
    • 将数据写到文件中,实现数据永久化存储
    • 读取文件中已经存在的数据
  • IO流介绍------输入/输出(Input/Output)
    • I表示intput,是数据从硬盘进内存的过程,称之为读。
    • O表示output,是数据从内存到硬盘的过程。称之为写。
    • 流:是一种抽象概念,是对数据传输的总称.也就是说数据在设备间的传输称为流,流的本质是数据传输
    • IO流就是用来处理设备间数据传输问题的.常见的应用: 文件复制; 文件上传; 文件下载
    • IO的数据传输,可以看做是一种数据的流动,按照流动的方向,以内存为参照物,进行读写操作。
    • 简单来说:内存在读,内存在写。
  • IO流的分类
    • 按照数据的流向
      • 输入流:读数据
      • 输出流:写数据
    • 按照数据类型来分
      • 字节流
        • 字节输入流
        • 字节输出流
      • 字符流
        • 字符输入流
        • 字符输出流

在这里插入图片描述

  • IO流的使用场景
    • 如果操作的是纯文本文件,优先使用字符流
    • 如果操作的是图片、视频、音频等二进制文件,优先使用字节流
    • 如果不确定文件类型,优先使用字节流.字节流是万能的流

1.2 字节流写数据

  • 字节流抽象基类
    • InputStream:这个抽象类是表示字节输入流的所有类的超类
    • OutputStream:这个抽象类是表示字节输出流的所有类的超类
    • 子类名特点:子类名称都是以其父类名作为子类名的后缀
  • 字节输出流
    • FileOutputStream(String name):创建文件输出流以指定的名称写入文件
  • 使用字节输出流写数据的步骤
    • 创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
      • 注意事项:
        • 如果文件不存在,就创建。
        • 如果文件存在就清空。
    • 调用字节输出流对象的写数据方法
      • 注意事项:
        • 写出的整数,实际写出的是整数在码表上对应的字母。
    • 释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
      • 注意事项:
        • 每次使用完流必须要释放资源。
 import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class OutputDemo1 {
    public static void main(String[] args) throws IOException {
        //1.创建字节输出流的对象 --- 告诉虚拟机我要往哪个文件中写数据了
        /*
        注意点:
            1.如果文件不存在,会帮我们创建
            2.如果文件存在,会把文件清空
        */
        FileOutputStream fos = new FileOutputStream("D:\\a.txt");
        //FileOutputStream fos = new FileOutputStream(new File("D:\\a.txt"));

        //2,写数据
        fos.write(97);
        //3,释放资源
        fos.close();
    }
}

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

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

public class OutputDemo3 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("bytestream\\a.txt");
        fos.write(97);
        fos.write(98);
        fos.write(99);

        fos.close();
    }
}
import java.io.FileOutputStream;
import java.io.IOException;

public class OutputDemo4 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos = new FileOutputStream("bytestream\\a.txt");

//        byte[] bys = {97, 98, 99};
//        fos.write(bys);

        byte[] bys = {97, 98, 99, 100, 101, 102, 103};
        fos.write(bys, 1, 2);

        fos.close();
    }
}

1.4 字节流写数据的两个小问题

  • 字节流写数据如何实现换行
    • windows:\r\n
    • linux:\n
    • mac:\r
  • 字节流写数据如何实现追加写入
    • public FileOutputStream(String name,boolean append)
    • 创建文件输出流以指定的名称写入文件。如果第二个参数为true ,则字节将写入文件的末尾而不是开头
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class OutputDemo5 {
    public static void main(String[] args) throws IOException {
        //第二个参数就是续写开关,如果没有传递,默认就是false,
        //表示不打开续写功能,那么创建对象的这行代码会清空文件.

        //如果第二个参数为true,表示打开续写功能
        //那么创建对象的这行代码不会清空文件.
        FileOutputStream fos = new FileOutputStream("bytestream\\a.txt",true);

        fos.write(97);
        //能加一个换行
        fos.write("\r\n".getBytes());
        fos.write(98);
        //能加一个换行
        fos.write("\r\n".getBytes());
        fos.write(99);
        //能加一个换行
        fos.write("\r\n".getBytes());
        fos.write(100);
        //能加一个换行
        fos.write("\r\n".getBytes());
        fos.write(101);
        //能加一个换行
        fos.write("\r\n".getBytes());

        fos.close();
    }
}

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

  • 异常处理格式
    • try-catch-finally

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

  • finally特点
    • 被finally控制的语句一定会执行,除非JVM退出
import java.io.FileOutputStream;
import java.io.IOException;

public class OutputDemo6 {
    public static void main(String[] args) {
        FileOutputStream fos = null;
        try {
            //System.out.println(2/0);
            fos = new FileOutputStream("D:\\a.txt");
            fos.write(97);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //finally语句里面的代码,一定会被执行.
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

1.6 字节流小结

  • 创建字节输出流对象
    • 文件不存在,就创建。
    • 文件存在就清空。如果不想被清空则加true
  • 写数据
    • 可以写一个字节,写一个字节数组,写一个字节数组的一部分
    • 写一个回车换行:\r\n
  • 释放资源

1.7 字节流读数据(一次读一个字节数据)

  • 字节输入流
    • FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
  • 字节输入流读取数据的步骤
    • 创建字节输入流对象
      • 注意事项:
      • 如果文件不存在,就直接报错。
    • 读数据
      • 注意事项:
      • 读出来的是文件中数据的码表值。 a -> 97
    • 释放资源
      • 注意事项:
      • 每次使用完流必须要释放资源。
import java.io.FileInputStream;
import java.io.IOException;

public class InputDemo7 {
    public static void main(String[] args) throws IOException {
        //如果文件存在,那么就不会报错.
        //如果文件不存在,那么就直接报错.
        FileInputStream fis = new FileInputStream("bytestream\\a.txt");

        int read = fis.read();
        //一次读取一个字节,返回值就是本次读到的那个字节数据.
        //也就是字符在码表中对应的那个数字.
        //如果我们想要看到的是字符数据,那么一定要强转成char

        System.out.println(read);//97
        System.out.println((char) read);//A

        //释放资源
        fis.close();
    }
}

1.8 字节流读数据(一次读一个字节数据多次读取)

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

public class InputDemo8 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("bytestream\\a.txt");
        //1,文件中多个字节我怎么办?
        /*while (true) {
            int i1 = fis.read();
            System.out.println(i1);
        }*/

        int b;
        while ((b = fis.read()) != -1) {
            System.out.println((char) b);
        }
        fis.close();
    }
}

1.9 字节流复制文件

案例需求

  • 把“C:\itheima\a.avi”复制到当前模块下

分析

  • 复制文件,其实就把文件的内容从一个文件中读取出来(数据源),然后写入到另一个文件中(目的地)
  • 数据源:
    • C:\itheima\a.avi — 读数据 — FileInputStream
  • 目的地:
    • 模块名称\copy.avi — 写数据 — FileOutputStream
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class InputDemo9 {
    public static void main(String[] args) throws IOException {
        //创建了字节输入流,准备读数据.
        FileInputStream fis = new FileInputStream("C:\\itheima\\a.avi");
        //创建了字节输出流,准备写数据.
        FileOutputStream fos = new FileOutputStream("bytestream\\a.avi");

        int b;
        while ((b = fis.read()) != -1) {
            fos.write(b);
        }

        fis.close();
        fos.close();
    }
}

在这里插入图片描述

1.10 字节流读数据(一次读一个字节数组数据)

  • 一次读一个字节数组的方法:
    • public int read​(byte[] b):从输入流读取最多b.length个字节的数据
    • 返回的是读入缓冲区的总字节数,也就是实际的读取字节个数
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

public class InputDemo10 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis = new FileInputStream("C:\\itheima\\a.avi");
        FileOutputStream fos = new FileOutputStream("bytestream\\a.avi");

        byte [] bytes = new byte[1024];
        int len; //本次读到的有效字节个数 -- 这次读了几个字节.

        while((len = fis.read(bytes))!=-1){
            fos.write(bytes,0,len);
        }

        fis.close();
        fos.close();
    }
}

在这里插入图片描述

2 字节缓冲流

  • BufferOutputStream:字节缓冲输出流

    • 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
  • BufferedInputStream:字节缓冲输入流

    • 创建BufferedInputStream将创建一个内部缓冲区数组.当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
    方法名说明
    BufferedOutputStream(OutputStream out)创建字节缓冲输出流对象
    BufferedInputStream(InputStream in)创建字节缓冲输入流对象
  • 为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?

    • 字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作
import java.io.*;

public class BufferDemo1 {
    public static void main(String[] args) throws IOException {
        //就要利用缓冲流去拷贝文件

        //创建一个字节缓冲输入流   BufferedInputStream(InputStream in)
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("bytestream\\a.avi"));
        //创建一个字节缓冲输出流   BufferedOutputStream(OutputStream out)
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("bytestream\\copy.avi"));

        //一次读取一个字节数据
//        int b;
//        while ((b = bis.read()) != -1) {
//            bos.write(b);
//        }

        //一次读取一个字节数组数据
        byte [] bytes = new byte[1024];
        int len;
        while((len=bis.read())!=-1){
            bos.write(bytes, 0, len);
        }

        //方法的底层会把字节流给关闭
        bis.close();
        bos.close();
    }
}

在这里插入图片描述
在这里插入图片描述

2.1 小结

字节流:

  • 可以操作(拷贝)所有类型的文件

字节缓冲流:

  • 可以提高读写效率
  • 不能直接操作文件,需要传递字节流

拷贝文件的四种方式:

  • 字节流一次读写一个字节
  • 字节流一次读写一个字节数组
  • 字节缓冲流一次操作一个字节
  • 字节缓冲流一次操作一个字节数组

3 字符流

3.1 为什么会出现字符流?

  • 如果利用字节流,把文本文件中的中文,读取到内存中,有可能出现乱码。
  • 如果利用字节流,把中文写到文本文件中,也有可能出现乱码。
  • 字符流 = 字节流 + 编码表

3.2 编码表

基础知识:

  • 计算机中储存的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果
  • 按照某种规则,将字符存储到计算机中,称为编码
  • 按照同样的规则,将存储在计算机中的二进制数解析显示出来,称为解码
  • 编码和解码的方式必须一致,否则会导致乱码。
    • 简单理解:
      • 存储一个字符a,首先需在码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储。
      • 读取的时候,先把二进制解析出来,再转成97,通过97查找到对应的字符是a。

什么是字符集

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

常见的字符集

  • ASCII字符集:
    • ASCII(American Standard Code for Information Interchange,美国信息交换标准代码,是基于拉丁字母的一套电脑编码系统):包括了数字,大小写字符和一些常见的标点符号。用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
    • 基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
    • 注意: ASCII码表中是没有中文的。
  • GBXXX字符集:
    • GBK:window系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字,最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案。
    • 注意: GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。
  • Unicode字符集:
    • UTF-8编码:由国际组织ISO 制定,是统一的万国码,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
      • 编码规则:
        • 128个US-ASCII字符,只需一个字节编码
        • 拉丁文等字符,需要二个字节编码
        • 大部分常用字(含中文),使用三个字节编码
        • 其他极少使用的Unicode辅助字符,使用四字节编码
    • 但是因为表示的字符太多,所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的,会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及 UTF-32的编码方式再存储到计算机,其中最为常见的就是UTF-8。
    • 注意: Unicode是万国码,以UTF-8编码后一个中文以三个字节的形式存储

3.3 编码表小结

在这里插入图片描述
在这里插入图片描述

3.4 字符串中的编码解码问题

方法名说明
byte[] getBytes​()使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
byte[] getBytes​(String charsetName)使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
String​(byte[] bytes)通过使用平台的默认字符集解码指定的字节数组来构造新的 String
String​(byte[] bytes, String charsetName)通过指定的字符集解码指定的字节数组来构造新的 String
import java.io.UnsupportedEncodingException;
import java.util.Arrays;

public class CharStreamDemo2 {
    public static void main(String[] args) throws UnsupportedEncodingException {
        method1();
        method2();
    }

    private static void method1() throws UnsupportedEncodingException {
        // byte[] getBytes():
        //使用平台的默认字符集将该 String编码为一系列字节,将结果存储到新的字节数组中
        // byte[] getBytes(String charsetName):
        //使用指定的字符集将该 String编码为一系列字节,将结果存储到新的字节数组中

        String s = "程序员";
        //利用idea默认的UTF-8将中文编码为一系列的字节
        byte[] bytes1 = s.getBytes();
        System.out.println(Arrays.toString(bytes1));//[-25, -88, -117, -27, -70, -113, -27, -111, -104]

        //byte[] bytes2 = s.getBytes("UTF-8");
        byte[] bytes2 = s.getBytes("GBK");
        System.out.println(Arrays.toString(bytes2));//[-77, -52, -48, -14, -44, -79]
    }

    private static void method2() throws UnsupportedEncodingException {
        // String(byte[] bytes):
        //通过使用平台的默认字符集解码指定的字节数组来构造新的 String
        // String(byte[] bytes, String charsetName):
        //通过指定的字符集解码指定的字节数组来构造新的 String

        //UTF-8
        byte[] bytes1 = {-25, -88, -117, -27, -70, -113, -27, -111, -104};
        //gbk
        byte[] bytes2 = {-77, -52, -48, -14, -44, -79};

        //利用默认的UTF-8进行解码
        String s1 = new String(bytes1);
        System.out.println(s1);//程序员

        //利用指定的GBK进行解码
        String s2 = new String(bytes2, "gbk");
        System.out.println(s2);//程序员
    }
}

3.5 字节流读取中文出现乱码的原因

  • 为什么字节流读取纯文本文件,可能会出现乱码?
    • 因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分所以就会出现乱码问题。
  • 为什么字节流拷贝不会出现乱码的问题?
    • 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文
    • 中文的字节存储方式
      • 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

小结

  • 想要进行拷贝,一律使用字节流或者字节缓冲流
  • 想要把文本文件中的数据读到内存中,请使用字符输入流。
    想要把内存中的数据写到文本文件中,请使用字符输出流
  • GBK码表一个中文两个字节,UTF-8编码格式一个中文3个字节。

3.6 字符流写数据

  • 介绍

    • Writer: 用于写入字符流的抽象父类
    • FileWriter: 用于写入字符流的常用子类
  • 构造方法

方法名说明
FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象
FileWriter(String fileName)根据给定的文件名构造一个 FileWriter 对象
FileWriter(File file, boolean append)根据给定的 File 对象构造一个 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()关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamDemo3 {
    public static void main(String[] args) throws IOException {
        //创建字符输出流的对象
        //FileWriter fw = new FileWriter(new File("charstream\\a.txt"));
        FileWriter fw = new FileWriter("charstream\\a.txt");

        method1(fw);
        method2(fw);
        method3(fw);
        method4(fw);
        method5(fw);
        //释放资源
        fw.close();
    }

    private static void method1(FileWriter fw) throws IOException {
        //void write(int c)  写一个字符
        fw.write(97);
        fw.write(98);
        fw.write(99);
    }

    private static void method2(FileWriter fw) throws IOException {
        //void write(char[] cbuf)      写出一个字符数组
        char[] chars = {97, 98, 99, 100, 101};
        fw.write(chars);
    }

    private static void method3(FileWriter fw) throws IOException {
        //void write(char[] cbuf, int off, int len)    写出字符数组的一部分
        char[] chars = {97, 98, 99, 100, 101};
        fw.write(chars, 0, 3);
    }

    private static void method4(FileWriter fw) throws IOException {
        //void write(String str)       写一个字符串
        String line = "程序员abc";
        fw.write(line);
    }

    private static void method5(FileWriter fw) throws IOException {
        //void write(String str, int off, int len)     写一个字符串的一部分
        String line = "程序员abc";
        fw.write(line, 0, 3);
    }
}
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamDemo5 {
    public static void main(String[] args) throws IOException {
        //flush()刷新流。刷新完毕之后,还可以继续写数据
        //close()关闭流。释放资源。一旦关闭,就不能写数据

        FileWriter fw = new FileWriter("charstream\\a.txt");

        fw.write("程序员");
        //fw.flush();
        fw.write("666");
        //fw.flush();
        fw.close();
        fw.write("aaa");//IOException:Stream closed
    }
}

字符流写数据步骤:

  • 创建字符输出流对象
    • 注意事项:
      • 如果文件不存在,就创建。但是要保证父级路径存在。
      • 如果文件存在就清空。
  • 写数据
    • 注意事项:
      • 写出int类型的整数,实际写出的是整数在码表上对应的字母。
      • 写出字符串数据,是把字符串本身原样写出。
  • 释放资源
    • 注意事项:
      • 每次使用完流必须要释放资源。

3.7 字符流读数据

  • 介绍

    • Reader: 用于读取字符流的抽象父类
    • FileReader: 用于读取字符流的常用子类
  • 构造方法

方法名说明
FileReader(File file)在给定从中读取数据的 File 的情况下创建一个新 FileReader
FileReader(String fileName)在给定从中读取数据的文件名的情况下创建一个新 FileReader
  • 成员方法
方法名说明
int read()一次读一个字符数据
int read(char[] cbuf)一次读一个字符数组数据
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class CharStreamDemo6 {
    public static void main(String[] args) throws IOException {
        //创建字符输入流的对象
        // FileReader fr = new FileReader(new File("charstream\\a.txt"));
        FileReader fr = new FileReader("charstream\\a.txt");

        //读取数据
        //一次读取一个字符
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.println((char) ch);
        }

        //一次读一个字符数组数据
        char[] chars = new char[1024];
        int len;
        //read方法还是读取,但是是一次读取多个字符
        //他把读到的字符都存入到chars数组。
        //返回值:表示本次读到了多少个字符。
        while ((len = fr.read(chars)) != -1) {
            System.out.println(new String(chars, 0, len));
        }

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

3.8 案例1 保存键盘录入的数据

案例需求

  • 将键盘录入的用户名和密码保存到本地实现永久化存储
    实现步骤
  • 获取用户输入的用户名和密码
  • 将用户输入的用户名和密码写入到本地文件中
  • 关流,释放资源
import java.io.FileWriter;
import java.io.IOException;
import java.util.Scanner;

public class CharStreamDemo8 {
    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 fw = new FileWriter("charstream\\a.txt");
        //将用户名和密码写到文件中
        fw.write(username);
        //表示写出一个回车换行符 windows \r\n  MacOS \r  Linux \n
        fw.write("\r\n");
        fw.write(password);
        //刷新流
        fw.flush();
        //释放资源
        fw.close();
    }
}

4 字符缓冲流

4.1 字符缓冲流介绍

  • BufferedWriter:
    • 将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
  • BufferedReader:
    • 从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途
方法名说明
BufferedWriter(Writer out)创建字符缓冲输出流对象
BufferedReader(Reader in)创建字符缓冲输入流对象
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class CharStreamDemo9 {
    public static void main(String[] args) throws IOException {
        //字符缓冲输入流
        BufferedReader br = new BufferedReader(new FileReader("charstream\\a.txt"));

        //读取数据
        char[] chars = new char[1024];
        int len;
        while ((len = br.read(chars)) != -1) {
            System.out.println(new String(chars, 0, len));
        }
        br.close();
    }
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamDemo10 {
    public static void main(String[] args) throws IOException {
        //字符缓冲输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\a.txt"));

        //写出数据
        //实际写出的是97对应的字符a
        bw.write(97);
        bw.write("\r\n");

        //实际写出的是97 - 101 对应的字符 abcde
        char[] chars = {97, 98, 99, 100, 101};
        bw.write(chars);
        bw.write("\r\n");

        //实际写的是abc
        bw.write(chars, 0, 3);
        bw.write("\r\n");

        //会把字符串的内容原样写出
        bw.write("程序员");
        bw.write("\r\n");

        //会把字符串的一部分写出 abcde
        String line = "abcdefg";
        bw.write(line, 0, 5);
        bw.flush();
        bw.close();
    }
}

4.2 字符缓冲流特有功能

BufferedWriter:

方法名说明
void newLine()写一行行分隔符,行分隔符字符串由系统属性定义
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class CharStreamDemo11 {
    public static void main(String[] args) throws IOException {
        //字符缓冲流的特有功能
        //字符缓冲输出流BufferedWrite : newLine  跨平台的换行符

        //创建对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\a.txt"));

        //写出数据
        bw.write("黑马程序员666");
        //跨平台的回车换行
        bw.newLine();
        bw.write("abcdef");
        //跨平台的回车换行
        bw.newLine();
        bw.write("-------------");

        //刷新流
        bw.flush();
        //释放资源
        bw.close();
    }
}

BufferedReader:

方法名说明
String readLine()读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null
import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class CharStreamDemo12 {
    public static void main(String[] args) throws IOException {
        //字符缓冲流的特有功能
        //字符缓冲输入流BufferedReader: readLine 读一整行

        //创建对象
        BufferedReader br = new BufferedReader(new FileReader("charstream\\a.txt"));

        //读取数据
//        String line1 = br.readLine();
//        String line2 = br.readLine();
//        String line3 = br.readLine();
//
//        //在之前,如果读不到数据,返回-1
//        //但是readLine如果读不到数据返回null
//        String line4 = br.readLine();
//        String line5 = br.readLine();
//
//        System.out.println(line1);
//        System.out.println(line2);
//        System.out.println(line3);
//        System.out.println(line4);
//        System.out.println(line5);
        
        //使用循环来进行改进
        String line;
        //可以读取一整行数据。一直读,读到回车换行为止。
        //但是他不会读取回车换行符。
        while((line = br.readLine()) != null){
            System.out.println(line);
        }

        // 释放资源
        br.close();
    }
}

4.3 案例2 符缓冲流操作文件中数据排序

案例需求

  • 使用字符缓冲流读取文件中的数据,排序后再次写到本地文件

实现步骤

  • 将文件中的数据读取到程序中
  • 对读取到的数据进行处理
  • 将处理后的数据添加到集合中
  • 对集合中的数据进行排序
  • 将排序后的集合中的数据写入到文件中
import java.io.*;
import java.util.Arrays;

public class CharStreamDemo14 {
    public static void main(String[] args) throws IOException {
        //需求:读取文件中的数据,排序后再次写到本地文件
        //分析:
        //1.要把文件中的数据读取进来。
        BufferedReader br = new BufferedReader(new FileReader("charstream\\sort.txt"));
        //输出流一定不能写在这里,因为会清空文件中的内容
        //BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\sort.txt"));
        String line = br.readLine();
        System.out.println("读取到的数据为" + line);
        br.close();

        //2.按照空格进行切换
        String[] split = line.split(" ");//9 1 2 5 3 10 4 6 7 8

        //3.把字符串类型的数组变成int类型
        int[] arr = new int[split.length];
        //遍历split数组,可以进行类型转换。
        for (int i = 0; i < split.length; i++) {
            String smallStr = split[i];
            //类型转换
            int number = Integer.parseInt(smallStr);
            //把转换后的结果存入到arr中
            arr[i] = number;
        }
        
        //4.排序
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr));

        //5.把排序之后结果写回到本地 1 2 3 4...
        BufferedWriter bw = new BufferedWriter(new FileWriter("charstream\\sort.txt"));
        //写出
        for (int i = 0; i < arr.length; i++) {
            bw.write(arr[i] + " ");
            bw.flush();
        }
        //释放资源
        bw.close();
    }
}

5 IO流总结

在这里插入图片描述

6 转换流注意点

在这里插入图片描述
在这里插入图片描述

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

  • InputStreamReader:是从字节流到字符流的桥梁,父类是Reader
    • 它读取字节,并使用指定的编码将其解码为字符
    • 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集
  • OutputStreamWriter:是从字符流到字节流的桥梁,父类是Writer
    • 是从字符流到字节流的桥梁,使用指定的编码将写入的字符编码为字节
    • 它使用的字符集可以由名称指定,也可以被明确指定,或者可以接受平台的默认字符集

6.2 转换流读写数据

方法名说明
InputStreamReader(InputStream in)使用默认字符编码创建InputStreamReader对象
InputStreamReader(InputStream in,String chatset)使用指定的字符编码创建InputStreamReader对象
OutputStreamWriter(OutputStream out)使用默认字符编码创建OutputStreamWriter对象
OutputStreamWriter(OutputStream out,String charset)使用指定的字符编码创建OutputStreamWriter对象
import java.io.*;
import java.nio.charset.Charset;

public class ConvertedDemo1 {
    public static void main(String[] args) throws IOException {
        method1();
        method2();
        method3();
    }


    private static void method1() throws IOException {
        //这个方法直接读取会产生乱码
        //因为文件是GBK码表
        //而idea默认的是UTF-8编码格式.
        //所以两者不一致,导致乱码
        FileReader fr = new FileReader("C:\\Users\\apple\\Desktop\\a.txt");
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.println((char) ch);
        }
        fr.close();
    }

    private static void method2() throws IOException {
        //如何解决乱码现象
        //文件是什么码表,那么咱们就必须使用什么码表去读取.
        //我们就要指定使用GBK码表去读取文件.
        InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\Users\\apple\\Desktop\\a.txt"), "gbk");
        int ch;
        while ((ch = isr.read()) != -1) {
            System.out.println((char) ch);
        }
        isr.close();

        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\Users\\apple\\Desktop\\b.txt"), "UTF-8");
        osw.write("我爱学习,谁也别打扰我");
        osw.close();
    }

    private static void method3() throws IOException {
        //在JDK11之后,字符流新推出了一个构造,也可以指定编码表
        FileReader fr = new FileReader("C:\\Users\\apple\\Desktop\\a.txt", Charset.forName("gbk"));
        int ch;
        while ((ch = fr.read()) != -1) {
            System.out.println((char) ch);
        }
        fr.close();
    }
}

7 对象操作流

案例引入:

  • 把这个用户信息保存到本地文件去.
public class User  {
    private String username;
    private transient String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class ConvertedDemo2 {
    public static void main(String[] args) throws IOException {
        User user = new User("zhangsan", "qwer");
        //需求:把这个用户信息保存到本地文件去.
        BufferedWriter bw = new BufferedWriter(new FileWriter("a.txt"));
        bw.write(user.getUsername());
        bw.newLine();
        bw.write(user.getPassword());
        bw.close();
    }
}

用户信息会直接保存到a.txt文件中,但是这样不利于保护用户隐私,只要找到a.txt文件就可以看见用户信息,所以java提供了对象操作流。

  • 对象序列化介绍
    • 对象操作流分为两类:对象操作输入流和对象操作输出流
      • 对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
      • 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
    • 对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
    • 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
    • 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
    • 反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
  • 对象操作流的特点
    • 可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中。

在这里插入图片描述

7.1 对象序列化流

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

//如果想要这个类的对象能被序列化,那么这个类必须要实现一个接口.Serializable

//Serializable 接口的意义
//称之为是一个标记性接口,里面没有任何的抽象方法
//只要一个类实现了这个Serializable接口,那么就表示这个类的对象可以被序列化.
public class User implements Serializable {
    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class ConvertedDemo3 {
    public static void main(String[] args) throws IOException {
        User user = new User("zhangsan", "qwer");

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(user);
        oos.close();
    }
}
  • 注意事项
    • 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
    • Serializable是一个标记接口,实现该接口,不需要重写任何方法

7.2 对象反序列化流

  • ObjectInputStream
    • ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
  • 构造方法
方法名说明
ObjectInputStream(InputStream in)创建从指定的InputStream读取的ObjectInputStream
  • 反序列化对象的方法
方法名说明
Object readObject()从ObjectInputStream读取一个对象
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;

public class ConvertedDemo4 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        User o = (User) ois.readObject();
        System.out.println(o);
        ois.close();
    }
}

7.3 对象操作流注意点serialVersionUID&transient

  • serialVersionUID
    • 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
      • 会出问题,会抛出InvalidClassException异常
    • 如果出问题了,如何解决呢?
      • 重新序列化
      • 给对象所属的类加一个serialVersionUID
        • private static final long serialVersionUID = 42L;
  • transient
    • 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
      • 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
import java.io.Serializable;

//如果想要这个类的对象能被序列化,那么这个类必须要实现一个接口.Serializable

//Serializable 接口的意义
//称之为是一个标记性接口,里面没有任何的抽象方法
//只要一个类实现了这个Serializable接口,那么就表示这个类的对象可以被序列化.
public class User implements Serializable {
    //serialVersionUID 序列号
    //如果我们自己没有定义,那么虚拟机会根据类中的信息会自动的计算出一个序列号.
    //问题:如果我们修改了类中的信息.那么虚拟机会再次计算出一个序列号.

    //第一步:把User对象序列化到本地. --- -5824992206458892149
    //第二步:修改了javabean类. 导致 --- 类中的序列号 4900133124572371851
    //第三步:把文件中的对象读到内存. 本地中的序列号和类中的序列号不一致了.

    //解决?
    //不让虚拟机帮我们自动计算,我们自己手动给出.而且这个值不要变.

    private static final long serialVersionUID = 1L;

    private String username;
    private transient String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
import java.io.*;

public class ConvertedDemo5 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.写对象
        //2.修改javabean类
        //3.读对象

        //写对象 --- 序列化
        method1();
        //读对象 --- 反序列化
        method2();
    }

    private static void method1() throws IOException {
        User user = new User("zhangsan", "qwer");
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(user);
        oos.close();
    }

    private static void method2() throws IOException, ClassNotFoundException {
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        User o = (User) ois.readObject();
        System.out.println(o);
        ois.close();
    }
}

7.4 对象操作流练习

案例需求

  • 创建多个Javabean类对象写到文件中,再次读取到内存

思路:

  • 创建学生对象
  • 利用对象操作输出流写到本地
  • 利用对象操作输入流读到内存

实现步骤

  • 创建序列化流对象
  • 创建多个学生对象
  • 将学生对象添加到集合中
  • 将集合对象序列化到文件中
  • 创建反序列化流对象
  • 将文件中的对象数据,读取到内存中
import java.io.Serializable;

public class Student implements Serializable {

    private static final long serialVersionUID = 2L;

    private String name;
    private int age;

    public Student() {
    }

    public Student(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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 方式一:
import java.io.*;

public class ConvertedDemo6 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student s1 = new Student("杜子腾", 16);
        Student s2 = new Student("张三", 23);
        Student s3 = new Student("李四", 24);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(s1);
        oos.writeObject(s2);
        oos.writeObject(s3);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        Object obj;
      /*  while((obj = ois.readObject()) != null){
            System.out.println(obj);
        }*/

        while (true) {
            try {
                Object o = ois.readObject();
                System.out.println(o);
            } catch (EOFException e) {//证明文件读到了结尾
                break;
            }
        }
        ois.close();
    }
}
  • 方式二:
import java.io.*;
import java.util.ArrayList;

public class ConvertedDemo7 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Student s1 = new Student("杜子腾", 16);
        Student s2 = new Student("张三", 23);
        Student s3 = new Student("李四", 24);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        ArrayList<Student> list = new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);

        //我们往本地文件中写的就是一个集合
        oos.writeObject(list);
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("a.txt"));
        ArrayList<Student> list2 = (ArrayList<Student>) ois.readObject();
        for (Student student : list2) {
            System.out.println(student);
        }
        ois.close();
    }
}

8 Properties集合

8.1 Properties作为Map集合的基本使用

  • 是一个Map体系的集合类
  • Properties中有跟IO相关的方法
    • Properties可以保存到流中或从流中加载
  • 只存字符串
    • 属性列表中的每个键及其对应的值都是一个字符串
import java.util.Map;
import java.util.Properties;
import java.util.Set;

public class PropertiesDemo1 {
    public static void main(String[] args) {
        Properties prop = new Properties();
        //增
        prop.put("小龙女", "尹志平");
        prop.put("郭襄", "杨过");
        prop.put("黄蓉", "欧阳克");
        System.out.println(prop);//{郭襄=杨过, 黄蓉=欧阳克, 小龙女=尹志平}

        System.out.println("------------");

        //删
        prop.remove("郭襄");
        System.out.println(prop);//{黄蓉=欧阳克, 小龙女=尹志平}

        System.out.println("------------");

        //改  put --- 如果键不存在,那么就添加,如果键存在,那么就覆盖.
        prop.put("小龙女", "杨过");
        System.out.println(prop);//{黄蓉=欧阳克, 小龙女=杨过}

        System.out.println("------------");

        //查
        Object value1 = prop.get("黄蓉");
        System.out.println(value1);//欧阳克

        System.out.println("------------");

        //遍历
        Set<Object> keys = prop.keySet();
        for (Object key2 : keys) {
            Object value2 = prop.get(key2);
            System.out.println(key2 + "=" + value2);//黄蓉=欧阳克  小龙女=杨过
        }

        System.out.println("------------");

        //装的是所有的键值对对象.
        Set<Map.Entry<Object, Object>> entries = prop.entrySet();
        for (Map.Entry<Object, Object> entry : entries) {
            Object key3 = entry.getKey();
            Object value3 = entry.getValue();
            System.out.println(key3 + "=" + value3);//黄蓉=欧阳克  小龙女=杨过
        }
    }
}

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

方法名说明
Object setProperty(String key, String value)设置集合的键和值,都是String类型,底层调用 Hashtable方法 put
String getProperty(String key)使用此属性列表中指定的键搜索属性
Set< String > stringPropertyNames()从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
import java.util.Properties;
import java.util.Set;

public class PropertiesDemo2 {
    public static void main(String[] args) {
        //Object setProperty(String key, String value) --- put
        //设置集合的键和值,都是String类型,底层调用 Hashtable方法 put
        Properties prop = new Properties();
        prop.setProperty("江苏", "南京");
        prop.setProperty("安徽", "南京");
        prop.setProperty("山东", "济南");
        System.out.println(prop);//{安徽=南京, 山东=济南, 江苏=南京}

        //String getProperty(String key)  --- get
        //使用此属性列表中指定的键搜索属性
        String value1 = prop.getProperty("江苏");
        System.out.println(value1);//南京

        //Set<String> stringPropertyNames()  --- keySet
        //从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
        Set<String> keys = prop.stringPropertyNames();
        for (String key2 : keys) {
            String value2 = prop.getProperty(key2);
            System.out.println(key2 + "=" + value2);//安徽=南京  江苏=南京  山东=济南
        }
    }
}

8.3 Properties和IO流相结合的方法

方法名说明
void load​(InputStream inStream)从输入字节流读取属性列表(键和元素对)
void load(Reader reader) 从输入字符流读取属性列表(键和元素对)
void store​(OutputStream out, String comments)将此属性列表(键和元素对)写入此 Properties表中,以适合于使用 load(InputStream)方法的格式写入输出字节流
void store​(Writer writer, String comments) 将此属性列表(键和元素对)写入此 Properties表中,以适合使用 load(Reader)方法的格式写入输出字符流
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Properties;

public class PropertiesDemo3 {
    public static void main(String[] args) throws IOException {
        load();
        store();
    }

    private static void load() throws IOException {
        //void load(Reader reader)                     将本地文件中的键值对数据读取到集合中
        Properties prop = new Properties();
        FileReader fr = new FileReader("prop.properties");
        //调用完了load方法之后,文件中的键值对数据已经在集合中了.
        prop.load(fr);
        fr.close();
        System.out.println(prop);
    }

    private static void store() throws IOException {
        //void store(Writer writer, String comments)   将集合中的数据以键值对形式保存在本地
        Properties prop = new Properties();
        prop.put("zhangsan", "123");
        prop.put("lisi", "456");
        prop.put("wangwu", "789");

        FileWriter fw = new FileWriter("prop.properties");
        prop.store(fw, null);
        fw.close();
    }
}

8.4 Properties集合练习

需求

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

实现步骤

  • 创建Properties集合,将本地文件中的数据加载到集合中
  • 获取集合中的键值对数据,封装到学生对象中
  • 创建序列化流对象,将学生对象序列化到本地文件中
import java.io.Serializable;

public class Student implements Serializable {
    private static final long serialVersionUID = 1L;

    private String name;
    private int age;

    public Student() {
    }

    public Student(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 "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
import java.io.*;
import java.util.Properties;

public class Test {
    //1.将姓名和年龄手动保存在本地properties文件中
    //2.读取本地properties文件中的数据
    //3.将集合中的数据获取出来并创建对象
    //4.将该对象写到本地
    public static void main(String[] args) throws IOException {
        Properties prop = new Properties();
        FileReader fr = new FileReader("prop.properties");
        prop.load(fr);
        fr.close();

        String name = (String) prop.get("name");
        int age = Integer.parseInt((String) prop.get("age"));
        Student s = new Student(name, age);

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("a.txt"));
        oos.writeObject(s);
        oos.close();
    }
}

9 IO和集合强化练习

9.1 案例1 点名器

需求

  • 有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。

思路

  • 把文件中的数据读取到到集合中
  • 使用Random产生一个随机数,获得随机索引。
  • 通过随机索引获取随机姓名
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;

public class Test1 {
    //有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。
    public static void main(String[] args) throws IOException {
        //1.先把文件中的姓名读取到集合中.
        //字节流输入流 字节缓冲输入流  --- 拷贝 -- pass
        //字符输入流   字符缓冲输入流

        //单列 List(保留重复)
        ArrayList<String> list = new ArrayList<>();
        BufferedReader br = new BufferedReader(new FileReader("collection-io-test\\names.txt"));
        String line;
        while ((line = br.readLine()) != null) {
            list.add(line);
        }
        br.close();
        System.out.println(list);

        //2.获取一个随机索引
        Random r = new Random();
        int index = r.nextInt(list.size());
        //3.通过这个随机索引获取对应的元素
        String name = list.get(index);
        System.out.println(name);
    }
}

9.1 案例2 点名器升级版

需求

  • 有一个文件里面存储了班级同学的姓名,每一个姓名占一行,要求通过程序实现随机点名器。
    • 第三次必定是张三同学

思路:

  • 第一次运行时随机的姓名
  • 第二次运行时随机的姓名
  • 第三次运行时随机的姓名

难点:

  • 如何确定当前是第几次运行程序?
import java.io.*;
import java.util.ArrayList;
import java.util.Random;

public class Test2 {
    public static void main(String[] args) throws IOException {
        // int count = 0;//第一次运行代码的时候,定义了一个count
        //第二次运行代码的时候,重新定义了一个count
        // count++;

        //1.先把文件中的数据读到集合中
        BufferedReader br = new BufferedReader(new FileReader("collection-io-test\\names.txt"));
        String line;
        ArrayList<String> list = new ArrayList<>();
        while ((line = br.readLine()) != null) {
            list.add(line);
        }
        br.close();
        System.out.println(list);


        //读取本程序已经运行了多少次.
        BufferedReader brCount = new BufferedReader(new FileReader("collection-io-test\\count.txt"));
        String countStr = brCount.readLine();
        //表示程序已经运行了多少次.
        int count = Integer.parseInt(countStr);
        brCount.close();

        //表示当前程序又运行了一次.
        count++;

        if (count == 3) {
            System.out.println("张三");
        } else {
            //2.随机获取一个索引
            Random r = new Random();
            int index = r.nextInt(list.size());
            //3.通过随机索引获取元素
            String name = list.get(index);
            System.out.println(name);
        }

        BufferedWriter bw = new BufferedWriter(new FileWriter("count.txt"));
        //是不能直接写一个count的值.
        bw.write(count + "");//表示先将数字变成字符串,然后将字符串原样写出. 1
        bw.close();

    }
}

9.1 案例3 登陆案例

需求

  • 写一个登陆小案例。

步骤

  • 将正确的用户名和密码手动保存在本地的userinfo.txt文件中。保存格式为:username=zhangsan&password=123
  • 让用户键盘录入用户名和密码
  • 比较用户录入的和正确的用户名密码是否一致
  • 如果一致则打印登陆成功
  • 如果不一致则打印登陆失败
import java.util.Objects;

public class User {
    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(username, user.username) &&
                Objects.equals(password, user.password);
    }


    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Scanner;

public class Test3 {
    public static void main(String[] args) throws IOException {
        //1.得到文件中正确的用户名和密码
        User rightUserInfo = getRightUserInfo();

        //2.得到用户键盘录入的用户名和密码
        User userInputInfo  = getUserInputInfo();

        //3.比较两者是否一致.
        //if(rightUserInfo.getUsername().equals(userInputInfo.getUsername())  && ){
        if(rightUserInfo.equals(userInputInfo)){
            System.out.println("登录成功");
        }else{
            System.out.println("登录失败");
        }
    }

    private static User getUserInputInfo() {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名");
        String username = sc.nextLine();
        System.out.println("请输入密码");
        String password = sc.nextLine();
        return new User(username,password);
    }

    private static User getRightUserInfo() throws IOException {
        BufferedReader br = new BufferedReader(new FileReader("collection-io-test\\userinfo.txt"));
        String line = br.readLine();
        br.close();
        System.out.println(line);//username=zhangsan&password=123
        String[] split = line.split("&");
        //0索引:username=zhangsan
        //1索引:password=123
        String username = split[0].split("=")[1];
        String password = split[1].split("=")[1];
        //可以把用户名和密码封装成一个javabean对象,直接返回这个对象就可以了.
        return new User(username,password);
    }
}

9.1 案例4 自动登陆案例

需求

  • 写一个自动登陆小案例。

步骤

  • 将正确的用户名和密码手动保存在本地的userinfo.txt文件中。保存格式为:username=zhangsan&password=123
  • 让用户键盘录入用户名和密码
  • 比较用户录入的和正确的用户名密码是否一致
  • 如果一致则打印登陆成功,并将用户录入的数据保存到本地cookie.txt文件中。
    • 保存格式为:username=zhangsan
      • password=123
  • 如果不一致则打印登陆失败
  • 再次运行时,则从本地cookie.txt文件中读取第一次保存的数据,实现自动登陆。
import java.util.Objects;

public class User {
    private String username;
    private String password;

    public User() {
    }

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(username, user.username) &&
                Objects.equals(password, user.password);
    }
    
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
import java.io.*;
import java.util.Scanner;

public class Test4 {
    public static void main(String[] args) throws IOException {
        //这个是正确的用户名和密码
        String rightUsername = "zhangsan";
        String rightPassword = "123";
        User rightUser = new User(rightUsername, rightPassword);

        File file = new File("collection-io-test\\cookie.txt");

        User user = null;
        if (file.exists()) {
            //存在 --- 表示本次是第二次或者多次登录
            //从本地读取数据
            user = readUserInfo(file);
        } else {
            //不存在 --- 表示本次是第一次登录
            //键盘录入用户名和密码
            user = getUserInputInfo();
        }
        //比较两者是否一致
        if (rightUser.equals(user)) {
            //一致,提示登录成功,需要将正确的用户名和密码写到本地文件,
            //目的:为了下次的自动登录.
            System.out.println("登录成功");
            if (!file.exists()) {
                writeUser(user);
            }
        } else {
            //不一致,提示登录失败
            System.out.println("登录失败");
        }
    }

    private static User readUserInfo(File file) throws IOException {
        BufferedReader br = new BufferedReader(new FileReader(file));
        String usernameInfo = br.readLine();//username=zhangsan
        String passwordInfo = br.readLine();//password=123
        br.close();
        String username = usernameInfo.split("=")[1];
        String password = passwordInfo.split("=")[1];
        return new User(username, password);
    }

    private static void writeUser(User user) throws IOException {
        BufferedWriter bw = new BufferedWriter(new FileWriter("collection-io-test\\cookie.txt"));
        bw.write("username=" + user.getUsername());
        bw.newLine();
        bw.write("password=" + user.getPassword());
        bw.close();
    }

    private static User getUserInputInfo() {
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入用户名");
        String username = sc.nextLine();
        System.out.println("请输入密码");
        String password = sc.nextLine();
        return new User(username, password);
    }
}

9.1 案例5 复制多级文件夹

需求

  • 把“E:\itcast”这个文件夹复制到 F盘目录下

分析

  • 数据源:E:\itcast
  • 目的地:F:\
  • 判断数据源是目录还是文件
    • 是文件:直接复制,用字节流
    • 是目录:
      • 在目的地下创建该目录
      • 遍历获取该目录下的所有文件的File数组,得到每一个File对象
      • 回到3继续(递归)

思路

  • 创建数据源File对象,路径是E:\itcast
  • 创建目的地File对象,路径是F:\
  • 写方法实现文件夹的复制,参数为数据源File对象和目的地File对象
  • 判断数据源File是否是文件
    • 是文件:直接复制,用字节流
    • 不是文件:
      • 在目的地下创建该目录
      • 遍历获取该目录下的所有文件的File数组,得到每一个File对象
      • 回到3继续(递归)
import java.io.*;

public class Test5 {
    public static void main(String[] args) throws IOException {
        //明确一件事情,拷贝,一定是从文件开始,到文件结束的.
        File src = new File("D:\\itheima\\src");
        File dest = new File("D:\\itheima\\dest");
        copy(src, dest);
    }

    private static void copy(File src, File dest) throws IOException {
        dest.mkdirs();//创建一个多级文件夹,如果文件夹存在,就创建失败,如果不存在就创建成功.

        //1.进入数据源文件夹,得到里面每一个文件和文件夹的路径
        File[] files = src.listFiles();
        //2.遍历
        for (File file : files) {
            //file依次表示src文件夹里面的每一个文件和文件夹的路径
            if (file.isFile()) {
                //3.判断 --- 拷贝
                FileInputStream fis = new FileInputStream(file);
                FileOutputStream fos = new FileOutputStream(new File(dest, file.getName()));
                int b;
                while ((b = fis.read()) != -1) {
                    fos.write(b);
                }
                fis.close();
                fos.close();
            } else {
                //4.判断 --- 递归
                copy(file, new File(dest, file.getName()));
            }
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值