Java的Object类常用的方法(详述版本)
文章目录
- 一、什么是Object类
- 二、常用方法:toString()
- 三、常用方法:对象比较equals()
- 四、常用方法:hashcode()
- 五、总结
一、什么是Object类
顾名思义,Object类是Java默认提供的一个类,而非程序员自己抽象出来的类。在Java里面所有的类都存在继承关系,即便是在没有显示定义继承关系之下也是存在继承关系的,因为这Java中,所有类都会默认继承Object类。即所有类的对象都可以用Object类类型的引用来进行接收。
public class Student {
protected String name;
protected int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
}
class ff extends Student {
public ff(String name, int age) {
super(name, age);
}
}
class test {
public static void main(String[] args) {
Object obj1 = new Student("小李",18);
Object obj2 = new ff("小明",18);
}
}
在Student类和ff类都没有显示继承Object类的情况下依然可以使用Object类类型的引用进行接收两个类的对象,在说明Object类是会被编译器默认继承的,但是需要注意的是:因为Java不支持多继承的原因,因此当ff类继承Student类的情况下,编译器就不会在为其继承Object类了,但是其父类Student类还是会继承Object类,此时ff类还是间接的继承了Object类,因此可以使用Object类类型来接收ff类实例化的对象。
因此:在开发之中,Object类是参数的最高同一类型。
在Object类当中Java也定义了一些方法:
二、常用方法:toString()
在之前我们就重写过该方法,将其在子类中进行重写,然后在打印对象的时候就会根据重写的toString()方法来打印。
在没有重写toString()方法的情况下会不会还打印类的内容呢?
显而易见,并没有打印类的内容,这也就意味着是因为我们重写了ToString()方法才导致的这样的结果,至于为什么会按照重写的toString()方法进行打印呢?我们来看一看println()方法实现的过程就知道了。
当我们调用println()方法打印引用obj1时,println()方法中将obj1传递给valueOf()方法,valueOf()方法中又调用toString()方法,直到toString方法执行完毕后打印完成,这一整个过程相当于通过Object类类型的obj1引用直接调用toString()方法,但因为其子类Student类重写了toString()方法,因此发生运行时绑定,变成调用子类的toString()方法,最终打印子类Student类的内容。
因此需要打印类的内容,只需要在类内重写Object类的toString()方法即可
三、常用方法:对象比较equals()
在Java中,== 进行比较时
1、如果 == 左右两边是基本类型变量,比较的是变量内的值是否相同
2、如果 == 左右两边是引用类型变量,比较的是变量内的值是否相同
3、如果要比较对象中的内容,必须要重写Objcet类中的equals()方法,因为在Object类中的equals()方法默认是按照地址来比较的,也就是使用 == 进行比较。
public class Test1 {
public static void main(String[] args) {
Student stu1 = new Student("小李",18);
Student stu2 = new Student("小李",18);
String s1 = "123";
String s2 = "123";
int a = 10;
int b = 10;
System.out.println(a == b); //true
System.out.println(s1.equals(s2)); //true
System.out.println(stu1.equals(stu2)); //false
}
}
此时Student类中并未重写Objcet类中的equals()方法,
直接使用equals()方法进行比较两个对象之间的内容是无效的,依然比较的是两个对象的地址而非对象;而String类当中已经重写了equals()方法了,无需我们再进行重写,因此当String类变量调用equals()方法进行比较时,会直接比较变量内的内容而非地址。
需要先理解:
引用型变量是在栈上创建,但是并不存储变量的值,而是存储堆空间的地址。
类也是如此,当类没有进行实例化的情况下,类不占用任何空间,当使用类类型创建了一个引用型变量时,编译器会在栈上开辟一块空间,但此时该空间内并未存储数据,当使用new关键字实例化对象后,会在堆上开辟一块空间用来存储数据,并将堆空间的地址返回给引用并存储在引用中
而基本数据类型不一样,使用基本数据类型创建的变量会直接在栈上开辟空间并且直接将数据存储在该空间内
而在我们的认知中,当对象的内容相同时,两者应该互为同一个对象,但我们发现代码运行后stu1和stu2返回为false,说明两者不相同,当我们观察Object类中的equals()方法的方法体时,我们发现方法体内还是比较的是对象的地址而非对象本身,而我们需要比较的是对象本身而非对象的地址,因此我们需要重写equals()方法,将其按照我们的想法来比较,比较对象的内容。
在子类中重写equals()方法,让其比较的是对象的内容而非对象的地址,而我们当前对象需要比较的就只有变量name和变量age,因此只需要比较对象的两个变量是否相同即可。
因为在调用equals()方法的时候发生了向上转型,父类的引用指向子类的对象,而父类的引用却无法调用子类特有的成员,因此我们需要将父类的引用转化为子类的引用,因此需要向下转型,此时再通过子类的引用调用子类的成员进行比较即可。
四、常用方法:hashcode()
hashcode()方法是用于计算一个具体的位置并将其通过数字的形式返回。在Object类中的toString()方法就调用了hashcode()方法。
在object类中有定义有hashcode()方法,但是因为该方法是被native关键字修饰的,我们无法直接观看到方法体,被native关键字修饰的方法是用c/c++代码实现的,存储在本地方法栈内。
我们认为,当两个内容相同的对象,应该存储在同一个位置,那么调用hashcode()方法,让其返回地址,那么两个对象的返回地址应该是相同的,而当我们调用该方法时,返回到数据却是不相同的,很显然和上面的方法一样,需要进行重写才能够适用于当前类。
在子类中重写Object类的hashcode()方法
public class Student {
protected String name;
protected int age;
public Student(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public boolean equals(Object obj) {
Student student = (Student) obj;
return age == student.age && student.name == this.name ;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
此时这次执行代码
1、hashcode()方法用于确定对象在内存中的存储位置是否相同(如果两个对象的内容相同,我们判断为两者是同一个对象,那么应该存储在内存中的同一块空间)
2、事实上hashcode()方法在散列表中才有用,在其他情况下没用。在散列表中hashcode()方法的作用是获取对象的散列码,进而确定该对象在散列表中的位置。
五、总结
当我们自定义类的时候,以上的方法都需要重写才适用于当前我们自定义的类,如果不重写Object类中的方法,直接使用Object类中的方法会出现我们意想不到的结果,比如:两个对象的内容相同,但是使用比较的equals()方法得出的结果却是false不相同,这就让人有点摸不清头脑了。因此我们自定义类的时候,我们并不知道未来是否会用到这些比较,打印方法,但是建议就是在定义类的时候顺便将这些方法也都定义了,因为我们不需要手动编辑这些方法,我们可以通过快捷键直接快速生成:
仔细想想,Object类有点类似抽象类,但是Object类并没有被abstract关键字修饰,也就是说:Objcet类不是抽象类。但Object类就是像类似抽象类但也不是很像,继承抽象类的子类,子类当中必须重写抽象类当中的所有的抽象方法,但是抽象类当中可以不定义抽象方法,可以定义正常的方法;而Object类中的方法,因为Object类不是抽象类,因此Object类当中不可能存在抽象方法,但是Object类的子类如果需要用到Object类的方法时,基本都需要进行重写才适用于当前类,否则不适用。
在这一点上和抽象类当中的抽象方法类似,不过也有本质的区别,抽象类当中抽象方法其子类必须要重写(强制),否则爆红,不是抽象方法不强制进行重写;Object类的方法不强制进行重写,但如果子类需要使用方法就必须要重写才适用于当前类,否则方法结果不正确。而定义抽象类的本质原因就是:当前类的抽象内容不足以实例化一个具体的对象就可以将当前类定义为抽象类。Object类没有定义为抽象类,其中也就没有抽象方法,不需要我们强制重写方法,在这一点上,Object类给我们的编程空间似乎比抽象类要大得多,当需要的时候就重写方法,不需要就不重写,不过抽象类的子类在重写抽象方法时也有快捷键快速生成代码,只是需要我们自己定义方法体。