java 基础---多态
多态
一,多态概述 :
对象的多种表现形态,因为父类的方法在子类中被重写,子类和父类的方法名称相同,只不过完成的功能不一样,所有覆盖也具有多态性。函数的多态性就是复写(两个方法一模一样)和重载(两个以上参数列表不同的同名方法)
1,多态在代码中的体现就是:父类的引用指向(接收了)了自己子类的对象或者接口类型的引用可以指向实现该类接口的类的实例这种表现形式。实质就是类型能自动提升。但向下转型必须要强制转换。至始至终都是子类的对象在做着类型的转换。
2,多态有什么好处?增强了程序的扩展性
3,弊端:只能使用父类的引用去调用共性的内容,不可以调用子类特有的内容。
4,多态的前提; (1),类和类之间必须要有关系。(继承和实现)
(2),还要有方法的覆盖。属性是没有覆盖特性的。
5,但使用多态方式调用方法时,JVm首先检查父类中是否有该方法,如果没有则编译失败,如果有再去调用子类的该方法(这就是覆盖)。
二,多态的代码示例
猫有猫的特性,又有动物的共性。
猫和狗有共性,抽取就出现了动物。
//因为动物的eat();方法各不相同,所以用抽象类来定义,以对子类eat();方法的规范。子类必须覆盖抽象类的抽象方法。
abstract class Animal
{
abstract void eat();
}
//类是对一类事物属性和行为的描述。下面描述的是猫的属性和方法。
class Cat extends Animal
{
public void eat()//覆盖前提是类有继承关系,覆盖的条件是子类方法与父类方法的返回类型一样,方法名一样,参数列表一样。并且子类的访问权限必须大于等于父类的访问权限,也就是父类是public时,子类也必须是public。
{
System.out.println(“鱼”);
}
public void catchMouse()
{
System.out.println(“抓老鼠”);
}
}
class Dog extends Animal
{
public void eat()//
{
System.out.println(“骨头”);
}
public void lookHome()
{
System.out.println(“看门”);
}
}
Class DuoTaiDemo
{
Public static void main(String【】 args)
{
//现在要用猫和狗,调用猫狗的共性功能eat,可以定义一个函数来调用该功能,只要把狗或者猫传进去就可以。
/**Cat c= new Cat();
c.eat();
Dog d = new Dog();
d.eat();*/
Animal a = new Cat();
Show(a);
//狗同理。
}
Public static void show(Animal a) //相当于Animal a = new Cat ();
{ a.eat();}//只要具体类型的动物类型传入并且定义了共性的方法就可以覆盖共性的方法。
}
三,多态中成员的变化(子父类中成员的特点)
1, 成员变量的特点:
编译时,看的是引用变量所属类中是否有要调用的成员,
运行时,执行的也是引用变量所属的类中的成员,
一句话:多态中成员变量都看左边。
2, 成员函数的特点;
编译时,看的是引用变量所属类是否有要调用的方法,
运行时,执行的是对象所属类中该同名方法的
3, 多态中成员的变化总结:
a)成员变量和static方法:编译和运行看左边
b)非静态成员;编译看左边,执行看右边。
abstract class Animal
{
int num = 4;
abstract void eat();
public void sleep()
{
System.out.println(“sleep”);
}
public static void method()
{
System.out.println(“animal nethod”);
}
}
class Cat extends Animal
{
int num = 5;
public void eat()
{
System.out.println(“鱼”);
}
public void catchMouse()
{
System.out.println(“抓老鼠”);
}
public void sleep()
{
SyStem.out.println(“半咪咪”);
}
public static void method()
{
System.out.println(“animal nethod”);
}
}
class Dog extends Animal
{
public void eat()//
{
System.out.println(“骨头”);
}
public void lookHome()
{
System.out.println(“看门”);
}
}
Class DuoTaiDemo
{
Public static void main(String[] args)
{
。
Cat c= new Cat();
c.num;/**结果是5
Animal d = new Cat(); //Cat类型提升为Animal类型,编译的时候JVM看的是变量d 所属类型中的num。
d.num; /**结果是4
d.cathMouse(); /**结果是编译不成功。编译的时候JVM看的是变量d 所属类型中的catchMouse();方法,
没有该方法就编译失败,如果有就编译成功。执行时还要看子类中是否有覆盖的可能。
d.sleep(); /**结果是执行的是子类Cat的方法。
掉用子类和父类中的静态方法。
d,method(); //结果是动物的method方法。因为静态方法可以被对象调用,也可以被类名调用。
静态下,对象调用静态方法走的也是方法区,实质还是类所属的静态方法。
当子父类中出现同名静态方法时,无论是否多态,只看引用变量所属类型。
静态方法不存在覆盖,因为所属于类。
}
函数的静态绑定动态绑定机制:
在多态中,编译时,类型变量动态绑定在所属类型上了,运行时,类型变量绑定在对象生成类型上。
子类当父类来用(类型的自动提升)
这种情况是子类复写了父类的方法的情况。如果没有复写,或者子
类特有的方法,就只能强转后再调用子类特有的方法。
List list = new ArrayList();
//在没有使用泛型的时候,这个集合中的元素不确定的Objece。
for(Object obj: list){
system.out.println(obj);
//在没有使用泛型的情况下,即使集合中存储的数据的类型都是
String .
println的时候是没有必要强转的。
原因是:编译的时候是子类当父类来用,但是在调用方法的时候,
具体是调用子类的toString方法还是父类的toString方法,
是在虚拟机中运行的时候,这个对象的具体类型。
这就是动态的绑定。在父类当子类用的情况下,父类变量指向的对
象类型在编译的时候是不能确定的 ,
但是在运行的时候,虚拟机通过变量找到调用的对象,并判断对象
的类型,再确定调什么方法,
}
对象类型的强制转换;(父类当子类来用)
1,没有继承或者实现关系的类实行强转就会出现类型强转异常。所以强转之前就要做 instanceof 的判断处理。
2,如果要实现强转,父类是接口,就要实现,父类是类就要继承。
3,兄弟类之间是不能实现强转的,除非再次实现相同的接口
4, 多态练习
学员:学习方法。
基本班
就业班
//提取共性定义抽象类,提供继承的前提,实现多态。
abstract class Student
{
abstract void student();
}
//定义基础班
class BaseStudent extended Student
{
public void study()
{
System.out.println(“bset Study”);
}
}
//定义就业班
class DevStudent extended Student
{
public void study()
{
System.out.println(“dev Study”);
}
}
//定义一个学习的类,便于生成对象,不同的类型调用学习的方法。
class Study
{
public void studentStudy(Student stu)//定义学生的引用,就能接收基础班,就业班以及以后的黑马班。
{
stu.study();//当要求学生对象调用学习方法十次时,就可以在这里定义十次方法。
}
}
public class DuoTai
{
public static void main(String [] args)
{
BaseStudent stu = new BaseStudent();
stu.study();
AdvStudent stu1 = new AdvStudent();
stu.study();
这是最原始的做法,生成一个基础班对象,调用一次学习方法。再生成一个就业班对象,调用一次学习方法。当学生的个数越来越多,调用的方法就越多。这样代码的重复度高。
为了提高复用性,定义一个能接收学员(不管是基础还是就业班,只要有学员的共性)的学习的类,生成学习的对象,
Study s = new Study();
s.studentStudy (stu);
s.studentStudy (stu1);
}
}
还要加几个示例