您的位置:首页 > 编程语言 > Java开发

java 序列化和 hashcode、equals 方法重写

2016-12-02 16:46 387 查看

1.序列化是干什么的?

简单的来说就是为了保存各个对象在内存中的状态(也就是实例的变量,不是方法),并且可以把保存的对象状态读出来。虽然你可以用你自己的各种各样的方法来保存 object states,但是java给你提供一种应该比你自己好的保存对象状态的机制,那就是序列化。

2.什么情况下需要序列化

1. 当你想把内存中的对象状态保存到一个文件或者是数据中的时候;
2. 当你想用 socket 在网络中传输对象的时候;
3. 当你想通过 RMI 传输对象的时候;


3.当对一个对象进行序列化的时候发生了什么

在没有序列化前,每个保存在堆中的对象都有相应的状态(state),即实例变量(instance ariable)比如:

Foo myFoo = new Foo();
myFoo.setWidth(37);
myFoo.setHeight(70);


当通过下面的代码实例化后,myFoo 对象中的 width 和 height 都被保存到文件foo.ser当中了,这样以后可以把它从文件中读取出来,重新再堆中创建原来的对象。当然保存的时候不仅仅是保存对象的实例变量的值,JVM还要保存一些小量信息,比如类的类型等一遍恢复原来的对象。

FileOutputStream fs = new FileOutputStream("foo.ser");
ObjectOutputStream os = new ObjectOutputStream(fs);
os.writeObject(myFoo);


序列化和反序列化的例子:

实体类:

package com.dada.foo;

import java.io.Serializable;

/**
* 实现了 Serializable 接口
* 没有重写 hashCode和 equals方法
* @author Administrator
*
*/
public class MyFoo1 implements Serializable {
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public MyFoo1(int width, int height) {
super();
this.width = width;
this.height = height;
}

}


测试类:

@Test
public void testWriteAndReadbject()  throws Exception {
MyFoo1 myFoo = new MyFoo1(20, 30);
// 把对象写到文件中
FileOutputStream fos = new FileOutputStream("D:\\myFoo.ser");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myFoo);

// 读取对象信息
FileInputStream fis =  new FileInputStream("D:\\myFoo.ser");
ObjectInputStream ois = new ObjectInputStream(fis);
MyFoo1 myFoo1 = (MyFoo1) ois.readObject();

}


简而言之:序列化的作用就是为了不同jvm之间共享实例对象的一种解决方案.由java提供此机制,效率之高,是其他解决方案无法比拟的

4.为什么序列化对象通常要重写 hashCode 和 equals方法

2个内容完全一样的对象,在我们“人”看来就是一样的对象,但是计算机不这么认为,为了保证对象在进行比较时候符合人们的思维习惯,所以我们需要重写这2个方法。

在向 set 这种集合中添加对象的时候,set 要求对象都是不重复的,这个检查重复的过程,首先是检查对象的 hashCode() 值,如果这个值一致的话再去通过 equals 方法进行比较,如果 equals方法返回true,那么这个对象就不会被加入到 set 当中。

类:MyFoo

package com.dada.foo;

/**
* 没有实现 Serializable 接口
* @author Administrator
*
*/
public class MyFoo {
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public MyFoo(int width, int height) {
super();
this.width = width;
this.height = height;
}

}


类:MyFoo1

package com.dada.foo;

import java.io.Serializable;

/**
* 实现了 Serializable 接口
* 没有重写 hashCode和 equals方法
* @author Administrator
*
*/
public class MyFoo1 implements Serializable {
private int width;
private int height;
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public MyFoo1(int width, int height) {
super();
this.width = width;
this.height = height;
}

}


类:MyFoo2

package com.dada.foo;

import java.io.Serializable;

/**
* 1.实现了 Serializable 接口
* 2.重写了 hashCode、equals 方法
* @author Administrator
*
*/
public class MyFoo2 implements Serializable {
private Long id;
private int width;
private int height;

public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}

public MyFoo2(Long id, int width, int height) {
super();
this.id = id;
this.width = width;
this.height = height;
}

@Override
public int hashCode() {
return id != null ? id.hashCode() : 0;
}

@Override
public boolean equals(Object o) {
// 比较引用
if(this == o) return true;
// 比较类型
if(o == null || getClass() != o.getClass()) return false;
// 比较内容,这里比较的是 id 值
MyFoo2 other = (MyFoo2) o;
// 判断逻辑就是:三元运算符 的结果如果等于null 那么就返回 false
// 首先确定当前 id 不等于 null,然后确定当前 id 不等于被比较的 id
// 最后条件都满足了之后还是不等null,说明肯定不一致就返回 false 就可以了
if (id != null ? !id.equals(other.id) : other.id != null) return false;
return true;
}

@Override
public String toString() {
return "MyFoo2{" +
"id=" + id +","+
"width=" + width +","+
"height=" + height +
"}";
}

}


测试类:

package com.dada.foo;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.HashSet;
import java.util.Set;

import org.junit.Test;

public class TestSerializable {

/**
* 1.实现了 Serializable 接口的类的对象的状态是可以被持久化的
* 2.没有实现这个接口的类是无法被实例化的
* 3.执行这个测试会报下名的报下面的错误
* java.io.NotSerializableException: com.dada.foo.MyFoo
* @throws Exception
*/
@Test
public void testWirteObject() throws Exception {
// 把 MyFoo1 对象写入到文件
MyFoo1 myFoo1 = new MyFoo1(20, 30);
FileOutputStream fos1 = new FileOutputStream("myFoo1.out");
ObjectOutputStream oos1 = new ObjectOutputStream(fos1);
oos1.writeObject(myFoo1);
oos1.close();

//  把  MyFoo 写入到文件
MyFoo myFoo = new MyFoo(10,20);
FileOutputStream fos = new FileOutputStream("myFoo.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
// 这里就会报错,因为 MyFoo1 没有实现 Serializable 接口
// 没有实现实例化接口的对象是无法被写到文件里面去的
oos.writeObject(myFoo);

}

/**
* 1.没有重写 hashcode 和 equals 方法的对象,在加入到 set 的过程中会被重复加入的
* 2.测试中 set 的size为:2
* @throws Exception
*/
@Test
public void testWriteAndReadbject()  throws Exception {
MyFoo1 myFoo = new MyFoo1(20, 30);
// 把对象写到文件中
FileOutputStream fos = new FileOutputStream("D:\\myFoo.out");
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myFoo);

Set<MyFoo1> set = new HashSet<MyFoo1>();
set.add(myFoo);

// 读取对象信息
FileInputStream fis =  new FileInputStream("D:\\myFoo.out");
ObjectInputStream ois = new ObjectInputStream(fis);
MyFoo1 myFoo1 = (MyFoo1) ois.readObject();

set.add(myFoo1);
System.out.println(set.size());

}

/**
* 1.为了解决上面的问题,我们需要把对象 hashCode,equals方法重写
* 2.这样当逻辑上同样的对象被加入同一个set中的时候,set就会首先调用 hashCode 来比较两个对象是不是一样
* 3.如果 hashCode 都一样的话,就会调用对象的 equals 方法进行比较,如果这个也是一样的话就不会把它加入到 set 中去
* 4.重写了 hashCode 方法和 equals 方法之后 size 为 :1
* throws Exception
*/
@Test
public void testHashCodeEquals() throws Exception {
MyFoo2 myFoo2 = new MyFoo2(1l, 20, 30);

System.out.println("被写入文件对象的 hasCode 值:" + myFoo2.hashCode());
System.out.println("toString :" + myFoo2);

Set<MyFoo2> set = new HashSet<MyFoo2>();
set.add(myFoo2);

// 把对象写到文件中
FileOutputStream fos = new FileOutputStream("D:\\myFoo2.out");

ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(myFoo2);

// 读取对象信息
FileInputStream fis =  new FileInputStream("D:\\myFoo2.out");
ObjectInputStream ois = new ObjectInputStream(fis);
MyFoo2 myFoo21 = (MyFoo2) ois.readObject();
System.out.println("=============================");
System.out.println("被读取文件对象的 hasCode 值:" + myFoo21.hashCode());
System.out.println("toString :" + myFoo2);
System.out.println(myFoo2 == myFoo21);

set.add(myFoo21);
System.out.println(set.size());

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: