• 深入理解Java IO流(第三篇)


    ✅作者简介:大家好我是@每天都要敲代码,一位材料转码农的选手,希望一起努力,一起进步!
    📃个人主页:@每天都要敲代码的个人主页

    🔥系列专栏:JavaSE从入门到精通
    💬推荐一款模拟面试、刷题神器,从基础到大厂面试题👉点击跳转刷题网站进行注册学习

    目录

    🥅目录拷贝

    🥅ObjectInputStream && ObjectOutputStream

    1.序列化的实现

    2.反序列化的实现

    3.序列化 && 反序列化多个对象

    4.序列化版本号

    🥅IO和Properties联合使用

    结束语


    🥅目录拷贝

    把一个目录拷贝到另一个目录
    需要用到:FileInputStream+FileOutputStream+File+递归的思想

    1. package com.bjpowernode.java.io;
    2. import java.io.*;
    3. public class CopyAll {
    4. public static void main(String[] args) {
    5. // 拷贝源
    6. File srcFile = new File("C:\\Java学习\\javaSE学习");
    7. // 拷贝目标
    8. File destFile = new File("D:\\a\\b\\c");
    9. // 调用方法进行拷贝
    10. copyDir(srcFile,destFile);
    11. }
    12. /**
    13. * 拷贝目录
    14. * @param srcFile 拷贝源
    15. * @param destFile 拷贝目标
    16. */
    17. private static void copyDir(File srcFile, File destFile) {
    18. //4、递归结束的条件,如果是文件就结束(就开始拷贝)
    19. if(srcFile.isFile()){
    20. // 是文件的是要要拷贝文件,一边读一遍写
    21. FileInputStream in = null;
    22. FileOutputStream out = null;
    23. try {
    24. // 读文件
    25. in = new FileInputStream(srcFile);
    26. // 写文件
    27. String path = (destFile.getAbsolutePath().endsWith("\\")?destFile.getAbsolutePath() :
    28. destFile.getAbsolutePath()+"\\")+srcFile.getAbsolutePath().substring(3);
    29. System.out.println(path);
    30. out = new FileOutputStream(path);
    31. // 一边读一边写
    32. byte[] bytes = new byte[1024*1024];// 1M
    33. int readCount = 0;
    34. while((readCount = in.read(bytes)) != -1){
    35. out.write(bytes,0,readCount);
    36. }
    37. // 刷新
    38. out.flush();
    39. } catch (FileNotFoundException e) {
    40. e.printStackTrace();
    41. } catch (IOException e) {
    42. e.printStackTrace();
    43. } finally {
    44. if (out != null) {
    45. try {
    46. out.close();
    47. } catch (IOException e) {
    48. e.printStackTrace();
    49. }
    50. }
    51. if (in != null) {
    52. try {
    53. in.close();
    54. } catch (IOException e) {
    55. e.printStackTrace();
    56. }
    57. }
    58. }
    59. return; // srcFile如果是一个文件,递归结束
    60. }
    61. //1、 获取拷贝源下面的子目录
    62. File[] files = srcFile.listFiles(); //列出源文件下的子目录(目录和文件)
    63. // 这个file有可能是目录,也有可能是文件;但都是一个File对象
    64. for(File file:files){
    65. // 获取所有文件的(目录和文件)绝对路径;进行打印测试
    66. //System.out.println(file.getAbsolutePath());
    67. // 3、创建对应的目录(目标文件对应的目录,要提前创建好)
    68. if(srcFile.isDirectory()){ // 如果是一个目录
    69. // 新建对应的目录
    70. String srcDir = file.getAbsolutePath(); // 拿出所有的源目录
    71. // 这里如果只是一个盘符的话,写上"//"会被识别出来;如果后面有其它目录,就识别不了"//"
    72. // srcDir.substring(3)表示从下标为3的地方开始截取
    73. String destDir = (destFile.getAbsolutePath().endsWith("\\")? destFile.getAbsolutePath() :
    74. destFile.getAbsolutePath()+"\\")+srcDir.substring(3); // 目标目录
    75. // 获取路径后,开始创建
    76. File newFile = new File(destDir);
    77. if(!newFile.exists()){ // 如果不存在,就递归创建目录
    78. newFile.mkdirs();
    79. }
    80. }
    81. //2、 递归调用(是目录的话一直往下拿,直到是一个文件就开始拷贝)
    82. copyDir(file,destFile);
    83. }
    84. }
    85. }

    🥅ObjectInputStream && ObjectOutputStream

    1、首先我们先明白两个概念:序列化(Serialize)反序列化(DeSerialize)

            序列化:将java对象存储到文件中,将java对象的状态保存下来的过程

            反序列化:将硬盘上的数据重新恢复到内存当中,恢复成java对象

    2、ObjectInputStream 和 ObjectOutputStream的作用

            ObjectOutputStream是用来序列化的---》拆分对象

            ObjectInputStream是用来反序列化的---》组装对象

    3、通过图,来理清楚它们之间的关系

     

    1.序列化的实现

    (1)参与序列化和反序列化的对象必须实现Serializable接口。如果下面的Student类没有实现Serializable接口,会报java.io.NotSerializableException,译为:Student对象不支持序列化!

    (2)注意:通过源代码发现,Serializable接口只是一个标志接口:

    1. public interface Serializable {
    2.     }

    (3)这个接口当中什么代码都没有;那么它起到一个什么作用呢?
           起到标识的作用,标志的作用,java虚拟机看到这个类实现了这个接口,可能会对这个类进行特殊待遇。Serializable这个标志接口是给java虚拟机参考的,java虚拟机看到Serializable这个接口之后,会为该类自动生成一个序列化版本号

    1. package com.bjpowernode.java.io;
    2. import java.io.FileOutputStream;
    3. import java.io.ObjectOutputStream;
    4. import java.io.Serializable;
    5. public class ObjectOutputStreamTest01 {
    6. public static void main(String[] args) throws Exception {
    7. // 创建Java对象
    8. Student stu = new Student(111,"zhangsan");
    9. // 序列化
    10. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("s文件"));
    11. // 序列化对象
    12. oos.writeObject(stu);
    13. // 刷新
    14. oos.flush();
    15. // 关闭
    16. oos.close();
    17. }
    18. }
    19. // 学生类
    20. class Student implements Serializable {
    21. private int no;
    22. private String name;
    23. // 构造方法
    24. public Student() {
    25. }
    26. public Student(int no, String name) {
    27. this.no = no;
    28. this.name = name;
    29. }
    30. // setter and getter
    31. public int getNo() {
    32. return no;
    33. }
    34. public void setNo(int no) {
    35. this.no = no;
    36. }
    37. public String getName() {
    38. return name;
    39. }
    40. public void setName(String name) {
    41. this.name = name;
    42. }
    43. // 重写toString
    44. public String toString() {
    45. return "Student{" +
    46. "no=" + no +
    47. ", name='" + name + '\'' +
    48. '}';
    49. }
    50. }

    2.反序列化的实现

    1. package com.bjpowernode.java.io;
    2. // 反序列化
    3. import java.io.FileInputStream;
    4. import java.io.ObjectInputStream;
    5. public class ObjectInputStreamTest01 {
    6. public static void main(String[] args) throws Exception{
    7. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("s文件"));
    8. // 开始反序列化,读
    9. Object obj = ois.readObject();
    10. // 反序列化回来是一个学生对象,所以会调用学生对象的toString方法。
    11. System.out.println(obj);
    12. // 关闭
    13. ois.close();
    14. }
    15. }

    3.序列化 && 反序列化多个对象

    1、可以一次序列化多个对象呢?
         可以将对象放到集合当中,序列化集合!
    2、参与序列化的ArrayList集合以及集合中的元素User都需要实现 java.io.Serializable接口

    3、补充一个关键字:transient;这个关键字表示游离的,不参与序列化。     

    序列化 

    1. package com.bjpowernode.java.io;
    2. import java.io.FileNotFoundException;
    3. import java.io.FileOutputStream;
    4. import java.io.ObjectOutputStream;
    5. import java.io.Serializable;
    6. import java.util.ArrayList;
    7. import java.util.List;
    8. public class ObjectOutputStreamTest02 {
    9. public static void main(String[] args) throws Exception {
    10. // 创建集合
    11. List userList = new ArrayList<>();
    12. // 增加元素
    13. userList.add(new User(1,"zhangsan"));
    14. userList.add(new User(2,"lisi"));
    15. userList.add(new User(3,"wangwu"));
    16. // 序列化
    17. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Users"));
    18. // 序列化集合--->一次性序列化多个对象
    19. oos.writeObject(userList);
    20. // 刷新
    21. oos.flush();
    22. // 关闭
    23. oos.close();
    24. }
    25. }
    26. // USer类
    27. class User implements Serializable {
    28. private int num;
    29. private transient String name; // name不参与序列化操作;name打印出来的结果就是null
    30. // 构造方法
    31. public User() {
    32. }
    33. public User(int num, String name) {
    34. this.num = num;
    35. this.name = name;
    36. }
    37. // setter and getter
    38. public int getNum() {
    39. return num;
    40. }
    41. public void setNum(int num) {
    42. this.num = num;
    43. }
    44. public String getName() {
    45. return name;
    46. }
    47. public void setName(String name) {
    48. this.name = name;
    49. }
    50. // 重写toStringF方法
    51. public String toString() {
    52. return "User{" +
    53. "num=" + num +
    54. ", name='" + name + '\'' +
    55. '}';
    56. }
    57. }

    反序列化 

    1. package com.bjpowernode.java.io;
    2. import java.util.List;
    3. import java.io.FileInputStream;
    4. import java.io.ObjectInputStream;
    5. // 反序列化
    6. public class ObjectInputStreamTest02 {
    7. public static void main(String[] args) throws Exception {
    8. // 开始反序列化,读
    9. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Users"));
    10. // 反序列化回来一个List集合
    11. //Object obj = ois.readObject();
    12. //System.out.println(obj instanceof List); // true,得到返回的是List集合
    13. List userList = (List)ois.readObject();
    14. for(User u:userList){
    15. System.out.println(u);
    16. }
    17. // 关闭
    18. ois.close();
    19. }
    20. }

    4.序列化版本号

    (1)java语言中是采用什么机制来区分类的?
          第一:首先通过类名进行比对,如果类名不一样,肯定不是同一个类。
          第二:如果类名一样,再怎么进行类的区别?靠序列化版本号进行区分。

    (2)序列化版本号有什么用呢?

        小鹏编写了一个类:com.bjpowernode.java.bean.Student implements Serializable
        胡浪编写了一个类:com.bjpowernode.java.bean.Student implements Serializable

    所以序列化版本号是用来区分类的!例如:类名相同,我们可以通过不通的序列化版本号来区分它;对于但也有缺点,对于同一个代码,我们更改了,必须重新编译才可以!

    例如:对于上述的Student类,我们采用默认的序列化版本号,一年前我们实现了这个功能,有一个序列化版本号;一年后我们优化更改了代码,此时JVM就会认为这已经不是同一个类,必须重新进行编译,才能反序列化
    (3)自动生成序列化版本号的好处和缺陷    

    好处:不同的人编写了同一个类,但“这两个类确实不是同一个类”。这个时候序列化版本就起上作用了。对于java虚拟机来说,java虚拟机是可以区分开这两个类的,因为这两个类都实现了Serializable接口,都有默认的序列化版本号,他们的序列化版本号不一样。所以区分开了。)
    缺陷:这种自动生成的序列化版本号缺点是一旦代码确定之后,不能进行后续的修改,因为只要修改,必然会重新编译,此时会生成全新的序列化版本号,这个时候java虚拟机会认为这是一个全新的类。

    (4)总结:从需求来看

    凡是一个类实现了Serializable接口,建议给该类提供一个固定不变的序列化版本号。这样,以后这个类即使代码修改了,但是版本号不变,java虚拟机会认为是同一个类。

    Java虚拟机看到Serializable接口之后,会自动生成一个序列化版本号。
    这里没有手动写出来,java虚拟机会默认提供这个序列化版本号。
    建议将序列化版本号手动的写出来。不建议自动生成
       

     private static final long serialVersionUID = 1L;

       java虚拟机识别一个类的时候先通过类名,如果类名一致,再通过序列化版本号。

    (5)设置IDEA自动生成序列版本号

    File--->Settings--->Code Style--->Inspections--->把下面这个打上对勾--->Apply--->OK

     

     最终回到我们继承Serializable接口的类名上,alt+Enter即可自动生成序列版本号

    🥅IO和Properties联合使用

    (1)设计理念:把以后经常改变的数据,可以单独写到一个文件中,使用程序动态读取。
        将来只需要修改这个文件的内容,java代码不需要改动,不需要重新编译,服务器也不需要重启;就可以拿到动态的信息。

    (2)类似于以上机制的这种文件被称为配置文件;并且当配置文件中的内容格式是:
            key1=value
            key2=value
    的时候,我们把这种配置文件叫做属性配置文件,在属性配置文件当中#注释,如果key重复的话,value会自动覆盖。

    (3)java规范中有要求:属性配置文件建议以.properties结尾,但这不是必须的
        这种以.properties结尾的文件在java中被称为:属性配置文件;其中Properties是专门存放属性配置文件内容的一个类

    (4)Properties是一个Map集合,key和value都是String类型

    1. package com.bjpowernode.java.io;
    2. import java.io.FileReader;
    3. import java.util.Properties;
    4. public class IoProperiesTest02 {
    5. public static void main(String[] args) throws Exception {
    6. // 新建一个输入流对象
    7. FileReader reader = new FileReader("temp.txt");
    8. // 新建一个Map集合
    9. Properties pro = new Properties();
    10. // 调用Properties对象的load方法将文件中的数据加载到Map集合中。
    11. pro.load(reader);// 文件中的数据顺着管道加载到Map集合中,其中等号=左边做key,右边做value
    12. // 通过key获取value
    13. String username = pro.getProperty("username");
    14. System.out.println(username); //admin
    15. // 如果我们把admin改成root,不需要重新进行编译,就能直接运行得到root
    16. System.out.println(pro.getProperty("password")); // 123
    17. }
    18. }
    19. /*temp.txt文件里的内容
    20. username=admin
    21. password=123
    22. */

    结束语

    今天的分享就到这里啦!快快通过下方链接注册加入刷题大军吧!各种大厂面试真题在等你哦!

     💬刷题神器,从基础到大厂面试题👉点击跳转刷题网站

  • 相关阅读:
    terraform简单的开始-vpc cvm创建
    数据集成面试题
    Hive新增/变更字段后,Hive metastore与Hive tblproperties缓存的spark schema不一致处理
    MongoDB 简介
    由浅入深——晶体管放大电路基础
    刷题记录:牛客NC51170石子合并
    Javascript知识【jQuery-基本操作】下篇
    华为略施小计,问界M7销量暴增10倍?
    Impeller-Flutter的新渲染引擎
    Rust中的输入输出格式变化(非常详细)
  • 原文地址:https://blog.csdn.net/m0_61933976/article/details/126151346