IO流:对象流、Poperties类

1、对象流OjectInputStream和ObjecOutputStream
   可以用它来实现对象的序列化和反序列化,但读写的对象必须实现Serializable序列化接口
   对象的输出流将指定的对象写入到文件的过程,就是将对象序列化的过程,对象的输入流将指定序列化好的文件读出来的过程,就是对象反序列化的过程
   
   常用构造方法:
   ObjectOutputStream oos = new ObjectOutputStream(OutputStream out);//创建一个写入指定OutputStream的ObjectOutputStream对象.   
   ObjectInputStream ois = new ObjectInputStream(InputStream in);//创建从指定 InputStream 读取的 ObjectInputStream

   public class Student implements Serializable{
    /**
     *但是,如果这时候这个obj.txt是我们项目中一个文件,而项目到后期在原来Student类的基础上添加成员变量String sex;   
     *private int id;
     *private String name;
     *private int age;
     *private String sex;//新添加成员变量
     *这时候如果我们再反序列化,则会引发异常:java.io.InvalidClassException: xuliehua.User; local class incompatible: stream classdesc serialVersionUID = 2161776237447595412, local class serialVersionUID = -3634244984882257127
     *serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是JVM(JAVA虚拟界)通过一个类的类名、成员、包名、工程名算出的一个数字。
     *    而这时候序列化文件中记录的serialVersionUID与项目中的不一致,即找不到对应的类来反序列化
     *如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后
     *    在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了
     */
    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private int age;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    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;
    }
    public Student(int id, String name, int age) {
        this.id = id;
        this.name = name;
        this.age = age;
    }
    public Student() {
    }
    public String toString() {
        return "Student [id=" + id + ", name=" + name + ", age=" + age + "]";
    }
   }

   public class ObjectInputStream {

    public static void main(String[] args) throws FileNotFoundException, IOException, ClassNotFoundException {
        //序列化
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:\test\test02.txt"));
        oos.writeObject(new Student(1,"小明",24));
        oos.writeObject(new Student(2,"小张",25));
        oos.writeObject(new Student(3,"小李",26));
        oos.writeObject(new Student(4,"小王",27));
        oos.writeObject(new Student(5,"小吕",28));
        oos.close();
        //反序列化
        java.io.ObjectInputStream ois = new java.io.ObjectInputStream(new FileInputStream("d:\test\test02.txt"));
        Object o = null;
        while((o = ois.readObject())!=null) {
            Student s = (Student)o;
            System.out.println(s);
        }
        ois.close();
    }

}

   写入文件内容:
� sr IOObject.Student       I ageI idL namet Ljava/lang/String;xp     t 灏忔槑sq ~       t 灏忓紶sq ~       t 灏忔潕sq ~       t 灏忕帇sq ~       t 灏忓悤
   
   运行打印内容:
Student [id=1, name=小明, age=24]
Student [id=2, name=小张, age=25]
Student [id=3, name=小李, age=26]
Student [id=4, name=小王, age=27]
Student [id=5, name=小吕, age=28]
反序列化时抛出java.io.EOFException异常

问题描述:在反序列化对象时,当对象出入流将文件的全部类反序列化之后,始终会抛出java.io.EOFException.

原因:java API文档中对于反序列化对象时使用的java.io.ObjectInputStream类的readObject()方法的描述有一句话是"该方法始终会抛出异常",也就是说该异常无法避免的.

解决方法:

  该异常是输入流已经到结尾了的标志,我们可以将其捕获,然后不做任何操作,即结束了该次反序列化操作

   public class ObjectStreamTest {
 
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        FileOutputStream out = new FileOutputStream("test3.txt");
        ObjectOutputStream outputStream = new ObjectOutputStream(out);
        outputStream.writeObject(new Student("魏金浩" , 23, 138896));
        outputStream.close();
        Student i;
        FileInputStream in = new FileInputStream("test3.txt");
        ObjectInputStream inputStream = new ObjectInputStream(in);
        i = (Student)inputStream.readObject();//容易造成异常的发生,可能读取超界
        System.out.println(i);
        inputStream.close();
    }
   }
   优化代码:
   public static void main(String[] args) throws IOException, ClassNotFoundException {
        FileOutputStream out = new FileOutputStream("test3.txt");
        ObjectOutputStream outputStream = new ObjectOutputStream(out);
        ArrayList<Student> a = new ArrayList<>();
        a.add(new Student("魏金浩" , 23, 138896));
        outputStream.writeObject(a);
        outputStream.close();
        ArrayList<Student> b;
        FileInputStream in = new FileInputStream("test3.txt");
        ObjectInputStream inputStream = new ObjectInputStream(in);
        b = (ArrayList)inputStream.readObject();//我们只需要读取一次所以不会造成越界
        inputStream.close();
        for (Student student : b) {
            System.out.println(student);
        }
   }


   transient关键字:
   当你不想要某些字段序列化时候,可以用transient关键字修饰
    private int id;
    private String name;
    private int age;
    private transient String sex;//新添加的成员变量//添加关键字transient后,序列化时忽略

   总结:

   1. 如果对象需要被写出到文件上,那么对象所属的类必须要实现Serializable接口。 Serializable接口没有任何的方法,是一个标识接口而已。
   2. 对象的反序列化创建对象的时候并不会调用到构造方法的。
   3. serialVersionUID 是用于记录class文件的版本信息的,serialVersionUID这个数字是通过一个类的类名、成员、包名、工程名算出的一个数字。
   4. 使用ObjectInputStream反序列化的时候,ObjeectInputStream会先读取文件中的serialVersionUID,然后与本地的class文件的serialVersionUID进行对比,如果这两个id不一致,反序列则失败。
   5. 如果序列化与反序列化的时候可能会修改类的成员,那么最好一开始就给这个类指定一个serialVersionUID,如果一类已经指定的serialVersionUID,然后在序列化与反序列化的时候,jvm都不会再自己算这个 class的serialVersionUID了。
   6. 如果一个对象某个数据不想被序列化到硬盘上,可以使用关键字transient修饰。
   7. 如果一个类维护了另外一个类的引用,则另外一个类也需要实现Serializable接口。

2、Poperties类

   Java中有个比较重要的类Properties(Java.util.Properties),主要用于读取Java的配置文件,各种语言都有自己所支持的配置文件,配置文件中很多变量是经常改变的,这样做也是为了方便用户,让用户能够脱离程序本身去修改相关的变量设置
   在Java中,其配置文件常为.properties文件,格式为文本文件,文件的内容的格式是“键=值”的格式,文本注释信息可以用"#"来注释。
   
   主要的方法:
1. getProperty ( String key),用指定的键在此属性列表中搜索属性。也就是通过参数 key ,得到 key 所对应的 value。

2. load ( InputStream inStream),从输入流中读取属性列表(键和元素对)。通过对指定的文件(比如说上面的 test.properties 文件)进行装载来获取该文件中的所有键 - 值对。以供 getProperty ( String key) 来搜索。

3. setProperty ( String key, String value) ,调用 Hashtable 的方法 put 。他通过调用基类的put方法来设置 键 - 值对。

4. store ( OutputStream out, String comments),以适合使用 load 方法加载到 Properties 表中的格式,将此 Properties 表中的属性列表(键和元素对)写入输出流。与 load 方法相反,该方法将键 - 值对写入到指定的文件中去。

5. clear (),清除所有装载的 键 - 值对。该方法在基类中提供。

   Java读取Properties文件
   最常用的还是通过java.lang.Class类的getResourceAsStream(String name)方法来实现,如下可以这样调用:
   InputStream in = getClass().getResourceAsStream("资源Name");

   或者下面这种也常用:
   InputStream in = new BufferedInputStream(new FileInputStream(filepath));
   综合实例:
   public class PopertyTest {

    public static void main(String[] args) throws IOException {
        Properties pp = new Properties();
        //读取文件中的信息并写入Properties
        FileInputStream f = new FileInputStream("a.poperties");
        pp.load(f);
        String s = pp.getProperty("name");
        System.out.println(s);
        //返回Properties表中的键的Set集合
        Set<String > set = pp.stringPropertyNames();
        Iterator<String> it = set.iterator();
        while(it.hasNext()) {
            String ss= it.next();
            System.out.println(ss+"="+pp.getProperty(ss));
        }
        
        FileOutputStream fos = new FileOutputStream("a.poperties");
        //增加Properties中的属性
        pp.setProperty("sex", "女");
        //将此 Properties 表中的属性列表(键和元素对)写入输出流  
        pp.store(fos, "alfj");
    }
   }

原文地址:https://www.cnblogs.com/snzd9958/p/9637121.html