其他常见API
Object类所在包是java.lang包。Object 是类层次结构的根,每个类都可以将 Object 作为超类(父类)。
所有类都直接或者间接的继承自Object类。Object类定义了一些基本的方法,对于所有对象都是通用的。
以下是Object类的主要功能:
equals()方法:Object类提供了用于比较对象是否相等的equals()方法。在默认情况下,equals()方法通过比较对象的引用来确定它们是否相等。但是,它可以被子类重写以提供自定义的相等性比较规则。
hashCode()方法:Object类定义了hashCode()方法,用于返回对象的散列码(哈希码)。hashCode()方法经常与equals()方法一起使用,以确保相等的对象具有相等的哈希码。hashCode()方法也可以被子类重写以提供自定义的哈希码实现。
toString()方法:Object类提供了toString()方法,用于返回对象的字符串表示。默认实现返回由类名、@符号和对象的散列码组成的字符串。可以通过在子类中重写toString()方法来提供更具信息性的字符串表示形式。
getClass()方法:Object类的getClass()方法返回对象的运行时类。它返回一个Class对象,该对象包含有关类的元数据信息。
垃圾回收:Object类定义了finalize()方法,当垃圾回收器准备回收对象时,会调用该方法。finalize()方法允许对象在被销毁之前执行一些清理操作。
wait()、notify()和notifyAll()方法:Object类提供了用于线程间通信的wait()、notify()和notifyAll()方法。这些方法可以与synchronized关键字一起使用,以实现多线程之间的同步和协作。
下表列出了Object类的所有方法,并提供简单的说明和示例:
方法名 | 说明 | 示例 |
---|---|---|
boolean equals(Object obj) | 比较对象是否相等。默认通过比较引用来判断,可以被子类重写。 | Integer x = 5; Integer y = 5; x.equals(y); // true |
int hashCode() | 返回对象的哈希码。默认实现基于对象的内存地址,可以被子类重写。 | String str = "Hello"; str.hashCode(); // 69609650 |
Class> getClass() | 返回对象的运行时类。 | String str = "Hello"; str.getClass(); // class java.lang.String |
String toString() | 返回对象的字符串表示。默认返回类名、@符号和散列码的组合,可以被子类重写。 | Integer num = 10; num.toString(); // "10" |
void notify() | 唤醒在该对象上等待的单个线程。 | synchronized (obj) { obj.notify(); } |
void notifyAll() | 唤醒在该对象上等待的所有线程。 | synchronized (obj) { obj.notifyAll(); } |
void wait() | 使当前线程等待,直到该对象收到notify或notifyAll通知。 | synchronized (obj) { obj.wait(); } |
void finalize() | 垃圾回收器准备回收对象时调用,允许对象进行一些清理操作。 | protected void finalize() throws Throwable {...} |
这些方法为所有Java对象提供了通用的行为和功能。
equals()
和hashCode()
方法用于比较对象的相等性和生成唯一标识。toString()
方法返回对象的字符串表示形式,方便输出调试信息。getClass()
方法返回对象的运行时类,用于获取类的元数据信息。wait()
、notify()
和notifyAll()
方法用于实现线程间通信和同步。finalize()
方法在对象被垃圾回收前执行清理操作。
重写toString()
方法的格式为:
@Override
public String toString() {
// 生成对象字符串表示的逻辑
}
重写toString()
方法的原因是为了提供更有意义和可读性的对象字符串表示,方便调试和输出信息。
例如:假设我们有一个Student
类,表示一个学生的信息,包含学生的姓名、年龄和学号属性。
没有重写toString()
方法的情况
调用默认toString()
方法返回的是对象的类名、@符号和散列码的组合。
示例代码如下:
public class Student {
private String name;
private int age;
private int studentNumber;
public Student(String name, int age, int studentNumber) {
this.name = name;
this.age = age;
this.studentNumber = studentNumber;
}
// 没有重写toString()方法
public static void main(String[] args) {
Student student = new Student("Tom", 18, 12345);
System.out.println(student.toString());
}
}
输出结果:
Student@<散列码>
这个输出结果不够直观,无法直观地看到一个学生的具体信息。
重写toString()
方法的情况
返回一个更有意义和可读性的对象字符串表示。
示例代码如下:
public class Student {
private String name;
private int age;
private int studentNumber;
public Student(String name, int age, int studentNumber) {
this.name = name;
this.age = age;
this.studentNumber = studentNumber;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + ", studentNumber=" + studentNumber + "]";
}
public static void main(String[] args) {
Student student = new Student("Tom", 18, 12345);
System.out.println(student.toString());
}
}
输出结果:
Student [name=Tom, age=18, studentNumber=12345]
通过重写toString()
方法,我们返回了一个包含学生姓名、年龄和学号的字符串,这样就更方便看到一个学生的具体信息。
重写equals()
方法的格式为:
@Override
public boolean equals(Object obj) {
// 判断传入的对象是否与当前对象相等的逻辑
}
重写equals()
方法的原因是为了自定义对象之间的相等性判断逻辑,以便符合需求。
例如:假设我们有一个Person
类表示一个人的信息,包含姓名和年龄属性。
没有重写equals()
方法的情况
默认情况下,调用equals()
方法会使用对象的引用地址进行比较,判断两个对象是否为同一个对象。
示例代码如下:
public class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 没有重写equals()方法
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Alice", 25);
System.out.println(person1.equals(person2));
}
}
输出结果:
false
在上述例子中,尽管
person1
和person2
的属性值相同,但由于默认的equals()
方法比较的是对象的引用地址,所以返回的结果为false
,不能正确判断对象之间的相等性。
重写equals()
方法的情况
现在我们要重写equals()
方法,自定义判断两个对象相等的条件。可以比较两个对象的属性值是否相等。
示例代码如下:
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 (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);
}
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
Person person2 = new Person("Alice", 25);
System.out.println(person1.equals(person2));
}
}
输出结果:
true
通过重写
equals()
方法,我们自定义了判断两个Person
对象相等的条件,即姓名和年龄都相同。这样,当person1
和person2
的属性值相同时,调用equals()
方法返回的结果为true
,能够正确判断对象之间的相等性。
对象克隆是指创建一个与原始对象具有相同状态和行为的新对象。
在Java中,对象克隆可以通过`Cloneable`接口和`clone()`方法实现。
在 Java 中,通过重写 clone()
方法来实现对象的克隆。clone()
方法是在 Object
类中定义的,因此所有的类都可以调用 clone()
方法进行克隆。
对象克隆可分为两种分类:浅克隆(Shallow Clone)和深克隆(Deep Clone)。
Object类默认的是浅克隆
克隆方法的格式如下:
protected Object clone() throws CloneNotSupportedException {
// 实现克隆的逻辑
}
clone()
方法的访问修饰符是protected
,这是因为clone()
方法被设计为在子类中进行重写,而且只能在类的内部或其子类中进行调用。
在实现
clone()
方法时,首先需要调用super.clone()
方法获得一个浅拷贝的副本,然后可以对需要深拷贝的数据进行独立副本的创建,以确保复制的对象与原对象没有关联。
重写
clone()
方法时,还需要注意抛出CloneNotSupportedException
异常,这是因为Cloneable
接口是一个标记接口,对于没有实现Cloneable
接口的对象调用clone()
方法,会抛出该异常。
示例:
class MyClass implements Cloneable {
private int number;
public MyClass(int number) {
this.number = number;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
上述示例是一个简单的自定义类
MyClass
,它实现了Cloneable
接口,并重写了clone()
方法。在clone()
方法中,我们只调用了super.clone()
,获得一个浅拷贝的副本。
【注意】上述示例中的 clone()
方法并没有实现深拷贝的逻辑,如果需要进行深拷贝,需要根据具体的需求,在 clone()
方法中编写相应的代码逻辑进行深拷贝操作。
浅克隆是指在克隆过程中,只复制对象的基本类型属性和对其他对象的引用。新对象与原始对象共享引用类型属性,因此对引用类型属性的修改会影响到原始对象和克隆对象。
不管对象内部的属性是基本数据类型还是引用数据类型,都完全拷贝过来
基本数据类型拷贝过来的是具体的数据,引用数据类型拷贝过来的是地址值。
浅克隆可以通过调用Object
类的clone()
方法来实现,前提是要实现Cloneable
接口并覆盖clone()
方法。
示例代码如下:
public class Person implements Cloneable {
private String name;
private int age;
private Address address;
public Person(String name, int age, Address address) {
this.name = name;
this.age = age;
this.address = address;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class Address {
private String city;
public Address(String city) {
this.city = city;
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Address address = new Address("London");
Person person1 = new Person("Alice", 25, address);
Person person2 = (Person) person1.clone();
// 修改 person2 的属性
person2.setName("Bob");
person2.getAddress().setCity("New York");
System.out.println(person1.getName()); // 输出 "Alice"
System.out.println(person1.getAddress().getCity()); // 输出 "New York"
}
在上述代码中,
Person
类实现了Cloneable
接口,并重写了clone()
方法。在main
方法中,创建了一个Person
对象person1
,并将其克隆给person2
。然后,修改了person2
的姓名和地址。
由于浅克隆只复制引用,因此
person1
和person2
的地址引用指向相同的地址对象。因此,修改person2
的地址会同时影响person1
的地址。
深克隆是指在克隆过程中,除了复制对象的基本类型属性外,还要复制对象的引用类型属性,使得新对象与原始对象拥有各自独立的引用类型属性。
可以实现 Cloneable
接口和重写 clone()
方法来实现深克隆。
基本数据类型拷贝过来,字符串复用,引用数据类型会重新创建新的
深克隆可以通过手动实现或使用第三方库(如Apache Commons Lang、Gson等)来实现。
示例代码如下:
通过手动实现深克隆
下面是一个简单的 Java 示例:
import java.util.ArrayList;
import java.util.List;
class Person implements Cloneable {
private String name;
private int age;
private List<String> hobbies;
public Person(String name, int age, List<String> hobbies) {
this.name = name;
this.age = age;
this.hobbies = hobbies;
}
// 重写clone()方法实现深克隆
@Override
public Person clone() throws CloneNotSupportedException {
// 先使用super.clone()创建浅克隆的对象
Person clonedPerson = (Person) super.clone();
// 对于要深克隆的引用类型(如List),进行独立副本的创建
clonedPerson.hobbies = new ArrayList<>(this.hobbies);
return clonedPerson;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", hobbies=" + hobbies +
'}';
}
}
public class Main {
public static void main(String[] args) {
List<String> hobbies = new ArrayList<>();
hobbies.add("Reading");
hobbies.add("Traveling");
Person person1 = new Person("Alice", 25, hobbies);
try {
Person person2 = person1.clone();
person2.setName("Bob");
person2.setAge(30);
person2.getHobbies().add("Swimming");
System.out.println(person1); // 输出: Person{name='Alice', age=25, hobbies=[Reading, Traveling]}
System.out.println(person2); // 输出: Person{name='Bob', age=30, hobbies=[Reading, Traveling, Swimming]}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
在上面的示例中,
Person
类实现了Cloneable
接口,并重写了clone()
方法来实现深克隆。在clone()
方法中,首先调用super.clone()
方法创建一个浅克隆的对象,然后对于需要深克隆的引用类型(这里是List
),使用新的副本来替换原始对象的引用。这样可以确保克隆后的对象与原始对象没有任何关联。
然后在
Main
类的main
方法中,创建了一个Person
对象person1
,通过调用person1.clone()
方法创建了一个深克隆的对象person2
。然后对person2
进行修改,发现对person1
没有任何影响,证明了深克隆的效果。
【注意】在这个示例中,clone()
方法中的 throws CloneNotSupportedException
是必需的,因为 Cloneable
接口是一个标记接口,没有实际的方法,而是起到了一个标识作用,告诉编译器这个类可以进行克隆操作。
import org.apache.commons.lang3.SerializationUtils;
public static void main(String[] args) {
Address address = new Address("London");
Person person1 = new Person("Alice", 25, address);
Person person2 = SerializationUtils.clone(person1);
// 修改 person2 的属性
person2.setName("Bob");
person2.getAddress().setCity("New York");
System.out.println(person1.getName()); // 输出 "Alice"
System.out.println(person1.getAddress().getCity()); // 输出 "London"
}
在上述代码中,
person1
通过Apache Commons Lang库的SerializationUtils.clone()
方法来进行深克隆。person2
是person1
的一个副本,但它与person1
具有不同的地址引用。所以,修改person2
不会影响person1
对象克隆的的注意事项:
实现 Cloneable 接口:要实现对象克隆,类必须实现 Cloneable
接口。否则,在调用 clone()
方法时会抛出 CloneNotSupportedException
异常。
重写 clone() 方法:在类中重写 clone()
方法,并使用 super.clone()
获得浅拷贝。如果需要进行深拷贝,则还需要对引用类型的成员变量进行独立的拷贝操作。
深拷贝和浅拷贝:克隆有两种方式,一种是浅拷贝,另一种是深拷贝。浅拷贝只复制对象及其引用类型的成员变量的引用,而深拷贝会创建一个新对象,并将所有成员变量都复制一份。根据需求选择合适的拷贝方式。
对象引用问题:克隆对象后,克隆结果是否与原对象共享引用类型的成员变量,取决于对引用类型的处理。如果希望克隆结果与原对象完全独立,需要对引用类型成员进行深拷贝,确保新对象拥有自己的独立副本。
异常处理:在重写 clone()
方法时,需要注意抛出 CloneNotSupportedException
异常。这是因为 Cloneable
接口是一个标记接口,并没有提供具体的实现,只是作为一个标识来告诉 clone()
方法是否能够进行克隆操作。
构造函数调用:对象克隆并不会调用构造函数,它是通过 clone()
方法直接复制对象的内存状态。如果需要在克隆过程中进行一些其他的初始化操作,可以考虑使用其他方式,例如拷贝构造函数、工厂方法等。
性能考虑:克隆操作可能会涉及到大量的对象复制,特别是在深拷贝的情况下。因此,在进行对象克隆时,需要仔细考虑性能和内存使用情况,避免因为频繁的克隆操作导致性能下降。
当需要在自定义的类中实现对象克隆时,应该重写 clone()
方法。
以下是重写 clone()
方法的格式:
@Override
public Object clone() throws CloneNotSupportedException {
// 克隆对象的实现逻辑
// 首先调用 super.clone() 方法以获得浅拷贝的对象
// 然后对需要深拷贝的属性进行复制
// 最后返回克隆后的对象
// 注意要处理可能抛出的 CloneNotSupportedException 异常
}
// 重写前
public class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 克隆方法
public Object clone() throws CloneNotSupportedException {
return super.clone(); // 默认的 clone() 方法只能浅拷贝对象
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
try {
Person person2 = (Person) person1.clone();
System.out.println(person1 == person2); // 输出:false
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
在上面的示例中,有一个
Person
类,它实现了Cloneable
接口。Person
类重写了clone()
方法,但方法内部调用了super.clone()
,默认的克隆方法只能进行浅拷贝,即复制对象的引用。在main()
方法中,创建了一个Person
对象person1
,然后通过调用clone()
方法复制了它。
输出结果如下:
false
可以看到,person1
和 person2
是两个不同的对象,它们的引用地址不同。
// 重写后
public class Person implements Cloneable {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 重写 clone() 方法
public Person clone() throws CloneNotSupportedException {
return (Person) super.clone(); // 自定义的 clone() 方法,进行深拷贝
}
}
public class Main {
public static void main(String[] args) {
Person person1 = new Person("Alice", 25);
try {
Person person2 = person1.clone();
System.out.println(person1 == person2); // 输出:false
System.out.println(person1.getName() == person2.getName()); // 输出:true
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
在这个示例中,
Person
类重写了clone()
方法,它使用强制类型转换来确保在复制过程中返回的是Person
对象。而且,它在进行克隆时,进行了深拷贝,即复制了对象的所有属性。在main()
方法中,创建了一个Person
对象person1
,然后通过调用自定义的clone()
方法进行复制。
输出结果如下:
false
true
可以看到,person1
和 person2
是两个不同的对象,它们的引用地址不同。而且,person1
和 person2
的 name
属性引用同一个对象,说明进行了深拷贝。
Object
的注意事项:
equals()
方法:Object
类中的 equals()
方法用于比较两个对象是否相等。默认情况下,equals()
方法比较的是对象的引用地址,而不是对象的内容。如果需要比较对象的内容,需要重写 equals()
方法。
hashCode()
方法:Object
类中的 hashCode()
方法返回对象的哈希码。当需要将对象用作散列结构(例如散列表)中的键时,需要重写 hashCode()
方法来确保相等的对象具有相等的哈希码。
toString()
方法:Object
类中的 toString()
方法返回对象的字符串表示。默认情况下,toString()
方法返回的是对象的类名,后跟 “@” 符号和对象的哈希码。可以重写 toString()
方法来返回自定义的字符串表示。
getClass()
方法:Object
类中的 getClass()
方法返回对象所属的类。可以使用这个方法来获取对象的类名、包名等信息。
finalize()
方法:Object
类中的 finalize()
方法是一个被垃圾回收器调用的方法,用于在对象被销毁之前执行清理操作。不建议在应用程序中重写 finalize()
方法,因为它的调用时机和频率不确定。
clone()
方法:Object
类中的 clone()
方法用于创建对象的副本。默认情况下,clone()
方法执行的是浅拷贝,即复制对象的引用。如果需要进行深拷贝,需要在自定义类中重写 clone()
方法。
wait()
、notify()
和 notifyAll()
方法:Object
类中的这些方法用于线程间的通信和同步。wait()
方法使当前线程等待,notify()
方法唤醒等待的线程,而 notifyAll()
方法唤醒所有等待的线程。
Objects
是 Java 提供的一个实用类,所在包是在java.util包下,因此在使用的时候需要进行导包。并且Objects类是被final修饰的,因此该类不能被继承。
它提供了一些静态方法来操作对象,主要用于对象的比较、处理空值和生成对象的哈希码。
Objects类中无无参构造方法,因此不能使用new关键字去创建Objects的对象。同时Objects类中所提供的方法都是静态的。因此可以通过类名直接去调用这些方法。
以下是 Objects
类的一些常用方法及其功能概述:
equals(Object a, Object b)
:比较两个对象是否相等。该方法处理了空值(null
)的情况,当两个对象都为空或者相等时返回 true
,否则返回 false
。
hashCode(Object obj)
:生成对象的哈希码。该方法会根据对象的内容生成一个哈希码值,可用于在哈希表等数据结构中进行对象的存储和查找。
toString(Object obj)
:将对象转换为字符串表示。如果对象为 null
,则返回字符串 "null"
,否则调用对象的 toString()
方法。
requireNonNull(T obj)
:检查指定的对象是否为 null
,如果为 null
,则抛出 NullPointerException
异常,否则返回该对象。
requireNonNull(T obj, String message)
:功能同上,同时可以自定义异常消息。
isNull(Object obj)
:检查指定的对象是否为 null
,返回 true
或 false
,用于判断对象是否为空。
nonNull(Object obj)
:检查指定的对象是否不为 null
,返回 true
或 false
,用于判断对象是否非空。
compare(T a, T b, Comparator super T> c)
:使用指定的比较器来比较两个对象。该方法允许使用自定义的比较器来对对象进行比较。
方法 | 说明 | 示例 |
---|---|---|
equals(Object a, Object b) |
比较两个对象是否相等。如果两个对象都为 null 或相等,则返回 true ;否则返回 false 。 |
Objects.equals("abc", "abc") |
hashCode(Object obj) |
生成对象的哈希码。如果对象为 null ,则返回 0;否则调用对象的 hashCode() 方法 |
Objects.hashCode("abc") |
toString(Object obj) |
将对象转换为字符串表示。如果对象为 null ,则返回字符串 "null" ,否则调用对象的 toString() 方法 |
Objects.toString("abc") |
requireNonNull(T obj) |
检查指定对象是否为 null ,如果为 null ,则抛出 NullPointerException 异常,否则返回该对象 |
Objects.requireNonNull("abc") |
requireNonNull(T obj, String message) |
检查指定对象是否为 null ,如果为 null ,则抛出 NullPointerException 异常,并可自定义异常消息 |
Objects.requireNonNull("abc", "不能为空") |
isNull(Object obj) |
检查指定对象是否为 null ,返回 true 或 false |
Objects.isNull("abc") |
nonNull(Object obj) |
检查指定对象是否为非 null ,返回 true 或 false |
Objects.nonNull("abc") |
compare(T a, T b, Comparator super T> c) |
使用指定的比较器来比较两个对象。 | Objects.compare("abc", "def", String.CASE_INSENSITIVE_ORDER) |
Objects
类的注意事项:
空值检查:Objects
类提供了许多用于空值检查的方法,如 requireNonNull
、isNull
和 nonNull
。在使用这些方法时,要确保传入的对象不为 null
,否则可能会抛出 NullPointerException
异常。
自定义对象的 equals()
方法:Objects.equals(Object a, Object b)
方法用于比较两个对象是否相等。对于自定义的类,如果需要比较对象是否相等,需要重写该类的 equals()
方法,并在其中使用适当的逻辑进行比较。
哈希码生成:Objects.hashCode(Object obj)
方法用于生成对象的哈希码。如果需要在自定义的类中使用哈希表或需要哈希码作为对象标识符,则需要重写该类的 hashCode()
方法,并遵循一致性原则,即如果两个对象相等,它们的哈希码应该相等。
比较器的使用:Objects.compare(T a, T b, Comparator super T> c)
方法用于使用指定的比较器对两个对象进行比较。当需要比较不具备自然顺序的对象或需要自定义顺序时,可以提供一个比较器来指定比较规则。
toString() 方法:Objects.toString(Object obj)
方法用于将对象转换为字符串表示。对于自定义的类,要确保在该类中正确实现 toString()
方法,以便在需要打印对象时获得有意义的输出。
其他常见API