在Java中,所有类都直接或间接的继承自 Object 类,Object 类是所有Java类的根基类。这也就意味着所有的Java对象都拥有 Object 类的属性和方法。如果在类的声明中未使用 extends 关键字指明其父类,则默认继承 Object 类。

public class Person /*extends Object*/

toString()方法

JavaSE-38-1

查阅API文档可以看到以上关于toString()方法的定义。

首先前半段是 getClass().getName() ,这部分表示的是包名+类名,例如:

package com.test;

public class Test {
    public void test(){
        System.out.println("This is a test");
    }

    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.getClass().getName()); // com.test.Test
    }
}

中间的 '@' 就是字符 @

后半段是 Integer.toHexString(hashCode()) ,这部分首先将对象在堆中的地址进行哈希算法返回一个码,即哈希码。然后对这个哈希码使用 toHexString() 方法,返回一个字符串,这个字符串是一个十六进制的数对应的字符串。

package com.test;

public class Test {
    public void test(){
        System.out.println("This is a test");
    }

    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.toString()); // com.test.Test@4554617c
    }
}

由此可见 Obeject 类的 toString 方法打印出来的东西可读性并不高。也正是因为这样,API文档才建议所有子类都重写此方法。例如在IDEA中可以使用快捷键 Alt + Insert 重写 toString() 方法:

package com.test;

public class Test {
    int age;
    double height;
    String name;
    public void test(){
        System.out.println("This is a test");
    }
    @Override
    public String toString() {
        return "Test{" +
                "age=" + age +
                ", height=" + height +
                ", name='" + name + '\'' +
                '}';
    }
}

equals()方法

通过下面这个例子来理解 equals() 方法:

public class Phone {
    private String brand; //品牌型号
    private double price; //价格
    private int year; //出产年份
    public String getBrand() {
        return brand;
    }
    public void setBrand(String brand) {
        this.brand = brand;
    }
    public double getPrice() {
        return price;
    }
    public void setPrice(double price) {
        this.price = price;
    }
    public int getYear() {
        return year;
    }
    public void setYear(int year) {
        this.year = year;
    }
    @Override
    public String toString() {
        return "Phone{" +
                "brand='" + brand + '\'' +
                ", price=" + price +
                ", year=" + year +
                '}';
    }
    public Phone() {}
    public Phone(String brand, double price, int year) {
        this.brand = brand;
        this.price = price;
        this.year = year;
    }
}
public class Test {
    public static void main(String[] args) {
        Phone p1 = new Phone("华为P40",2035.98,2020);
        Phone p2 = new Phone("华为P40",2035.98,2020);
        System.out.println(p1==p2); //对于引用数据类型来说,比较的是地址值。所以一定返回的是false
        boolean flag = p1.equals(p2); //点进源码发现,底层比较的还是地址值。一定返回的是false,需要重写
        System.out.println(flag);
    }
}

首先照常编写一个Phone类,并创建两个它的对象,想要比较每个属性是否完全相同。但是对于引用数据类型来说,== 比较的是地址值,p1==p2 返回值一定是 false 。父类 Object 类提供了 equals() 方法比较对象的内容是否相等,但是当我们点进 equals() 方法的源码发现它其实也是用 == 比较地址值,没有实际意义,还是需要在子类中重写:

// equals()方法的源码
public boolean equals(Object obj) {
    return (this == obj);
}

在上面的例子中,重写的代码如下:

public boolean equals(Object obj) { //Object obj = new Phone();
    Phone other = (Phone)obj; //将obj转为Phone类型,向下转型,为了获取子类中特有的内容
    if(this.getBrand()==other.getBrand()&&this.getPrice()==other.getPrice()&&this.getYear()==other.getYear()){
        return true;
    }
    return false;
}

instanceof

其实上面重写的代码是存在问题的,当传入的对象根本跟 Phone 类没有任何关系时(比如 Cat 类的对象),Phone other = (Phone)obj; 这行代码会产生类型转换错误。所以我们要确保传入的对象是 Phone 类的实例,这时就需要用到 instanceof 运算符。

重写的代码改进如下:

public boolean equals(Object obj) { //Object obj = new Phone();
    if(obj instanceof Phone){ //a instanceof b意为判断a对象是否为b这个类的实例
        Phone other = (Phone)obj; //将obj转为Phone类型,向下转型,为了获取子类中特有的内容
        if(this.getBrand()==other.getBrand()&&this.getPrice()==other.getPrice()&&this.getYear()==other.getYear()){
            return true;
        }
    }
    return false;
}

利用IDE快捷重写equals()方法

eclipse

public boolean equals(Object obj){
    if(this == obj)
        return true; //如果比较的是同一个对象,则返回true
    if(obj == null)
        return false; //防止传入的参数是null
    if(getClass() != obj.getClass())
        return false; //防止比较的对象不是同一个类的
    Phone other = (Phone)obj;
    if(brand == null){
        if(other.brand != null)
            return false;
    } else if(!brand.equals(other.brand))
        return false;
    if(Double.doubleToLongBits(price) != Double.doubleToLongBits(other.price)) //由于浮点数表示的特性,可能存在精度问题,导致不准确的比较结果,所以用了doubleToLongBits方法
        return false;
    if(year != other.year)
        return false;
    return true;
}

IDEA

public boolean equals(Object o) {
    if (this == o) return true; //如果比较的是同一个对象,则返回true
    if (o == null || getClass() != o.getClass()) return false; //防止传入的参数是null,防止比较的对象不是同一个类的
    Phone phone = (Phone) o;
    return Double.compare(phone.price, price) == 0 && //同样避免了直接使用==运算符可能引起的浮点数精度问题,如果返回0,表示相等;如果返回正数,表示第一个参数大于第二个参数;如果返回负数,表示第一个参数小于第二个参数。
            year == phone.year &&
            Objects.equals(brand, phone.brand);
}