java常用类之Objec类
Java常用类之Object类
- 我们都知道Object类是所有类的父类,任何类都默认继承Object类
- 在使用是都默认或者间接导入import java.lang.Object包;由于所有类都继承Object类,所以可以省略了extends Object这一步骤。
//Object类源码内容
package java.lang;
import jdk.internal.HotSpotIntrinsicCandidate;
public class Object {
private static native void registerNatives();
@HotSpotIntrinsicCandidate
public Object() {
}
@HotSpotIntrinsicCandidate
public final native Class<?> getClass();
@HotSpotIntrinsicCandidate
public native int hashCode();
public boolean equals(Object obj) {
return this == obj;
}
@HotSpotIntrinsicCandidate
protected native Object clone() throws CloneNotSupportedException;
public String toString() {
return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
}
@HotSpotIntrinsicCandidate
public final native void notify();
@HotSpotIntrinsicCandidate
public final native void notifyAll();
public final void wait() throws InterruptedException {
this.wait(0L);
}
public final native void wait(long var1) throws InterruptedException;
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
if (timeoutMillis < 0L) {
throw new IllegalArgumentException("timeoutMillis value is negative");
} else if (nanos >= 0 && nanos <= 999999) {
if (nanos > 0) {
++timeoutMillis;
}
this.wait(timeoutMillis);
} else {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
}
/** @deprecated */
@Deprecated(
since = "9"
)
protected void finalize() throws Throwable {
}
static {
registerNatives();
}
}
-
该类中主要有以下方法:hashcode(), toString(),getClass(),equals(),clone(),finalize(), 其中toString(),getClass(),equals是其中最为重要的方法。
-
注意:因为 Object类中的getClass(),notify(),notifyAll(),wait()等方法被定义为final类型,因此不能重写。
hashCode()方法
public native int hashCode();
-
返回该对象的hashcode值,使用此方法的目的是为了提高哈希表的性能;
-
因为该方法是一个public方法,所以子类可以重写它。
-
在 Java 应用程序执行期间,在对同一对象多次调用
hashCode
方法时,必须一致地返回相同的整数,前提是将对象进行equals
比较时所用的信息没有被修改。从某一应用程序的一次执行到同一应用程序的另一次执行,该整数无需保持一致。 -
如果两个对象的equals相等,那么这两个对象的hashCode一定也相同。
-
如果两个对象不相等,那么对这两个对象中的任一对象上调用
hashCode
方法不要求一定生成不同的整数结果。但是,我们应该意识到,为不相等的对象生成不同整数结果可以提高哈希表的性能 -
重写了equals方法一般都要重写hashCode方法。
/*
比如:下面Test类重写了equals方法,但是没有重写hashCode方法,看输出结果,对象a和对象b使用equals方法相等,按照上面的hashcode的用法,那么他们两个的hashcode肯定相等,但是这里由于没重写hashcode方法,他们两个hashcode并不一样,所以,我们在重写了equals方法后,尽量也重写了hashcode方法,通过一定的算法,使他们在equals相等时,也会有相同的hashcode值。
*/
class Test{
int a=1;
int b=2;
@Override
public boolean equals(Object obj) {//重写equals方法
if(this==obj);
Test test = (Test) obj;
if (this.a==test.a && this.b==test.b){
return true;
}
return false;
}
}
public class hashCodetest {
public static void main(String[] args) {
Test a = new Test();
Test b = new Test();
System.out.println(a.hashCode());//结果为381259350
System.out.println(b.hashCode());//结果为2129789493
System.out.println(a.equals(b));//结果为true
}
}
equals()方法
public boolean equals(Object obj) {
return this == obj;
}
用于比较当前对象与目标对象是否相等,默认是比较引用是否指向同一对象。为public方法,子类可重写。
equals
方法在非空对象引用上实现相等关系:
- 自反性:对于任何非空引用值
x
,x.equals(x)
都应返回true
。 - 对称性:对于任何非空引用值
x
和y
,当且仅当y.equals(x)
返回true
时,x.equals(y)
才应返回true
。 - 传递性:对于任何非空引用值
x
、y
和z
,如果x.equals(y)
返回true
,并且y.equals(z)
返回true
,那么x.equals(z)
应返回true
。 - 一致性:对于任何非空引用值
x
和y
,多次调用x.equals(y)
始终返回true
或始终返回false
,前提是对象上equals
比较中所用的信息没有被修改。 - 对于任何非空引用值
x
,x.equals(null)
都应返回false
。
Object
类的 equals
方法实现对象上差别可能性最大的相等关系;即,对于任何非空引用值 x
和 y
,当且仅当 x
和 y
引用同一个对象时,此方法才返回 true
(x == y
具有值 true
)。
-
注意:当此方法被重写时,通常有必要重写
hashCode
方法,以维护hashCode
方法的常规协定,该协定声明相等对象必须具有相等的哈希码。//只针对equals例子 class Test1{ int a=1; int b=2; @Override public boolean equals(Object obj) { if(this==obj) return true; Test1 o = (Test1) obj; if (o.a==o.b){ return true; } return false; } } public class Equalstest{ public static void main(String[] args) { Test1 m=new Test1(); Test1 n=new Test1(); System.out.println(m.equals(n));//false } }
toString()方法
public String toString() {
return this.getClass().getName() + "@" + Integer.toHexString(this.hashCode());
}
-
Object
类的toString
方法返回一个字符串,该字符串由类名(对象是该类的一个实例)、at 标记符“@
”和此对象哈希码的无符号十六进制表示组成。如:java.lang.Object@2d98a335 -
返回该对象的字符串表示。通常,
toString
方法会返回一个“以文本方式表示”此对象的字符串。结果应是一个简明但易于读懂的信息表达式。建议所有子类都重写此方法。 -
为什么需要toString方法?返回当前对象的字符串表示,可以将其打印方便查看对象的信息,方便记录日志信息提供调试。
public class toStringtest {
public static void main(String[] args) {
Object t = new Object();
System.out.println(t.toString());//结果为java.lang.Object@2d98a335
}
}
clone()方法
protected native Object clone() throws CloneNotSupportedException;
-
创建并返回此对象的一个副本。“副本”的准确含义可能依赖于对象的类。
-
保护方法,实现对象的浅拷贝,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常。
-
如果对象的类不支持
Cloneable
接口,则重写clone
方法的子类也会抛出此异常,以指示无法复制某个实例。 -
在java里除了8种基本类型传参数是值传递,其他的类对象传参数都是引用传递,我们有时候不希望在方法里讲参数改变,这是就需要在类中复写clone方法(实现深拷贝)。
对于clone()和copy的区别
假设现在有一个Animal对象,Animal cat =new Animal(“huahua”,6);
通常我们会有这样的赋值Animal dog=cat,这个时候只是简单了copy了一下相关内容,而cat和dog都指向内存中同一个object,这样就导致了cat或者dog中的任何一个操作都可能影响到对方。
比如,如果我们通过cat.raiseSalary()方法改变了salary域的值,那么dog通过getSalary()方法得到的就是修改之后的salary域的值,这显然这不是我们愿意看到的。
我们希望得到dog的一个精确拷贝,同时两者互不影响,这时候我们就可以使用clone()方法来满足我们的需求。Animal dog=cat.clone(),这时会生成一个新的Animal对象,并且和cat具有相同的属性值和方法。
/*通过例子区别一下clone()方法和copy之间的差异,copy可以理解改一下,则其他也会变;而一旦使用clon方法,则会创建一个新的对象,其内容改变不会影响其它的*/
public class CloneTest implements Cloneable{
private String name;
private int age;
protected CloneTest clone() throws CloneNotSupportedException {
return (CloneTest)super.clone();
}
public static void main(String args[]) throws CloneNotSupportedException{
CloneTest cat = new CloneTest("huahua",6);
CloneTest pig = cat;
System.out.println(cat==pig);//true
System.out.println(cat.name==pig.name);//true
System.out.println(cat.age==pig.age);//true
System.out.println("========================");
CloneTest dog = cat.clone();
System.out.println(cat==dog);//false
System.out.println(cat.name==dog.name);//true
System.out.println(cat.age==dog.age);//true
}
}
浅拷贝,深拷贝
- 浅拷贝:拷贝的是引用;从上面用clone方法的输出结果来看,clone的对象是虽然是一个新的对象;但原对象与clone对象的 String类型 的name却是同一个引用,这说明,当super.clone方法对成员变量进行操作时,如果是引用类型,则进行是浅拷贝。
- 深拷贝:新开辟内存空间,进行值拷贝;如果想要深拷贝一个对象, 这个对象必须要实现Cloneable接口,实现clone方法,并且在clone方法内部,把该对象引用的其他对象也要clone一份 , 这就要求这个被引用的对象必须也要实现Cloneable接口并且实现clone方法。
//深拷贝案例
public class deepClonetest {
static class Body implements Cloneable {
public Head head;
public Body() {
}
public Body(Head head) {
this.head = head;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Body newBody = (Body) super.clone();
newBody.head = (Head) head.clone();
return newBody;
}
}
static class Head implements Cloneable {
public Face face;
public Head() {
}
public Head(Face face) {
this.face = face;
}
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
static class Face{}
public static void main(String[] args) throws CloneNotSupportedException {
Body body = new Body(new Head());
Body body1 = (Body) body.clone();
System.out.println(body == body1);//false
System.out.println(body.head == body1.head);//false
System.out.println(body.head.face == body1.head.face);//true
}
}
getClass()方法
public final native Class<?> getClass();
返回次Object的运行时类类型。不可重写,要调用的话,一般和getName()联合使用,如getClass().getName();
- 具体理解:类加载的第一阶段类的加载就是将.class文件加载到内存,并生成一个java.lang.Class对象的过程。getClass()方法就是获取这个对象,这是当前类的对象在运行时类的所有信息的集合。
notify()方法与notifyAll()方法
- notify
public final native void notify();
-
唤醒在此对象监视器上等待的单个线程。如果所有线程都在此对象上等待,则会选择唤醒其中一个线程。选择是任意性的,并在对实现做出决定时发生。线程通过调用其中一个
wait
方法,在对象的监视器上等待。 -
此方法只应由作为此对象监视器的所有者的线程来调用。通过以下三种方法之一,线程可以成为此对象监视器的所有者:
- 通过执行此对象的同步实例方法。
- 通过执行在此对象上进行同步的
synchronized
语句的正文。 - 对于
Class
类型的对象,可以通过执行该类的同步静态方法。
注:一次只能有一个线程拥有对象的监视器。
- notifyAll
public final native void notifyAll();
- 唤醒在此对象监视器上等待的所有线程。线程通过调用其中一个
wait
方法,在对象的监视器上等待。 - 此方法只应由作为此对象监视器的所有者的线程来调用。
两者共同点:1、IllegalMonitorStateException
- 如果当前线程不是此对象监视器的所有者;
2、此方法只应由作为此对象监视器的所有者的线程来调用
wait() | wait(long timeout) | wait(long timeout,int naos)方法
- wait()
public final void wait() throws InterruptedException {
this.wait(0L);
}
- wait(long timeout)
public final native void wait(long var1) throws InterruptedException;
- wait(long timeout,int naos)
//long timeout:要等待的最长时间(以毫秒为单位); int naos:额外时间(以微毫秒为单位)
public final void wait(long timeoutMillis, int nanos) throws InterruptedException {
if (timeoutMillis < 0L) {
throw new IllegalArgumentException("timeoutMillis value is negative");
} else if (nanos >= 0 && nanos <= 999999) {
if (nanos > 0) {
++timeoutMillis;
}
this.wait(timeoutMillis);
} else {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
}
-
抛出的异常:
- IllegalArgumentException - 如果超时值是负数,或者毫微秒值不在 0-999999 范围内。
- IllegalMonitorStateException - 如果当前线程不是此对象监视器的所有者。
- InterruptedException - 如果在当前线程等待通知之前或者正在等待通知时,任何线程中断了当前线程。在抛出此异常时,当前线程的中断状态被清除。
-
这三个方法都是是用来线程间通信用的,其作用是阻塞当前线程 ,等待其他线程调用notify()/notifyAll()方法将其唤醒。这些方法都是public final的,不可被重写。
注意:
- 此方法只能在当前线程获取到对象的锁监视器之后才能调用,否则会抛出IllegalMonitorStateException异常。
- 调用wait方法,线程会将锁监视器进行释放;而Thread.sleep,Thread.yield()并不会释放锁 。
- wait方法会一直阻塞,直到其他线程调用当前对象的notify()/notifyAll()方法将其唤醒;而wait(long)是等待给定超时时间内(单位毫秒),如果还没有调用notify()/nofiyAll()会自动唤醒;waite(long,int)如果第二个参数大于0并且小于999999,则第一个参数+1作为超时时间;
调用该方法后当前线程进入睡眠状态,直到以下事件发生。
(1)其他线程调用了该对象的notify方法。
(2)其他线程调用了该对象的notifyAll方法。
(3)其他线程调用了interrupt中断该线程。
(4)时间间隔到了。
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常。
其他
推荐两篇优质文章: