Java面向对象特征之三多态

定义解析

某一类事物的多种存在形态,
比如:人存在男人和女人两种形态
动物存在猫、狗、猪、鸭、鹅等诸多形态。

  • 多态的体现
    父类的引用指向了自己的子类对象,父类的引用可以接受自己的子类对象
    提高代码的扩展性。
  • 多态的前提
    必须是类与类之间有关系,要么继承,要么实现,
    通常还有个前提,方法存在覆盖
  • 多态的好处
    多态的出现,大大的提高了程序的扩展性。
  • 多态的弊端
    提高了扩展性,但是只能使用父类引用访问父类中的成员,
  • 多态的应用
    可以直接调用父类已经定义的方法,但是实现的是自己重写父类的方法。
    若想使用子类自己特有的方法,则可以通过强制将父类的引用,转成子类类型。比如:
    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()));//哈希值的十六进制表达式

	}

}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章