文章目录
- 1.object类
- 2.toString方法调用过程
- 2.1具体案例
- 2.2源代码查看
- 2.3方法的重写
- 2.4重写效果
- 3.equals方法调用过程
- 3.1现象的描述
- 3.2方法的重写
- 3.3IDEA自动填充
- 4.hashcode方法
1.object类
java里面除了object类,所有的类都是存在继承关系的,object类是我们任何类的父类,所有的类的对象都可以使用object的引用进行接收;
object里面哟有很多的方法需要我们去掌握,但是今天我们只会去演示这个toString()方法,equals()方法,hashcode()方法:
2.toString方法调用过程
2.1具体案例
下面的这个就是我们自己的这个一个包,在这个包里面我们自己定义了一个student.java文件,这个文
件的内容如下图所示,这个里面有我们的这个person类,我们的这个student这个类是继承了我们的这
个person类,在这个主函数里面,我们使用这个基类创建了一个对象,并对于这个基类里面的成员变
量进行初始化,最后使用这个println打印输出这个对象的相关的信息;
打印结果是有多个部分组成,例如这个里面的demo2是我们的包的名字,person是这个包里面的类,后面的这个就是地址,总之这个打印结果客观的反应了我们的这个对象的相关的信息,但是这个好像不是我们想要的,因为我们使用这个println想要得到的是这个对象的内容,例如这个成员变量之类的,而不是这个类似于路径加上地址的东西;
我们可以按照下面的这个途径,看一下这个结果是怎么一步一步的调用显示打印出来的:
2.2源代码查看
选中我们的这个println,按住这个ctrl+鼠标右键,这个时候我们的这个idea里面进行跳转;(按下这个ctrl之后,选中这个时候println下面会出现蓝色的下划线,这个时候我们点击就可以实现跳转);
在这个代码里面我们可以发现,这个println这个里面实际上是调用的我们的这个valueof(x)就是把我们传递的参数给了这个valueof这个函数,接着我们按照相同的操作进行这个valueof的跳转;
这个里面是一个三目运算符,判断我们的参数是不是空的,这个里面我们需要始终记住我们的这个传递的参数就是我们的自己写的这个.java文件里面的person1这个对象,无论这个怎么跳转,我们传递的都是person1这个对象,我们的这个对象肯定不是null,所以在执行这个valueof函数里面的这个内容的时候,会执行这个obj.tostring()这个部分;
这个就是我们上面说的,实际上就是执行的我们的obj类里面的方法,这个就和我们上面介绍的内容,连接起来了,我们也在这个过程中发现了这个tostring这个方法是怎么一步一步地被调用到的,相信你也明白了;
按住这个ctrl继续跳转,这个时候就出现了我们的这个tostring方法的具体的内容,我们通过这个return语句也可以看到这个结果到底是有什么组成的了;这个语句里面有这个hashcode这个我们上面也说了,也是我们今天后面会介绍的三个方法之一;
总结一下:这个里面的obj对象(在valueof函数里面的参数)是我们的person1对象的引用,当我们没有重写的时候,这个调用的就是我们的这个obj引用的这个tostring方法,但是如果我们对于这个父类的方法进行重写,这个时候进行调用,就可以调用到我们子类自己的,这个就是动态绑定;
没有重写就调用父类的方法,重写之后就调用自己的重写方法,这个就是动态绑定的核心要义;
2.3方法的重写
我们想要实现打印这个对象的成员变量的内容,但是上面的这个object里面的这个方法显然不是我们想要的,这个时候我们就需要去对于这个方法进行重写
接下来,我们在person这个类里面对于我们的方法进行重写:这个其实也不是我们自己写,只需要点几下,这个IDEA里面就会自动生成:
首先我们要选对位置,就是我们的这个重写是写到哪里去的,这个里面是写到我们的这个person这个类里面,所以就是把我们的光标放到这个成员变量下面的这一行,进行右键,选择generate,为什么要说这个地方,就是因为我自己刚开始第一次学的时候,并没有注意到这个细节,所以重写的时候IDEA就提示说不可以写(这个就是因为没有注意这个位置,位置不对,IDEA肯定不可以写啊);
这个里面有很多的东西,可能我们后来会用到,但是暂时只需要看到这个toString就可以了,因为我们要使用这个;
接下来这个idea就会让我们选择这个内容,其实这个就是成员变量,因为我们的这个类里面只定义了一个成员变量,因此这个地方只会显示出来一个,如果我们定义了多个,这个位置就会显示出来很多,我们直接全选,点击确定;
这个时候,我们的这个方法重写就会被自动的填充到我们的这个代码块里面去了,@override表示的就是这个方法是被重写的;
2.4重写效果
这个时候,我们再次运行,显示的结果就是我们创建的这个对象的相关姓名信息,因为我们是进行了初始化的,这个就说明我们的这个成员方法生效了;
3.equals方法调用过程
3.1现象的描述
下面的这个是为了介绍这个equals方法,在原有的这个内容的基础上面添加了一些内容,例如这个age成员变量,以及这个带参数的构造函数,但是为了防止这个继承这个父类的子类报错,还是自己手动添加了一个无参数的构造函数,不然这个不写的话下面的子类里面就会报错
我们的这个带参数的构造函数,实际上也是可以自动生成的,就是按照上面的这个tostring方法重写的步骤,只不过选择的时候选择这个constractor这个部分,这个就是构造的意思吧,然后这个就会根据我们的这个成员变量写出来这个带参数的构造函数;
我们定义的两个对象,都是使用的带参数的构造函数进行初始化,但是这个在姓名和年龄都相同的情况下,这个打印的结果却是false,肯定是和这个方法的底层实现有关了,我们之前比较是不是相等的时候,使用的是这个equals这个方法,于是我们尝试去使用这个看看是不是可以使用这个equals对于两个对象进行比较,但是很遗憾,这个是不可以的,因为打印的结果还是false,我们使用这个ctrl跳转之后,发现这个equals的参数是object对象,我们的这个就是向上转型(从person转型到这个object这个父类),但是主要问题是这个return使用的是thisobj进行判断的,其实这个就和我们的person1person2没有什么区别了;
为什么这么说呢,因为熟悉this的朋友都是知道的,这个equals里面是有一个默认的这个thsi对象的,我们的这个person1就是这个this,因为我们的person1调用的这个方法,这个本质上就是person1和person2的比较,因此上面的两个写法就是一样的逻辑,比较出来的这个结果必然一样;
为什么我们之前使用的string可以进行equals的比较呢,因为我们的这个String是一个类,在这个类里面是重写了这个equals这个方法的,因此在进行比较的时候,这个调用的就是重写的这个方法,而不是父类的,但是我们的这个person是没有重写这个equals方法的,因此调用的时候只能调用这个父类的,但是父类使用的==进行比较,所以无法达到我们想要实现的效果,想要正确的进行比较,我们的这个person类就需要像这个string一样自己去实现这个方法的重写,但时候进行调用的时候调用我们自己重写的方法;
3.2方法的重写
下面的这个就是方法的重写,这个方法的头就是从string那个模拟实现照搬过来的:因为这个参数一个父类对象,这个地方需要进行这个类型的转换,转换为我们自己定义的这个类,然后进行判断,因为这个name是string类型的,因此这个里面的name.equals调用的就是string里面重写的,这个时候不会调用这个obj里面的,这个temp.age调用的就是这个obg里面的,反正这个1010这个可以判断,但是namename这个obj判断不出来;
3.3IDEA自动填充
还是进行这个generate,只不过这个时候选择的是我们的这个equals和hashcode这个,然后一路next就可以了;
下面的这个就是帮助我们自动填充好的这个情况:
4.hashcode方法
其实上面我们或多或少都用到了这个hashcode方法,这个方法就是用来获取这个对象的具体位置,相当于是我们熟悉的地址,下面演示一下这个函数的用法:
我们上面已经获取了这个重写的hashcode的代码,我们先把重写的方法注释掉,我们的两个对象的内容完全一致,我们想要他们的地址是一样的,但是这个时候运行起来就是两个不相等的地址,因为我们的object是我们的父类,所以这个调用的是我们的父类里面的这个haskCode方法;
我们跳转之后发现这个object这个父类里面的方法是使用native修饰的,使用这个关键字进行修饰,表达的意思就是我们的这个方法是使用的C/C++代码实现的,因此没有公开,我们是看不到的;
但是,我们把3.3里面自动填充的这个hasnCode放开之后,这个打印的地址就是一样的了,这个就是因为这个时候调用的是我们的自动填充的这个hashCode,而不是我们的父类里面的这个hasnCode方法;