Skip to Content

Java类Object

equals方法

默认地址比较

如果没有覆盖重写equals方法,那么Object类中默认进行==运算符的对象地址比较,只要不是同一个对象,结果必然为false。

==既可以判断基本数据类型,又可以判断引用数据类型,如果判断基本数据类型,则判断的是值是否相等。 例如:

int i = 10; double d = 10.0; boolean result = (i == d);

如果判断引用数据类型,判断的是地址是否相等,即判断是不是同一个对象。

public class Equals { public static void main(String[] args) { A a = new A(); A b = a; A c = b; System.out.println(a == c); // true System.out.println(b == c); // true B bObj = a; System.out.println(bObj == c); // true } } class A extends B{} class B {}

对象内容比较

equals是Object类中的方法,只能判断引用类型,默认是判断两个对象的地址值是否相等。

如果希望进行对象的内容比较,即所有或指定的部分成员变量相同就判定两个对象相同,则可以覆盖重写equals方法。

equals源码:

Object类的equals

// 返回一个布尔值 public boolean equals(Object obj) { // 上面讲过, == 是判断对象的地址引用是否相等 // 如果调用者和传入的参数的对象地址相等,则返回true return (this == obj); }

String类的equals

// 返回一个布尔值 public boolean equals(Object anObject) { // 判断地址值是否相等,如果相等,则认为两个对象是相等的 if (this == anObject) { return true; } // 判断传入的对象是否是String类或者是否是子类(因为String类被final修饰,所以没有子类) // 如果是的话则进行下一步判断 if (anObject instanceof String) { // 如果传入的对象是String,则将该对象转为String对象 String anotherString = (String)anObject; int n = value.length; // 判断传入的字符串长度是否和调用者的字符串长度相等 // 如果相等,则进行下一步判断 if (n == anotherString.value.length) { // 将调用者和传入的字符串转换为char类型的数组 char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { // 循环比较数组每个元素是否相等,如果有一个不相等 // 则返回false,两个字符串是不相等的 if (v1[i] != v2[i]) return false; i++; } // 如果循环结束还没有返回false的话,则这两个字符串是相等的 return true; } } // 如果传入的对象不是String类型的,或者两个字符串长度不同,则返回false return false; }

Integer的equals

// 返回一个布尔值 public boolean equals(Object obj) { // 判断传入的对象是否是Integer类的对象 if (obj instanceof Integer) { // 如果是的话,将该对象转为Integer类对象,并获取该对象的值 // 如果值相等,则返回true,不相等则返回false return value == ((Integer)obj).intValue(); } // 如果传入的对象不是Integer类的对象,则返回false return false; }

重写equals方法

class Student { private String name; private int age; private char gender; public Student(String name, int age, char gender) { this.name = name; this.age = age; this.gender = gender; } // (省略)get方法和set方法..... }

当没有重写equals方法的时候,执行如下代码返回的是false,因为equals方法默认比较的是对象的引用地址值是否相同,因为new了两次,两个对象的必然是不同的,所以返回false

public static void main(String[] args) { Student stu1 = new Student("张三", 10, '男'); Student stu2 = new Student("张三", 10, '男'); System.out.println(stu1.equals(stu2)); }

这里是重写equals的方法

class Student { private String name; private int age; private char gender; // 重写Object类的equals的方法 public boolean equals(Object obj) { // 首先判断传入的对象和该对象的地址值相同 // 则直接返回true, 这里this指的是当前对象 if (this == obj) { return true; } // 判断传入的对象是否是Student类型的对象 // 如果是,再比较,如果不是,返回false if (obj instanceof Student) { // 类型转换,将转入的对象转换为Student类型的对象 // 只有向下转型了,才能够比较两个对象里面的值 Student stu = (Student) obj; // 这里判断的是,如果调用该方法的对象的名字和传入参数对象的名字相等,并且年龄相等,并且性别也相等 // 则返回true,如果其中一个不相等,这个布尔表达式就会返回false return this.name.equals(stu.getName()) && this.age == stu.getAge() && this.gender == stu.gender; } return false; } public Student(String name, int age, char gender) { this.name = name; this.age = age; this.gender = gender; } // (省略)get方法和set方法..... }

此时执行上面的main方法,则会返回true,这时,Student类中的equals方法,this指的是调用者的对象,也就是stu1,而传入的参数是stu2

Objects的equals方法

JDK7添加了一个Objects工具类,它提供了一些方法来操作对象,它由一些静态的实用方法组成,这些方法是null-save(空指针安全的)或null-tolerant(容忍空指针的),用于计算对象的hashcode、返回对象的字符串表示形式、比较两个对象。

在比较两个对象的时候,Objectequals方法容易抛出空指针异常,而Objects类中的equals方法就优化了这个问题。方法源码如下:

public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b)); }

hashCode方法

hashCode可以提高具有哈希结构的容器的效率。

  • 两个引用,如果指向的是同一个对象,则哈希值肯定是一样的。
  • 两个引用,如果指向的是不同的对象,则哈希值是不一样的(也可能一样,后续详解)。
  • 哈希值主要根据地址号来的,但是不能完全将哈希值等价于地址值。
  • 在集合中,如果有需要hashCode的话,也会重写该方法(后续集合的时候详解)。

下面是jdk1.6中,对hashCode方法的中文解释:

toString方法

toString方法返回该对象的字符串表示。

默认返回:全类名(包名+类名) + @ + 哈希值的十六进制值 这里哈希值的十六进制就是hashCode方法的哈希值转换成十六进制的形式

如果不希望使用toString方法的默认行为,则可以对它进行覆盖重写。重写后打印或者拼接对象时,都会调用该对象的toString形式。 当直接输出一个对象时,toString方法会被默认调用

Object类的toString源码

public String toString() { // getClass().getName():返回的是类的全类名(包名+类名) // Integer.toHexString():返回的是将传入的对象转换成16进制的字符串 // hashCode():上面讲过,返回的是该对象的哈希值 return getClass().getName() + "@" + Integer.toHexString(hashCode()); }

重写toString方法

class BB { private String name; public BB(String name) { this.name = name; } // (省略)get方法和set方法..... }

此时没有重写该方法,那么调用toString方法时实际上调用的是Object类的toString方法,运行下面main方法后可以得到下面的结果,就是全类名(包名+类名) + @ + 哈希值的十六进制值

public static void main(String[] args) { BB bb = new BB("小明"); System.out.println(bb.toString()); }
class BB { private String name; // (省略)构造器、get方法和set方法..... public String toString() { return "BB{" + "name='" + name + '\'' + '}'; } }

重写后可以得到下面的结果:

重写的toString方法可以根据实际需要写,当然也可以使用idea的默认的格式

finalize方法

当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 当对象被回收时,系统自动调用该对象的finalize方法。子类可以重写该方法,做一些释放资源的操作。

什么时候被回收? 当某个对象没有人话引用时,jvm则认为这个对象是一个垃圾对象,就会使用垃圾回收机制来销毁该对象,再销毁对象前,会先调用finalize方法。 垃圾回收机制的调用是由系统来决定的,也可以通过System.gc()主动触发垃圾回收机制。

默认回收:

public class Finalize { public static void main(String[] args) { Cat cat = new Cat(); // 此时,当Cat对象没有被引用的时候,垃圾回收器就会销毁对象 // 在销毁对象之前,会调用该对象的finalize方法 // 可以在finalize方法中写入自己的业务代码 // 如果不重写finalize方法则默认调用Object中的方法 cat = null; } } class Cat { @Override protected void finalize() throws Throwable { System.out.println("清理垃圾....."); System.out.println("销毁对象....."); } }

执行该main方法可以看到finalize方法中的代码并没有被执行,这是因为垃圾回收机制有自己的算法,根据自己的算法来确定什么时候进行垃圾回收。

可以调用System.gc()方法主动进行垃圾回收(虽然是主动调用,但是垃圾回收器不一定会立刻执行)。

public static void main(String[] args) { Cat cat = new Cat(); cat = null; System.gc(); System.out.println("程序退出了..."); }

这里可以看到先执行的 程序退出了 后执行的finalize方法,因为调用Sysem.gc()方法后垃圾回收器可能不会立刻执行,此时的程序也不会阻塞在这行代码,所以会先执行下面的代码。

Last updated on