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

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

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

你可能感兴趣的:(java,java,开发语言)