Object类
1、Object类
java.lang.Object类是类层次结构的根类,每个类(除了Object类本身)都使用Object类作为超类。一个类如果没有显示声明继承另一个类,则相当于默认继承了Object类。换句话说,Object类的变量可以接收任意类型的对象。Java规定Object[]可以接收任意类型对象的数组,但是不能接收基本数据类型的数组。
Object类只有一个空参构造器,所有类的对象创建最终都会通过super()语句调用到Object类的无参构造器中。如果一个类没有显示继承另一个类,那么在它的构造器中出现的super()语句表示调用的就是Object类的无参构造器。
Object类是其他类的根父类,因此Object类的所有方法都会继承到子类中,包括数组对象,Object类的主要方法如下所示。
1.1 toString方法
源码如下:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
toString方法的作用是返回对象的字符串形式,也就是任意类型对象想转换成String类型,都可以调用toString方法。toString方法的原型返回的是一个类似地址值的字符串,不够简明并且对开发人员来讲没有意义,所以建议子类在重写该方法时,返回一个简明易懂的信息表达式,一般为对象的属性信息。
没有重写toString()之前的Person类代码
public class ToStringTest {
public static void main(String[] args) {
Person per = new Person("codeleader", 32);
System.out.println(per);
System.out.println(per.toString());
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
从上述结果来看,当我们打印一个对象时,调用toString方法和不调用toString方法的最终打印结果都是一样的。这说明当我们使用System.out.println或System.out.print方法打印对象时,会默认调用对象的toString方法。其实在Java中当一个对象与字符串进行拼接时,也会自动调用该对象的toString方法。
另外,toString方法默认返回的是“全类名+@+对象的哈希值”。
重写toString方法之后的Person类的实例代码
package chap11.test;
public class ToStringTest {
public static void main(String[] args) {
Person per = new Person("codeleader", 32);
System.out.println(per);
System.out.println(per.toString());
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
1.2 equals方法
比较两个基本数据类型的值是否相等应使用"==",而比较两个字符串的内容是否相等应使用equals方法。
这是因为==对于基本数据类型来说,变量中存储的是数据值,所以直接使用==
比较即可。对于应用数据类型来说,变量中存储的是对象的首地址,所以直接用==
比较时,只是比较两个对象的首地址是否相等,而不是比较两个对象的内容是否相等。==
如果要比较两个对象的内容是否相等,则需要调用equals方法,该方法在Object类中的源码如下所示:
public boolean equals(Object obj) {
return (this == obj);
}
Object类中该方法的作用是比较两个对象的内容是否相等。从上面的代码可以看出,在Object类中,默认实现的equals方法与==
的效果是一样的。
没有重写Person类的示例代码:
public class TestPersonEquals {
public static void main(String[] args) {
Person per1 = new Person("codeleader", 32);
Person per2 = new Person("codeleader", 32);
System.out.println(per1==per2);
System.out.println(per1.equals(per2));
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
正如前面所看到的,Object类中默认实现的equals方法也是通过
==
判断对象是否相等。而对于引用类型变量,==
判断的不是属性信息,而实而这引用的是否是同一个对象,也就是地址是否相等。一般来讲,开发人员希望判断的是两个对象的内容是否相等,所以往往需要重写equals方法。
重写了equals方法和hashCode方法之后的代码:
package chap11.equals;
import java.util.Objects;
public class TestPersonEquals {
public static void main(String[] args) {
Person per1 = new Person("codeleader", 32);
Person per2 = new Person("codeleader", 32);
System.out.println(per1==per2);
System.out.println(per1.equals(per2));
}
}
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 o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
从上面模板生成的equals方法代码主要分为三个方面。
- 两个对象的地址不一样,肯定返回
true
- 两个对象的类型不一样,肯定返回
false
- 两个对象被选择比较的属性信息完全一样,肯定返回
true
,有不一样的返回false
equals方法的重写需要满足自反性、传递性、一致性、对称性、非空对象。
==
和equals方法的区别?
==
可用于判断两个基本数据类型变量,也可以用于判断两个引用类型变量。但都需要保证判断双方的类型一致或兼容,否则编译出错。- equals方法只能用于判断应用类型的变量,因为只有对象才有方法,默认判断的是对象的内容,如果重写Object类的equals方法,则一般判断的是对象的内容是否相等。
1.3 hashCode方法
源码如下:
hashCode方法的说明有以下几点:
- hashCode方法用于返回对象的哈希码值。支持此方法是为了提高哈希表(如java.util.Hashtable提供的哈希表)的性能。
- hashCode在Object类中有native修饰,是本地方法,该方法的方法体不是Java实现的,是由C/C++实现的,最后编译为.dll文件,然后由Java调用。
示例代码:
public class HashCodeTest {
public static void main(String[] args) {
Person per1 = new Person("康师傅", 32);
Person per2 = new Person("康师傅", 32);
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
System.out.println(per1.equals(per2));
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
}
我们也可以重写hashCode方法,手动重写或使用IDE开发工具提供的模板重写。
hashCode方法重写时要满足如下几个要求:
- 如果两个对象调用equals方法返回true,那么要求这两个对象的hashCode值一定是相等的。
- 如果两个对象的hashCode值不相等,那么要求这两个对象调用equals方法一定是false。
- 如果两个对象的hashCode值相等,那么这两个对象调用equals方法可能是true,也可能是false。
重写Person类的hashCode和equals方法:
import java.util.Objects;
public class HashCodeTest {
public static void main(String[] args) {
Person per1 = new Person("康师傅", 32);
Person per2 = new Person("康师傅", 32);
System.out.println(per1.hashCode());
System.out.println(per2.hashCode());
System.out.println(per1.equals(per2));
}
}
class Person{
private String name;
private int age;
public Person(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
Java中的hashCode值遵循如下规定:
- 如果两个对象的hashCode值不相等,那么这两个对象一定不相等。
- 如果两个对象的hashCode值是相等的,那么这两个对象不一定相等。
1.4 getClass方法
对象有编译时类型和运行时类型两种类型,而且编译时类型可能与运行时类型不一致。编译时类型就是变量声明时的类型,那么如何在运行时获取某个变量中对象的运行时类型呢,Object类为我们提供了一个getClass
方法,可以获取对象的运行时类型。该方法源码如下:
示例代码:
public class GetClassTest {
public static void listen(Animal animal){
System.out.println("The animal is a "+animal.getClass());
animal.shut();
}
public static void main(String[] args) {
listen(new Dog());
listen(new Cat());
}
}
abstract class Animal{
public abstract void shut();
}
class Dog extends Animal{
@Override
public void shut() {
System.out.println("汪汪汪~");
}
}
class Cat extends Animal{
@Override
public void shut() {
System.out.println("喵喵喵~");
}
}
在上述代码的listen方法中,animal变量的编译时类型是Animal,而运行时类型变量由传入的实参对象决定,可能是Dog,也可能是Cat。
1.5 clone方法
开发中如果要复制一个对象,则可以使用Object类提供的clone方法。源码如下:
调用该方法时可以创建并返回当前对象的一个副本。从源码中可以发现该方法的权限修饰符是protected,说明默认Object类的clone方法只能在java.lang包或其他包的子类中调用。因此,如果在测试类中要通过自定义类的对象来调用clone方法,则必须重写该方法。如果重写该方法,则子类必须实现java.lang.Cloneable
接口,否则会抛出CloneNotSupportedException
。
示例代码:
import java.util.Objects;
public class CloneTest {
public static void main(String[] args) {
try {
Person1 per = new Person1("codeleader", 18);
Object clone = per.clone();
System.out.println(per==clone);
System.out.println(per.equals(clone));
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
}
}
class Person1 implements Cloneable{
private String name;
private int age;
public Person1(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public int hashCode() {
return Objects.hash(name,age);
}
@Override
public boolean equals(Object obj) {
if(this==obj) return true;
if(obj ==null||getClass()!=obj.getClass()) return false;
Person1 person1=(Person1) obj;
return age==person1.age&&Objects.equals(name,person1.name);
}
}
1.6 finalize方法
对于初学者来说,finalize方法是最没有机会接触到的方法,简单了解以下即可。源码如下:
finalize方法是Object类中的protected方法,子类可以重写该方法以实现资源清理工作,GC在回收对象之前会调用该方法,即该方法不是由开发人员手动调用的。
当对象变为不可达时,即对象成为需要被回收的垃圾对象时,GC会判断该对象是否覆盖了finalize方法,若未覆盖,则直接将其回收。若对象未执行过finalize方法,则将其放入F-Queue队列,有一个低优先级线程执行该队列中对象的finalize方法。执行完finalize方法后,GC会再次判断该对象是否可达,若不可达,则进行回收,否则对象复活,复活后的对象下次回收时,将不再放入F-Queue队列,即不再执行finalize方法。
Java语言规范并不能保证finalize方法会被及时执行,而且根本不能保证它们会被执行。所以不建议finalize方法完成非内存资源清理工作以外的任务。
- 0
- 0
-
分享