java中Object类的常用方法

  • 1.常用方法
    • 1.clone方法
      • 1.1 基本概念
      • 1.2 方法声明
      • 1.3 工作原理
      • 1.4 注意事项
      • 1.5 示例代码
    • 2.finalize方法
      • 2.1 基本概念
      • 2.2 方法声明
      • 2.3 工作原理
      • 2.4 注意事项
      • 2.5 替代方案
    • 3.equals方法
      • 3.1 方法声明
      • 3.2 默认行为
      • 3.3 重写规范
      • 3.4 示例代码
      • 3.5 注意事项
    • 4.hashcode方法
      • 4.1 实现方式
      • 4.2 遵循原则
      • 4.3 示例代码
    • 5.wait方法
    • 6.notify方法
    • 7.notifyAll方法
    • 8.toString方法
    • 9.getClass方法

1.常用方法

1.clone方法

1.1 基本概念

  • clone() 方法是属于 Object 类的一个方法,因此所有的类都可以重写该方法。
  • clone() 方法被设计为返回一个新的对象,该对象和原始对象具有相同的状态。
  • clone() 方法与 new 关键字不同,new 关键字会调用构造函数创建新的对象,而 clone() 方法则不会调用构造函数。

1.2 方法声明

protected native Object clone() throws CloneNotSupportedException;
  • protected:clone() 方法的访问修饰符是 protected,意味着只能在同一包中或子类中访问。
  • native:clone() 方法是一个本地方法,它使用 C/C++ 实现。
  • Object:clone() 方法返回一个 Object 类型的对象,需要进行类型转换以获取其真实类型。
  • throws CloneNotSupportedException:clone() 方法声明了 CloneNotSupportedException 异常,如果该方法所在的类没有实现 Cloneable 接口,则调用该方法时会抛出该异常。

1.3 工作原理

  • 在默认情况下,clone() 方法会创建一个新的对象,该对象和原始对象具有相同的状态。
  • 如果要创建的新对象是可变的,应该针对该对象进行深拷贝(deep copy),以避免新对象和原始对象引用相同的可变对象。
  • 如果要创建的新对象是不可变的,可以进行浅拷贝(shallow copy),以节省时间和空间。

1.4 注意事项

  • 要使用 clone() 方法,该方法所在的类必须实现 Cloneable 接口,并覆盖 clone() 方法。
  • Cloneable 接口是一个标记接口,它没有任何方法,只是表示该类可以被克隆。
  • 在重写 clone() 方法时,需要将方法的访问修饰符改为 public,并调用 super.clone() 方法以获取原始对象的副本。
  • 在进行深拷贝时,需要递归遍历所有可变对象,并对其进行拷贝。如果拷贝的对象中包含其他可变对象的引用,则需要继续递归拷贝。

1.5 示例代码

下面是一个简单的示例代码,演示如何使用 clone() 方法创建对象的副本:

public class MyClass implements Cloneable {
    private int value;

    public MyClass(int value) {
        this.value = value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public int getValue() {
        return value;
    }

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        MyClass obj1 = new MyClass(10);
        MyClass obj2 = (MyClass) obj1.clone();
        System.out.println(obj1.getValue()); // 输出 10
        System.out.println(obj2.getValue()); // 输出 10
        obj2.setValue(20);
        System.out.println(obj1.getValue()); // 输出 10
        System.out.println(obj2.getValue()); // 输出 20
    }
}

在上面的示例代码中,MyClass 类实现了 Cloneable 接口,并重写了 clone() 方法。在 Main 类中,我们创建了两个 MyClass 对象,分别为 obj1 和 obj2,并使用 clone() 方法创建了 obj2 的副本。最后,我们修改 obj2 的属性值,并打印出两个对象的属性值,可以看到它们是独立的。

2.finalize方法

2.1 基本概念

  • finalize() 方法是属于 Object 类的一个方法,因此所有的类都可以重写该方法。
  • finalize() 方法被设计为在对象即将被垃圾回收时调用,以便进行一些清理操作或资源释放。

2.2 方法声明

protected void finalize() throws Throwable
  • protected:finalize() 方法的访问修饰符是 protected,意味着只能在同一包中或子类中访问。
  • void:finalize() 方法没有返回值。
  • throws Throwable:finalize() 方法声明了 Throwable 异常,允许抛出任何异常。

2.3 工作原理

  • 当一个对象不再被引用时,垃圾回收器会在适当的时机对其进行回收。
  • 在垃圾回收过程中,如果该对象的 finalize() 方法被重写并且未被调用过,垃圾回收器会先调用该方法。
  • finalize() 方法只会被调用一次,因此在方法内部一般需要执行一些清理操作,如关闭打开的文件、释放占用的系统资源等。

2.4 注意事项

  • 不建议过多依赖 finalize() 方法进行资源释放,因为垃圾回收的时间是不确定的,可能会延迟执行,从而导致资源长时间占用。
  • finalize() 方法的执行时间和顺序是不可控的,无法保证在对象被销毁前立即执行。
  • 由于 finalize() 方法是 protected 的,子类可以重写该方法以实现自定义的清理操作。

2.5 替代方案

  • 更好的方式是使用 try-finally 结构或者实现 Closeable 接口,在合适的时候手动释放资源。
  • 在 Java 7 引入的 try-with-resources 语句块中,自动关闭资源更加简洁和安全。

总结:finalize() 方法是在对象被垃圾回收之前调用的方法,用于执行一些清理操作或资源释放。然而,由于其执行时间和顺序不可控,并且可能导致资源长时间占用,因此建议使用其他替代方案来进行资源的释放操作。

3.equals方法

3.1 方法声明

public boolean equals(Object obj)
  • equals() 方法是 Object 类的一个公共方法,因此所有的类都可以重写该方法。
  • 该方法接收一个 Object 类型的参数,用于与调用该方法的对象进行比较。

3.2 默认行为

  • 在 Object 类中,equals() 方法的默认实现是使用 == 运算符来比较两个对象的引用是否相同,即判断两个对象是否是同一个对象。
  • 因此,默认情况下,如果没有在自定义类中重写 equals() 方法,那么两个对象只有在引用相同时才被认为相等。

3.3 重写规范

  • 在自定义类中,通常需要根据类的语义去重新定义 equals() 方法,以实现对对象内容的比较而非引用的比较。
  • 重写 equals() 方法时,通常需要满足以下约定:
    • 自反性(Reflexive):对于任何非空引用值 x,x.equals(x) 应当返回 true。
    • 对称性(Symmetric):对于任何非空引用值 x 和 y,如果 x.equals(y) 返回 true,则 y.equals(x) 也应当返回 true。
    • 传递性(Transitive):对于任何非空引用值 x、y 和 z,如果 x.equals(y) 返回 true,并且 y.equals(z) 也返回 true,则 x.equals(z) 应当返回 true。
    • 一致性(Consistent):对于任何非空引用值 x 和 y,只要 equals 的比较操作在对象中所用的信息没有被修改,多次调用 x.equals(y) 应当始终返回相同的结果。
    • 非空性(Non-nullity):对于任何非空引用值 x,x.equals(null) 应当返回 false。

3.4 示例代码

下面是一个简单的示例代码,演示如何重写 equals() 方法:

public class MyClass {
    private int value;

    public MyClass(int value) {
        this.value = value;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        MyClass myObj = (MyClass) obj;
        return value == myObj.value;
    }
}

3.5 注意事项

  • 在重写 equals() 方法时,通常也需要重写 hashCode() 方法,以保证当两个对象相等时它们的哈希码也相等。
  • 使用 IDE 自动生成 equals() 方法和 hashCode() 方法可以提高开发效率,减少出错的可能性。

总结起来,重写 equals() 方法是为了根据对象的内容来判断两个对象是否相等,而不仅仅是比较它们的引用。在重写该方法时,需要遵循一定的规范,以确保对象比较的正确性和一致性。

4.hashcode方法

在Java中,hashCode()方法是用来获取一个对象的哈希码(散列码)的方法。它返回的是一个int类型的数字,该数字是根据对象的特征计算出来的,并且是唯一的。因此,我们可以将这个数字作为该对象的标识符。

4.1 实现方式

hashCode()方法的实现方式如下:

  1. 对象的存储地址。默认情况下,hashCode()方法返回的就是对象在内存中的存储地址,也就是对象的唯一标识符。但是,这种方式有一个缺点,就是每次程序运行时,同一个对象的哈希码都会不同,因为它们可能被分配到不同的内存地址上。

  2. 对象的属性值。当对象的属性值改变时,hashCode()方法返回的哈希码也应该改变,以保证相同属性的对象拥有相同的哈希码。因此,在实现hashCode()方法时,我们通常会根据对象的属性值进行计算,并返回一个与属性值相关的哈希码。

在Java中,如果两个对象的equals()方法返回true,则它们的哈希码必须相等,即hashCode()方法返回的值相等。因此,在实现hashCode()方法时,我们需要确保对于相等的对象,它们的哈希码也相等。

Java中的一些类已经实现了hashCode()方法,例如String、Integer、Double等。对于自定义的类,我们需要根据实际情况来实现hashCode()方法。一般来说,实现hashCode()方法的方式如下:

  1. 声明一个int类型的变量result,并初始化为一个非零常数。

  2. 对象的每个重要属性,例如字符串、数字等,都需要计算它们的哈希码并与result进行结合运算(如乘法、异或等),以便得到一个新的哈希码。

  3. 最后返回result作为该对象的哈希码。

4.2 遵循原则

需要注意的是,实现hashCode()方法时应遵循以下原则:

  1. 如果两个对象的equals()方法返回true,则它们的hashCode()方法必须返回相同的值。

  2. 如果两个对象的equals()方法返回false,则它们的hashCode()方法不一定要返回不同的值,但最好是不同的,以提高哈希表的性能。

  3. 在计算哈希码时,尽可能使用所有重要的属性,以提高哈希表的性能。

总之,hashCode()方法的目的是为了加快查找对象的速度,因此在实现hashCode()方法时,我们需要确保它能够产生良好的分布,以减少哈希冲突和哈希表的性能问题。

4.3 示例代码

下面是一个比较详细的hashCode()方法的实现示例:

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object obj) {
        if (obj == null) return false;
        if (this == obj) return true;
        if (obj.getClass() != this.getClass()) return false;
        Person p = (Person) obj;
        return this.name.equals(p.name) && this.age == p.age;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        result = prime * result + age;
        return result;
    }
}

上述代码中,hashCode()方法的实现遵循了以下原则:

  1. 首先声明了一个int类型的变量result,初始化为一个非零常数1。

  2. 然后使用常数31作为哈希码的乘数,这是因为31是一个素数,而且比较适合用于哈希码的计算。

  3. 对于对象的每个重要属性,例如字符串、数字等,都需要计算它们的哈希码并与result进行结合运算(如乘法、异或等),以便得到一个新的哈希码。

  4. 最后返回result作为该对象的哈希码。

在上述代码中,我们使用了name.hashCode()来计算字符串类型的属性name的哈希码,这是因为字符串类型已经实现了hashCode()方法。如果是其他类型,则需要根据实际情况计算其哈希码。

同时,在equals()方法中,我们也使用了相同的属性进行比较,以保证在两个对象相等时,它们的哈希码也相等。

总之,hashCode()方法的实现应该遵循上述原则,并且要注意根据实际情况合理选择哈希码的计算方式,以提高哈希表的性能。

5.wait方法

在 Java 中,wait() 是 Object 类的一个方法,可以用于线程同步和协作。它是一个阻塞方法,调用它的线程会被挂起,等待其他线程调用 notify() 或 notifyAll() 方法来唤醒它。

以下是使用 wait() 方法进行线程同步和协作的示例:

public class SharedResource {
    private int counter = 0;

    public synchronized void increment() throws InterruptedException {
        while (counter == 1) {
            wait(); // 如果计数器已经为 1,则等待其他线程调用 notify() 方法
        }
        counter++;
        System.out.println("Counter incremented: " + counter);
        notify(); // 唤醒其他可能在等待的线程
    }

    public synchronized void decrement() throws InterruptedException {
        while (counter == 0) {
            wait(); // 如果计数器已经为 0,则等待其他线程调用 notify() 方法
        }
        counter--;
        System.out.println("Counter decremented: " + counter);
        notify(); // 唤醒其他可能在等待的线程
    }
}

在这个示例中,我们定义了一个名为 SharedResource 的类,其中包含了两个方法:increment() 和 decrement()。这两个方法都是同步方法,即它们使用了 synchronized 关键字,以确保线程安全。

在 increment() 和 decrement() 方法中,我们使用了 while 循环来判断是否满足某个条件(例如计数器是否为 0 或 1),如果不满足则调用 wait() 方法,挂起当前线程。

在等待其他线程唤醒它之后,当前线程会继续执行剩余的代码。在执行完毕之后,我们调用了 notify() 方法,唤醒其他可能在等待的线程。

需要注意的是,wait() 和 notify() 方法只能在已经获取了对象的锁的情况下使用,否则会抛出 IllegalMonitorStateException 异常。此外,wait() 和 notify() 方法也只能用于同步方法或同步块中。

6.notify方法

在Java中,notify()方法是Object类的一个方法,用于唤醒正在等待该对象锁的某个线程。当调用notify()方法时,会选择性地通知等待在该对象上的一个线程,告诉它可以继续执行了。

同步队列中的线程是给抢占 CPU 的线程,等待队列中的线程指的是等待唤醒的线程

以下是notify()方法的详细解释:

  • notify()方法的作用是唤醒等待在当前对象上的一个线程。如果有多个线程在等待,只会随机选择一个线程进行唤醒。被唤醒的线程将从等待状态转为可运行状态,但并不是立即执行,而是需要等待获取对象的锁才能继续执行。

  • 调用notify()方法前,必须先获得当前对象的锁。通常情况下,notify()方法应该在synchronized代码块或synchronized方法中使用,以确保在调用notify()方法时拥有对象的锁。

  • 如果没有等待在当前对象上的线程,即没有调用wait()方法进入等待状态的线程,调用notify()方法不会有任何效果。因此,在调用notify()方法前,通常需要检查是否有线程处于等待状态。

  • notify()方法不会立即释放锁,而是继续执行当前的synchronized代码块或synchronized方法,直到离开同步块,才会释放锁。这意味着,被唤醒的线程只有在获取到锁之后才能继续执行。

以下是notify()方法的示例代码:

public class Example {
    public static void main(String[] args) {
        Object lock = new Object();

        // 线程1
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("线程1等待");
                    lock.wait(); // 线程1等待,并释放lock对象的锁
                    System.out.println("线程1被唤醒");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        // 线程2
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                try {
                    System.out.println("线程2开始");
                    Thread.sleep(2000); // 线程2休眠2秒钟
                    lock.notify(); // 唤醒等待在lock对象上的线程1
                    System.out.println("线程2结束");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread1.start();
        thread2.start();
    }
}

在这个例子中,我们创建了一个名为Example的类。在main()方法中,我们创建了一个共享对象lock,并创建了两个线程thread1和thread2。

线程1首先获取lock对象的锁,并调用wait()方法进行等待,这会释放lock对象的锁。接着,线程2获取lock对象的锁,并调用notify()方法来唤醒等待在lock对象上的线程1。最后,线程1被唤醒后继续执行。

当我们运行上述代码时,将会输出以下结果:

线程1等待
线程2开始
线程2结束
线程1被唤醒

可以看到,通过调用wait()和notify()方法,我们可以实现线程间的通信。wait()方法使线程进入等待状态并释放对象锁,而notify()方法则唤醒等待在该对象上的某个线程。

总之,notify()方法是Java中用于线程间通信的重要方法之一,但需要注意的是,notify()方法只会唤醒等待在当前对象上的一个线程。如果想要唤醒所有等待的线程,可以使用notifyAll()方法。notifyAll()方法会唤醒所有等待在当前对象上的线程,让它们从等待状态转为可运行状态。

7.notifyAll方法

notifyAll()方法是Java中用于线程间通信的方法之一。它属于Object类,用于唤醒等待在当前对象上的所有线程。

以下是notifyAll()方法的详细解释:

  1. notifyAll()方法的作用是唤醒等待在当前对象上的所有线程。被唤醒的线程将从等待状态转为可运行状态,但并不是立即执行,而是需要等待获取对象的锁才能继续执行。

  2. 调用notifyAll()方法前,必须先获得当前对象的锁。通常情况下,notifyAll()方法应该在synchronized代码块或synchronized方法中使用,以确保在调用notifyAll()方法时拥有对象的锁。

  3. 如果没有等待在当前对象上的线程,即没有调用wait()方法进入等待状态的线程,调用notifyAll()方法不会有任何效果。因此,在调用notifyAll()方法前,通常需要检查是否有线程处于等待状态。

  4. notifyAll()方法不会立即释放锁,而是继续执行当前的synchronized代码块或synchronized方法,直到离开同步块,才会释放锁。这意味着,被唤醒的线程只有在获取到锁之后才能继续执行。

需要注意的是,notifyAll()方法会唤醒所有等待在当前对象上的线程,包括已经获取了锁但是还没有执行的线程。因此,在使用notifyAll()方法时,需要仔细考虑是否需要唤醒所有线程,以及如何确保线程安全。

线程间通信是多线程编程中非常重要的概念,通过使用wait()、notify()和notifyAll()等方法,可以实现线程之间的协调和同步。这些方法的使用需要注意正确的同步机制,以避免死锁和竞态条件等问题。

8.toString方法

在Java中,toString() 方法是Object类的一个方法,它用于返回表示对象的字符串表示形式。当我们打印一个对象或将其转换为字符串时,实际上是调用了对象的toString()方法。

如果我们没有在自定义的类中重写toString()方法,那么默认情况下会使用Object类中的toString()方法。Object类的toString()方法返回的字符串包含对象的类名和哈希码的十六进制表示。

以下是一个使用toString()方法的示例:

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // 重写toString()方法
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public static void main(String[] args) {
        Person person = new Person("John", 25);
        System.out.println(person.toString()); // 调用toString()方法
        System.out.println(person); // 简化的调用方式,实际上也是调用toString()方法
    }
}

在上面的示例中,我们创建了一个Person类,并重写了toString()方法。在toString()方法中,我们返回了一个包含name和age属性的字符串表示。

在main()方法中,我们创建了一个Person对象,并调用了toString()方法来打印对象的字符串表示。此外,我们还使用了简化的方式直接打印对象,实际上也是调用了toString()方法。

当我们运行上述代码时,将会输出以下结果:

Person{name='John', age=25}
Person{name='John', age=25}

可以看到,通过重写toString()方法,我们可以自定义对象的字符串表示形式,使其更易读和有意义。在实际开发中,通常会根据需要重写toString()方法,以便更好地表示对象的状态和属性。

9.getClass方法

在 Java 中,getClass() 方法是Object类的一个方法,它用于返回表示对象所属类的Class对象。每个对象都可以调用getClass()方法来获取其所属类的信息。

以下是getClass()方法的详细解释:

public class Example {
    public static void main(String[] args) {
        String str = "Hello";
        Class<? extends String> clazz = str.getClass();
        System.out.println(clazz.getName());
    }
}

在这个例子中,我们创建了一个名为Example的类。在main()方法中,我们创建了一个字符串对象str,然后调用了getClass()方法来获取该对象的类信息。最后,我们使用getName()方法打印了类的名称。

当我们运行上述代码时,将会输出以下结果:

java.lang.String

可以看到,通过调用getClass()方法,我们可以获取对象的运行时类型信息,然后使用Class对象提供的方法来获取类的名称、方法、字段等信息。

需要注意的是,由于getClass()方法是从Object类继承而来的,因此所有的Java对象都可以调用getClass()方法。但是,如果对象是null,调用getClass()方法将会导致NullPointerException异常。

另外,对于数组对象而言,getClass()方法返回的是描述数组类型的Class对象,而不是数组中元素的类型的Class对象。如果想要获取数组中元素的类型,可以使用Class对象的getComponentType()方法。

总之,getClass()方法是一个非常重要的方法,它允许我们在运行时获取对象的类型信息,并进行相应的操作。

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