定义解析
某一类事物的多种存在形态,
比如:人存在男人和女人两种形态
动物存在猫、狗、猪、鸭、鹅等诸多形态。
- 多态的体现
父类的引用指向了自己的子类对象,父类的引用可以接受自己的子类对象
提高代码的扩展性。 - 多态的前提
必须是类与类之间有关系,要么继承,要么实现,
通常还有个前提,方法存在覆盖 - 多态的好处
多态的出现,大大的提高了程序的扩展性。 - 多态的弊端
提高了扩展性,但是只能使用父类引用访问父类中的成员, - 多态的应用
可以直接调用父类已经定义的方法,但是实现的是自己重写父类的方法。
若想使用子类自己特有的方法,则可以通过强制将父类的引用,转成子类类型。比如:
Student stu = new BaseStudent();//类型提升(向上转型)。就好象 byte b =2; int x=b-》b就会提升int类型
BaseStudent bstu = (BaseStudent)stu;//强制将父类引用转成子类类型。
多态自始至终都是子类对象在做着变化(变成父亲,去讲自己的课,脱掉父亲的伪装去看电影)
abstract class Student{
int age = 18;
public abstract void study();
public void sleep() {
System.out.println("躺着睡");
}
public static void cry() {
System.out.println("不用哄");
}
}
//将对象调用变简单了,找到这批对象的共同点,抽了出来。相当于叫一批学生学习和睡觉,而不是单独的叫某个学生。
class DoStudent{
public void doSome(Student stu) {
stu.study();
stu.sleep();
if(stu instanceof BaseStudent) {//instanceof 用于判断是否为某个对象
BaseStudent bstu = (BaseStudent)stu;
bstu.play();
}else if(stu instanceof AdvStudent) {
AdvStudent astu = (AdvStudent)stu;
astu.sing();
}
}
}
class BaseStudent extends Student{
int age = 16;
@Override
public void study() {
System.out.println("base study");
}
public void sleep() {
System.out.println("坐着睡");
}
public void play() {
System.out.println("打篮球");
}
public static void cry() {
System.out.println("要糖哄");
}
}
class AdvStudent extends Student{
@Override
public void study() {
System.out.println("Adv study");
}
public void sleep() {
System.out.println("撅着屁股睡");
}
public void sing() {
System.out.println("唱歌");
}
}
public class DuoTaiDemo2 {
public static void main(String[] args) {
DoStudent ds = new DoStudent();
ds.doSome(new BaseStudent());//Student stu = new BaseStudent();
ds.doSome(new AdvStudent());//Student stu1 = new AdvStudent();
}
}
结果:
base study
坐着睡
打篮球
Adv study
撅着屁股睡
唱歌
- 在多态中成员函数的特点:
-
在编译时期,参阅引用型变量所属的的类中是否有调用的方法,如果有,编译通过,如果没有,编译失败.。
比如:如果学生里没有play的方法,若不转换成BaseStudent 的话,直接是stu.play()调用的话,编译是无法通过的。 -
在运行时,参阅对象所属的类中是否有调用的方法。
比如:所有的学生都有睡觉的方法,但是默认学生是躺着睡(父类),基础班学生重写了父类方法,变成坐睡。在运行时,直接stu.sleep()则会得出坐着睡。
Student stu = new BaseStudent();
stu.sleep();
- 在多态中,成员变量的特点
- 无论编译和运行,都参考左边(引用型变量所属的类)
比如:
默认学生年龄是18岁,基础班的学生年龄变成16岁,直接打印还是会打印父类的年龄。
Student stu = new BaseStudent();
System.out.println(stu.age);
静态成员变量也是一样。
- 在多态中,静态成员函数的特点
- 无论编译运行都参考左边。因为静态不需要对象存在,父类引用还在,找的就是引用型的变量先加载。
比如:默认学生哭都不用哄,但想调用基础班要糖哄的哭,却因为静态修饰调用不起来了,
Student stu = new BaseStudent();
stu.cry();
解释:当cry()方法一进内存,就已经被绑定,绑定在方法所属的类上,所属于父。属于静态绑定, 对象是谁就是谁的是动态绑定。非静态的时候。
Object类中equals方法
Object类是所有类的父类,当我们创建类时,其实默认是集成了Object类的。
Object 类中已经提供了对象是否相同的比较方法,equals方法,但是此方法只比较的对象是否相等,比较的是内存地址值。
BaseStudent bstu = new BaseStudent();
BaseStudent bstu2 = new BaseStudent();
BaseStudent bstu3 = bstu;
System.out.println(bstu.equals(bstu2));
System.out.println(bstu==bstu2);
System.out.println(bstu.equals(bstu3));
System.out.println(bstu==bstu3);
结果:
false
false
true
true
如果自定义中也有比较相同的功能,没有必要重新定义,只要沿袭父类中的功能,建立自己特有比较内容即可,这就是覆盖。
public boolean equals(Object obj) {//Object obj = new BaseStudent();
if(!(obj instanceof BaseStudent)) {
return false;
}//或者抛出异常
BaseStudent bstu =(BaseStudent)obj;
return this.age ==bstu.age;//复写比较自身要比较的地方,判断和转换的动作。这样就是正常比较的是两个对象的年龄,而不是对象的本身
}
main
BaseStudent bstu = new BaseStudent(16);
BaseStudent bstu2 = new BaseStudent(16);
BaseStudent bstu3 = bstu;
System.out.println(bstu.equals(bstu2));
System.out.println(bstu==bstu2);
System.out.println(bstu.equals(bstu3));
System.out.println(bstu==bstu3);
结果:
true
false
true
true
Object类中toString方法
toString方法其实就是getClass().getName+’@’+Integer.toHexString(hashCode());
当没有重写这个方法时,打印出来的是:
com.hira.Per
com.hira.Per@2c13da15
2c13da15
当重写这个方法,则打印
com.hira.Per
Per:4
2c13da15
package com.hira;
class Per{
private int num = 4;
Per(int num){
this.num = num;
}
public String toString() {
return "Per:"+num;
}
}
public class Object_toString {
public static void main(String[] args) {
Per per = new Per(4);
Class c = per.getClass();//返回类文件
System.out.println(c.getName());//获取类文件名字
System.out.println(per.toString());//toString=getClass().getName+'@'+Integer.toHexString(hashCode());
System.out.println(Integer.toHexString(per.hashCode()));//哈希值的十六进制表达式
}
}