序列化流、打印流

参考:http://www.cnblogs.com/chenpi/p/6185773.html

Java中对象的序列化指的是将对象转换成以字节序列的形式来表示,这些字节序列包含了对象的数据和信息,一个序列化后的对象可以被写到数据库或文件中,也可用于网络传输,一般当我们使用缓存cache(内存空间不够有可能会本地存储到硬盘)或远程调用rpc(网络传输)的时候,经常需要让我们的实体类实现Serializable接口,目的就是为了让其可序列化。

Java 提供了一种对象序列化的机制。用一个字节序列可以表示一个对象,该字节序列包含该对象的数据对象的类型对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。

反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化对象的数据对象的类型对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化。

对象的序列化流_ObjectOutputStream

java.io.ObjectOutputStream extends OutputStream
ObjectOutputStream:对象的序列化流
作用:把对象以流的方式写入到文件中保存
构造方法:
    ObjectOutputStream(OutputStream out) 创建写入指定 OutputStream 的 ObjectOutputStream。
    参数:
        OutputStream out:字节输出流
特有的成员方法:
    void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream。
使用步骤:
    1.创建ObjectOutputStream对象,构造方法中传递字节输出流
    2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
    3.释放资源

序列化对象

/*
    序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常
    类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
    Serializable接口也叫标记型接口
        要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
        当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
            有:就可以序列化和反序列化
            没有:就会抛出 NotSerializableException异常

    去市场买肉-->肉上有一个蓝色章(检测合格)-->放心购买-->买回来怎么吃随意


    static关键字:静态关键字
        静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
        被static修饰的成员变量不能被序列化的,序列化的都是对象
        private static int age;
        oos.writeObject(new Person("小美女",18));
        Object o = ois.readObject();
        Person{name='小美女', age=0}

    transient关键字:瞬态关键字
        被transient修饰成员变量,不能被序列化
        private transient int age;
        oos.writeObject(new Person("小美女",18));
        Object o = ois.readObject();
        Person{name='小美女', age=0}

 */

package com.itheima.demo04.ObjectStream;

import java.io.Serializable;
public class Person implements Serializable{
    private static final long serialVersionUID = 1L;
    private String name;
    //private static int age;
    //private transient int age;
    public int age;
    public Person() {
    }
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", 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;
    }
}

对象

实现Serializable接口

package com.itheima.demo04.ObjectStream;

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class Demo01ObjectOutputStream {
    public static void main(String[] args) throws IOException {
        //1.创建ObjectOutputStream对象,构造方法中传递字节输出流
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\person.txt"));
        //2.使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
        oos.writeObject(new Person("小美女",18));
        //3.释放资源
        oos.close();
    }
}

 

对象的反序列化流_ObjectInputStream

java.io.ObjectInputStream extends InputStream
ObjectInputStream:对象的反序列化流
作用:把文件中保存的对象,以流的方式读取出来使用

构造方法:
    ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream。
    参数:
        InputStream in:字节输入流
特有的成员方法:
    Object readObject() 从 ObjectInputStream 读取对象。

使用步骤:
    1.创建ObjectInputStream对象,构造方法中传递字节输入流
    2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
    3.释放资源
    4.使用读取出来的对象(打印)

 readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
 当不存在对象的class文件时抛出此异常
 反序列化的前提:
    1.类必须实现Serializable
    2.必须存在类对应的class文件
package com.itheima.demo04.ObjectStream;

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

public class Demo02ObjectInputStream {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.创建ObjectInputStream对象,构造方法中传递字节输入流
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\person.txt"));
        //2.使用ObjectInputStream对象中的方法readObject读取保存对象的文件
        Object o = ois.readObject();
        //3.释放资源
        ois.close();
        //4.使用读取出来的对象(打印)
        System.out.println(o);
        Person p = (Person)o;
        System.out.println(p.getName()+p.getAge());
    }

}

执行结果:

Person{name='小美女', age=18}
小美女18

Process finished with exit code 0

transient关键字_瞬态关键字 

static关键字:静态关键字
    静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
    被static修饰的成员变量不能被序列化的,序列化的都是对象
    private static int age;
    oos.writeObject(new Person("小美女",18));
    Object o = ois.readObject();
    Person{name='小美女', age=0}

transient关键字:瞬态关键字
    被transient修饰成员变量,不能被序列化
    private transient int age;
    oos.writeObject(new Person("小美女",18));
    Object o = ois.readObject();
    Person{name='小美女', age=0}

InvalidClassException异常_原理和解决方案

另外,当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改(反序列化时修改了序列化对象),那么反序列化操作也会失败,抛出一个InvalidClassException异常。发生这个异常的原因如下:

  • 该类的序列版本号与从流中读取的类描述符的版本号不匹配

  • 该类包含未知数据类型

  • 该类没有可访问的无参数构造方法

Serializable 接口给需要序列化的类,提供了一个序列版本号。

反序列化时修改类对象后,版本号不匹配了

serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。

在需要序列化和反序列化的类中自定义一个自己的serialVersionUID,这样就算反序列化时修改了序列化的对象,serialVersionUID不会改变。

练习_序列化集合

/*
    练习:序列化集合
        当我们想在文件中保存多个对象的时候
        可以把多个对象存储到一个集合中
        对集合进序列化和反序列化
    分析:
        1.定义一个存储Person对象的ArrayList集合
        2.往ArrayList集合中存储Person对象
        3.创建一个序列化流ObjectOutputStream对象
        4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
        5.创建一个反序列化ObjectInputStream对象
        6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
        7.把Object类型的集合转换为ArrayList类型
        8.遍历ArrayList集合
        9.释放资源
 */
package com.itheima.demo04.ObjectStream;

import java.io.*;
import java.util.ArrayList;

public class Demo03Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1.定义一个存储Person对象的ArrayList集合
        ArrayList<Person> list = new ArrayList<>();
        //2.往ArrayList集合中存储Person对象
        list.add(new Person("张三",18));
        list.add(new Person("李四",19));
        list.add(new Person("王五",20));
        //3.创建一个序列化流ObjectOutputStream对象
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("10_IO\\list.txt"));
        //4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
        oos.writeObject(list);
        
        
        //!上面为序列化,下面为反序列化
        
        //5.创建一个反序列化ObjectInputStream对象
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("10_IO\\list.txt"));
        //6.使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
        Object o = ois.readObject();
        //7.把Object类型的集合转换为ArrayList类型
        ArrayList<Person> list2 = (ArrayList<Person>)o;   //转换类型
        //8.遍历ArrayList集合
        for (Person p : list2) {
            System.out.println(p);
        }
        //9.释放资源
        ois.close();
        oos.close();
    }
}

打印流_概述和使用

/*
    java.io.PrintStream:打印流
        PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
    PrintStream特点:
        1.只负责数据的输出,不负责数据的读取
        2.与其他输出流不同,PrintStream 永远不会抛出 IOException
        3.有特有的方法,print,println
            void print(任意类型的值)
            void println(任意类型的值并换行)
    构造方法:
        PrintStream(File file):输出的目的地是一个文件
        PrintStream(OutputStream out):输出的目的地是一个字节输出流
        PrintStream(String fileName) :输出的目的地是一个文件路径
    PrintStream extends OutputStream
    继承自父类的成员方法:
        - public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
        - public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
        - public void write(byte[] b):将 b.length字节从指定的字节数组写入此输出流。
        - public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
        - public abstract void write(int b) :将指定的字节输出流。
    注意:
        如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
 */
package com.itheima.demo05.PrintStream;

import java.io.FileNotFoundException;
import java.io.PrintStream;

public class Demo01PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        //System.out.println("HelloWorld");

        //创建打印流PrintStream对象,构造方法中绑定要输出的目的地
        PrintStream ps = new PrintStream("10_IO\\print.txt");
        //如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
        ps.write(97);
        //如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
        ps.println(97);
        ps.println(8.8);
        ps.println('a');
        ps.println("HelloWorld");
        ps.println(true);

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

执行结果:

 

改变输出语句的目的地(打印流的流向)

/*
    可以改变输出语句的目的地(打印流的流向)
    输出语句,默认在控制台输出
    使用System.setOut方法改变输出语句的目的地改为参数中传递的打印流的目的地
        static void setOut(PrintStream out)
          重新分配“标准”输出流。
 */
package com.itheima.demo05.PrintStream;

import java.io.FileNotFoundException;
import java.io.PrintStream;

public class Demo02PrintStream {
    public static void main(String[] args) throws FileNotFoundException {
        System.out.println("我是在控制台输出");

        PrintStream ps = new PrintStream("10_IO\\目的地是打印流.txt");
        System.setOut(ps);//把输出语句的目的地改变为打印流的目的地   改变输出目的地
        System.out.println("我在打印流的目的地中输出");

        ps.close();
    }
}

System.out.println不再输出到终端(默认),而是自己设置的目的地。

执行结果:

设置之前默认在终端输出,设置之后在设置的文件中输出。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值