47

我是Java的新手。现在我正在研究equals和==以及equals和toString的重新定义。

我想使用我重新定义的 toString 方法和从 Object 类继承的默认方法。

我没有使用那个超级修改器来达到那个方法。

这仅用于教育目的。如果您查看我的代码中的注释,我想得到的会更清楚。

你能帮帮我吗?

我的代码是:

public class EqualTest{
    public static void main(String[] args){ 
        Employee alice1 = new Employee("Alice Adams", 75000, 1987, 12, 15);
            //System.out.super.println(alice1);

        Employee alice2 = alice1;
            //System.out.super.println(alice2);

        Employee alice3 = new Employee("Alice Adams", 75000, 1987, 12, 15);
            //System.out.super.println(alice3);

        System.out.println("alice1==alice2: " + (alice1==alice2));
        System.out.println("alice1 == alice3: " + (alice1==alice3));
        System.out.println("alice1.equals(alice3): " + alice1.equals(alice3));
    }
}

class Employee{
...
    public String toString(){
        return getClass().getName() + "[name = " + name + 
            ", salary=" + salary + ", hireDay=" + hireDay + "]";
    }

}
4

5 回答 5

81

严格来说,你不能在纯 Java 中打印对象的地址。生成的字符串中看起来像对象地址的数字Object.toString()是对象的“身份哈希码”。它可能与对象的当前地址相关,也可能不相关:

  • 规范没有说明身份哈希码是如何计算的。故意不指定它。

  • 由于该数字是哈希码,因此无法更改。因此,即使它(通常)与对象地址相关,也将是第一次访问哈希码时对象的地址。这可能与其当前地址不同,如果自第一次观察到对象的身份哈希码以来 GC 已经移动了对象,情况也会不同。

  • 在 64 位 JVM(具有足够大的堆大小/不使用压缩 oops)上,地址不适合作为int.

无论如何,获得这个号码的方法是打电话System.identityHashCode(obj)


如果您真的想要一个对象的当前地址,您可以使用 JNI 和本机方法(以及一些抽象中断)或通过使用Unsafe类中的方法来获取它(请参阅如何在 java 中获取对象的内存位置?)。但请注意,这两种方法都是不可移植的。此外,当 GC 运行时,它们给您的对象地址可能会“中断”,这使得它们对于许多(可能是大多数)潜在用例来说都是有问题的。


对于怀疑者,这就是 Java 10 javadocs 在“hashcode != address”点上所说的:

“(在某个时间点, hashCode可能会或可能不会被实现为对象内存地址的某个函数。)”

重点补充。实际上,对于最近的 JVM,默认行为是根本不将 hashCode 基于内存地址。至少从 Java 7 开始就是这样。

您可以通过包含来确认这一点,-XX:+PrintFlagsFinal 以找出hashcode标志的默认值,然后查看 OpenJDK 源代码以了解其含义。(代码在某些版本的“vm/runtime/synchronizer.cpp”文件中,但 YMMV。)

于 2013-08-23T07:18:42.143 回答
19

如果要实现某种默认toString()行为,可以使用System.identityHashCode()方法。默认toString()将如下所示:

public String toString(Object o) {
    return o.getClass().getName() + "@" + 
           Integer.toHexString(System.identityHashCode(o));
}
于 2013-08-23T07:15:01.757 回答
1

您可以调用 super() 方法来执行相应的超类方法。

class Employee{
...
    public String toString(){
         String s = super.toString();
        return getClass().getName() + "[name = " + name + 
            ", salary=" + salary + ", hireDay=" + hireDay + "]" + s;
    }

Object 类中的 toString() 如下

public String toString() {
    return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
于 2013-08-23T07:14:28.460 回答
0

这是关于覆盖等于和哈希码的深入答案

在 Java 中覆盖 equals 和 hashCode 时应该考虑哪些问题?

关键是这两种方法之间的关系是:

每当 a.equals(b) 时,a.hashCode() 必须与 b.hashCode() 相同。

于 2013-08-23T07:15:00.270 回答
0

您可以在 Employee 类中创建另一个方法来使用 super toString 方法。

参见示例:

public class Employee {
    public String toString() {
        return "MyToStringMethod";
    }

    public String superToString() {
        return super.toString();
    }

    public static void main(String[] args) {
        Employee b = new Employee();
        System.out.println(b);
        System.out.println(b.superToString());
    }
}

或将两者结合在一种方法中:

public class Employee {
    public String toString() {
        return super.toString() + " MyToStringMethod";
    }

    public static void main(String[] args) {
        Employee b = new Employee();
        System.out.println(b);
    }
}
于 2013-08-23T07:17:56.550 回答