黑马程序员技术交流社区

标题: 【石家庄校区】就业班day10--缓冲流 转换流 序列化流 打印流 [打印本页]

作者: xiekai_sjz    时间: 2018-9-26 16:08
标题: 【石家庄校区】就业班day10--缓冲流 转换流 序列化流 打印流
day10 缓冲流 转换流 序列化流 打印流
    今天我们在昨天的IO基础上,学习几种特殊的流-提高读写效率的缓冲流、字节流和字符流的桥梁转换流、用于对象序列化的序列化流和用于打印的打印流。
    以下是今天的学习目标:


    以下是今天的详细笔记:
缓冲流缓冲流的原理缓冲流的原理:
        底层也是使用基本流(FileXxx)来读写
        但缓冲流内部定义了一个缓冲数组, 在读的时候类似于我们一次读一个数组的方式, 减少了磁盘操作次数, 提高了程序效率

缓冲字节输出流: BufferedOutputStream
字节缓冲流
        |_ BufferedInputStream     # 缓冲字节输入流
        |_ BufferedOutputStream    # 缓冲字节输出流

字符缓冲流
        |_ BufferedReader          # 缓冲字符输入流
        |_ BufferedWriter          # 缓冲字符输出流
       
java.io.BufferedOutputStream类: 缓冲字节输出流
        // 构造方法
        BufferedOutputStream(OutputStream out): 使用基本流创建一个缓冲字节输出流
        BufferedOutputStream(OutputStream out, int size): 使用基本流创建一个缓冲字节输出流, 设置缓冲区大小
       
BufferedOutputStream使用步骤:
        1.创建FileOutputStream对象, 构造方法中绑定要输出的目的地
        2.创建BufferedOutputStream对象, 构造方法中传递FileOutputStream对象, 提高FileOutputStream效率
        3.使用BufferedOutputStream对象中的方法 write(), 把数据写入到内部缓冲区中
        4.使用BufferedOutputStream对象中的方法 flush(), 把内部缓冲区中的数据,刷新到文件中
        5.释放资源(会先调用flush方法刷新数据, 第4步可以省略)
[Java] 纯文本查看 复制代码
FileOutputStream fos = new FileOutputStream("文件路径");
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        bos.write("你好".getBytes());
        // bos.flush();  // 可以省略
        bos.close();



5分钟练习: 使用BufferedOutputStream
需求:
使用BufferedOutputStream向当前模块的"testBufferedOutput1.txt"中, 写入"我把数据写入到内存缓冲区中"这个字符串
[Java] 纯文本查看 复制代码
public class Test {
    public static void main(String[] args) throws IOException {
        // 创建基本流对象
        /*FileOutputStream fos = new FileOutputStream("day10\\testBufferedOutput1.txt");
        // 创建缓冲流对象
        BufferedOutputStream bos = new BufferedOutputStream(fos);*/

        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day10\\testBufferedOutput1.txt"));

        // 写数据
        bos.write("我把数据写入到内存缓冲区中".getBytes());
        // 刷新并释放资源
        bos.close();  // 关闭缓冲流对象即可, 该方法内部会将基本流一起关闭
    }
}

缓冲字节输入流: BufferedInputStream
java.io.BufferedInputStream类: 缓冲字节输入流
        // 构造方法
        BufferedInputStream(InputStream in): 使用基本流创建一个缓冲字节输入流
        BufferedInputStream(InputStream in, int size): 使用基本流创建一个缓冲字节输入流, 设置缓冲区大小
       
使用步骤:
        1.创建FileInputStream对象, 构造方法中绑定要读取的数据源
        2.创建BufferedInputStream对象, 构造方法中传递FileInputStream对象, 提高FileInputStream效率
        3.使用BufferedInputStream对象中的方法 read(), 读取文件
        4.释放资源 close()
[Java] 纯文本查看 复制代码
FileInputStream fis = new FileInputStream("文件路径");
        BufferedInputStream bis = new BufferedInputStream(fis);
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
        System.out.println(new String(bytes, 0, len));
    }
        bis.close();

5分钟练习: 使用BufferedInputStream读取文件
需求:
使用BufferedInputStream读取刚才的"testBufferedOutput1.txt"文件, 打印到控制台
代码:
[Java] 纯文本查看 复制代码
public class Test {
    public static void main(String[] args) throws IOException {
        // 创建缓冲字节输入流对象
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day10\\testBufferedOutput1.txt"));

        // 一次读一个字节数组
        byte[] bytes = new byte[1024];
        int len;
        while ((len = bis.read(bytes)) != -1) {
            // 打印字符串
            System.out.println(new String(bytes, 0, len));
        }

        bis.close();
    }
}

缓冲流的效率测试: 复制文件
缓冲流 + 一次读写一个字节数组 效率最高
缓冲字符输出流: BufferedWriterjava.io.BufferedWriter类:
        // 构造方法
        BufferedWriter(Writer out): 使用基本流创建一个缓冲字符输出流
        BufferedWriter(Writer out, int size): 使用基本流创建一个缓冲字符输出流, 设置缓冲区大小
        // 特有方法
        void newLine(): 写入一个换行符, 换行符自动根据当前系统确定
5分钟练习: 使用BufferedWriter
需求:
使用BufferWriter向当前模块下的"testBufferedWriter1.txt"写入10行"传智播客"
代码:
[Java] 纯文本查看 复制代码
public class Test {
    public static void main(String[] args) throws IOException {
        // 创建字符缓冲输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("day10\\testBufferedWriter1.txt"));

        // 循环写入
        for (int i = 0; i < 10; i++) {
            bw.write("传智播客");
            bw.newLine();  // 写入换行符
        }

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

缓冲字符输入流: BufferedReader
java.io.BufferedReader类: 缓冲字符输入流
        // 构造方法
        BufferedReader(Reader in): 使用基本流创建一个缓冲字符输入流
        BufferedReader(Reader in, int size): 使用基本流创建一个缓冲字符输入流, 设置缓冲区大小
        // 特有方法
        String readLine(): 一次读一行字符串, "不包含换行符". 读到文件末尾返回null
5分钟练习: 使用BufferedReader
需求:
使用BufferedReader将当前模块下的"testBufferedWriter1.txt"中的内容读取出来, 打印到控制台
代码:
[Java] 纯文本查看 复制代码
public class Test {
    public static void main(String[] args) throws IOException{
        // 创建缓冲字符输入流对象
        BufferedReader br = new BufferedReader(new FileReader("day10\\testBufferedWriter1.txt"));

        // 循环读取
        String line;  // 定义一个字符串变量, 用于保存每次读到的一行字符串
        while ((line = br.readLine()) != null) {
            // 将读到的一行字符串打印到控制台
            System.out.println(line);
        }

        br.close();
    }
}

练习: 文本排序5分钟练习: 文本排序
需求:
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。

将以上文本按照序号排序, 写入到当前模块下的out.txt中

Map key 不可重复
        key排序: 大小顺序  TreeMap key    HashSet HashMap 哈希表 123456.....10877 10876 10878

分析:
        序号和文本是一对一关系: 用HashMap集合
                HashMap集合底层哈希表, key是存取无序的, 但是对于整数的key, 比较小的数字会自动按大小排序
        读取文件:
                一次读一行: 用
                拆分序号和内容: 用 split("\\.")
        写到新文件:
                遍历Map, 获取key和value
                按照.拼接key和value
                将字符串写入文件, 并写入换行
代码:
[Java] 纯文本查看 复制代码
public class Test {
    public static void main(String[] args) throws IOException {
        // 创建缓冲字符输入流对象
        BufferedReader br = new BufferedReader(new FileReader("day10\\in.txt"));
        // 创建缓冲字符输出流对象
        BufferedWriter bw = new BufferedWriter(new FileWriter("day10\\out.txt"));

        // 创建HashMap集合
        HashMap<String, String> map = new HashMap<>();

        // 读取文件
        String line;
        while ((line = br.readLine()) != null) {
            // 将读取到的字符串按照.拆分
            String[] split = line.split("\\.");
            // 添加到集合中
            map.put(split[0], split[1]);
        }
        // 遍历Map集合, 拼接好, 写出到新文件
        for (String key : map.keySet()) {
            String value = map.get(key);
            // 拼接
            String result = key + "." + value;
            // 写出
            bw.write(result);
            bw.newLine();
        }

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

转换流字符编码和字符集
编码: 字符 -> 字节  'a' -> 97
解码: 字节 -> 字符  97 -> 'a'

编码表: 字符和二进制数字的对应规则

字符集和编码表: 字符集包含编码表
        ASCII字符集
                ASCII编码表
        ISO-8859-1字符集:
                Latin-1: 拉丁字符. 没有中文. 每个字符由1个byte组成
        GB字符集
                GB2312编码表: 每个字符由2个byte组成
                GBK编码表: 每个字符由2个byte组成
                GB18030编码表: 每个字符由1, 2, 4个byte组成
        Unicode
                UTF-8: ASCII字符占1个byte, 拉丁字符占2个byte, 中文占3个byte, Unicode辅助字符占4个byte
                UTF-16
                UTF-32
        ANSI: 表示使用系统默认编码表

乱码问题: FileReader读取GBK编码
乱码原因: 读写编码不一致
        GBK文件中存储的是"你好"在GBK中对应的byte
        而IDEA中使用FileReader读取文件时, 是将byte按照UTF-8编码表查找字符, 结果找不到, 就显示了问号
乱码原理: 字符流和转换流的关系
java.io.Reader
        |_ InputStreamReader: 转换字符输入流 读 char char[]  byte -> char  97 -> 'a'
                |_ FileReader  byte -UTF-8-> char
java.io.Writer  char char[] String
        |_ OutputStreamWriter: 转换字符输出流 char char[] String -> byte
                |_ FileWriter char -UTF-8-> byte
               
FileReader=InputStreamReader+FileInputStream, 只不过固定按照IDEA中的UTF-8来将读取的字节转换为字符
FileWriter=OutputStreamWriter+FileOutputStream, 只不过固定按照IDEA中的UTF-8来将写出的字符转换为字节
字符流 = 字节流 + 编码表

转换流InputStreamReader, OutputStreamWriter在读写时, 可以指定按照哪个编码表来进行转换
OutputStreamWriter类介绍及使用
java.io.OutputStreamWriter类: 输出转换流. 字符流通往字节流的桥梁
        // 构造方法
        OutputStreamWriter(OutputStream out): 使用默认编码表创建转换流
        OutputStreamWriter(OutputStream out, String charsetName): 使用指定编码表创建转换流
                write('a') -> "GBK" -> 97 OutputStream out -> 文件
       
    // 使用默认UTF-8
    OutputStreamWriter o = new OutputStreamWriter(new FileOutputStream("a.txt"));

    // 使用指定的GBK
    OutputStreamWriter o = new OutputStreamWriter(new FileOutputStream("a.txt"), "GBK");
                                o.write("你") -> GBK -> 7676 -> 文件
                                写数据: 字符流 -------> 字节流
InputStreamReader类介绍及使用
java.io.InputStreamReader类: 输入转换流. 字节流通往字符流的桥梁
        // 构造方法
        InputStreamReader(InputStream in): 使用默认编码表创建转换流
        InputStreamReader(InputStream in, String charsetName): 使用指定编码表创建转换流
       
        // 使用默认UTF-8
    InputStreamReader r = new InputStreamReader(new FileInputStream("a.txt"));

    // 使用指定的GBK
    InputStreamReader r = new InputStreamReader(new FileInputStream("a.txt"), "GBK");
                 bbb int ch = r.read()   <------GBK-----------   字节byte 989898
                        r.read(chs)
                             读数据: 字符流  <------  字节流       
练习: 转换文件编码5分钟练习: 转换文件编码
需求:
读取GBK编码文件, 转为UTF-8文件

分析:
        GBK编码文件中保存的字符, 是根据GBK查询到的byte. UTF-8文件中要存根据UTF-8查到的byte
                GBK"你好"的byte --> Java程序按GBK读"你好", 按UTF-8写 --> UTF-8"你好"的byte
        使用InputStreamReader读取GBK编码文件时, 需要指定编码为GBK
        使用OutputStreamWriter将读取到的字符, 按照UTF-8写入到UTF-8编码文件
        边读边写类似于复制文件
        释放资源
代码:
[Java] 纯文本查看 复制代码
public class Test {
    public static void main(String[] args) throws IOException {
        // 创建输入转换流
        InputStreamReader isr = new InputStreamReader(new FileInputStream("day10\\我是GBK文件.txt"), "GBK");
        // 创建输出转换流
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day10\\我是UTF_8文件.txt"), "UTF-8");

        // 循环读写: 一次读写一个字符char
        int ch;
        while ((ch = isr.read()) != -1) {
            // 读到的字符, 经过InputStreamReader已经按照GBK转换为正确的"你好"
            // 接下来交给OutputStreamWriter写出去, 写的时候会按照UTF-8转换为UTF-8的byte
            osw.write(ch);
        }

        // 刷新并释放资源
        isr.close();
        osw.close();
    }
}

序列化流(对象流)序列化和反序列化概述
序列化: 内存中的对象转换为字节序列, 以流的方式写入到磁盘的文件中
        对象转字节
反序列化: 磁盘文件中的字节序列, 以流的方式读取到内存中变成对象
        字节转对象

通过序列化流, 我们可以将内存中的数据方便的存储到磁盘上, 下次程序启动后也能从磁盘读取恢复之前的对象状态

OutputStream
        |_ ObjectOutputStream类: 对象字节输出流
InputStream
        |_ ObjectInputStream类: 对象字节输入流
对象序列化流: ObjectOutputStream
java.io.ObjectOutputStream类: 对象字节输出流
        // 构造方法
        ObjectOutputStream(OutputStream out)
        // 特有成员方法
        void writeObject(Object obj): 将对象写出
[Java] 纯文本查看 复制代码
// 创建对象输出流
    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("student.txt"));
    // 写对象
    Student s = new Student("小美女", 18);
    oos.writeObject(s);
    // 释放资源
    oos.close();

注意:
        被读写的对象的类, 必须实现 java.io.Serializable 接口, 否则会抛出 NotSerializableException
       
        该类的所有属性也都必须是可序列化的. (实现 java.io.Serializable 接口)
        如果某个属性不需要序列化, 则需要用 transient 修饰为瞬态的
        如果要将集合序列化, 那么集合中存储的对象也必须是可序列化的

对象反序列化流: ObjectInputStream
java.io.ObjectInputStream类: 对象字节输入流
        // 构造方法
        ObjectInputStream(InputStream in)
        // 特有成员方法
        Object readObject(): 读取对象
[Java] 纯文本查看 复制代码
// 创建对象输入流
    ObjectInputStream oos = new ObjectInputStream(new FileInputStream("student.txt"));
    // 读对象
    Object o = oos.readObject();
    Student s = (Student)o;
    System.out.println(s);
    // 释放资源
    oos.close();

transient瞬态关键字: 避免属性序列化
static 修饰的成员变量属于类不属于对象, 所以不能序列化
transient 修饰的成员变量, 不能被序列化

如果对象的某个属性不希望被序列化, 可以使用transient修饰, 这样就不会被对象流写到文件中
InvalidClassException异常: 原因和解决方案
serialVersionUID序列号的作用:
        序列化操作时, 会根据类生成一个默认的 serialVersionUID 属性, 写入到文件中.
        该版本号是根据类中成员计算出来的一个数值. 当类的成员发生改变时, 序列版本号也会发生变化
        当代码中的类和读取的对象序列版本号不一致时, 就会抛出InvalidClassException

为了避免这种问题, 我们可以手动写死这个序列号, 这样无论成员如何变化, 序列版本号都是一样的
补充: IDEA设置生成序列版本号:
Setting -> Editor -> Inspections -> Java -> Serialization issues -> 勾选Serializable class without 'serialVersionUID' 然后在类名上按Alt + Enter提示就会有Add 'serialVersionID' field
练习: 序列化集合5分钟练习: 序列化集合
需求:
定义Person类, 实现Serializable接口
        私有成员变量: String name, int age
        生成空参/有参构造, set/get方法, toString方法
定义测试类:
        创建ArrayList集合, 存储3个Person对象:
                张三, 18
                李四, 19
                王五, 20
        使用序列化流将集合写入当前模块的list.txt文件中, 再反序列化并遍历集合打印元素
代码:
[Java] 纯文本查看 复制代码
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 创建集合
        ArrayList<Person> list = new ArrayList<>();

        // 添加对象
        list.add(new Person("张三", 18));
        list.add(new Person("李四", 19));
        list.add(new Person("王五", 20));

        // 创建序列化流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day10\\list.txt"));
        // 写集合对象
        oos.writeObject(list);
        
        // 创建反序列化流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day10\\list.txt"));
        // 读取集合对象
        Object o = ois.readObject();
        // 强转为ArrayList
        ArrayList<Person> readList = (ArrayList<Person>)o;
        // 遍历集合
        for (Person person : readList) {
            System.out.println(person);
        }

        // 释放资源
        oos.close();
        ois.close();

    }
}

打印流打印流PrintStream: 概述和使用
PrintStream特点:
        1. 只有输出流, 没有输入流
        2. PrintStream永远不会抛出IOException
        3. 有特殊方法 print(), println(), 可以输出任意类型的值

java.io.PrintStream类: 字节打印流
        // 构造方法
        PrintStream(File file): 创建字节打印流, 输出到一个文件
        PrintStream(OutputStream out): 创建字节打印流, 输出到一个字节输出流
        PrintStream(String fileName): 创建字节打印流, 输出到一个文件路径
       
注意事项:
        如果用 write() 方法, 会查编码表
        如果用 print(), println(), 则原样输出
       
java.lang.System类:
        // 静态方法
        static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流
IO框架体系结构
// 体系结构:
字节流
        |_ InputStream                 # 字节输入流
        |        |_ FileInputStream         # 专门操作文件的字节输入流
        |        |_ BufferedInputStream     # 带有缓冲区的字节输入流, 效率高
        |        |_ ObjectInputStream       # 对象输入流
        |
        |_ OutputStream                # 字节输出流
                |_ FileOutputStream        # 专门操作文件的字节输出流
                |_ BufferedOutputStream    # 带有缓冲区的字节输出流, 效率高
                |_ ObjectOutputStream      # 对象输出流
                |_ PrintStream             # 字节打印流
字符流
        |_ Reader                      # 字符输入流
        |        |_ BufferedReader          # 带有缓冲区的字符输入流, 效率高
        |        |_ InputStreamReader       # 将字节流转换为字符流输入的转换输入流
        |                |_ FileReader          # 专门操作文件的字符输入流       
        |
        |_ Writer                      # 字符输出流
                |_ BufferedWriter          # 带有缓冲区的字符输出流, 效率高
                |_ OutputStreamWriter      # 将字符流转换为字节流输出的转换输出流
                |     |_ FileWriter         # 专门操作文件的字符输出流

今日API
java.io.BufferedOutputStream类: 缓冲字节输出流
        // 构造方法
        BufferedOutputStream(OutputStream out): 使用基本流创建一个缓冲字节输出流
        BufferedOutputStream(OutputStream out, int size): 使用基本流创建一个缓冲字节输出流, 设置缓冲区大小

java.io.BufferedInputStream类: 缓冲字节输入流
        // 构造方法
        BufferedInputStream(InputStream in): 使用基本流创建一个缓冲字节输入流
        BufferedInputStream(InputStream in, int size): 使用基本流创建一个缓冲字节输入流, 设置缓冲区大小
       
java.io.BufferedWriter类:
        // 构造方法
        BufferedWriter(Writer out): 使用基本流创建一个缓冲字符输出流
        BufferedWriter(Writer out, int size): 使用基本流创建一个缓冲字符输出流, 设置缓冲区大小
        // 特有方法
        void newLine(): 写入一个换行符, 换行符自动根据当前系统确定
       
java.io.BufferedReader类: 缓冲字符输入流
        // 构造方法
        BufferedReader(Reader in): 使用基本流创建一个缓冲字符输入流
        BufferedReader(Reader in, int size): 使用基本流创建一个缓冲字符输入流, 设置缓冲区大小
        // 特有方法
        String readLine(): 一次读一行字符串, "不包含换行符". 读到文件末尾返回null
       
java.io.OutputStreamWriter类: 输出转换流. 字符流通往字节流的桥梁
        // 构造方法
        OutputStreamWriter(OutputStream out): 使用默认编码表创建转换流
        OutputStreamWriter(OutputStream out, String charsetName): 使用指定编码表创建转换流
       
java.io.InputStreamReader类: 输入转换流. 字节流通往字符流的桥梁
        // 构造方法
        InputStreamReader(InputStream in): 使用默认编码表创建转换流
        InputStreamReader(InputStream in, String charsetName): 使用指定编码表创建转换流
       
java.io.ObjectOutputStream类: 对象字节输出流
        // 构造方法
        ObjectOutputStream(OutputStream out)
        // 特有成员方法
        void writeObject(Object obj): 将对象写出
       
java.io.ObjectInputStream类: 对象字节输入流
        // 构造方法
        ObjectInputStream(InputStream in)
        // 特有成员方法
        Object readObject(): 读取对象
       
java.io.PrintStream类: 字节打印流
        // 构造方法
        PrintStream(File file): 创建字节打印流, 输出到一个文件
        PrintStream(OutputStream out): 创建字节打印流, 输出到一个字节输出流
        PrintStream(String fileName): 创建字节打印流, 输出到一个文件路径
       
java.lang.System类:
        // 静态方法
        static void setOut(PrintStream out): 设置System.out的输出目的地为参数的打印流



























作者: lisuzhu    时间: 2018-9-26 18:00
加油,一起努力
作者: 1500744428    时间: 2018-9-26 22:36
终于找到那个用map集合的题了,纠结了老半天,谢谢了
作者: cuipu    时间: 2018-9-27 08:27
那个赞是我点的!!!
作者: 18032086639    时间: 2018-11-24 19:12





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2