3 - 7:Java中的super使用
我们在使用继承特性的时候,经常还会用到super关键字,super关键字通常用在我们对象的内部,可以代表父类对象。通常来讲,如果我想得到父类属性,那么我就可以调用super.age,如果我想调用父类对象的方法,我就可以用super.eat();,好像很简单。那我们来写一个例子。
现在我们有两个类,一个是父类Animal,一个是Dog子类。那我在Animal父类里面定义了一个age属性,并给它一个初始化的值为10,然后也给了它一个eat的方法,这个eat方法是显示出动物具有吃的能力;在Dog类里面,首先Dog类是Animal的子类,它有个int属性是20,也有一个eat方法,重写这个方法,显示出狗有吃骨头的能力:
那现在我们应该怎么用super这个关键字呢,那我首先定义了一个method方法,如果我在其他方法当中我想得到父类的属性,我就可以通过“super.age”:
然后我在main函数里调用这个方法,显然大家发现,在main函数里输出的值是源于父类的:
同理,我在这个方法中想得到子类的属性值,我就可以直接输入这个属性就可以了:
之前说过,当我们创建子类对象的时候,其实他也会创建一个父类对象,所以大家一定要注意,父类对象和子类对象的属性并无关系,是两个属性。
好,我们再来看一下,我不但可以调用父类的属性,我还可以调用父类的方法。依然使用super关键字:
可以看到,父类和子类两种方法,父类是加了super关键字的。
super关键字和构造方法也有紧密的关系。
a.子类的构造的过程当中必须调用其父类的构造方法。我们之前写代码的时候发现,当我们创建子类对象的时候,其实它会自动的执行父类的构造方法,并且创建父类的构造对象。那此时,其实就隐式地使用了我们的super关键字。我们来看一下。
我们有一个父类Animal,我们在里面写一个无参的构造方法。有一个子类,我也写了一个无参的构造方法。注意,在子类里,我们隐式地写了一个super,但是以前我们并没有写这个关键字,所以它是隐式显示的。什么叫隐式显示呢,就是我们程序员是看不见的,但程序里是有这句话的。但后当我们创建子类对象的时候,他就会自动执行父类的构造方法。那在子类构造方法的执行过程当中,它执行了父类的构造方法。当你new子类构造方法的时候,它这里相当于隐式地创建了一个super关键字,帮我们调用了一个父类的构造方法。所以说这里我写或者不写,效果是一样的。
b.如果子类的构造方法中没有显示的调用父类构造方法,则系统默认调用父类无参构造方法。刚才这边已经给大家演示了,这就是通过super关键字。如果显示的调用构造方法,必须在子类的构造方法第一行。也就是说,我想在代码里面通过我们的super关键字体现出来调用我们父类的方法,我们是可以的,我们把这种叫做显示,但它必须放在工作方法的第一行。也就是说,如果我把它放到其他位置是不行的:
我们如果想调用我们父类的构造方法,必须放到子类工作方法的第一行。
c.那我们在使用构造方法的时候还要注意一个问题,如果子类构造方法中既没有显示调用父类构造方法,而父类又没有无参构造方法,则编译出错。什么意思呢,如果子类构造方法中不显示的调用super,再在父类里建立个有参构造方法,参数给一个in的age,然后把父类无参构造方法注释掉,我们之前说过,如果自己定义了一个有参构造方法,系统就不会帮我们弄出一个无参构造方法。而子类里又隐式地调用了一个无参构造方法,所以说,此时就会编译报错。所以大家要注意这个问题。
3 - 8:Java中的Object类
Object类是所有类的父类,就相当于Java世界里所有类的老祖。如果一个类没有使用extends关键字明确标识继承另外一个类,那么这个类就是继承的是Object类。
Object类中所以方法,适用于所有子类。
有几个比较重要的方法:
1.toString()方法:在Object类里面定义同String()方法的时候返回的对象的哈希code码(对象字符串)。如果我们在代码里面直接输出一个对象,你会发现它输出的对象在内存当中的地址,我们把它称为哈希码。哈希码是通过哈希算法生成的一个字符串。是用来唯一区分我们对象的。就让人一样,每个人都有且只有一个身份证号。那这个身份证号就是用来区分我们每一个人的。身份证号有一个生成规律或生成算法。在计算机世界里呢,这个算法就是哈希码。如果我们希望输出一个对象值后,你能帮我直接输出这个对象的属性值只要你重写toString方法,它就可以自动帮我们生成。我们来看一下。
这里我们创建了一个Dog对象,我们来观察一下,Dog继承了Animal,而Animal并没有明确标明这个是继承哪一个父类的。那么它就相当于继承了我们的Object类,所以说Animal是Object的子类。Dog又是Animal的子类,显然,Dog也是Object的子类。下面左边的图,去掉注释也无所谓。
如果我想直接输出这个dog对象的值,你会发现,它输出的值是dog在内存对象的一个地址:
而我们经常在输出dog对象的时候我希望知道它的属性值,那此时我就要重写我们从父类继承过来的toString方法。 那这里呢,大家不用一个一个去打,其实我们Eclipse已经帮我们提供了一个工具,自动的帮我们生成重写的方法:
我们选择菜单栏的Source→Generate toString()...
他就会根据我门对象的属性,帮我们生成一个toString方法。由于我该对象有age和name属性,我就光给age属性给予一个toString的方法:
注意,toString方法返回的是一个字符串,当我再次输出对象的值的时候,他输出的是我的类名加属性名了(此时main函数不变):
在Object类里面还有一个比较重要的方法,就是equals方法。比较的是对象的 引用 是否指向同一块地址,这个方法返回值是布尔类型。我们在以前在new一个类的构造方法的时候创建一个对象,然后将对象赋给dog,很多人认为,这个dog就是我们创建的对象,其实不对,他仅仅是我们对象在内存当中创建的地址,那你在操作这个地址的时候,其实等于间接地操作了我们的对象的值。就有点类似于我们家里的遥控器,当你按遥控的时候,电视会切换节目,其实这个遥控器并不能代表对象的本身,你只是通过遥控器间接地操作我们的电视。我们这个dog对象呢,也是一样的,准确的说,这个dog应该叫做 引用 。它只是引用了内存当中的一块地址,所以说这个equals方法默认情况下只是比较了对象的引用是否是相同的。也就是说,是否是指向同一块内存区域。但是我们生活当中来判断两个对象是否相同其实是有两种情况的:
1:坐车,把手机丢了,现在你手里的手机和我的一样,那你手里的手机是我昨天丢的吗,这样如果用equals方法比较,如果返回true,则这是我昨天丢的手机,如果是false,那么这是你自己买的手机了。
2:我昨天买了个手机,你今天也买了个。咱俩的手机是一样的,但这是两个手机,有两个内存地址,如果用equals方法来检查,返回的就是false了。
但是我们在生活当中很多时候其实是比较两个对象的值是否相同,那该怎么办呢,意思就是,如果父类的方法解决不了我们的问题,我们就要重写这个方法。所以说,我们只需要重写就可以了。
我们之前写的代码里面有个dog对象,那么我们先来说第一种情况。我现在创建了两个dog对象,这两个对象如果我用equals判断,它只是比较了两个对象的内存地址是否相同。虽然我们创建的是同一个类型的对象,但我new了两次相当于开辟了两个内存区域,所以说,这两个对象的引用肯定不同。
有人说,这两个对象有了,但是里面的在不一定是相同的,当然是不同啦!好,我现在把这两个对象的值赋成相同的,再来执行:
那这种情况跟我们的”==“运算符有关,我们以前用==运算符可以判断两个基本数据类型的数据值是否相同,但大家注意,如果你判断的是两个引用值是否相同,它比较的也是内存地址。显然这是两个内存对象在内存中开辟了两个内存空间,那它们的地址值肯定不同。
如果在生活当中碰到第二种情况,我是两个对象,但我想判断两个对象数据值是否相同怎么办?这样,equals方法就不行了,我们需要重写才行。那这个equals方法也可以不用自己去写,在我们菜单栏Souse→Generate hashCode() and equals()...里面,可以帮我们生成哈希码和equals方法,hashCode是用来唯一标识一个对象的,但是他们两个会一起生成。那我们现在说了,两个对象判断他们是否相等,我是应该比较两个对象中每一个值。但是,我们点进去Generate hashCode() and equals()...后,出现一个界面,里面可以自定义什么能比较,能么能不比较,注意,重写一定要在父类里才行,不然会出现错误:
错误可能是这样:
重写后:
我们发现,equals方法比较复杂,我们来看一下,每一步是干什么的。
首先它会判断,两个引用的值是否相同(第19行),我们说过,引用的值,判断的是两个引用的地址是否相同。如果两个引用的地址相同,其实就是他们引用了同一个对象,那这两个对象肯定相同。第二(第21行),如果第二个对象是空值,那么不用比了,跟空值比肯定不等,返回假。第三(第23行),这个getClass方法shi可以得到一个类对象,当我们new一个类的时候,我们得到的对象叫类的对象,如果我们这个对象调用getClass方法,我们的得到的是类对象。类的对象和类对象区别:类对象描述的是类的代码信息,类的代码信息就是这个类有哪些属性、类型、变量名是什么,有哪些方法、方法名、里面的代码都是什么;类的对象指的是类里面属性值的信息,或者说这个对象的数据信息。我们可以通过类对象判断这个对象的类型,这句话(23行)就是判断,两个类里面的类型是否相同。执行到第四步(第25行)的时候显然此时两对象类型就是相通的了,那么我就可以把传进来的对象转换成Animal类型,转换成相同类型以后,我才能进行属性比较,再来判断,两个对象属性值是否相等(第26行),如果两个对象属性值相同,这两个对象就相同,否相就不等。
好,我门重写了equals方法,主函数没变,我们在运行一下:
可以看到,两对象相同了。
大家可以自己写一下有关Telephone的代码,赋予相同屏幕、CPU、内存,再重建equals方法,看看行不行!
3 - 9:练习题
没有重写 toString 方法的情况下直接用 print 打印对象,输出的是( )
A、此对象的地址
B、此对象的属性值
C、会报错
D、什么都不输出
答案:A。解析:默认继承 Object 的 toString( ) 方法,输出对象地址