Java Object类详解

发布时间:2023年12月28日

????????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 对象提供了许多反射操作,如获取类名、获取字段和方法信息等。

  1. 返回类型

    getClass()?方法返回的是?Class?类型的对象,它是 Java 反射机制中的一个重要类。
  2. 用途

    getClass()?方法通常用于获取对象的运行时类型信息,包括类名、父类、接口等信息。通过?Class?对象,可以进行许多反射操作,如获取字段和方法信息、创建新的对象实例等。
  3. 示例

    下面是一个简单的示例,展示如何使用 getClass() 方法获取对象的类名:
    Object obj = new String("Hello, World!"); 
    Class<?> cls = obj.getClass(); 
    System.out.println(cls.getName()); 
    // 输出:java.lang.String
    上述代码中,我们首先创建了一个 String 类型的对象,并将其赋给一个 Object 类型的引用变量 obj。然后,通过 getClass() 方法获取 obj 对象的运行时类型信息,即 String 类型的 Class 对象。最后,通过 getName() 方法获取该类的名称并输出。

????????需要注意的是,getClass() 方法返回的是对象的运行时类型,而不是编译时类型。这意味着,在多态的情况下,可能会返回不同的 Class 对象。如果需要获取编译时类型的信息,可以使用 instanceof 运算符。

hashCode() 方法:

? ?hashCode()?方法返回对象的哈希码值,它用于在哈希表等数据结构中快速查找对象。默认情况下,hashCode()?方法返回的是对象的内存地址的整数表示。如果重写了?equals()?方法,通常也需要同时重写?hashCode()?方法,以保持一致性。

  1. 返回类型

    • hashCode()?方法返回的是一个?int?类型的哈希码值。
  2. 用途

    • 哈希码是对象的一个整数标识,用于支持高效的哈希表数据结构,如?HashMapHashSet?等。它被用来快速定位对象存储的位置,以提高查找、插入和删除操作的性能。
    • 在 Java 中,如果两个对象的?equals()?方法返回?true,那么它们的哈希码必须相等。因此,重写?equals()?方法时通常也需要同时重写?hashCode()?方法,以保持这个约定。
  3. 默认实现

    • Object?类中的?hashCode()?方法的默认实现是根据对象的内存地址计算得到的,即每个对象的哈希码都是唯一的。这在大多数情况下是不可接受的,因为我们希望具有相等属性的对象具有相同的哈希码。
    • 因此,在自定义类中,应该根据对象的内容来重写?hashCode()?方法,以便相等的对象具有相同的哈希码。通常,可以使用对象的字段来计算哈希码,确保相等的对象生成相同的哈希码。
  4. 重写规则

    • 如果重写了?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);
    }
}

????????在上述示例中,根据类的字段 idname 来计算哈希码,并在 equals() 方法中比较这些字段的值。这样,具有相同 idname 字段值的对象将具有相同的哈希码。

equals() 方法

? ? equals()?方法用于判断两个对象是否相等。默认情况下,equals()?方法比较的是两个对象的引用是否相等,即它们是否指向同一个内存地址。可以通过重写?equals()?方法来改变对象的相等比较方式。

  1. 方法签名

    • 在?Object?类中,equals()?方法的签名为?public boolean equals(Object obj)
  2. 默认实现

    • 在?Object?类中,equals()?方法的默认实现是使用?==?运算符来比较两个对象的引用是否相同,即判断对象的内存地址是否一致。
    • 因此,默认情况下,如果不重写?equals()?方法,它将和?==?运算符具有相同的行为。这意味着只有当两个对象引用指向内存中的同一块区域时,equals()?方法才会返回?true
  3. 重写规则

    • 在自定义类中,通常需要根据对象的内容来判断相等性,因此需要重写?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
  4. 示例

    下面是一个简单的示例,展示如何重写 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);
        }
    }
    

????????在上述示例中,根据类的字段 idname 来判断两个对象是否相等。这样,具有相同 idname 字段值的对象将被视为相等。

????????总之,equals() 方法用于判断两个对象是否相等,但默认情况下它比较的是对象的引用。因此,通常根据具体的业务需求来重写 equals() 方法,以便根据对象的内容来确定相等性。

clone()方法:

? ?clone() 方法用于创建并返回当前对象的副本。这个方法执行的是浅拷贝(shallow copy),也就是复制对象的字段值。下面是关于 clone() 方法的详解:

  1. 使用方法

    ????????要使用?clone()?方法,首先需要确保被克隆的类实现了?Cloneable?接口。该接口是一个标记接口,意味着它没有任何方法,只是用于标识能够进行克隆的类。然后,在要进行克隆的类中重写?clone()?方法,并在方法内部调用父类的?clone()?方法进行克隆。
  2. 浅拷贝

    ? ? ? ?默认情况下,clone()?方法执行的是浅拷贝,即只复制对象的字段值。如果对象包含其他引用类型的字段,那么克隆后的新对象和原对象将共享这些引用类型的字段。如果需要实现深拷贝(deep copy),即复制对象及其引用类型字段的内容,可以在?clone()?方法中手动处理这些引用类型字段的克隆。
  3. 返回类型

    ? ? ??clone()?方法的返回类型是?Object,因此在使用时需要进行类型转换。通常,将返回的?Object?对象转换为克隆的具体类型。
  4. 克隆方法的保护性质

    ? ? ? ?clone()?方法在?Object?类中被声明为受保护的,这意味着只能在当前类及其子类中访问该方法。如果一个类没有实现?Cloneable?接口或者尝试在其他类中调用该类的?clone()?方法,将会抛出?CloneNotSupportedException?异常。

????????需要注意的是,虽然 clone() 方法提供了一种对象的复制方式,但它并不是推荐使用的方式。因为它存在一些问题,如对于可变对象的处理、构造函数的绕过以及性能开销等。更好的方式是使用拷贝构造函数或工厂方法来创建对象的副本,以实现更精确的控制和避免潜在的问题。

toString()方法:

????toString() 方法是 Object 类中的一个方法,用于返回表示对象的字符串表示。

  1. 方法签名

    • 在?Object?类中,toString()?方法的签名为?public String toString()
  2. 默认实现

    • 在?Object?类中,toString()?方法的默认实现返回一个包含类名和对象的哈希码的字符串,格式为?类名@哈希码
    • 默认的字符串表示对于调试和日志记录可能不够有用,因此在大多数情况下需要重写?toString()?方法。
  3. 重写规则

    • 在自定义类中,通常需要根据对象的字段来返回一个更具描述性的字符串表示,因此需要重写?toString()?方法。
    • 在重写?toString()?方法时,应该返回一个包含对象信息的字符串,以便能够描述对象的状态和属性。
    • 重写?toString()?方法的目的是为了提供可读性高、易于理解和调试的字符串表示。
  4. 示例

    • 下面是一个示例,展示如何重写 toString() 方法:

      public class MyClass {
          private int id;
          private String name;
      
          // 构造方法、getter 和 setter 省略
      
          @Override
          public String toString() {
              return "MyClass{" +
                      "id=" + id +
                      ", name='" + name + '\'' +
                      '}';
          }
      }
      

? ? ? ? 在上述示例中,根据类的字段 idname 来返回一个包含对象信息的字符串表示。通过重写 toString() 方法,我们可以得到类似于 "MyClass{id=1, name='John'}" 的字符串表示。

????????总之,toString() 方法用于返回对象的字符串表示,以便能够描述对象的状态和属性。默认情况下,它返回一个不太有用的字符串,因此通常需要根据具体的业务需求来重写 toString() 方法,以提供更有意义的字符串表示。

notify()、notifyAll()方法:

????notify() 方法是 Object 类中的一个方法,用于唤醒正在等待该对象的线程;notifyAll() 方法是 Object 类中的一个方法,用于唤醒正在等待该对象的所有线程。

  1. 方法签名:

    • 在?Object?类中,notify()?方法的签名为?public final void notify()
    • 在?Object?类中,notifyAll()?方法的签名为?public final void notifyAll()
  2. 使用条件:

    • notify()?、notifyAll()方法必须在同步块或同步方法中调用,并且只能由持有对象监视器(锁)的线程来调用。
    • 如果当前线程不是持有对象监视器的线程,那么调用?notify()、notifyAll()?方法将会抛出?IllegalMonitorStateException?异常。
  3. 功能:

    • 当调用??notify()/notifyAll()? 方法时,它将唤醒正在等待该对象的线程中的 某一/全部 线程。被唤醒的线程将从等待状态转变为可运行状态,并且与其他线程一起竞争对象的监视器锁。
    • 注意,notify()?,notifyAll()方法不会立即释放对象的监视器锁,而是在当前线程执行完同步代码块或同步方法后才会释放。
  4. 示例:

    • 下面是一个示例,展示如何使用 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() 方法的详解:

  1. 方法签名:

    • 在?Object?类中,wait()?方法有多个重载形式,其中最常用的签名为?public final void wait()
  2. 使用条件:

    • wait()?方法必须在同步块或同步方法中调用,并且只能由持有对象监视器(锁)的线程来调用。
    • 如果当前线程不是持有对象监视器的线程,那么调用?wait()?方法将会抛出?IllegalMonitorStateException?异常。
  3. 功能:

    • 当调用?wait()?方法时,它会使当前线程进入等待状态,释放对象的监视器锁,直到其他线程调用了该对象的?notify()?或?notifyAll()?方法来唤醒它。
    • 注意,被唤醒的线程从等待状态转变为可运行状态后,并不是立即执行,而是需要等待获取对象的监视器锁。
  4. 示例:同notify()、notifyAll()方法事例

????????总之,wait() 方法用于将当前线程置于等待状态,直到其他线程调用了该对象的 notify()notifyAll() 方法来唤醒它。使用 wait() 方法需要注意同步块或同步方法的使用,并且调用 wait() 方法的线程必须持有对象的监视器锁。

wait(long timeout) 方法:

wait(long timeout) 方法是 Object 类中的一个重载方法,用于将当前线程置于等待状态,直到其他线程调用了该对象的 notify()notifyAll() 或指定的超时时间过去。以下是关于 wait(long timeout) 方法的详解:

  1. 方法签名:

    • 在?Object?类中,wait(long timeout)?方法的签名为?public final void wait(long timeout) throws InterruptedException
  2. 使用条件:

    • wait(long timeout)?方法必须在同步块或同步方法中调用,并且只能由持有对象监视器(锁)的线程来调用。
    • 如果当前线程不是持有对象监视器的线程,那么调用?wait(long timeout)?方法将会抛出?IllegalMonitorStateException?异常。
  3. 功能:

    • 当调用?wait(long timeout)?方法时,它会使当前线程进入等待状态,释放对象的监视器锁,并等待指定的时间。
    • 线程可以被其他线程调用该对象的?notify()?或?notifyAll()?方法唤醒,或者等待超时时间到达后自动恢复执行。
    • 如果在等待期间对象的?notify()?或?notifyAll()?方法被调用,且等待时间还未到达,线程将被唤醒并继续执行。
    • 如果等待时间到达而没有被其他线程唤醒,当前线程将重新获取对象的监视器锁并恢复执行。
  4. 示例:

????????下面是一个示例,展示如何使用 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) 方法的详解:

  1. 方法签名:

    • 在?Object?类中,wait(long timeout, int nanos)?方法的签名为?public final void wait(long timeout, int nanos) throws InterruptedException
  2. 使用条件:

    • wait(long timeout, int nanos)?方法必须在同步块或同步方法中调用,并且只能由持有对象监视器(锁)的线程来调用。
    • 如果当前线程不是持有对象监视器的线程,那么调用?wait(long timeout, int nanos)?方法将会抛出?IllegalMonitorStateException?异常。
  3. 功能:

    • 当调用?wait(long timeout, int nanos)?方法时,它会使当前线程进入等待状态,释放对象的监视器锁,并等待指定的时间(包括纳秒级别的时间)。
    • 线程可以被其他线程调用该对象的?notify()?或?notifyAll()?方法唤醒,或者等待超时时间到达后自动恢复执行。
    • 如果在等待期间对象的?notify()?或?notifyAll()?方法被调用,且等待时间还未到达,线程将被唤醒并继续执行。
    • 如果等待时间到达而没有被其他线程唤醒,当前线程将重新获取对象的监视器锁并恢复执行。
  4. 示例:

    • 下面是一个示例,展示如何使用 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() 方法的详解:

  1. 方法签名:

    • 在?Object?类中,finalize()?方法的签名为?protected void finalize() throws Throwable
  2. 功能:

    • finalize()?方法是 Java 垃圾回收机制的一部分,它用于在对象被垃圾回收之前进行一些必要的清理操作。
    • 当对象没有被引用时,即将被垃圾回收时,垃圾回收器会在对象的内存释放之前调用?finalize()?方法。
    • finalize()?方法可以被子类重写,以实现特定的清理操作,例如关闭文件、释放资源等。
  3. 执行时机:

    • finalize()?方法的执行时机是不确定的,取决于垃圾回收器的调度和系统资源的可用性。
    • 调用?finalize()?方法的时间点是在垃圾回收器将对象标记为可回收,并在对象被实际回收之前。
    • 由于垃圾回收器的行为是非确定性的,因此无法保证?finalize()?方法一定会被调用。
  4. 注意事项:

    • finalize()?方法应该小心使用,因为它的执行时间是不确定的,可能会影响应用程序的性能。
    • 在实际编程中,更推荐使用显式的资源释放方式,例如在?try-finally?语句块中关闭文件、释放资源等,而不是依赖于?finalize()?方法。
    • 在 Java 9 及之后的版本中,finalize()?方法已被废弃。建议使用?try-with-resources?或显式地调用资源释放方法来替代。
  5. 示例:

    • 下面是一个示例,展示了如何重写 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 类中的一个方法,用于在对象被垃圾回收之前执行清理操作。它应该小心使用,并且在实际编程中更推荐使用显式的资源释放方式替代。

更多消息资讯,请访问昂焱数据

文章来源:https://blog.csdn.net/tszc95/article/details/135256419
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。