文章导览
Object类
类 Object 是类层次结构的根类。每个类都使用 Object 作为超类。所有对象(包括数组)都实现这个类的方法。
Object类的通用方法
1、toString方法
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
一个简单的例子
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
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;
}
}
/*
Person类默认继承了Object类,所以可以使用其中的toString()方法
String toString() 返回该对象的字符串表示。
*/
Person p = new Person("张三", 18);
System.out.println(p.toString()); //object.demo01.Person@1b6d3586
也就是说在输出这个Person对象的时候,默认输出的是一个对象在堆内存上的地址值(也就是调用了Object类下面的toString()方法,如上上图所示的类的全限定名@十六进制哈希值字符串),如果要输出对象的内容必须覆盖重写toSting()方法。
/*
直接打印对象的地址值没有意义,所以我们要重写下toString方法,打印对象的属性(name,age)
*/
@Override
public String toString() {
return "Person{name = " + name + ",age = " + age + "}";
}
2、equals方法
public boolean equals(Object obj) {
return (this == obj);
}
等价关系
两个对象等价需要满足如下条件
- 自反性
x.equals(x);//true
- 对称性
x.equals(y) == y.equals(x);//true
- 传递性
if(x.equals(y) && y.equals(z))
x.equals(z);//true
- 一致性
x.equlas(y) == x.equals(y);//true
- 与null的比较
x.equals(null);//false
equals来比较对象内容是否相等
直接调用equals方法来比较的是两个对象的地址值,如果需要比较两个对象的内容,则需要重写equals方法。
public class Person {
private String name;
private int age;
}
/*
Person类默认继承了Object类,所以可以使用Object类的equals()方法
boolean equals(Object obj) 指示其他某个对象是否与此对象“相等”。
*/
Person p1 = new Person("迪丽热巴", 18);
Person p2 = new Person("古力娜扎", 19);
Person p3 = new Person("迪丽热巴", 18);
System.out.println(p1.equals(p3));//false
/*
Object类的equals方法,默认比较的是两个对象的地址值,没有意义
所以我们要重写equals方法,比较两个对象的属性(name,age)
问题:
隐含一个多态(弊端:无法使用子类特有的内容)
Object obj = p1 = new Person("迪丽热巴",18);
解决:可以向下转型
*/
@Override
public boolean equals(Object obj) {
//增加一个判断,传递的参数obj如果是this本身,直接返回true,提高程序效率
if (obj == this) return true;
//增加一个判断,如果是空,直接返回false 提高程序的效率
if (obj == null) return false;
if (obj instanceof Person) {
//向下转型
Person p = (Person) obj;
//比较两个对象的属性
return this.name.equals(p.name) && this.age == p.age;
}
return false;
}
Person p1 = new Person("迪丽热巴", 18);
Person p2 = new Person("古力娜扎", 19);
Person p3 = new Person("迪丽热巴", 18);
System.out.println(p1.equals(p3));//true
3、hashCode方法
hashCode()返回哈希值,而equals是用来判断两个对象是否等价。等价的两个对象散列值一定相等,但是散列值相同的两个对象不一定相等,这是因为哈希值具有随机性,两个值不同的对象可能计算出相同的哈希值。
所以在覆盖重写equals方法的同时,应当要也要覆盖重写hashCode方法,保证两个对象哈希值也相等。
HashSet和HashMap等集合类使用了hashCode()方法来计算对象应该存储的位置,因此要将对象添加到这些集合类中,需要让对应的类实现hashCode()方法
如下面的代码,EqualsExample没有实现hashCode()方法,因此这两个对象的哈希值是不同的,最终导致集合添加了两个等价的对象。
EqualExample e1 = new EqualExample(1, 1, 1);
EqualExample e2 = new EqualExample(1, 1, 1);
System.out.println(e1.equals(e2)); // true
HashSet<EqualExample> set = new HashSet<>();
set.add(e1);
set.add(e2);
System.out.println(set.size()); // 2
理想的哈希函数应当具有均匀性,即不相等的对象应当均匀分布到所有可能的哈希值上。这就要求了哈希函数要把所有域的值都考虑进来。可以将每个域都当成 R 进制的某一位,然后组成一个 R 进制的整数。
R 一般取 31,因为它是一个奇素数,如果是偶数的话,当出现乘法溢出,信息就会丢失,因为与 2 相乘相当于向左移一位,最左边的位丢失。并且一个数与 31 相乘可以转换成移位和减法:31*x == (x<<5)-x,编译器会自动进行这个优化。
@Override
public int hashCode() {
int result = 17;
result = 31 * result + x;
result = 31 * result + y;
result = 31 * result + z;
return result;
}
4、clone方法
1、cloneable
clone()是Object类中用protected修饰的方法,所以如果一个类不显示的去覆盖重写这个方法,其他类就无法调用该类实例的clone()方法
public class CloneExample {
private int a;
private int b;
@Override
protected CloneExample clone() throws CloneNotSupportedException {
return (CloneExample) super.clone();
}
}
CloneExample c1 = new CloneExample();
//c1.clone(); 'clone()' has protected access in 'java.lang.Object'
try {
CloneExample clone = c1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
会抛出CloneNotSupportedException异常,这是因为CloneExample没有实现Cloneable接口。
应该注意的是clone()方法并不是Cloneable接口的方法,而是Object类的一个projected方法。Cloneable接口只是规定如果一个类没有实现Cloneable接口又调用了clone()方法,那么就会抛出CloneNotSupportedException。
public class CloneExample implements Cloneable{
private int a;
private int b;
@Override
protected CloneExample clone() throws CloneNotSupportedException {
return (CloneExample) super.clone();
}
}
2、浅拷贝
拷贝对象和原始对象的引用类型引用同一个对象
/*
浅拷贝:拷贝对象和原始对象的引用类型引用同一个对象
*/
public class ShallowCloneExample implements Cloneable {
private int[] arr;
public ShallowCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override
protected ShallowCloneExample clone() throws CloneNotSupportedException {
return (ShallowCloneExample) super.clone();
}
}
ShallowCloneExample e1 = new ShallowCloneExample();
ShallowCloneExample e2 = null;
try {
e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e1.set(2,222);
System.out.println(e2.get(2));//222
3、深拷贝
拷贝对象和原始对象的引用类型引用不同对象
public class DeepCloneExample implements Cloneable {
private int[] arr;
public DeepCloneExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
@Override
protected DeepCloneExample clone() throws CloneNotSupportedException {
DeepCloneExample result = (DeepCloneExample) super.clone();
result.arr = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
result.arr[i] = arr[i];
}
return result;
}
}
DeepCloneExample e3 = new DeepCloneExample();
DeepCloneExample e4 = null;
try {
e4 = e3.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
e3.set(2,222);
System.out.println(e4.get(2));//2
4、clone()的替代方案
使用clone()方法来拷贝一个对象既复杂又有风险,他会抛出异常,而且还需要类型转换。Effective Java书上讲到,最好不要去使用clone(0,可以使用拷贝构造函数或者拷贝工厂来拷贝一个对象。
public class CloneConstructorExample {
private int[] arr;
public CloneConstructorExample() {
arr = new int[10];
for (int i = 0; i < arr.length; i++) {
arr[i] = i;
}
}
public CloneConstructorExample(CloneConstructorExample original) {
arr = new int[original.arr.length];
for (int i = 0; i < original.arr.length; i++) {
arr[i] = original.arr[i];
}
}
public void set(int index, int value) {
arr[index] = value;
}
public int get(int index) {
return arr[index];
}
public static void main(String[] args) {
CloneConstructorExample e1 = new CloneConstructorExample();
CloneConstructorExample e2 = new CloneConstructorExample(e1);
e1.set(2,222);
System.out.println(e2.get(2));//2
}
}