Java基础进阶-7-序列化+解序列化+文本存储

目录

1、内部类

2、序列化

3、serialVersionUID

4、解序列化

5、文本存储


1、内部类

  • 内部类可以使用外部类的所有方法和变量,即使是私有的。
  • 内部类的实例一定会绑在外部类的实例上。
  • 内部类的作用:让一个类(外部类)可以实现同一个接口的多次。或者让一个类可以继承父类多次。-------这样做的目的:就是为了让一个类可以保持接口的多种不同状态。

2、序列化

如果我们在一些应用中,需要保存我们某个对象到本地的话,可以使用序列化的形式来存储。

  1. 被序列化的对象保存了自身实例变量的值,因此在我们写入文件之中之后,再被取回来的时候,可以转换为我们原来的对象。
  2. 当对象被序列化时,被该对象引用的实例变量也会被序列化,所以如果它的实例变量中有类对象,那么这个类也会被序列化。
  3. Serializable接口又被称为marker或tag类的标记用接口,因为此接口并没有任何方法需要实现。它的唯一目的就是声明实现它的类可以被序列化。
  4. 如果父类可以被序列化,那么它的子类也可以被序列化(不需要明确实现Serializable接口)。
  5. 静态变量不会被序列化,因为所有的对象都会共享同一份静态变量值。
  6. 更多属性在下面的代码zho
public class Test_Serializable {

    static class Student implements Serializable {
        
        static final long serialVersionUID=23333;
        String name;
        int age;
        /**
         * 1、Student内所有类对象都应该可以被序列化,否则Student将序列化失败,并报错。
         * 2、如果两个Student对象中对Person的引用指向同一个对象,那么这个Person只会被存储
         *    一次,另一个Student被复原以后,会拥有该对象的指向。
         */
        Person p = new Person();
        /**
         * 1、当然如果你不需要对该对象进行序列化的话,可以用transient标记,这样即使Teacher
         *    没有实现Serializable接口,Student也能序列化成功。
         */
        transient Teacher t=new Teacher();

        Student(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }

    /**
     * 1、因为Student持有对Person对象的引用,所以Person类也必须实现Serializable接口,否则
     *    Student对象将会序列化失败。
     */
    static class Person implements Serializable{
        String name;
    }
    static class Teacher {
        String name;
    }
    /**
     * 1、FileOutputStream是写入字节的方法,所以我们需要先把我们的对象转换成字节,
     *   ObjectOutputStream就是将我们的对象转换成可以写入的串流数据。
     */
    private static void testWrite() throws IOException {

        Student s = new Student("张三", 22);
        Student s1 = new Student("李四", 12);
        // 创建存取文件的FileOutputStream对象
        FileOutputStream fileStream = new FileOutputStream("Student.ser");
        // 创建写入对象的ObjectOutputStream对象
        ObjectOutputStream os = new ObjectOutputStream(fileStream);
        os.writeObject(s);
        os.writeObject(s1);
        os.close();
    }
    
    public static void main(String[] args) {
        try {
            testWrite();
            testRead();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

3、serialVersionUID

当对象被序列化的同时,该对象会被添加一个类的版本识别ID。这个ID被成为serialVersionUID,它是根据类的结构信息计算出来的。

在对象被解序列化时,如果在对象被序列化之后有了不同的serialVersionUID,则还原操作会失败。

如果你认为类会被演化(序列化存储之后,修改该类),可以把serialVersionUID放在类中,就比如上面的 Student类中。

4、解序列化

* 解序列化过程:
* 1、对象从stream流中读取出来;
* 2、Java虚拟机通过存储信息判断出对象的class类型;
* 3、Java虚拟机尝试寻找和加载对象的类。如果Java虚拟机找不到或无法加载该类,
*    则Java虚拟机会抛出异常ClassNotFoundException;
* 4、新的对象会被配置在堆上,但是构造函数不会被执行!执行会抹除我们对象的信息。
* 5、如果对象在继承树上有一个不可序列化的祖先类,则该不可序列化类以及在它之上
*    的类的构造函数(就算是可序列化也会执行)就会执行。一旦构造函数连锁启动之后
*    将无法停止。也就是说,从第一个不可序列化的父类开始,全部都会重新初始状态。
* 6、对象的实例变量会被还原成序列化之前的状态值。transient的变量会被赋予
*    默认值。
    /**
     * 解序列化过程:
     * 1、对象从stream流中读取出来;
     * 2、Java虚拟机通过存储信息判断出对象的class类型;
     * 3、Java虚拟机尝试寻找和加载对象的类。如果Java虚拟机找不到或无法加载该类,
     *    则Java虚拟机会抛出异常ClassNotFoundException;
     * 4、新的对象会被配置在堆上,但是构造函数不会被执行!执行会抹除我们对象的信息。
     * 5、如果对象在继承树上有一个不可序列化的祖先类,则该不可序列化类以及在它之上
     *    的类的构造函数(就算是可序列化也会执行)就会执行。一旦构造函数连锁启动之后
     *    将无法停止。也就是说,从第一个不可序列化的父类开始,全部都会重新初始状态。
     * 6、对象的实例变量会被还原成序列化之前的状态值。transient的变量会被赋予
     *    默认值。
     */
    private static void testRead() throws IOException, ClassNotFoundException {

        FileInputStream fileStream=new FileInputStream("Student.ser");
        ObjectInputStream os=new ObjectInputStream(fileStream);
        // 每次调用readObject都会都会从stream流中读取下一个对象,读取顺序与写入顺序相同,次数超出会抛出异常
        Object o1=os.readObject();
        Object o2=os.readObject();
        os.close();
        Student s1= (Student) o1;
        Student s2= (Student) o2;
        System.out.println("Student1.name="+s1.name);
        System.out.println("Student2.name="+s2.name);
    }

5、文本存储

public class Test_Write_String {

    private static void testWrite() {
        String s = "我想要存储在本地my.txt文件中\n";
        try {
            // false参数可以每次清空my.txt文件内的内容,重新写入语句
            FileWriter writer = new FileWriter("my.txt", false);
            writer.write(s);
            writer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 1、BufferedWriter可以将我们要写入磁盘的文本先存储到BufferedWriter内部,缓存起来,当缓存空间占满之后,
     * 开始往本地磁盘写入,这样可以减少我们访问本地磁盘的次数,加快写入的效率。
     * 2、当然,如果我们向立即将BufferedWriter缓存内部的文本写入磁盘的话,可以使用 buffer.flush();
     */
    private static void testBufferWrite() {
        String s = "我先交由BufferedWriter存储,当它存满以后,把我写入到本地磁盘\n";
        String s1 = "我基础写入缓存中1\n";
        String s2 = "我基础写入缓存中2\n";
        String s3 = "我基础写入缓存中3\n";
        try {
            // true 可以在不删除前面内容的情况下,继续写入内容
            BufferedWriter buffer = new BufferedWriter(new FileWriter("my.txt", true));
            buffer.write(s);
            buffer.write(s1);
            buffer.write(s2);
            buffer.write(s3);
            buffer.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 1、利用BufferedReader可以提高我们的读取效率,它只有在读取BufferedReader的缓存没有任何内容的时候,
     * 才会去读取磁盘。
     */
    private static void testReader() {
        try {

            File file = new File("my.txt");
            FileReader reader = new FileReader(file);
            BufferedReader buffer = new BufferedReader(reader);

            String result;
            while ((result = buffer.readLine()) != null) {
                System.out.println("\n输出:" + result);
            }
            buffer.close();

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    /**
     * File对象代表磁盘上的文件,或者目录的路径名称;
     * 它不能读取或者代表文件中的数据。
     */
    private static void testFile() {

        File dir = new File("test.txt");
        boolean isMk = dir.mkdir();
        System.out.println("mkdir=" + isMk);
        System.out.println("绝对目录=" + dir.getAbsolutePath());
        if (dir.isDirectory()) {
            String[] list = dir.list();
            for (String s : list) {
                System.out.println("目录:" + s);
            }
        }
    }

    public static void main(String[] args) {
        testWrite();
        testFile();
        testBufferWrite();
        testReader();
    }
}

 

扫描二维码关注公众号,回复: 11195028 查看本文章

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

原创文章 120 获赞 34 访问量 28万+

猜你喜欢

转载自blog.csdn.net/qq_34589749/article/details/105295994