序列化和反序列化概述:
把对象以流的方式,写入到文件中保存,叫写对象,也叫对象的序列化
对象中包含的不仅仅是字符,使用字节流
ObjectOutputStream:对象的序列化流
writeObject(p);
把文件中保存的对象,以流的方式读取出来,叫做读对象,也叫对象的反序列化
读取的文件保存的都是字节,使用字节流
ObjectInputStream:对象的反序列化流
readObject();
对象的序列化流_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.资源释放*/ public class Demo01ObjectOutputStream { public static void main(String[] args) throws IOException { ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream("IO\\person.txt")); os.writeObject(new Person("小明",18)); os.close(); } } /* * 序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常 * 类的序列化由实现java.io.Serializable接口的类启用。 不实现此接口的类将不会使任何状态序列化或反序列化。 * Serializable接口也叫标记型接口 * 要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记 * 当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记 * 有:就可以序列化和反序列化 * 没有:就会抛出NotSerializableException异常 * 去市场买肉--》肉上有一个蓝色章(检测合格)--》放心购买--》买回来怎么吃随意 * */ public class Person implements Serializable{ private String name; private int age; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } public Person(String name, int age) { this.name = name; this.age = age; } public Person() { } 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; } }
对象反序列化流_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文件 * */ public class Demo02ObjectInputStream { public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream ois=new ObjectInputStream(new FileInputStream("IO\\person.txt")); Object o = ois.readObject(); ois.close(); System.out.println(o);//Person{name='小明', age=18} Person p = (Person) o; System.out.println(p.getName()+p.getAge());//小明18 } }
transient关键字_瞬态关键字:
static关键字:静态关键字 * 静态优先于非静态加载到内存中(静态优先于对象进入到内存中) * 被static修饰的成员变量是不能被序列化的,序列化的都是对象 * private static int age; * os.writeObject(new Person("小明",18)); * Object o = ois.readObject(); * Person{name='小明', age=0} * * transient关键字:瞬态关键字 * 被transient修饰的成员变量不能被序列化 * private transient int age; * os.writeObject(new Person("小明",18)); * Object o = ois.readObject(); * Person{name='小明', age=0}
InvalidClassException异常_原理:
序列化运行时将每个可序列化的类与称为serialVersionUID的版本号相关联,该序列号在反序列化期间用于验证序列化对象的发送者和接收者是否已加载与该序列化兼容的对象的类。 如果接收方加载了一个具有不同于相应发件人类的serialVersionUID的对象的类,则反序列化将导致InvalidClassException
。 一个可序列化的类可以通过声明一个名为"serialVersionUID"
的字段来显式地声明它自己的serialVersionUID,该字段必须是静态的,最终的,类型是long。
static final long serialVersionUID = 42L;
练习:序列化集合:
/* * 练习:序列化集合 * 当我们想在文件中保存多个对象的时候 * 可以把多个对象存储到集合中 * 对集合进行序列化和反序列化 * 分析: * 1.定义一个存储Person对象的ArrayList集合 * 2.往ArrayList集合中存储Person对象 * 3.创建一个序列化流ObjectOutputStream对象 * 4.使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化 * 5.创建一个反序列化OutputInputStream对象 * 6.使用ObjectInputStream对象中的方法readObject读取文中保存的集合 * 7.把Object类型的集合转换为ArrayList类型 * 8.遍历ArrayList集合 * 9.释放资源 * */ public class Demo03Test { 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)); list.add(new Person("小宫",21)); ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("IO\\list.txt")); oos.writeObject(list); ObjectInputStream ois=new ObjectInputStream(new FileInputStream("IO\\list.txt")); Object o = ois.readObject(); ArrayList<Person> list2=(ArrayList<Person>)o; for (Person p : list2) { System.out.println(p); } ois.close(); oos.close(); } }
打印流:
/* * java.io.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 * 继承自父类的成员方法: * void write(int c) 写一个字符 * void write(char[] cbuf) 写入一个字符数组。 * void write(char[] cbuf, int off, int len) 写入字符数组的一部分。 * void write(String str) 写一个字符串 * void write(String str, int off, int len) 写一个字符串的一部分。 * void flush() 刷新流 * void close() 关闭流,先刷新。 * 注意:如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97-》a * 如果使用自己的特有的方法print/println方法写数据,写的数据原样数据 97-》97 **/ public class Demo04PrintStream { public static void main(String[] args) throws FileNotFoundException { //System.out.println("hello world!"); PrintStream ps=new PrintStream("IO\\print.txt"); ps.write(97);//a ps.println(97);//97 ps.println(8.8); ps.println('a'); ps.println("HelloWorld"); ps.println(true); ps.println(97); ps.close(); } } /* * 可以改变输出语句的目的地(打印流的流向) * 输出语句,默认在控制台输出 * 使用System.setOut方法改变输出语句的目的地改为参数传递的打印流的目的地 * static void setOut(PrintStream out):重新分配“标准”输出流。 * */ public class Demo05PrintStream { public static void main(String[] args) throws FileNotFoundException { System.out.println("我是在控制台输出的"); PrintStream ps=new PrintStream("IO\\目的地是打印流.txt"); System.setOut(ps);//把输出语句的目的地改变为打印流的目的地 System.out.println("我在打印流的目的地中输出"); ps.close(); } }