Java Object类详解
????????Java 中的 Object
类是所有类的根类,它位于 java.lang
包中。所有其他类都直接或间接地继承自 Object
类。以下是关于 Object
类的一些详解:
/**
* native 方法,用于返回当前运行时对象的 Class 对象,使用了 final 关键字修饰,故不允许子类重写。
*/
public final native Class<?> getClass()
/**
* native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的HashMap。
*/
public native int hashCode()
/**
* 用于比较 2 个对象的内存地址是否相等,String 类对该方法进行了重写以用于比较字符串的值是否相等。
*/
public boolean equals(Object obj)
/**
* native 方法,用于创建并返回当前对象的一份拷贝。
*/
protected native Object clone() throws CloneNotSupportedException
/**
* 返回类的名字实例的哈希码的 16 进制的字符串。建议 Object 所有的子类都重写这个方法。
*/
public String toString()
/**
* native 方法,并且不能重写。唤醒一个在此对象监视器上等待的线程(监视器相当于就是锁的概念)。如果有多个线程在等待只会任意唤醒一个。
*/
public final native void notify()
/**
* native 方法,并且不能重写。跟 notify 一样,唯一的区别就是会唤醒在此对象监视器上等待的所有线程,而不是一个线程。
*/
public final native void notifyAll()
/**
* native方法,并且不能重写。暂停线程的执行。注意:sleep 方法没有释放锁,而 wait 方法释放了锁 ,timeout 是等待时间。
*/
public final native void wait(long timeout) throws InterruptedException
/**
* 多了 nanos 参数,这个参数表示额外时间(以纳秒为单位,范围是 0-999999)。 所以超时的时间还需要加上 nanos 纳秒。。
*/
public final void wait(long timeout, int nanos) throws InterruptedException
/**
* 跟之前的2个wait方法一样,只不过该方法一直等待,没有超时时间这个概念
*/
public final void wait() throws InterruptedException
/**
* 实例被垃圾回收器回收的时候触发的操作
*/
protected void finalize() throws Throwable { }
getClass() 方法:
? ?getClass()
?方法返回对象的运行时类,即对象所属的类的 Class 对象。Class 对象提供了许多反射操作,如获取类名、获取字段和方法信息等。
-
返回类型:
getClass()
?方法返回的是?Class
?类型的对象,它是 Java 反射机制中的一个重要类。 -
用途:
getClass()
?方法通常用于获取对象的运行时类型信息,包括类名、父类、接口等信息。通过?Class
?对象,可以进行许多反射操作,如获取字段和方法信息、创建新的对象实例等。 -
示例:
下面是一个简单的示例,展示如何使用 getClass() 方法获取对象的类名:
上述代码中,我们首先创建了一个 String 类型的对象,并将其赋给一个 Object 类型的引用变量 obj。然后,通过 getClass() 方法获取 obj 对象的运行时类型信息,即 String 类型的 Class 对象。最后,通过 getName() 方法获取该类的名称并输出。Object obj = new String("Hello, World!"); Class<?> cls = obj.getClass(); System.out.println(cls.getName()); // 输出:java.lang.String
????????需要注意的是,getClass()
方法返回的是对象的运行时类型,而不是编译时类型。这意味着,在多态的情况下,可能会返回不同的 Class
对象。如果需要获取编译时类型的信息,可以使用 instanceof
运算符。
hashCode() 方法:
? ?hashCode()
?方法返回对象的哈希码值,它用于在哈希表等数据结构中快速查找对象。默认情况下,hashCode()
?方法返回的是对象的内存地址的整数表示。如果重写了?equals()
?方法,通常也需要同时重写?hashCode()
?方法,以保持一致性。
-
返回类型:
hashCode()
?方法返回的是一个?int
?类型的哈希码值。
-
用途:
- 哈希码是对象的一个整数标识,用于支持高效的哈希表数据结构,如?
HashMap
、HashSet
?等。它被用来快速定位对象存储的位置,以提高查找、插入和删除操作的性能。 - 在 Java 中,如果两个对象的?
equals()
?方法返回?true
,那么它们的哈希码必须相等。因此,重写?equals()
?方法时通常也需要同时重写?hashCode()
?方法,以保持这个约定。
- 哈希码是对象的一个整数标识,用于支持高效的哈希表数据结构,如?
-
默认实现:
Object
?类中的?hashCode()
?方法的默认实现是根据对象的内存地址计算得到的,即每个对象的哈希码都是唯一的。这在大多数情况下是不可接受的,因为我们希望具有相等属性的对象具有相同的哈希码。- 因此,在自定义类中,应该根据对象的内容来重写?
hashCode()
?方法,以便相等的对象具有相同的哈希码。通常,可以使用对象的字段来计算哈希码,确保相等的对象生成相同的哈希码。
-
重写规则:
- 如果重写了?
equals()
?方法,通常也需要同时重写?hashCode()
?方法。 - 在重写?
hashCode()
?方法时,应该保证满足以下规则:- 如果两个对象通过?
equals()
?方法判断为相等,那么它们的哈希码必须相等。 - 如果两个对象的哈希码相等,它们不一定是相等的,即?
equals()
?方法可能返回?false
。
- 如果两个对象通过?
- 这样可以确保对象在使用哈希表数据结构时具有正确的行为和性能。
- 如果重写了?
下面是一个示例,展示如何重写 hashCode()
方法:
public class MyClass {
private int id;
private String name;
// 构造方法、getter 和 setter 省略
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + id;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null || getClass() != obj.getClass())
return false;
MyClass other = (MyClass) obj;
return id == other.id && Objects.equals(name, other.name);
}
}
????????在上述示例中,根据类的字段 id
和 name
来计算哈希码,并在 equals()
方法中比较这些字段的值。这样,具有相同 id
和 name
字段值的对象将具有相同的哈希码。
equals() 方法:
? ? equals()
?方法用于判断两个对象是否相等。默认情况下,equals()
?方法比较的是两个对象的引用是否相等,即它们是否指向同一个内存地址。可以通过重写?equals()
?方法来改变对象的相等比较方式。
-
方法签名:
- 在?
Object
?类中,equals()
?方法的签名为?public boolean equals(Object obj)
。
- 在?
-
默认实现:
- 在?
Object
?类中,equals()
?方法的默认实现是使用?==
?运算符来比较两个对象的引用是否相同,即判断对象的内存地址是否一致。 - 因此,默认情况下,如果不重写?
equals()
?方法,它将和?==
?运算符具有相同的行为。这意味着只有当两个对象引用指向内存中的同一块区域时,equals()
?方法才会返回?true
。
- 在?
-
重写规则:
- 在自定义类中,通常需要根据对象的内容来判断相等性,因此需要重写?
equals()
?方法。 - 在重写?
equals()
?方法时,应该保证满足以下几个约定:- 自反性:对于任何非空引用值 x,x.equals(x) 应该返回?
true
。 - 对称性:对于任何非空引用值 x 和 y,如果 x.equals(y) 返回?
true
,那么 y.equals(x) 也应该返回?true
。 - 传递性:对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回?
true
?并且 y.equals(z) 也返回?true
,那么 x.equals(z) 也应该返回?true
。 - 一致性:对于任何非空引用值 x 和 y,只要 equals() 方法的比较操作在对象中所用的信息没有被修改,多次调用 x.equals(y) 应该一直返回相同的结果。
- 对于任何非空引用值 x,x.equals(null) 应该返回?
false
。
- 自反性:对于任何非空引用值 x,x.equals(x) 应该返回?
- 在自定义类中,通常需要根据对象的内容来判断相等性,因此需要重写?
-
示例:
下面是一个简单的示例,展示如何重写equals()
方法:public class MyClass { private int id; private String name; // 构造方法、getter 和 setter 省略 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; MyClass other = (MyClass) obj; return id == other.id && Objects.equals(name, other.name); } }
????????在上述示例中,根据类的字段 id
和 name
来判断两个对象是否相等。这样,具有相同 id
和 name
字段值的对象将被视为相等。
????????总之,equals()
方法用于判断两个对象是否相等,但默认情况下它比较的是对象的引用。因此,通常根据具体的业务需求来重写 equals()
方法,以便根据对象的内容来确定相等性。
clone()方法:
? ?clone()
方法用于创建并返回当前对象的副本。这个方法执行的是浅拷贝(shallow copy),也就是复制对象的字段值。下面是关于 clone()
方法的详解:
-
使用方法:
????????要使用?clone()
?方法,首先需要确保被克隆的类实现了?Cloneable
?接口。该接口是一个标记接口,意味着它没有任何方法,只是用于标识能够进行克隆的类。然后,在要进行克隆的类中重写?clone()
?方法,并在方法内部调用父类的?clone()
?方法进行克隆。 -
浅拷贝:
? ? ? ?默认情况下,clone()
?方法执行的是浅拷贝,即只复制对象的字段值。如果对象包含其他引用类型的字段,那么克隆后的新对象和原对象将共享这些引用类型的字段。如果需要实现深拷贝(deep copy),即复制对象及其引用类型字段的内容,可以在?clone()
?方法中手动处理这些引用类型字段的克隆。 -
返回类型:
? ? ??clone()
?方法的返回类型是?Object
,因此在使用时需要进行类型转换。通常,将返回的?Object
?对象转换为克隆的具体类型。 -
克隆方法的保护性质:
? ? ? ?clone()
?方法在?Object
?类中被声明为受保护的,这意味着只能在当前类及其子类中访问该方法。如果一个类没有实现?Cloneable
?接口或者尝试在其他类中调用该类的?clone()
?方法,将会抛出?CloneNotSupportedException
?异常。
????????需要注意的是,虽然 clone()
方法提供了一种对象的复制方式,但它并不是推荐使用的方式。因为它存在一些问题,如对于可变对象的处理、构造函数的绕过以及性能开销等。更好的方式是使用拷贝构造函数或工厂方法来创建对象的副本,以实现更精确的控制和避免潜在的问题。
toString()方法:
????toString()
方法是 Object
类中的一个方法,用于返回表示对象的字符串表示。
-
方法签名:
- 在?
Object
?类中,toString()
?方法的签名为?public String toString()
。
- 在?
-
默认实现:
- 在?
Object
?类中,toString()
?方法的默认实现返回一个包含类名和对象的哈希码的字符串,格式为?类名@哈希码
。 - 默认的字符串表示对于调试和日志记录可能不够有用,因此在大多数情况下需要重写?
toString()
?方法。
- 在?
-
重写规则:
- 在自定义类中,通常需要根据对象的字段来返回一个更具描述性的字符串表示,因此需要重写?
toString()
?方法。 - 在重写?
toString()
?方法时,应该返回一个包含对象信息的字符串,以便能够描述对象的状态和属性。 - 重写?
toString()
?方法的目的是为了提供可读性高、易于理解和调试的字符串表示。
- 在自定义类中,通常需要根据对象的字段来返回一个更具描述性的字符串表示,因此需要重写?
-
示例:
-
下面是一个示例,展示如何重写
toString()
方法:public class MyClass { private int id; private String name; // 构造方法、getter 和 setter 省略 @Override public String toString() { return "MyClass{" + "id=" + id + ", name='" + name + '\'' + '}'; } }
-
? ? ? ? 在上述示例中,根据类的字段 id
和 name
来返回一个包含对象信息的字符串表示。通过重写 toString()
方法,我们可以得到类似于 "MyClass{id=1, name='John'}"
的字符串表示。
????????总之,toString()
方法用于返回对象的字符串表示,以便能够描述对象的状态和属性。默认情况下,它返回一个不太有用的字符串,因此通常需要根据具体的业务需求来重写 toString()
方法,以提供更有意义的字符串表示。
notify()、notifyAll()方法:
????notify()
方法是 Object
类中的一个方法,用于唤醒正在等待该对象的线程;notifyAll()
方法是 Object
类中的一个方法,用于唤醒正在等待该对象的所有线程。
-
方法签名:
- 在?
Object
?类中,notify()
?方法的签名为?public final void notify()
。 - 在?
Object
?类中,notifyAll()
?方法的签名为?public final void notifyAll()
。
- 在?
-
使用条件:
notify()
?、notifyAll()
方法必须在同步块或同步方法中调用,并且只能由持有对象监视器(锁)的线程来调用。- 如果当前线程不是持有对象监视器的线程,那么调用?
notify()、notifyAll()
?方法将会抛出?IllegalMonitorStateException
?异常。
-
功能:
- 当调用??
notify()/notifyAll()
? 方法时,它将唤醒正在等待该对象的线程中的 某一/全部 线程。被唤醒的线程将从等待状态转变为可运行状态,并且与其他线程一起竞争对象的监视器锁。 - 注意,
notify()
?,notifyAll()
方法不会立即释放对象的监视器锁,而是在当前线程执行完同步代码块或同步方法后才会释放。
- 当调用??
-
示例:
-
下面是一个示例,展示如何使用
wait()
和notify()
?、notifyAll()
方法进行线程间的通信:public class MyClass { public synchronized void doSomething() { System.out.println("Thread A: Doing something..."); try { wait(); // 线程 A 等待 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Thread A: Resumed!"); } public synchronized void notifyThread() { notify(); // 唤醒等待的线程 //notifyAll(); // 唤醒全部等待的线程 } } public class Main { public static void main(String[] args) { MyClass myObject = new MyClass(); Thread threadA = new Thread(() -> myObject.doSomething()); Thread threadB = new Thread(() -> myObject.notifyThread()); threadA.start(); // 启动线程 A threadB.start(); // 启动线程 B } }
在上述示例中,线程 A 调用了
doSomething()
方法后进入等待状态,并通过wait()
方法释放了对象的监视器锁。然后,线程 B 调用了notifyThread()
方法,唤醒了线程 A,并让线程 A 重新获得对象的监视器锁并继续执行。
-
????????总之,notify()
?/ notifyAll()
方法用于唤醒正在等待该对象的线程中的 某一/全部 线程,让它从等待状态转变为可运行状态。使用 notify()、
notifyAll()
方法需要注意同步块或同步方法的使用,并且调用 notify()
、
notifyAll()
方法的线程必须持有对象的监视器锁。
wait()
方法:
????wait()
方法是 Object
类中的一个方法,用于将当前线程置于等待状态,直到其他线程调用了该对象的 notify()
或 notifyAll()
方法来唤醒它。以下是关于 wait()
方法的详解:
-
方法签名:
- 在?
Object
?类中,wait()
?方法有多个重载形式,其中最常用的签名为?public final void wait()
。
- 在?
-
使用条件:
wait()
?方法必须在同步块或同步方法中调用,并且只能由持有对象监视器(锁)的线程来调用。- 如果当前线程不是持有对象监视器的线程,那么调用?
wait()
?方法将会抛出?IllegalMonitorStateException
?异常。
-
功能:
- 当调用?
wait()
?方法时,它会使当前线程进入等待状态,释放对象的监视器锁,直到其他线程调用了该对象的?notify()
?或?notifyAll()
?方法来唤醒它。 - 注意,被唤醒的线程从等待状态转变为可运行状态后,并不是立即执行,而是需要等待获取对象的监视器锁。
- 当调用?
-
示例:同notify()、notifyAll()方法事例
????????总之,wait()
方法用于将当前线程置于等待状态,直到其他线程调用了该对象的 notify()
或 notifyAll()
方法来唤醒它。使用 wait()
方法需要注意同步块或同步方法的使用,并且调用 wait()
方法的线程必须持有对象的监视器锁。
wait(long timeout)
方法:
wait(long timeout)
方法是 Object
类中的一个重载方法,用于将当前线程置于等待状态,直到其他线程调用了该对象的 notify()
、notifyAll()
或指定的超时时间过去。以下是关于 wait(long timeout)
方法的详解:
-
方法签名:
- 在?
Object
?类中,wait(long timeout)
?方法的签名为?public final void wait(long timeout) throws InterruptedException
。
- 在?
-
使用条件:
wait(long timeout)
?方法必须在同步块或同步方法中调用,并且只能由持有对象监视器(锁)的线程来调用。- 如果当前线程不是持有对象监视器的线程,那么调用?
wait(long timeout)
?方法将会抛出?IllegalMonitorStateException
?异常。
-
功能:
- 当调用?
wait(long timeout)
?方法时,它会使当前线程进入等待状态,释放对象的监视器锁,并等待指定的时间。 - 线程可以被其他线程调用该对象的?
notify()
?或?notifyAll()
?方法唤醒,或者等待超时时间到达后自动恢复执行。 - 如果在等待期间对象的?
notify()
?或?notifyAll()
?方法被调用,且等待时间还未到达,线程将被唤醒并继续执行。 - 如果等待时间到达而没有被其他线程唤醒,当前线程将重新获取对象的监视器锁并恢复执行。
- 当调用?
-
示例:
????????下面是一个示例,展示如何使用 wait(long timeout)
方法进行线程间的通信和超时等待:
public class MyClass {
public synchronized void doSomething() {
System.out.println("Thread A: Doing something...");
try {
wait(2000); // 线程 A 最多等待 2 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread A: Resumed!");
}
public synchronized void notifyThread() {
notify(); // 唤醒等待的线程
}
}
public class Main {
public static void main(String[] args) {
MyClass myObject = new MyClass();
Thread threadA = new Thread(() -> myObject.doSomething());
Thread threadB = new Thread(() -> myObject.notifyThread());
threadA.start(); // 启动线程 A
threadB.start(); // 启动线程 B
}
}
????????在上述示例中,线程 A 调用了 doSomething()
方法后进入等待状态,并通过 wait(2000)
方法释放了对象的监视器锁,最多等待 2 秒。然后,线程 B 调用了 notifyThread()
方法,在等待时间到达前唤醒了线程 A,并让线程 A 重新获得对象的监视器锁并继续执行。
????????总之,wait(long timeout)
方法用于将当前线程置于等待状态,直到其他线程调用了该对象的 notify()
或 notifyAll()
方法,或者指定的超时时间到达。使用 wait(long timeout)
方法需要注意同步块或同步方法的使用,并且调用 wait(long timeout)
方法的线程必须持有对象的监视器锁。
wait(long timeout, int nanos) 方法:
wait(long timeout, int nanos)
方法是 Object
类中的一个重载方法,用于将当前线程置于等待状态,直到其他线程调用了该对象的 notify()
、notifyAll()
方法或指定的超时时间过去。该方法还允许设置纳秒级别的超时时间。以下是关于 wait(long timeout, int nanos)
方法的详解:
-
方法签名:
- 在?
Object
?类中,wait(long timeout, int nanos)
?方法的签名为?public final void wait(long timeout, int nanos) throws InterruptedException
。
- 在?
-
使用条件:
wait(long timeout, int nanos)
?方法必须在同步块或同步方法中调用,并且只能由持有对象监视器(锁)的线程来调用。- 如果当前线程不是持有对象监视器的线程,那么调用?
wait(long timeout, int nanos)
?方法将会抛出?IllegalMonitorStateException
?异常。
-
功能:
- 当调用?
wait(long timeout, int nanos)
?方法时,它会使当前线程进入等待状态,释放对象的监视器锁,并等待指定的时间(包括纳秒级别的时间)。 - 线程可以被其他线程调用该对象的?
notify()
?或?notifyAll()
?方法唤醒,或者等待超时时间到达后自动恢复执行。 - 如果在等待期间对象的?
notify()
?或?notifyAll()
?方法被调用,且等待时间还未到达,线程将被唤醒并继续执行。 - 如果等待时间到达而没有被其他线程唤醒,当前线程将重新获取对象的监视器锁并恢复执行。
- 当调用?
-
示例:
-
下面是一个示例,展示如何使用
wait(long timeout, int nanos)
方法进行线程间的通信和纳秒级别的超时等待:
-
????????
public class MyClass {
public synchronized void doSomething() {
System.out.println("Thread A: Doing something...");
try {
wait(2_000, 500_000); // 线程 A 最多等待 2 秒 500 毫秒
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Thread A: Resumed!");
}
public synchronized void notifyThread() {
notify(); // 唤醒等待的线程
}
}
public class Main {
public static void main(String[] args) {
MyClass myObject = new MyClass();
Thread threadA = new Thread(() -> myObject.doSomething());
Thread threadB = new Thread(() -> myObject.notifyThread());
threadA.start(); // 启动线程 A
threadB.start(); // 启动线程 B
}
}
????????在上述示例中,线程 A 调用了 doSomething()
方法后进入等待状态,并通过 wait(2_000, 500_000)
方法释放了对象的监视器锁,最多等待 2 秒 500 毫秒。然后,线程 B 调用了 notifyThread()
方法,在等待时间到达前唤醒了线程 A,并让线程 A 重新获得对象的监视器锁并继续执行。
????????总之,wait(long timeout, int nanos)
方法用于将当前线程置于等待状态,直到其他线程调用了该对象的 notify()
或 notifyAll()
方法,或者指定的超时时间到达(包括纳秒级别的时间)。使用 wait(long timeout, int nanos)
方法需要注意同步块或同步方法的使用,并且调用 wait(long timeout, int nanos)
方法的线程必须持有对象的监视器锁。
finalize()方法:
finalize()
是 Object
类中的一个方法,用于在垃圾回收器对对象进行垃圾回收之前执行清理操作。以下是关于 finalize()
方法的详解:
-
方法签名:
- 在?
Object
?类中,finalize()
?方法的签名为?protected void finalize() throws Throwable
。
- 在?
-
功能:
finalize()
?方法是 Java 垃圾回收机制的一部分,它用于在对象被垃圾回收之前进行一些必要的清理操作。- 当对象没有被引用时,即将被垃圾回收时,垃圾回收器会在对象的内存释放之前调用?
finalize()
?方法。 finalize()
?方法可以被子类重写,以实现特定的清理操作,例如关闭文件、释放资源等。
-
执行时机:
finalize()
?方法的执行时机是不确定的,取决于垃圾回收器的调度和系统资源的可用性。- 调用?
finalize()
?方法的时间点是在垃圾回收器将对象标记为可回收,并在对象被实际回收之前。 - 由于垃圾回收器的行为是非确定性的,因此无法保证?
finalize()
?方法一定会被调用。
-
注意事项:
finalize()
?方法应该小心使用,因为它的执行时间是不确定的,可能会影响应用程序的性能。- 在实际编程中,更推荐使用显式的资源释放方式,例如在?
try-finally
?语句块中关闭文件、释放资源等,而不是依赖于?finalize()
?方法。 - 在 Java 9 及之后的版本中,
finalize()
?方法已被废弃。建议使用?try-with-resources
?或显式地调用资源释放方法来替代。
-
示例:
-
下面是一个示例,展示了如何重写
finalize()
方法进行资源的清理操作:public class MyClass { private File file; public MyClass() { this.file = new File("example.txt"); } @Override protected void finalize() throws Throwable { try { // 清理操作,例如关闭文件 if (file != null) { file.close(); } } finally { super.finalize(); } } }
在上述示例中,
MyClass
类重写了finalize()
方法,在方法中执行了关闭文件的操作。当对象被垃圾回收之前,垃圾回收器会调用finalize()
方法来确保文件被关闭。
-
????????总之,finalize()
方法是 Object
类中的一个方法,用于在对象被垃圾回收之前执行清理操作。它应该小心使用,并且在实际编程中更推荐使用显式的资源释放方式替代。
更多消息资讯,请访问昂焱数据。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!