Java Object类(超级详细)
Java 是通过类来构建代码结构的,类分为两种:
我们写的 Java 程序,其实就是由 Java 提供的类和自定义的类组成的,打开 Eclipse,在 JRE System Library 中存放的就是 Java 提供的类,如下图所示:
图 1 Java提供的类
开发者自定义的类存放在 src 目录下,如下图所示。
图 2 自定义的类
JRE System Library 中的类全部是编译之后的字节码文件,即 class 格式的文件,我们可以看到源码,但是不能修改,如下图所示。
图 3 class格式的文件
Object 就是 Java 提供的一个类,位于 java.lang 包中,该类是所有类的直接父类或间接父类。无论是 Java 提供的类,还是开发者自定义的类,都是 Object 的直接子类或间接子类。或者说 Java 中的每一个类都是 Object 的后代,Object 是所有类的祖先。
一个类在定义时如果不通过 extends 指定其直接父类,系统就会自动为该类继承 Object 类,Object 类的源码如下所示:
图 4 继承Object类的方法
接下来我们就一起看看 Object 类经常被子类所重写的那些方法,如下表所示。
toString()方法的实现如下所示:
equals() 方法的实现是:
比如说,两个字符串“String str1 = new String(“Hello”);”和“String str2 = new String( “Hello”);”,虽然 str1 和 str2 是两个完全不同的对象,但是它们的值是相等的,就可以认为这两个字符串相等。
我们需要对 equals() 方法进行重写,String 类已经完成了重写的工作,直接使用即可,重写的代码为:
hashCode() 方法如下所示:
但是上面例子中我们认为,如果两个 People 对象的成员变量值都相等,就是同一个对象,那么它们的散列值也应该相等,如果直接调用父类的 hashCode() 方法,两个对象的散列值是不相等的,例如:
- Java 提供的,无需开发者自定义,可直接调用;
- 由开发者根据不同的业务需求自定义的类。
我们写的 Java 程序,其实就是由 Java 提供的类和自定义的类组成的,打开 Eclipse,在 JRE System Library 中存放的就是 Java 提供的类,如下图所示:
图 1 Java提供的类
开发者自定义的类存放在 src 目录下,如下图所示。
图 2 自定义的类
JRE System Library 中的类全部是编译之后的字节码文件,即 class 格式的文件,我们可以看到源码,但是不能修改,如下图所示。
图 3 class格式的文件
Object 就是 Java 提供的一个类,位于 java.lang 包中,该类是所有类的直接父类或间接父类。无论是 Java 提供的类,还是开发者自定义的类,都是 Object 的直接子类或间接子类。或者说 Java 中的每一个类都是 Object 的后代,Object 是所有类的祖先。
一个类在定义时如果不通过 extends 指定其直接父类,系统就会自动为该类继承 Object 类,Object 类的源码如下所示:
public class Object { private static native void registerNatives(); static { registerNatives(); } public final native Class<?> getClass(); public native int hashCode(); public boolean equals(Object obj) { return (this == obj); } protected native Object clone() throws CloneNotSupportedException; public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); } public final native void notify(); public final native void notifyAll(); public final native void wait(long timeout) throws InterruptedException; public final void wait(long timeout, int nanos) throws InterruptedException { if (timeout < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (nanos < 0 || nanos > 999999) { throw new IllegalArgumentException( "nanosecond timeout value out of range"); } if (nanos > 0) { timeout++; } wait(timeout); } public final void wait() throws InterruptedException { wait(0); } protected void finalize() throws Throwable { } }可以看到 Object 类中提供了很多用 public 和 protected 修饰的方法,子类是可以直接继承这些方法的,即 Java 中的任何一个类,都可以调用 Object 类中的 public 和 protected 方法,当然 private 是不能调用的,如下图所示。
图 4 继承Object类的方法
重写Object类的方法
Object 是所有类的父类,每一个类都可以直接继承 Object 类的非私有方法,实例化对象可以直接调用这些方法。但是通常情况下不会直接调用这些方法,而是需要对它们进行重写,因为父类中统一的方法并不能适用于所有的子类。就像老爹房子的装修风格是老爹喜欢的,儿子们审美各有不同,老爹的房子并不能满足他们的需求,所以儿子们会把房子的旧装修覆盖掉,重新装修以适应他们的需求。这种方式是多态的一种体现,父类信息通过不同的子类呈现出不同的形态。接下来我们就一起看看 Object 类经常被子类所重写的那些方法,如下表所示。
方 法 | 描 述 |
---|---|
public String toString() | 以字符串的形式返回该类的实例化对象信息。 |
public boolean equals(Objeect obj) | 判断两个对象是否相等。 |
public native int hashCode() | 返回对象的散列码。 |
toString()方法的实现如下所示:
*@return a string representation of the object. */ public String toString(){ return getClass().getName() + "@" + Integer.toHexString(hashCode()); }原生的 toString() 方法会返回对象的类名以及散列值,直接打印对象默认调用 toString() 方法,例如:
public class Test { public static void main(String[] args) { People people = new People(); people.setId(1); people.setName("张三"); people.setAge(22); people.setGender('男'); System.out.println(people); } }程序的运行结果为:
com.southwind.entity.People@61064425
但是在实际开发中返回这样的信息意义不大,我们更希望看到的是对象的属性值,而非它的内存地址,所以我们需要对 toString() 方法进行重写,例如:public class People { …… @Override public String toString() { return "People [id=" + id + ", name=" + name + ", age=" + age + ", gender=" + gender + "]"; } } public class Test { public static void main(String[] args) { People people = new People(); people.setId(1); people.setName("张三"); people.setAge(22); people.setGender('男'); System.out.println(people); } }程序的运行结果为:
People [id=1, name=张三, age=22, gender=男]
equals() 方法的实现是:
* @see Java.util.HashMap */ public boolean equals(Object obj){ return (this == obj); }通过内存地址对两个对象进行判断,即两个对象的引用必须指向同一块内存程序才会认为它们相等,但是在不同的场景下,这种方式不见得都适用。
比如说,两个字符串“String str1 = new String(“Hello”);”和“String str2 = new String( “Hello”);”,虽然 str1 和 str2 是两个完全不同的对象,但是它们的值是相等的,就可以认为这两个字符串相等。
我们需要对 equals() 方法进行重写,String 类已经完成了重写的工作,直接使用即可,重写的代码为:
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }你可以看到 String 类中对 equals() 方法的重写,是将两个字符串中的每一个字符依次取出进行比对,如果所有字符完全相等,则认为两个对象相等,否则不相等,字符串比较的过程如下:
public class Test { public static void main(String[] args) { String str1 = new String("Hello"); String str2 = new String("Hello"); System.out.println(str1.equals(str2)); } }程序的运行结果为:
true
自定义类也可以根据需求对 equals() 方法进行重写,如我们定义一个 People 类,创建该类的实例化对象,认为只要成员变量的值都相等就是同一个人,用程序的语言来表述就是两个对象相等,但是如果直接调用 equals() 方法进行比较,结果却并不是我们所预期的,代码如下:public class People { private int id; private String name; private int age; private char gender; //getter、setter方法 } public class Test { public static void main(String[] args) { People people = new People(); people.setId(1); people.setName("张三"); people.setAge(22); people.setGender('男'); People people2 = new People(); people2.setId(1); people2.setName("张三"); people2.setAge(22); people2.setGender('男'); System.out.println(people.equals(people2)); } }程序的运行结果为:
true
现在对 People 类继承自 Object 类的 equals() 方法进行重写,如果两个对象的成员变量值都相等,则它们就是同一个对象,具体实现如下:public class People { @Override public boolean equals(Object obj) { // TODO Auto-generated method stub People people = (People) obj; if(people.getId() == this.id && people.getName().equals(this.name) && people.getGender() == this.gender && people.getAge() == this.age){ return true; } return false; } }再次运行程序,结果为:
true
hashCode() 方法如下所示:
* @return a hash code value for this object. * @see java.lang.Object#equals(java.lang.Object) * @see java.lang.System#identityHashCode public native int hashCode();该方法返回一个对象的散列值,这个值是由对象的内存地址结合对象内部信息得出的,任何两个对象的内存地址肯定是不一样的。
但是上面例子中我们认为,如果两个 People 对象的成员变量值都相等,就是同一个对象,那么它们的散列值也应该相等,如果直接调用父类的 hashCode() 方法,两个对象的散列值是不相等的,例如:
public class Test { public static void main(String[] args) { People people = new People(); people.setId(1); people.setName("张三"); people.setAge(22); people.setGender('男'); People people2 = new People(); people2.setId(1); people2.setName("张三"); people2.setAge(22); people2.setGender('男'); System.out.println(people.hashCode()); System.out.println(people2.hashCode()); } }程序的运行结果为:
1627800613
2065530879
public class People { //...... @Override public int hashCode() { return this.id*this.name.hashCode()*this.age*this.gender; } }再次运行程序,结果为:
444964682
444964682