0
点赞
收藏
分享

微信扫一扫

IO流 Ⅱ

一只1994 2022-02-19 阅读 77
javastream

File类 (day11)

IO流

IO流的概述和分类

字节流

字符流

其他流

字符流 (day12)

转换流

对象操作流

Properties

01. 字节流操作文本文件出现乱码问题

使用字节流读写带中文数据的文件, 会出现什么问题?
    中文乱码
    
代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //读取带有中文的文件
            FileInputStream fis = new FileInputStream("day12\\Demo01.txt");
            int b;
            while ((b = fis.read()) != -1){
                System.out.println((char)b);
                //中文乱码
            }
            fis.close();
        }
    }

02. 字符流-编码表

了解内容1
    计算机中存储的信息都是以二进制数表示的
        编码: 按照某种规则, 将字符变为二进制, 再存储到计算机中
        解码: 按照同样的规则, 将存储在计算机中的二进制数解析显示出来
    编码和解码的方式如果不统一, 会造成乱码问题
    
了解内容2
    ASCII码表: 每个信息交换标准代码, 包含了数字, 大小写字母和常见标点符号(不包含中文)
    GBK码表: windows系统默认的码表, 兼容ASCII, 包含了21003个汉字并支持繁体字以及部分日韩文字
        在GBK码表中, 一个中文占两个字节
    Unicode码表: 被称为万国码, 计算机科学领域的一项业界标准, 容纳世界上大多数国家的常见文字和符号
 
了解内容3
    由于要表示的字符太多, 所以Unicode码表中的数据不是以二进制的形式直接存储的
    会通过UTF-7,UTF-7.5,UTF-8,UTF-16,UTF-32等进行编码, 然后再存储到计算机, 其中最为常见的是UTF-8
    在Unicode万国码中, 以UTF-8编码后一个中文占三个字节
   
总结
    ASCII -> 各个国家的(GBK,EUC-KR,Shift_JIS) -> 统一 -> Unicode
    Windows默认的编码是GBK, 支持中文, 一个中文占2个字节
    以后我们使用Unicode的UTF-8只是一种编码方式, 不是编码表, 支持中文, 一个中文占3个字节
    

03. 字符流-编码和解码的方法

String字符串中, 编码和解码的方法
    1.编码
        byte[] getBytes(); 使用平台默认的字符集将String编码为字节, 返回字节数组
        byte[] getBytes(String charsetName); 使用指定的字符集将String编码为字节, 返回字节数组
    2.解码
        String(byte[] bytes); 使用平台默认的字符集解码字节数组, 构造成新的字符串
        String(byte[] bytes, String charsetName); 使用指定的字符集解码字节数组, 构造成新的字符串
    再次记忆!
        windows默认采用编码表 -> GBK -> 支持中文, 一个中文占3个字节
        IDEA默认采用编码方式 -> Unicode表中的UTF-8编码方式, 一个中文占2个字节
        
代码示例
public class Demo {
    public static void main(String[] args) throws UnsupportedEncodingException {
        /*
            解码
         */
        String s = "道北吴彦祖";
        byte[] bytes1 = s.getBytes();
        System.out.println(Arrays.toString(bytes1)); //IDEA默认UT8, 一个中文占3字节
        //[-23, -127, -109, -27, -116, -105, -27, -112, -76, -27, -67, -90, -25, -91, -106]
​
        byte[] bytes2 = s.getBytes("GBK"); //windows默认GBK, 一个中文占2字节
        System.out.println(Arrays.toString(bytes2));
        //[-75, -64, -79, -79, -50, -30, -47, -27, -41, -26]
​
        /*
            编码
         */
        byte[] b1 = new byte[]{-23, -127, -109, -27, -116, -105, -27, -112, -76, -27, -67, -90, -25, -91, -106};
        String s1 = new String(b1);
        System.out.println(s1); //道北吴彦祖
​
        byte[] b2 = new byte[]{-75, -64, -79, -79, -50, -30, -47, -27, -41, -26};
        String s2 = new String(b2);
        System.out.println(s2); //����������
        // 为什么乱码?? 因为这个b2数组, 是26行通过GBK解码得来的. 现在通过IDEA默认的UT8进行编码, 字符集不统一
        // 怎么解决?? 构造新String时参数指定字符串为GBK即可
​
        String s3 = new String(b2,"GBK");
        System.out.println(s3); //道北吴彦祖
    }
}

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

为什么通过"字节流"读写中文会出现乱码?
    因为字节流一次读一个字节, 而不管是GBK还是UT8, 一个中文占多个字节, 字节流一次只读取了中文的一部分
 

05. 字符流-读取中文的过程

字符流本质
    字符流 = 字节流 + 编码表
    
无论在哪张码表中
    中文的第一个字节一定是: 负数
    
字符流读取中文的过程
    当字符流读数据时, 读到了负数的字节就知道读到了中文
    再去看使用的什么码表, 决定一次读几个字节 (GBK中2个, UTF-8中3个)
    
技术选型(应用场景)
    1. 拷贝文件 -> 字节流/字节缓冲流
    2. 读取文本文件中的数据到内存 -> 字符输入流FileInputStream.read
    3. 将内存中的数据写入文本文件 -> 字符输出流FileOutputStream.write
​

06. 字符流-写出数据 -------------------- 字符流写数据

字符输出流构造
    FileWriter(File对象);
    FileWriter(字符串路径);
​
字符流写数据的5种方式 
    1. void write(int c); 写一个字符
    2. void write(char[] cbuf); 写一个字符数组
    3. void write(char[] cbuf, int off, int len); 写一个字符数组的一部分
    4. void write(String str); 写一个字符串
    5. void write(String str, int off, int len); 写一个字符串的一部分
    
字符流写数据步骤
    1. 创建字符输出流对象
    2. 写数据
    3. 国际惯例
    
代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //FileWriter fw = new FileWriter(new File("day12\\Demo02.txt"));
            //传递路径作为构造参数, 底层会自动封装为File对象
            FileWriter fw = new FileWriter("day12\\Demo02.txt");
            
            //1. void write(int c); 写一个字符
            //fw.write(97);
            //fw.write(98);
            //fw.write(99); //abc
​
            char[] chars = {'d','e','f','g','h'};
            //2. void write(char[] cbuf); 写一个字符数组
            //fw.write(chars); //defgh
​
            //3. void write(char[] cbuf, int off, int len); 写一个字符数组的一部分
            //fw.write(chars,0,3); //def
​
            String str = "123黑马程序员";
            //4. void write(String str); 写一个字符串
            //fw.write(str); //123黑马程序员
            //5. void write(String str, int off, int len); 写一个字符串的一部分
            fw.write(str,3,5); //黑马程序员
​
            //3. 国际惯例
            fw.close();
        }
    }

07. 字符流-写出数据的注意事项

字符流写数据的注意事项
    1. 如果文件不存在, 会创建新的
    2. 如果路径错误, 会报错 
    3. 如果文件存在, 先清空再写入
    4. 构造第二个参数不写, 默认为false, 表示新数据会覆盖老数据 (先清空再写入) , 追加写入需要手动给true
    5. 如果写出int类型的整数, 那么写出是该整数在码表对应的字符
    6. 如果写出字符串, 是将该字符串本身原样写出
    7. 必须释放资源
    
代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //注意: 如果文件不存在, 会创建新的
            FileWriter fw = new FileWriter("day12\\Demo03.txt");
​
            //注意: 如果路径错误, 会报错
            //FileWriter fw = new FileWriter("day12\\aaa\\Demo02.txt"); //java.io.FileNotFoundException(系统找不到指定的路径。)
​
            //注意: 构造第二个参数不写默认为false, 表示新数据会覆盖老数据, 追加写入需要手动给true
            //FileWriter fw = new FileWriter("day12\\Demo03.txt",true);
​
            //注意: 如果文件存在, 先清空再写入
            //fw.write("我是新数据");
​
            fw.write(97); //a
            // 如果就要写97怎么办
            fw.write("97"); //97
​
            //注意: 必须释放资源
            fw.close();
        }
    }
​
        

08. 字符流-flush和close方法

flush和close方法
    flush(); 刷新流, 刷新后还可以写数据
    close(); 关闭流释放资源, 注意关闭前会先刷新流, 再关闭; 但是关闭后就不能继续写数据了
    
代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            FileWriter fw = new FileWriter("day12\\Demo04.txt");
​
            fw.write(97);
            fw.flush(); //这里刷新后, 文件中只显示a
            fw.write(98);
            fw.flush(); //加上这里刷新后, 文件中才显示ab
​
            fw.close();
            //close(); 关闭流释放资源, 注意关闭前会先刷新流, 再关闭; 但是关闭后就不能继续写数据了
            fw.write(100); //java.io.IOException: Stream closed
        }
    }

09. 字符流-读取数据 -------------------- 字符流读数据

字符流读数据步骤
	1. 创建字符输入流对象
	2. 读数据 (读一个)
	3. 国际惯例

代码示例(一次读一个)
    public class Demo01 {
        public static void main(String[] args) throws IOException {
            //1. 创建字符输入流对象
            FileReader fr = new FileReader("day12\\Demo05.txt"); //如果文件不存在则报错

            //2. 读数据 (读一个)
            int ch;
            while ((ch = fr.read()) != -1){
                //读取每一个字节并打印
                //System.out.print(ch + " "); //40657 39532 31243 24207 21592 54 54 54

                //字节强转为字符
                System.out.print((char) ch + " "); //黑 马 程 序 员 6 6 6
            }

            //3. 国际惯例
            fr.close();
        }
    }

字符流读数据步骤
    1. 创建字符输入流对象
    2. 读数据 (读多个)
       2.1 创建字符数组
       2.2 定义len表示读到的有效字符个数
       2.3 如果没到文件结尾就继续读
       2.4 将读到的字符数组作为参数, 构造新的字符串
    3. 国际惯例
    
代码示例
    public class Demo02 {
        public static void main(String[] args) throws IOException {
            //1. 创建字符输入流对象
            FileReader fr = new FileReader("day12\\Demo05.txt"); //如果文件不存在则报错

            //2. 读数据 (读多个)
            //2.1 创建字符数组
            char[] chars = new char[1024];
            //2.2 定义len表示读到的有效字符个数
            int len;
            //2.3 如果没到文件结尾就继续读
            while ((len = fr.read(chars)) != -1) {
                //2.4 将读到的字符数组作为参数, 构造新的字符串
                System.out.println(new String(chars, 0, len));
            }

            //3. 国际惯例
            fr.close();
        }
    }
    

10. 字符流-练习

练习需求:
    将用户输入的username和password存入本地文件实现永久化存储
    要求username和password各自独占一行
    
代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //拿到用户名和密码
            Scanner sc = new Scanner(System.in);
            System.out.println("请输入用户名: ");
            String username = sc.next();
            System.out.println("请输入密码: ");
            String password = sc.next();
            
            //创建字符输出流对象
            FileWriter fw = new FileWriter("day12\\Demo06.txt");
            //写数据
            fw.write(username);
            //换行后再写数据
            fw.write("\r\n");
            fw.write(password);
            //刷新流
            fw.flush();
            //国际惯例
            fw.close();
        }
    }
    

11. 字符缓冲输入流-读取数据

字符缓冲流
	BufferedWriter: 字符缓冲输出流, 可以高效的写出数据
	BufferedReader: 字符缓冲输入流, 可以高效的读取数据
	
为什么缓冲流更高效?
	和字节缓冲流底层一个道理, 都是提供了一个8K的缓冲区, 尽量让操作在内存中实现
	
代码示例(BufferedWriter: 字符缓冲输出流, 可以高效的写出数据)
    public class Demo {
        public static void main(String[] args) throws IOException {
            // 创建字符缓冲输入流
            BufferedReader br = new BufferedReader(new FileReader("day12\\Demo06.txt"));

            // 读数据
            char[] chars = new char[1024];
            int len;
            while ((len =br.read(chars)) != -1){
                System.out.println(new String(chars,0,len));
            }

            //国际惯例
            br.close();
        }
    }

12. 字符缓冲输出流-输出数据

代码示例(BufferedReader: 字符缓冲输入流, 可以高效的读取数据)
    public class Demo {
        public static void main(String[] args) throws IOException {
            // 创建字符缓冲输出流
            BufferedWriter bw = new BufferedWriter(new FileWriter("day12\\Demo07.txt"));

            // 一次写一个字符, 整数对应表中的字符
            //bw.write(97);

            // 一次写一个字符数组
            //bw.write(new char[]{'a','b','c'}); //abc

            // 一次写一个字符数组的一部分
            //bw.write(new char[]{'a','b','c'},0,2); //ab

            // 一次写一个字符串
            //bw.write("97"); //97

            // 一次写一个字符串的一部分
            // bw.write("黑马程序员666",0,5); //黑马程序员

            //刷新流
            bw.flush();
            //国际惯例
            bw.close();
        }
    }

13. 缓冲流-特有方法

缓冲流特有方法
	1. BufferWriter: void newLine(); 底层匹配系统, 写一行分隔(换行)符
	2. BufferReader: void String readLine(); 读一行文字, 不读取分隔(换行)符, 到达文件结尾为null
	
代码演示(BufferWriter: void newLine())
    public class Demo01 {
        public static void main(String[] args) throws IOException {
            // 写数据
            BufferedWriter bw = new BufferedWriter(new FileWriter("day12\\Demo08.txt"));
            bw.write("黑马程序员");
            bw.newLine();
            bw.write("传智专修学院");
            bw.newLine();
            bw.write("-----------");
            /*
                黑马程序员
                传智专修学院
                -----------
             */
            //国际惯例
            bw.flush();
            bw.close();
        }
    }
	
代码演示(BufferReader: void String readLine())
    public class Demo02 {
        public static void main(String[] args) throws IOException {
            //读数据
            BufferedReader br = new BufferedReader(new FileReader("day12\\Demo08.txt"));
            //一次读一行
            //System.out.println(br.readLine()); //黑马程序员
            //System.out.println(br.readLine()); //传智专修学院
            //System.out.println(br.readLine()); //-----------
            //System.out.println(br.readLine()); //null

            //标准读数据代码
            String line;
            while ((line = br.readLine()) != null){
                System.out.println(line);
                /*
                    黑马程序员
                    传智专修学院
                    -----------
                 */
            }
            //国际惯例
            br.close();
        }
    }

14. 缓冲流-练习

练习需求: 读取文件中的数据, 排序后将数据写到本地文件

代码示例
public class Demo {
    public static void main(String[] args) throws IOException {
        //读数据
        BufferedReader br = new BufferedReader(new FileReader("day12\\Demo09.txt"));
        String line = br.readLine(); //6 5 7 9 2 1 3 4 8 10
        br.close();

        //根据空格切割
        String[] srr = line.split(" ");
        //排序
        //Arrays.sort(srr);
        //System.out.println(Arrays.toString(arr)); //[1, 10, 2, 3, 4, 5, 6, 7, 8, 9] 注意直接排序会按照字符串首字符升序

        //创建整数数组, 将srr元素转为整数存入
        int[] arr = new int[srr.length];
        for (int i = 0; i < srr.length; i++) {
            arr[i] = Integer.parseInt(srr[i]);
        }
        //排序
        Arrays.sort(arr);
        System.out.println(Arrays.toString(arr)); //[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

        //写数据
        BufferedWriter bw = new BufferedWriter(new FileWriter("day12\\Demo10.txt"));
        //遍历整数数组
        for (int i = 0; i < arr.length; i++) {
        	//加入字符串空格, 同时转为字符串
            bw.write(arr[i] + " "); //1 2 3 4 5 6 7 8 9 10
        }
        bw.flush();
        bw.close();
    }
}

15. IO流小结

IO流
	流向分: 输入流(读)和输出流(写)
    类型分: 字节流(拷贝)和字符流(读写文本数据)
        字节流: 一个一个字节拷贝慢, 通过数组+底层的缓冲区提升(缓冲流)
        	1.FileInputStream: 字节输入流 -> BufferedInputStream: 字节缓冲输入流
        	2.FileOuputStream: 字节输出流 -> BufferedOuputStream: 字节缓冲输出流
        字符流: 
	1.FileReader: 字符输入流 -> BufferedReader: 字符缓冲输入流 -> readLine()读一行
        2.FileWriter: 字符输出流 -> BufferedWriter: 字符缓冲输出流 -> newLine()换行

16. 转换流-概念 -------------------- 其他流

什么是转换流? 
	可以将字符流和字节流相互转换

回忆字符流的底层?
	字符流 = 字节流 + 编码表
    
InputStreamReader  
	从字节流 -> 字符流的桥梁, 使用指定的charset将"读到的字节解码为字符"
OutputStreamWriter: 
	从字符流 -> 字节流的桥梁, 使用指定的charset将"写入的字符编码为字节"

关系图示
	文件 -> 字节流 -> 转换流InputStreamReader -> 字符流FileReader -> 内存
	内存 -> 字节流 -> 转换流OutputStreamWriter -> 字符流FileWriter -> 文件
	

17. 转换流-指定编码读写

转换流的使用场景
	JDK11之前: 指定编码读写
	JDK11之后: 一般不会直接操作转换流了
		
代码示例(使用转换流读写数据)
    public class Demo {
        public static void main(String[] args) throws IOException {
            //使用转换流, 按照指定charset读数据
            InputStreamReader isr = new InputStreamReader(new FileInputStream("C:\\Users\\Administrator\\Desktop\\a.txt"), "GBK");
            int ch;
            while ((ch = isr.read()) != -1) {
                System.out.print((char) ch + " "); //道 北 吴 彦 祖
            }
            isr.close();

            //使用转换流, 按照指定charset写数据
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("C:\\Users\\Administrator\\Desktop\\b.txt"), "UTF-8");
            osw.write("从IDEA中朝桌面写数据,指定字符集为UTF-8才不会乱码!");
            osw.flush();
            osw.close();

            //注意: txt文本文档会自动改变字符集, 我们可以通过查看字节验证编码格式的改变, 这里了解即可!
        }

        //字符流读数据
        private static void method01() throws IOException {
            FileReader fr = new FileReader("C:\\Users\\Administrator\\Desktop\\a.txt");
            int ch;
            while ((ch = fr.read()) != -1) {
                System.out.print((char) ch + " "); //� � � � � � � Ա
                /*
                    乱码问题
                        1. a.txt是在电脑桌面保存的, a.txt使用的是windows系统默认的编码表GBK中的ANSI编码格式
                        2. 我们在IDEA中写代码读取a.txt, IDEA默认使用是Unicode码表中的UTF-8编码格式
                        3. 编码和解码的编码格式不一致, 就会出现乱码!
                 */
            }
            fr.close();
        }
    }

JDK11之后: 一般不会直接操作转换流了
    因为FileReader在底层帮我们提供了一个新的构造, 可以直接指定Charset
        Charset.for(字符集名称);

构造源码
    Since 11 : 这个注释的意思代表, 该构造从JDK11开始可以使用
    public FileReader(String fileName, Charset charset) throws IOException {
        super(new FileInputStream(fileName), charset);
    }
    
代码示例
    public class Demo02 {
        public static void main(String[] args) throws IOException {
            // 使用字符输入流读数据(底层帮忙简化了转换流)
            FileReader fr = new FileReader("C:\\Users\\Administrator\\Desktop\\a.txt", Charset.forName("GBK"));
            int ch;
            while ((ch = fr.read()) != -1){
                System.out.print((char) ch + " "); //道 北 吴 彦 祖
            }
        }
    }

18. 对象操作流-基本特点

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

19. 对象操作流-序列化

对象操作流
    1. 对象操作输入流 (对象反序列化流): ObjectInputStream
    2. 对象操作输出流 (对象序列化流): ObjectOutputStream
        oos.writeObject(对象名);

Serializable接口
    1. 是一个标记性接口, 接口中没有提供任何抽象方法
    2. 实现这个接口代表该类对象可以被序列化, 否则会报错

代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //创建对象
            User user = new User("张三","qwer");
            //创建对象操作输出流 (对象序列化流): ObjectOutputStream
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\Demo11.txt"));
            //写出对象数据: writeObject(对象名);
            oos.writeObject(user);
            //国际惯例
            oos.close();

            /*
                文件中的内容:
                    �� sr )com.demo19_对象操作流_序列化.
                    User��u�sCԓ L namet Ljava/lang/String;
                    L passwordq ~ xpt 张三t qwer
             */
        }
    }

    /*
        如果改类对象需要序列化, 必须实现Serializable接口, 否则报错:
            java.io.NotSerializableException: com.demo19_对象操作流_序列化.User
     */
    class User implements Serializable {
        private String name;
        private String password;

        public User() {
        }

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

        public String getName() {
            return name;
        }

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

        public String getPassword() {
            return password;
        }

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

20. 对象操作流-反序列化

对象操作流
	对象操作输入流 (对象反序列化流): ObjectInputStream

代码示例(读取上一个代码写出的Demo11.txt)
    public class Demo {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            //对象操作输入流 (对象反序列化流): ObjectInputStream
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\Demo11.txt"));
            //读数据
            User user = (User) ois.readObject();
            //打印对象
            System.out.println(user); //打印对象出现地址? 怎么看属性?
        }
    }

21. 对象操作流-两个注意点1

我们使用序列化流写入一个对象数据, 假如后来修改了对象所属的类文件, 那么再次读该对象时会不会出现问题?
    会的, 会报错
        Exception in thread "main" java.io.InvalidClassException:
        com.demo19_对象操作流_序列化.User; local class incompatible:
        stream classdesc serialVersionUID = -4901757396016966509,
        local class serialVersionUID = -3344419051753874665
    serialVersionUID: 序列号
    通过序列化流写入一个对象数据, 如果我们没有提供序列号, 系统会给一个
    这个错误表示, 写入时的类中序列号和, 读取时的类中序列号不一样

如果出现问题, 该如何解决?
    我们自己定义一个序列号即可
    private static final long serialVersionUID = 1L;

如果一个对象中的某个成员变量的值, 不想被序列化, 应该怎么实现?
    将该变量使用transient关键字修饰, 代表该变量就不参与序列化
		private String name;
		private transient String password; //该属性不参与序列化, 没有赋值那么写入文件为null

22. 对象操作流-两个注意点2

 

23. 对象操作流-练习

练习需求:
    创建多个JavaBean对象到文件中, 再次读取到程序中

注意: 读取对象读到文件末尾, 不是-1, 而会报异常
    解决方式1:
        我们需要try...catch捕获异常, 然后结束while循环
    解决方式2:
        写对象时, 存入一个容器, 整体序列化这个容器即可
        
代码示例
public class Demo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //实例化对象
        Singer s1 = new Singer("周杰伦", 18);
        Singer s2 = new Singer("林俊杰", 19);
        Singer s3 = new Singer("潘玮柏", 20);

        //解决方式2: 写对象时, 存入一个容器, 整体序列化这个容器即可
        ArrayList<Singer> list = new ArrayList<>();
        list.add(s1);
        list.add(s2);
        list.add(s3);

        //写对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\Demo13.txt"));
        oos.writeObject(list);
        oos.close();

        //读对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\Demo13.txt"));
        ArrayList<Singer> newList = (ArrayList<Singer>) ois.readObject();
        System.out.println(newList); //[Singer{name='周杰伦', age=18}, Singer{name='林俊杰', age=19}, Singer{name='潘玮柏', age=20}]

    }

    private static void method01() throws IOException, ClassNotFoundException {
        //实例化对象
        Singer s1 = new Singer("周杰伦", 18);
        Singer s2 = new Singer("林俊杰", 19);
        Singer s3 = new Singer("潘玮柏", 20);

        //写对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("day12\\Demo13.txt"));
        oos.writeObject(s1);
        oos.writeObject(s2);
        oos.writeObject(s3);
        oos.close();

        //读对象
//        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\Demo13.txt"));
//        Object obj;
//        while ((obj = ois.readObject()) != null){
//            System.out.println(obj);
//            /*
//                Singer{name='周杰伦', age=18}
//                Singer{name='林俊杰', age=19}
//                Singer{name='潘玮柏', age=20}
//                //数据是读到了, 但是当用readObject读到文件结尾, 会EOFException报错
//                Exception in thread "main" java.io.EOFException:
//             */
//        }
        //读对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("day12\\Demo13.txt"));
        Object obj;
        //解决方式1: 我们需要try...catch捕获异常, 然后结束while循环
        while (true) {
            try {
                obj = ois.readObject();
                System.out.println(obj);
                /*
                    Singer{name='周杰伦', age=18}
                    Singer{name='林俊杰', age=19}
                    Singer{name='潘玮柏', age=20}
                 */
            } catch (EOFException e) {
                // 解决1: 如果读到EOFException, 结束循环
                break;
            }
        }
    }
}

class Singer implements Serializable {
    //提供序列号: 不能修改变量名
    //private static final long serialVersionUIDhahaha = 2L;
    private static final long serialVersionUID = 2L;

    private String name;
    private int age;

    public Singer() {
    }

    public Singer(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 "Singer{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

24. Properties-概述

Properties继承自Hashtable, Hashtable实现了Map接口, 所以Properties本质是一个map集合

为什么IO这里讲Properties集合呢?
    因为Properties中有两个和IO相关的方法, 非常好用
    
Properties的使用注意?
	Properties创建时不写泛型, 可以存储任意类型的数据
	但是一般我们都存字符串 (键值对定义为String类型)

25. Properties-作为map集合的基本使用

Properties作为map集合的基本使用练习
    public class Demo {
        public static void main(String[] args) {
            Properties pro = new Properties();
            // 增
            pro.put("黄蓉", "郭靖");
            pro.put("小龙女", "尹志平");
            pro.put("赵敏", "张无忌");
            pro.put("沈璧君", "萧十一郎");
            pro.put("永琪", "尔康");
            System.out.println(pro); 
            // 删
            pro.remove("永琪");
            System.out.println(pro); 
            // 改
            pro.put("永琪", "小燕子");
            System.out.println(pro);
            // 查
            // 遍历map1
            Set<Object> keys = pro.keySet();
            for (Object key : keys) {
                Object value = pro.get(key);
                System.out.println(key + "--" + value);
            }
            System.out.println("----------------");
            // 遍历map2
            Set<Map.Entry<Object, Object>> entries = pro.entrySet();
            for (Map.Entry<Object, Object> entry : entries) {
                System.out.println(entry.getKey() + "--" + entry.getValue());
            }
        }
    }
    

26. Properties-特有方法

Properties特有方法
	1. Object setProperty(String key, String value); 设置集合的键和值,底层调HashTable的put
	2. String getProperty(String key); 根据键返回值值
	3. Set<String> stringPropertyNames(); 返回一个不可修改的键集, 键和值都是字符串
	
代码示例
    public class Demo {
        public static void main(String[] args) {
            Properties pro = new Properties();
            //1. Object setProperty(String key, String value); 设置集合的键和值,底层调HashTable的put
            pro.setProperty("张三", "北京");
            pro.setProperty("李四", "西安");
            pro.setProperty("王五", "厦门");
            System.out.println(pro); //{李四=西安, 张三=北京, 王五=厦门}

            pro.setProperty("张三", "草滩六路");
            System.out.println(pro); //{李四=西安, 张三=草滩六路, 王五=厦门}

            //2. String getProperty(String key); 根据键返回值值
            System.out.println(pro.get("张三")); //草滩六路
            System.out.println(pro.get("李四")); //西安
            System.out.println(pro.get("王五")); //厦门

            //3. Set<String> stringPropertyNames(); 返回一个不可修改的键集, 键和值都是字符串
            Set<String> keys = pro.stringPropertyNames();
            for (String key : keys) {
                Object value = pro.get(key);
                System.out.println(key + "--" + value);
                /*
                    李四--西安
                    张三--草滩六路
                    王五--厦门
                 */
            }
        }
    }
    

27. Properties-load

Properties和IO流结合的方法
	1. void load(Reader reader); 从Properties文件读取键值对到内存中
	2. void store(OutputStream out, String comments); 从内存写键值对数据到Properties文件
	
代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //创建Properties对象
            Properties pro = new Properties();
            //创建输入流对象
            FileReader fr = new FileReader("day12\\prop.properties");
            //读数据到Properties集合
            pro.load(fr);
            //释放资源
            fr.close();
            //展示结果
            System.out.println(pro); //{password=123, username=admin}

            //加入多个键值对数据的结果
            /*
                {password=123, password2=12345, password1=1234, username2=admin2, username1=admin1, username=admin}
             */
        }
    }
    

28. Properties-store

代码示例
    public class Demo {
        public static void main(String[] args) throws IOException {
            //创建Properties对象
            Properties pro = new Properties();
            //添加元素
            pro.put("黄蓉", "郭靖");
            pro.put("小龙女", "尹志平");
            pro.put("赵敏", "张无忌");
            pro.put("沈璧君", "萧十一郎");
            pro.put("永琪", "尔康");

            //创建输出流对象
            FileWriter fw = new FileWriter("day12\\prop1.properties");
            //写数据
            pro.store(fw, "我是注释 aaa 111 哈哈哈");
            //释放资源
            fw.close();
        }
    }
    

举报

相关推荐

0 条评论