- 基本介绍
- 构造方法
- 成员方法
- hashCode()
- getClass()
- toString()
- equals()
- finalize()
- JavaBean重写Object类的方法
- 重写toString
- 重写equals
一、基本介绍
Object类是java类层次最顶层的基类(父类),所有类都是直接或间接继承自Object类,因此,所有类都可以使用Object类中的成员方法。
Object类属于java.base模块,java.lang包下(java.lang包下的类可以直接使用,不需要导包),如下图所示 :
二、构造方法
Object() : 构造一个对象。
如下图所示 :
Object类仅一个构造器,根据继承机制,所有子类在初始化前都会默认优先调用该构造器。
三、成员方法
0.总览 :
Object类常用的方法,如下图所示 :
像notify,wait这些与线程相关的
方法,因为我们还没有讲到java高级篇——多线程的内容,因此这里不做说明。我们来一起看一看up用粗线条标注出的这五个方法。
①int hashCode() |
返回当前对象的哈希码值,该方法通过对象的地址值进行计算,不同对象的返回值一般不同 |
②Class <?> getClass() |
返回调用此方法的对象的运行时类对象(字节码文件对象) |
③String toString() |
返回该对象的字符串表示(默认打印的是"全类名 + @ + 哈希码值的十六进制") |
④boolean equals() |
返回其他某个对象是否与此对象“相等”(默认情况下比较的是两个对象的引用) |
⑤void finalize() |
当“垃圾回收器”确定不存在该对象的更多引用时,由对象的垃圾回收器调用此方法 |
1.hashCode() :
其实我们在上一专题的封装篇中,就已经提到过hashCode方法,并且还给出了hashCode的应用和常规协定。不知道大家是否还有印象,如果忘了也不要紧,这里再给大家演示一下 :
演示 :
up以HashCode_类为测试类,以Student类为演示类。
代码如下 :
packageknowledge.api.objectmethods; /*** @author : Cyan_RA91. hashCode() : 返回当前对象的哈希码值,该方法通过对象的地址值进行计算,不同对象的返回值一般不同。*/publicclassHashCode_ { publicstaticvoidmain(String[] args) { //创建学生类对象Studentstudent_0=newStudent(); Studentstudent_1=newStudent(); //比较不同学生对象的哈希码值System.out.println("student_0对象的哈希码值为 : "+student_0.hashCode()); System.out.println("student_1对象的哈希码值为 : "+student_1.hashCode()); System.out.println("student_0对象的哈希码值为 : "+student_0.hashCode()); } } classStudent {}
运行结果 :
从运行结果可以看出,当我们打印不同对象的哈希码值时,得到的结果不同。而同一对象的哈希码值相同。
2.getClass() :
getClass方法需要用一个Class类来作接收,即获取字节码文件对象(即Class对象)。并且getClass方法要通过对象来调用,因此使用前一定要创建一个对象。这个方法呢,其实我也讲过😂。只不过是在未来的java反射专题(虽然我已经写好了)。同样的,不要紧,再来演示一下不就完了。
演示 :
up以GetClass_类为测试类,以Grape类和Apple类作为演示类。
代码如下 :
packageknowledge.api.objectmethods; /*** @author : Cyan_RA92.Class <?> getClass() : 返回调用此方法的对象的运行时类对象(字节码文件对象)*/publicclassGetClass_ { publicstaticvoidmain(String[] args) { //创建葡萄类对象Grapegrape_0=newGrape(); Grapegrape_1=newGrape(); //获取葡萄类的Class对象Classclass_grape_0=grape_0.getClass(); Classclass_grape_1=grape_1.getClass(); Classgrape_0_class=grape_0.getClass(); //比较通过葡萄类不同对象获得的葡萄类的Class对象有什么不同。System.out.println("grape_0对象的字节码文件对象是:"+class_grape_0); System.out.println("grape_1对象的字节码文件对象是:"+class_grape_1); System.out.println("grape_0对象的字节码文件对象是:"+grape_0_class); System.out.println("--------------------------------------------------"); //创建苹果类对象Appleapple_0=newApple(); Appleapple_1=newApple(); //获取苹果类的Class对象Classapple_0_class=apple_0.getClass(); Classapple_1_class=apple_1.getClass(); //比较通过苹果类不同对象获得的苹果类的Class文件有什么不同System.out.println("apple_0对象的字节码文件对象是:"+apple_0_class); System.out.println("apple_1对象的字节码文件对象是:"+apple_1_class); } } classGrape {} classApple {}
运行结果 :
从输出结果我们可以看出,一个类只有一个字节码文件对象。我们通过创建同一个类的不同对象来分别调用getClass方法,得到的Class对象是相同的。而不同类的字节码文件是不同的。同时,我们也能发现字节码文件对象的格式为“class + 类的正名(类的正名 = 包类+ 类名)”。
3.toString() :
关于toString方法要注意一点 : 当我们直接输出一个对象时,默认调用toString方法。
演示 :
up以ToString_类为测试类,以Animal类为演示类。
代码如下 :
packageknowledge.api.objectmethods; /*** @author : Cyan_RA93. toString() : 返回该对象的字符串表示(默认打印的是"全类名 + @ + 哈希码值的十六进制")*/publicclasstoString_ { publicstaticvoidmain(String[] args) { //创建Animal类对象Animalanimal_0=newAnimal(); Animalanimal_1=newAnimal(); Animalanimal_2=newAnimal(); //toString() 方法的返回值类型为String类型(也可不做接收)Stringanimal_0_string=animal_0.toString(); System.out.println("animal_0对象的字符串表示是:"+animal_0_string); System.out.println(animal_0); System.out.println("------------------------------------------------"); System.out.println("animal_1对象的字符串表示是:"+animal_1.toString()); System.out.println("animal_2对象的字符串表示是:"+animal_2.toString()); } } classAnimal {}
运行结果 :
运行结果可以验证我们之前的结论——当我们直接输出一个对象时,默认调用toString方法。而且toString() 方法默认打印的的确是“类的正名 + @ + 哈希码值的十六进制”。当然,可能有p小将(Personable小将,指风度翩翩的人)要怀疑这里“@”后面跟着的一大堆玩意儿是不是当前对象哈希码值的十六进制?
哎😂,其实验证方法也很简单,你先把这里的某个十六进制数转换成十进制数,然后利用hashCode() 方法打印出该对象的哈希值,比对一下就知道了。当然up这里就不演示了,绝不是因为我懒!是因为我已经明确知道结论了,才给大家演示的,对不对?
这不是重点。大家有没有想过,toString() 方法既然要打印出对象的字符串表示形式,那肯定是要有用的啊,最起码给人直观想象的机会。但是现在就这一堆乱七八糟的玩意儿,给谁第一眼看了也觉得糟心。那怎么办?诶~,别急,等等我们讲到子类重写Object类成员方法时再说。
4.equals() :
Object类中的equals()方法默认比较的是两个对象的引用。
演示 :
up以Equals_类为测试类,以Teacher类为演示类。
代码如下 :
packageknowledge.api.objectmethods; /*** @author : Cyan_RA94. equals() : 返回其他某个对象是否与此对象“相等”(默认情况下比较的是两个对象的引用)*/publicclassEquals_ { publicstaticvoidmain(String[] args) { //创建教师类对象Teacherteacher_0=newTeacher(); Teacherteacher_1=newTeacher(); //通过对象调用equals() 方法System.out.println("teacher_0和teacher_1是一个对象吗?"+teacher_0.equals(teacher_1)); System.out.println("teacher_0和teacher_0是一个对象吗?"+teacher_0.equals(teacher_0)); } } classTeacher {}
相信大家看代码也能感觉出来了——这方法的原版没什么意义!你只比较个地址值有个啥用?雷声大雨点小!我们创建对象的目的是什么?创建对象是为了描述某一类事物的一个具体存在,每个对象的属性和行为都不尽相同。所以,我想你也猜到了,这个方法也要在子类重写的。
5.finalize() :
垃圾回收机制的调用,是由系统来决定的,即有自己的GC算法;也可以通过System.gc()来尝试主动触发,虽然也有可能无效。你也可以重写此方法,做一些释放资源的操作。
finalize()方法在实际开发中用的并不多,对于初学者几乎用不到,因此这里不做演示。
四、JavaBean重写Object类的方法
0.需求 :
实际开发中,通常需要将对象转换成字符串形式进行运输,也需要对即将使用的对象进行相等判断。而toString() 方法和equals() 方法本身的特点无法实现以上需求。因此,这时候就需要我们定义标准JavaBean类,并重写Object类的toString() 方法 和 equals() 方法。
IDEA中提供了自动重写toString和equals方法的机制,可以使用快捷键Alt + Enter,选择要定义或重写的方法,即可自动完成。
1.重写toString() 方法
up以ToString_EX类为测试类,以Teacher类为演示类。
代码如下 :
packageknowledge.api.objectmethods.override_; /*** @author : Cyan_RA9* @purpose : 重写Object类的toString方法*/publicclassToString_EX { publicstaticvoidmain(String[] args) { Teacherteacher_0=newTeacher(); teacher_0.setName("王斌"); teacher_0.setAge(44); teacher_0.setSalary(20000); Teacherteacher_1=newTeacher("许银川", 48, 100000); //直接输出对象,默认调用toString() 方法System.out.println(teacher_0); System.out.println(teacher_1); } } classTeacher { /** 以JavaBean标准来敲 *///成员变量privateStringname; privateintage; privatedoublesalary; //构造器publicTeacher() {} publicTeacher(Stringname, intage, doublesalary) { this.name=name; this.age=age; this.salary=salary; } //getter,setter方法publicStringgetName() { returnname; } publicvoidsetName(Stringname) { this.name=name; } publicintgetAge() { returnage; } publicvoidsetAge(intage) { this.age=age; } publicdoublegetSalary() { returnsalary; } publicvoidsetSalary(doublesalary) { this.salary=salary; } //重写的toString() 方法publicStringtoString() { return"Teacher{"+"name='"+name+'\''+", age="+age+", salary="+salary+'}'; } }
运行结果 :
可以看到,重写后的toString方法本身并不复杂,就是返回了一个拼接后的字符串。而当我们在Teacher类中重写toString方法后,输出Teacher类对象调用的就是我们重写后的toString方法,成功打印出了Teacher类对象的相关信息。因此,重写toString() 方法的目的,往往是将对象的信息能够显式地打印出来,让我们看到。
2.重写equals() 方法(重点)
up以Equals_EX类为测试类,以Animal类为演示类。
代码如下 :
packageknowledge.api.objectmethods.override_; importjava.util.Objects; /*** @author : Cyan_RA9* @purpose : 重写Object类的equals方法*/publicclassEquals_EX { publicstaticvoidmain(String[] args) { //利用带参构造创建不同Animal类对象Animalanimal_0=newAnimal("猫", 15); Animalanimal_1=newAnimal("狗", 12); Animalanimal_2=newAnimal("猫", 15); //查看不同Animal对象的信息System.out.println("animal_0对象的信息为:"+animal_0); System.out.println("animal_1对象的信息为:"+animal_1); System.out.println("animal_2对象的信息为:"+animal_2); System.out.println("----------------------------------------------"); //通过重写后的equals方法比较不同对象的信息是否相同System.out.println("animal_0对象和animal_1对象信息相同吗?"+animal_0.equals(animal_1)); System.out.println("animal_0对象和animal_2对象信息相同吗?"+animal_0.equals(animal_2)); System.out.println("animal_1对象和animal_2对象信息相同吗?"+animal_1.equals(animal_2)); } } classAnimal { /** JavaBean标准 *///成员变量privateStringspecies_name; privateintaverage_age; //构造器publicAnimal() {} publicAnimal(Stringspecies_name, intaverage_age) { this.species_name=species_name; this.average_age=average_age; } //getter,setter方法publicStringgetSpecies_name() { returnspecies_name; } publicvoidsetSpecies_name(Stringspecies_name) { this.species_name=species_name; } publicintgetAverage_age() { returnaverage_age; } publicvoidsetAverage_age(intaverage_age) { this.average_age=average_age; } //重写equals方法publicbooleanequals(Objecto) { if (this==o) returntrue; if (o==null||getClass() !=o.getClass()) returnfalse; Animalanimal= (Animal) o; returnaverage_age==animal.average_age&&species_name.equals(animal.species_name); } publicinthashCode() { returnObjects.hash(species_name, average_age); } //重写toString方法以打印出对象的信息publicStringtoString() { return"Animal{"+"species_name='"+species_name+'\''+", average_age="+average_age+'}'; } }
运行结果 :
我们先来解读一下关于equals方法的重写 :
//重写equals方法 @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Animal animal = (Animal) o; return average_age == animal.average_age && species_name.equals(animal.species_name); }
解读①——该方法的形参列表是一个Object类型的对象,也就是多态了。
解读②——方法第一行是一个if条件语句的判断,判断条件“this == o”中,用到了比较运算符“==”,我们之前讲过,==比较运算符如果判断引用类型,判断的是二者的地址是否相同,即判定是否为同一对象;因此这里就是像看看你要判断的对象是不是它本身,如果判断为true,意思就是你拿一个对象和它自己比去了。那还用比吗😅,这不就和你照镜子一样,比个🐔儿?因此直接return true,不再进行后续的比较。
解读③——接着,又来了第二个if条件语句的判断,判断条件“o == null || getClass() != o.getClass())”中,由逻辑运算符短路或“||”分为了两部分,短路或的判断中,从左向右执行,且只要满足第一部分的判断,之后的部分便不再执行。第一部分“o==null”,是判断传入的对象是否为空。你想,一个在堆空间中真正存在的对象和一个空能比出个啥来吗?就像你比身高,让你和空气比,这不虚空对线呢😅?因此,如果传入的对象为空,立刻return false,没啥比头。来看第二部分“getClass() != o.getClass())”,这不我们前面刚说过getClass方法么,巧了😋。其实,第一个getClass() 前面隐含了“this.”,当然你也可以手动加上。这部分判断是看你传入的对象和调用该方法的对象是否是同一类对象。因为一个类的只有一个字节码文件,因此同一个类的字节码文件对象一定相同。你想,如果一条狗和你手里的泡泡糖都叫“大大”,那**能比么?因此,如果第一部分判断条件不通过,说明传入的对象不为空;如果第二部分判断条件不通过,说明传入的对象和调用方法的对象是同一类型的对象,可做后续的比较。反之,return false,不再进行后续的判断。
解读④——当前两个if条件语句都没有把o对象拦下来后,我们已经确定传入的对象既不是调用方法的对象本身,也不为空,并且和调用方法的对象是同一类对象。这时候它来了一个强制转型“Animal animal = (Animal) o;”,没毛病吧,妥妥地强转,原因估计你也猜到了,现在的o对象是一个Object类型的对象,根据多态的弊端,父类引用不能直接使用子类对象的特有成员。因此,这里要把o对象进行强制向下转型,以改变o对象的编译类型,使得做接收后的新的引用变量可以调用要判断类型(此处为Animal类)的特有成员,以便进行后续的判断。
解读⑤——最后就可以开始判断了。IDEA也很干脆,直接return了一个boolean类型。注意,具体的比较语句中up还要说一下,判断非引用类型直接用比较运算符“==”即可,如果判断的是引用类型,比如说字符串,要使用“字符串.equals()”的形式来判断,很明显字符串属于String类,而String类中重写了Object类的equals方法,可以比较两个字符串的内容是否相同。
其次,看到代码后,可能会有小伙伴儿们疑惑——
为什么我用快捷键重写equals方法时,IDEA会默认同时重写equals方法和hashCode方法呢?
这要说起来可就大费篇章了😂。这与hashCode的常规协定有关,也是为了避免某些程序执行出现异常的情况。现在不需要深究,大家了解一下即可。等你到找工作面试的时候,你不想懂也得懂它了🤗。
五、总结
其实,关于Object类中的方法,大家只要理解这几个原初的方法,并且掌握toString方法和equals方法的重写问题就没问题了。
🆗,以上就是我们关于API常用类Object类内容分享的全部内容了,下一节String类我们不见不散,感谢阅读!