Java Object类常用的方法
目录:
#1、toString()
#2、equals()与hashCode()
#3、clone()
#4、finalize()方法
#5、getClass()方法
#6、wait(),notify(),notifyAll() 多线程编程时会使用
1、toString()
Object类的toString()方法默认返回该对象实现类的“创建对象类的类的名字+@+对象的引用的字符串表示(hashcode)”值。很多类都对其进行了改写。
2、equals()与hashCode()
(1)Object类的equals()方法用来判断两个对象是不是相同。
Object类对它的实现是判断两个对象的引用是不是相同,即Object类认为两个对象是否相同的标准是它的引用是否相同。
原型为:
public boolean equals (Object x){
return this == x;
}
但是有时因为判断两个对象是否相同的标准不同,我们常对它进行重写。例如,有一个人的类,里面有一个身份证号属性,这时我们就可以将equals()方法改写,用身份证号是否相同的标准,代替引用是否相同的标准。
(虽然有时候Object的equals()方法可以满足我们一些基本的要求,但是我们必须要清楚我们很大部分时间都是进行两个对象的比较,这个时候Object的equals()方法就不可以了,
实际上JDK中,String、Math等封装类都对equals()方法进行了重写。
在覆写equals()方法时,一般都是推荐使用getClass来进行类型判断,不是使用instanceof。我们都清楚instanceof的作用是判断其左边对象是否为其右边类的实例,返回boolean类型的数据。可以用来判断继承中的子类的实例是否为父类的实现。
注意后面这句话:可以用来判断继承中的子类的实例是否为父类的实现,正是这句话在作怪。
在java中进行比较,我们需要根据比较的类型来选择合适的比较方式:
1) 对象域,使用equals方法 。
2) 类型安全的枚举,使用equals或== 。
3) 可能为null的对象域 : 使用 == 和 equals 。
4) 数组域 : 使用 Arrays.equals 。
5) 除float和double外的原始数据类型 : 使用 == 。
6) float类型: 使用Float.foatToIntBits转换成int类型,然后使用==。
7) double类型: 使用Double.doubleToLongBit转换成long类型,然后使用==。)
上述()内文字,引自https://blog.csdn.net/JustForWorld/article/details/87952843
(2)hashCode():得到一个JVM根据一定策略为这个Object对象分配的一个int类型的数值,JVM会使用对象的hashCode值来提高对HashMap、Hashtable哈希表存取对象的使用效率。
关于hashCode()的这个策略到底是啥,可以看看这篇文章https://blog.csdn.net/Dzq_Boyka/article/details/78963656
为什么要将这两个方法提到一起讲呢?这是因为两者之间有很密切的关系。在实现equals()方法时,一定要实现hashCode()方法。这个是因为为了维持hashCode的一种约定,相同的对象必须要有相同的hashCode值。如果equals()返回的是false,即不相同的对象,那么他们的hashCode可以相同,也可以不相同。
下面是这个约定的运用:
我们应该先了解java判断两个对象是否相等的规则。 在java的集合中,判断两个
对象是否相等的规则是:
首先,判断两个对象的hashCode是否相等
如果不相等,认为两个对象也不相等
如果相等,则判断两个对象用equals运算是否相等
如果不相等,认为两个对象也不相等
如果相等,认为两个对象相等
先判断hashCode是因为我们在equals方法中需要向下转型,效率很低,所以先判断hashCode方法可以提高效率。
3、clone()
clone()方法在Object中定义如下:
{/**
* Class Object is the root of the class hierarchy. Every class has Object as a superclass.
* All objects, including arrays, implement the methods of this class.
*/
public class Object {
/**
* Creates and returns a copy of this object. The precise meaning of "copy" may depend on the class of the object.
* The general intent is that, for any object {@code x}, the expression: x.clone() != x will be true,
* and that the expression: x.clone().getClass() == x.getClass() will be true, but these are not absolute requirements.
* While it is typically the case that: x.clone().equals(x) will be true, this is not an absolute requirement.
*/
protected native Object clone() throws CloneNotSupportedException;
}
1、关于protected修饰
根类Object的clone方法是用protected关键字修饰,这样做是为避免我们创建每一个类都默认具有克隆能力。这样做造成的后果就是:对于那些简单使用一下这个类的客户程序员来说,他们不会默认使用这个方法;其次,我们不能利用指向基础类的一个句柄来调用clone方法。在编译期间,实际上是通知我们对象不可克隆的一种方式。
这个不太理解,是从https://blog.csdn.net/one_Jachen/article/details/78246047处引用的
2、native
在Java中有native修饰的方法,比没有native修饰的方法速度要快。这也就解释了,为什么不new一个对象,然后再将内容再复制过去。因为用clone要快。
clone()用来复制当前的对象(调用他的对象),一个对象要是想用clone(),必须要重写clone(),并且要实现Cloneable接口(标记接口)。
clone()函数的实现过程:
根类Object中的clone()方法负责建立正确的存储容量,并通过“按位复制”将所有二进制从原始对象中复制到新对象的存储空间。也就是说,它并不只是预留存储空间以及复制一个对象–实际需要调查出欲复制的新对象准确大小,然后再复制那个对象。由于这些工作都是由根类定义的clone()方法内部代码进行的,这个过程需要用RTTI(运行时类型鉴定)来判断欲克隆对象的实际大小。采用这种方式,clone()方法便可建立起正确数量的存储空间,并对那个类型进行正确的按位复制。
clone有浅复制与深复制之分。当一个对象被复制时,复制得到的对象内部的引用对象和原对象内部的引用对象地址相等,这就是浅复制(在复制引用对象时,只是复制了它的引用)。引用对象地址不等,这就是深复制(在复制引用对象时,复制了它的值)。
浅复制例子:
重写的函数:
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
深复制例子:
想要深复制,被克隆对象中的引用对象也需要被显示复制。引用的对象中也要实现接口和复写方法clone()。
public class Animal implements Cloneable {
public Cat cat;
@Override
protected Object clone() throws CloneNotSupportedException {
Animal clone = (Animal) super.clone();
// 引用对象也拷贝一份
clone.cat = (Cat) cat.clone();
return clone;
}}
public class Cat implements Cloneable {
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}}
4、finalize()方法
如果一个对象想使用finalize()方法,则必须要在创建它类中对finalize()进行重写
finalize()方法是Object类中提供的一个方法,在GC准备释放对象所占用的内存空间之前,它将首先调用finalize()方法。其在Object中定义如下:
protected void finalize() throws Throwable { }
使用场景:
Java中的所有内存回收都是通过GC来进行的,但是GC回收的只是java通过new关键字开辟出的内存空间。如果使用方式开辟的内存空间,是不能被GC进行垃圾回收的。这时我们就要用到这个方法。
另外,既然finalize()作为一个GC时才调用的方法,我们可以在里面执行一些最终结果的判断。
此处这个大佬有详细的解释,讲的清晰明白:https://blog.csdn.net/crazylai1996/article/details/84900818
注:
1、任何一个对象的finalize()方法都只会被系统自动调用一次
2、GC只有在内存不足时,才进行垃圾回收,如果一直到程序结束,内存都充足,GC是不会进行垃圾回收的。
3、在java中如果我们想手动调用垃圾收集器,我们可以做System.gc()
https://blog.csdn.net/baidu_37107022/article/details/89277790这是对finalize()方法讲的比较深的。
5、getClass()方法
每个类的运行时的类型信息会有一个Class对象保存。它包含了与类有关的信息。
getClass()方法就是得到这个Class类
例如:
有一个类,创建了一个对象e
e.getClass()就是得到e的类的描述信息
最常用的Class方法getName,会返回类的名字。
例:
e.getClass().getName()就是返回创建e这个对象的类的名字
6、wait(),notify(),notifyAll() 多线程编程时会使用
wait()让一个线程等待,除非有其他的线程调用了notify()唤醒它,或者调用了notifyAll()
notify()唤醒一个线程
notifyAll()唤醒所有线程