说到继承,不得不提到的是所有对象的共同父类:Object 。
尽管Object是一个具体类,但设计它主要是为了扩展。
一:相等测试equals
查看Object的源代码我们可以知道,Object的equals比较的就是两个具体对象是否有相同的引用。
因此当我们不需要考虑类是否逻辑相等,或者超类覆盖的equals子类适用,那么我们都不需要覆盖Object的equals方法。
而当我们需要考虑类的逻辑相等,也就是一个类中的属性域比较时,我们需要覆盖,但如果不注意会有很多错误发生,下面举例说明:
class Human{
private int age;
public Human(int age) {
this.age = age;
}
@Override
public boolean equals(Object obj){
if (obj == this) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Human)) {
return false;
}
return this.age == ((Human)obj).age;
}
}
public class Main {
public static void main(String[] args) {
HashMap<Human, String> map = new HashMap<Human, String>();
map.put(new Human(15), "nice");
String s = map.get(new Human(15));
System.out.println(s);
}
}
>>>out:null
我们期望map.get(new Human(15))会返回"nice",但实际上却返回了null。这是为什么呢?
因为我们没有覆盖hashCode()方法:
1.如果两个对象相等那么它们必须有相同的hashCode
2.如果两个对象的hashCode相同,它们不一定相等
一般来说我们可以用Eclipse自带的hashCode生成器生成如下方法
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
return result;
}
若在企业级应用中可以使用第三方库Apache commons HashcodeBuilder
同时在设计equals方法的时候注意满足以下关系:
对于任何非null的x,y,z
*自反性 x.equals(x) 必须返回 true
*对称性 当 y.equals(x) 为true时 x.equals(y) 为true
*传递性 当 x.equals(y) 为true 且 y.equals(z) 为 true 那么 x.equals(z)为true
*一致性 只要x和y没有被修改 那么 x.equals(y) 不管调用多少次 都一直返回true 或 一直返回false
二:toString方法 及 clone方法
Object提供了一个toString的实现,但只是返回类名@hashCode,我们无法通过这个toString得到更多的信息,因此一般会约定所有的子类都覆盖这一个方法,以便我们得到更详细的关于这个类所创建的对象的信息。
为什么我们需要clone,clone又什么用呢?
在编程中我们经常会遇到这样的情况:某A对象是可变的,当A初始化成功并赋值,这时我们需要一个以A为模板和A完全一样的B,如果我们直接B=A那么当A改变的时候,B也会随之改变,这时我们希望有一个可以完全拷贝A对象的内容的方法,这就是clone方法(当然我们还可以有其它的办法,如类的序列化,这个就不是本章涉及的内容了)。
关于clone我们有浅拷贝和深拷贝之分,下面举例说明:
class Human implements Cloneable{
private String name;
private Leg leg;
public Human(String name, Leg leg){
this.name = name;
this.leg = leg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Leg getLeg() {
return leg;
}
public void setLeg(Leg leg) {
this.leg = leg;
}
@Override
public String toString(){
return "Human: name= " + this.name + ";his leg = " + this.leg;
}
@Override
public Human clone() throws CloneNotSupportedException{
return (Human)super.clone();
}
}
class Leg{
private int length;
public Leg(int length){
this.length = length;
}
public void setLength(int length) {
this.length = length;
}
public int getLength(){
return this.length;
}
public String toString(){
return "Leg: length= " + this.length;
}
}
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
Leg leg = new Leg(10);
Human human = new Human("xiaoming", leg);
Human human2 = human.clone();
human.setName("lilei");
leg.setLength(15);
System.out.println("human: " + human);
System.out.println("human2: " + human2);
}
}
>>>out:
human: Human: name= lilei;his leg = Leg: length= 15
human2: Human: name= xiaoming;his leg = Leg: length= 15
我们可以看到浅拷贝时,当对象的域为数值或基本类型的时候没有问题,改变A但B不会改变,但当域为可变类型的时候,像Leg改变后A和B都跟着改变了其中的值,为了实现深拷贝,必须克隆所有可变的实例域:
class Human implements Cloneable{
private String name;
private Leg leg;
public Human(String name, Leg leg){
this.name = name;
this.leg = leg;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Leg getLeg() {
return leg;
}
public void setLeg(Leg leg) {
this.leg = leg;
}
@Override
public String toString(){
return "Human: name= " + this.name + ";his leg = " + this.leg;
}
@Override
public Human clone() throws CloneNotSupportedException{
Human human = (Human)super.clone();
human.leg = (Leg) leg.clone();
return human;
}
}
class Leg implements Cloneable{
private int length;
public Leg(int length){
this.length = length;
}
public void setLength(int length) {
this.length = length;
}
public int getLength(){
return this.length;
}
public String toString(){
return "Leg: length= " + this.length;
}
public Leg clone() throws CloneNotSupportedException{
return (Leg) super.clone();
}
}
public class Demo {
public static void main(String[] args) throws CloneNotSupportedException {
Leg leg = new Leg(10);
Human human = new Human("xiaoming", leg);
Human human2 = human.clone();
human.setName("lilei");
leg.setLength(15);
System.out.println("human: " + human);
System.out.println("human2: " + human2);
}
}
>>>out:
human: Human: name= lilei;his leg = Leg: length= 15
human2: Human: name= xiaoming;his leg = Leg: length= 10