Object是java所有类的基类,是整个类继承结构的顶端,也是最抽象的一个类。大家天天都在使用toString()、equals()、hashCode()、wait()、notify()、getClass()等方法
1getClass()方法
类加载的第一阶段类的加载就是将.class文件加载到内存,并生成一个java.lang.Class对象的过程。getClass()方法就是获取这个对象。
2hashCode()方法
这是一个public的方法,所以 子类可以重写它。这个方法返回当前对象的hashCode值,这个值是一个整数范围内的(-2^31 ~ 2^31 - 1)数字。
在 Java 应用程序执行期间,在对同一对象多次调用 hashCode 方法时,必须一致地返回相同的整数,前提是将对象进行 equals 比较时所用的信息没有被修改;
如果两个对象 x.equals(y) 方法返回true,则x、y这两个对象的hashCode必须相等。
如果两个对象x.equals(y) 方法返回false,则x、y这两个对象的hashCode可以相等也可以不等。
默认的hashCode是将内存地址转换为的hash值,重写过后就是自定义的计算方式;
3equals()方法
public boolean equals(Object obj):用于比较当前对象与目标对象是否相等,默认是比较引用是否指向同一对象。为public方法,子类可重写。
public class Object{
public boolean equals(Object obj) {
return (this == obj);
}
}
为什么需要重写equals方法?
因为如果不重写equals方法,当将自定义对象放到map或者set中时;如果这时两个对象的hashCode相同,就会调用equals方法进行比较,这个时候会调用Object中默认的equals方法,而默认的equals方法只是比较了两个对象的引用是否指向了同一个对象,显然大多数时候都不会指向同一个,这样就会将重复对象存入map或者set中。这就 破坏了map与set不能存储重复对象的特性,会造成内存溢出 。
重写equals方法的几条约定:
自反性:即x.equals(x)返回true,x不为null;
对称性:即x.equals(y)与y.equals(x)的结果相同,x与y不为null;
传递性:即x.equals(y)结果为true, y.equals(z)结果为true,则x.equals(z)结果也必须为true;
一致性:即x.equals(y)返回true或false,在未更改equals方法使用的参数条件下,多次调用返回的结果也必须一致。x与y不为null。
如果x不为null, x.equals(null)返回false。
4 clone()方法
1 new 一个对象的过程和 clone 一个对象的过程区别?
过程:
new操作符的本意是分配内存:程序执行 到new操作时,根据new操作符后面的类型分配相应的内存空间。分配完内存之后,再调用构造函数完成对象的初始化,填充对象的各个域;构造方法返回后,一个对象创建完毕,可以把它引用发布到外部,在外部就可以使用这个引用操作这个对象。
clone在第一步和new相似,都是分配内存,调用clone方法时,分配的内存和原对象相同,然后再使用原对象中对应的各个域,填充新对象的域,填充完成后,clone方法返回,一个新的对象被创建,同样可以把这个新对象的引用发布到外部。
区别:
clone()不会调用构造方法,new会调用构造方法。
clone()更快:clone()能快速创建一个已有对象的副本,即创建对象并且将已有对象中所有属性值克隆;new只能在JVM中申请一个空的内存区域,对象的属性值要通过构造方法赋值。
2 clone 对象的使用
public class User implements Cloneable {
private String id;
private String name;
public User() {
}
public User(String id, String name) {
this.id = id;
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User("userId", "userName");
User userClone = (User) user.clone();
System.out.println(user);
System.out.println(userClone);
}
}
从打印结果可以看出,两个对象的地址是不同的,也就是说创建了新的对象, 而不是把原对象的地址赋给了一个新的引用变量:
User@4554617c
User@74a14482
3深拷贝和浅拷贝
public class User implements Cloneable {
private String id;
private String name;
public User() {
}
public User(String id, String name) {
this.id = id;
this.name = name;
}
@Override
protected User clone() throws CloneNotSupportedException {
return (User) super.clone();
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
由于 age 是基本数据类型,那么对它的拷贝没有什么疑议,直接将一个 4 字节的整数值拷贝过来就行。但是 name是 String 类型的, 它只是一个引用, 指向一个真正的 String 对象,那么对它的拷贝有两种方式: 直接将原对象中的 name 的引用值拷贝给新对象的 name 字段, 或者是根据原 User 对象中的 name 指向的字符串对象创建一个新的相同的字符串对象,将这个新字符串对象的引用赋给新拷贝的 User 对象的 name 字段。这两种拷贝方式分别叫做浅拷贝和深拷贝。深拷贝和浅拷贝的原理如下图所示:
如果两个 Person 对象的 name 的地址值相同, 说明两个对象的 name 都指向同一个String 对象,也就是浅拷贝, 而如果两个对象的 name 的地址值不同, 那么就说明指向不同的 String 对象, 也就是在拷贝 Person 对象的时候, 同时拷贝了 name 引用的 String 对象, 也就是深拷贝。验证代码如下:
public class Main {
public static void main(String[] args) throws CloneNotSupportedException {
User user = new User("userId", "userName");
User userClone = user.clone();
System.out.println(
user.getId() == userClone.getId()
);
System.out.println(
user.getName() == userClone.getName()
);
}
}
true
true
clone 是浅拷贝的
5toString()方法
返回当前对象的字符串表示,可以将其打印方便查看对象的信息,方便记录日志信息提供调试。
6finalize()方法
此方法是在垃圾回收之前,JVM会调用此方法来清理资源。此方法可能会将对象重新置为可达状态,导致JVM无法进行垃圾回收。
finalize()方法具有如下4个特点:
永远不要主动调用某个对象的finalize()方法,该方法由垃圾回收机制自己调用;
finalize()何时被调用,是否被调用具有不确定性;
当JVM执行可恢复对象的finalize()可能会将此对象重新变为可达状态;
当JVM执行finalize()方法时出现异常,垃圾回收机制不会报告异常,程序继续执行。