Java学习——常用API Object
在Java中,Object类是所有类的根类,位于java.lang包下。如果一个类没有明确指定继承自哪个类,那么它默认继承自Object类。这意味着Java中的每个类都直接或间接地继承自Object类。因此,Object类定义的方法对所有Java对象都是可用的,这些方法提供了一些基本的行为,如对象的比较、克隆、获取对象的哈希码等。
主要方法
Object类提供了一些重要的方法,这些方法在Java中的所有对象中都可用:
- equals(Object obj):比较调用此方法的对象与指定对象是否相等。默认实现是比较对象的内存地址(引用比较)。
- hashCode():返回对象的哈希码值,该值用于支持哈希表类的效率,如
HashMap、HashSet等。默认情况下,是根据对象的内存地址计算得到的。 - toString():返回对象的字符串表示。默认实现返回类名加上“@”符号后跟对象的哈希码的无符号十六进制表示。
- getClass():获取运行时类的
Class对象。Class类的实例表示正在运行的Java应用程序中的类和接口。 - clone():创建并返回此对象的一个副本。默认实现是浅复制。
- finalize():当垃圾收集器确定不存在对该对象的更多引用时,由垃圾收集器调用此方法。用于释放资源或执行其他清理。
- wait()、notify()、notifyAll():这些方法与对象的监视器相关,用于线程间的协调。
重写equals和hashCode方法
在实际编程中,经常需要根据对象的内容而不是它们的引用来判断对象是否相等。这时,就需要重写equals方法和hashCode方法,以确保基于内容的相等性,并且保持equals和hashCode方法之间的一致性(即两个对象通过equals方法比较相等,则它们的hashCode方法返回的值也应该相等)。
示例:重写equals和hashCode
public class Person {private String name;private int age;public Person(String name, int age) {this.name = name;this.age = age;}@Overridepublic boolean equals(Object obj) {if (this == obj) return true;if (obj == null || getClass() != obj.getClass()) return false;Person person = (Person) obj;return age == person.age && Objects.equals(name, person.name);}@Overridepublic int hashCode() {return Objects.hash(name, age);}@Overridepublic String toString() {return "Person{" +"name='" + name + '\'' +", age=" + age +'}';}
}
在Java中,==操作符和equals()方法都用于比较对象,但它们之间有重要的区别:
== 操作符
- 用途:用于比较基本数据类型和引用类型。
- 基本数据类型:当用于比较基本数据类型时(如
int、double、char等),==比较的是值是否相等。 - 引用数据类型:当用于比较引用数据类型(如对象)时,
==比较的是两个引用是否指向堆内存中的同一个对象。即,它比较的是对象在内存中的地址。
equals() 方法
- 用途:
equals()方法是Object类的一个方法,用于比较两个对象的等价性。默认情况下,Object类的equals()方法和==操作符的行为相同,即比较对象引用。 - 覆盖
equals()方法:通常,类会重写(override)equals()方法以提供逻辑上的等价性比较,例如比较对象的内容是否相同,而不是比较引用。这对于类的实例来说是非常有用的,尤其是在比较如字符串、日期或自定义对象时。
示例
考虑下面的例子,说明==和equals()的使用和区别:
String str1 = new String("hello");
String str2 = new String("hello");// 使用 == 操作符比较
System.out.println(str1 == str2); // 输出 false,因为str1和str2引用不同的对象// 使用 equals() 方法比较
System.out.println(str1.equals(str2)); // 输出 true,因为str1和str2的内容相同
在这个例子中,尽管str1和str2引用不同的对象(即它们在内存中的地址不同),但它们的内容是相同的。String类重写了Object类的equals()方法,以比较字符串的内容而不是它们的引用。
重要性
正确理解==操作符和equals()方法的区别对于编写正确的Java程序来说非常重要,特别是在涉及对象相等性比较时。务必记住默认情况下equals()方法继承自Object类的实现,可能需要根据你的类的需要重写它。重写equals()时,还应该考虑重写hashCode()方法,以保持equals()和hashCode()之间的一致性约定。
Clone
在Java中,clone()方法用于创建并返回调用此方法的对象的一个副本。这个方法是Object类的一个protected方法,这意味着它默认在所有类中都可用,但只能在类的内部或者其子类中被调用。为了在类的外部使用clone()方法,类需要重写clone()方法,并改变其访问级别为public。
实现 Cloneable 接口
在Java中,一个对象的clone()方法只能在该对象所属的类实现了Cloneable接口时才能被成功调用。如果调用一个未实现Cloneable接口的对象的clone()方法,将抛出CloneNotSupportedException。Cloneable接口是一个标记接口,没有包含方法,它的作用是告诉clone()方法可以合法地对该类实例进行字段到字段的复制。
浅拷贝 vs. 深拷贝
- 浅拷贝(Shallow Copy):
clone()方法默认执行的是浅拷贝。在浅拷贝中,对象的非基本数据类型字段仅仅复制引用,而不复制引用的对象本身。因此,原始对象和克隆对象在这些字段上共享引用。 - 深拷贝(Deep Copy):深拷贝不仅复制对象本身,还复制对象引用的所有对象。这意味着克隆对象和原始对象在所有层次上都是完全独立的。实现深拷贝通常需要重写
clone()方法,并在该方法中手动复制所有内部对象。
重写 clone() 方法
为了让一个类可克隆,你需要:
- 实现
Cloneable接口。 - 重写
clone()方法,并声明为public。
public class Example implements Cloneable {private int[] arr;public Example() {arr = new int[10];// 初始化arr}public int[] getArr() {return arr;}@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}
示例:深拷贝的实现
public class DeepExample implements Cloneable {private int[] arr;public DeepExample() {arr = new int[10];// 初始化arr}public int[] getArr() {return arr;}@Overridepublic DeepExample clone() throws CloneNotSupportedException {DeepExample cloned = (DeepExample) super.clone();cloned.arr = arr.clone(); // 深拷贝return cloned;}
}
在这个例子中,DeepExample类通过调用数组的clone()方法实现了深拷贝。
注意事项
- 实现深拷贝可能会很复杂,尤其是当对象图包含多个对象,甚至循环引用时。
- 使用克隆方法时需要谨慎,因为不当的使用可能导致问题,如意外共享、破坏封装性等。
- 考虑到克隆的复杂性和潜在问题,有时使用拷贝构造函数或者拷贝工厂方法是更好的选择。
总的来说,clone()方法提供了对象复制的能力,但实现和使用它需要深思熟虑,确保正确处理浅拷贝和深拷贝,以及遵守Cloneable接口的约定。