poly + morphism
许多 形态 即多种形态
extends继承或者implements实现,是多态性的前提。
多态意味着父类型可以引用子类型的对象
继承关系使一个子类能继承父类的特征,并且附加一些新特征。子类是它的父类的特殊化,每个子类的实例都是其父类的实例,但是反过来并不成立。例:每个圆都是几何对象,但并非每个几何对象都是圆。因此,总可以将子类的实例传给需要父类型的参数
Eg1:
//父类
public class Father {
public void method() {
System.out.println("父类方法");
}
public void methodf() {
System.out.println("父类特有方法");
}
}
//子类
public class Son extends Father{
@Override
public void method() {
System.out.println("子类方法");
}
}
/*
代码体现多态性的一句话,父类引用指向子类对象
格式:
父类名称 对象名 = new 子类名称();
*/
public class Demo1 {
public static void main(String[] args) {
Father obj = new Son();
obj.method();//输出 子类方法
obj.methodf();//输出 父类特有方法
}
}
Eg2:
public class Father {
int num=20;
public void method() {
System.out.println(num);
}
}
public class Son extends Father{
int num=10;
int age=16;
@Override
public void method() {
System.out.println(num);
}
}
/*
访问成员变量的两种方式:
1.直接通过对象名称访问成员变量:等号左边是谁,优先用谁,没有则向上找
2.间接通过成员方法访问:看该方法属于谁,优先用谁,没有则向上找
*/
public class Demo1 {
public static void main(String[] args) {
Father obj = new Son();
System.out.println(obj.num);//输出20
// System.out.println(obj.age);错误写法,不会向下找的,只会向上找,找objcect,而object里面肯定没有age
obj.method();//输出10,如果子类没有方法重写的话就输出20
}
}
Eg3:
public class Father {
public void method() {
System.out.println("父类方法");
}
public void methodf() {
System.out.println("父类特有方法");
}
}
public class Son extends Father{
public void method() {
System.out.println("子类方法");
}
public void methods() {
System.out.println("子类特有方法");
}
}
/*
在多态的代码中,成员方法的访问规则是:
看new的是谁,就优先用谁,没有则向上找
编译看左边,运行看右边
*/
public class Demo1 {
public static void main(String[] args) {
Father obj = new Son();
obj.method();
obj.methodf();
obj.methods();//编译报错,因为父类没有methods这个方法
}
}
总结:多态:基类型对象访问派生类重写的方法
对象的向上转型,其实就是多态写法
父类名称 对象名 = new 子类名称(); //Animal animal = new Cat() 创建了一只猫,把他当作动物
含义:右侧创建一个子类对象,把他当做父类来看待使用
==注==:向上转型一定是安全的,从小范围到大范围。但也有一个弊端,就是对象一旦向上转型成父类,那么就无法再调用子类原本特有的内容。(即上面Demo1里面注释编译报错的那一行,因为methods是子类特有的内容),此时就有了向下转型
类似于:
double num=100;//正确,int->double
对象的向下转型,其实就是一个【还原】的动作
格式:子类名称 对象名 = (子类名称) 父类对象;(类似于强制类型转换)
含义:将父类对象,还原成本来的子类对象
Animal animal = new Cat();//本来是猫,向上转型为动物
Cat cat = (Cat) animal;//本来是猫,已经被当作动物了,现在还原成原来的猫
注意:a.必须保证对象本来创建的时候,就是猫,才能向下转型成为猫,如果对象原来创建的时候不是猫,现在非要向下转型成为猫,就会报错。(编译不会出错,运行会出错)
那怎么知道父类引用的对象中本来是什么子类呢?
这时就需要使用instanceof
public class Demo2 {
public static void main(String[] args) {
giveMeAPet(new Dog());
}
}
public static void giveMeAPet(Animal animal){
if(animal instanceof Dog){
Dog dog = (Dog) animal;
dog.watchHourse();
}
if(animal instanceof Cat){
Cat.cat = (Cat) animal;
cat.catchHouse();
}
}
//给我了一只动物,但我只知道她是动物,所以此时要用instanceof判断他是什么,从而物尽其用。
1.循环调用基类对象,访问不同派生类方法
public static void main(String[] args) {
GradedActivity[] tests = new GradedActivity[3];
// 第一次考试采用五级计分制,考了75
tests[0] = new GradedActivity();
tests[0].setScore(75);
// 第二次考试采用二级计分制(P或者F)。总共20题,每题分值相同,考生答错5题。
// 通过的最低分数线是60分
tests[1] = new PassFailExam(20, 5, 60);
// 第三次是期末考试也采用五级计分制. 总共50题,每题分值相同,考试答错7题
tests[2] = new FinalExam(50, 7);
// 显示每次考试的分数和等级
for(int i=0; i<tests.length; i++){
showValue(tests[i]);
}
}
public static void showValue(GradedActivity exam){
System.out.println("Score: " + exam.getScore()+ "\t" +
"Grade: " + exam.getGrade());
}
2.实参是派生类,形参是基类
图形类(Geometry)是矩形和圆形等具体形状类的共同父类,他有一个getArea方法的功能是计算图形的面积,但是在图形类中并不知道具体的形状,因此无法计算,也就是说,getArea方法的方法在父类中实际上没有任何意义。
解决办法:通过abstract关键字将getArea方法修饰为抽象的,此时的方法称为抽象方法。
抽象方法是出现在基类中的一种方法,但要求在派生类中被重写。(如果派生类没有重写抽象方法,编译器就会报错,抽象方法被用来确保派生类会实现这个方法)
格式:访问修饰符 abstract 返回类型 方法名(参数列表);
例:public abstract void getArea();//关键字abstract 出现在方法头中,方法头以分号结尾
若类含有抽象方法,则类必须以abstract关键字声明为抽象类。
格式: public abstract class 类名
注意
1.不论抽象类是否含抽象方法,其都不允许实例化,即不能创建抽象类的对象,因为其描述的是抽象概念。它只能作为其他类的基类。
2.若父类是抽象类,且子类不想成为抽象类,则子类必须将父类中的所有抽象方法重写为带方法体的普通方法,否则子类仍必须是抽象类。