IO流:对象流Poperties类
Posted snzd9958
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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");
}
}
以上是关于IO流:对象流Poperties类的主要内容,如果未能解决你的问题,请参考以下文章