多态polymorphic
引出多态的问题
由于使用传统方法带来的问题是:代码的复用性不高,而且不利于代码维护
解决方案:使用多态解决
多态基本介绍和具体体现
基本介绍:方法或对象具有多种形态。是面向对象的第三大特征,多态是建立在封装和继承基础之上的。
多态的具体体现(记住)
1.方法的多态
重写和重载就是体现多态
重载多态:通过不同的参数个数去调用sum方法,就会去调用不同方法,因为对sum方法来说,就是多种状态的体现
重写多态:根据对象不一样,我们调用的方法不一样
2.对象的多态(核心)
- 一个对象的编译类型和运行类型可以不一致
- 编译类型在定义对象时,就确定了,不能改变
- 运行类型是可以变化的
- 编译类型看定义时 = 号的左边 运行类型看 = 号的右边
Animal animal = new Dog();【animal编译类型是Animal,运行类型是Dog,就是animal是固定的不能改变】
animal = new Cat();【animal的运行类型变成了Cat,编译类型仍然是Animal不能改变,运行类型是指animal开始指向Dog对象,后来指向了Cat对象】
package JAVA面向对象中级部分.encapsulationAndInherit.polymorphic_.obectpoly;
public class Animal {
public void cry(){
System.out.println("Animal中的cay() 动物在叫。。。");
}
}
package JAVA面向对象中级部分.encapsulationAndInherit.polymorphic_.obectpoly;
public class Cat extends Animal{
public void cry(){
System.out.println("Cat 小猫喵喵叫。。。");
}
}
package JAVA面向对象中级部分.encapsulationAndInherit.polymorphic_.obectpoly;
public class Dog extends Animal{
public void cry(){
System.out.println("Dog 小狗汪汪叫。。。");
}
}
package JAVA面向对象中级部分.encapsulationAndInherit.polymorphic_.obectpoly;
public class Poly01 {
public static void main(String[] args) {
//体验对象多态特点
//animal编译类型是Animal,运行类型是Dog
Animal animal = new Dog();//animal指向Dog对象
//因为运行时,执行到这行时,animal的运行类型是Dog,所以cry就是Dog的cry中的方法
animal.cry();//小狗汪汪叫
//animal编译类型是Animal运行类型就是Cat
animal = new Cat();//animal指向Cat对象
animal.cry();//小猫喵喵叫
}
}
多态注意事项和细节(记住)
多态的前提是:两个对象(类)存在继承关系
多态的向上转型
本质:父类的引用指向子类的对象
语法:父类类型 引用名 = new 子类类型();
特点(记住)
- 编译类型看左边,运行类型看右边
- 可以调用父类中的所有成员(成员方法和属性并且需要遵守访问权限),不能调用子类中特有成员
- 最终运行效果看子类的具体实现
注意是:编译和运行是两个不同的阶段,两者所遵守的规则有差异,编译阶段父类的引用不能调用子类特有的方法和属性,运行阶段调用却从子类向上查找调用(遵守就近原则)(记住规则)
多态的向下转型
语法:子类类型 引用名 = (子类类型)父类引用;
特点(记住)
- 只能强转父类的引用,不能强转父类的对象
- 要求父类的引用必须指向的是当前目标类型的对象(意思就是父类的引用当前是指向Cat,强转时也必须只能强转成Cat,不能强转别的类否则报错)
- 当向下转型后,可以调用子类类型中所有的成员
package JAVA面向对象中级部分.encapsulationAndInherit.detail_;
public class PolyDetail {
public static void main(String[] args) {
//向上转型:父类的引用指向了子类的对象
//语法:父类类型 引用名 = new 子类类型();
Animal animal = new Cat();
//Object object = new Cat();//可以Object也是Cat的父类
/**
* 向上转型调用方法的规则如下:
* 1.可以调用父类中的所有成员(需要遵守访问权限)
* 2.但是不能调用子类的特有的成员:因为在编译阶段,能调用哪些成员(成员方法和属性),是由编译类型来决定的
* 3.最终运行效果看子类(运行类型)的具体实现,即调用方法时,按照从子类(运行类型)开始查找方法,规则就是就近规则
*/
animal.eat();//输出猫吃鱼是因为Cat中有此方法
animal.run();
animal.show();
animal.sleep();
//多态的向下转型
//1.语法:子类类型 引用名 = (子类类型)父类引用;
//问一个问题?cat的编译类型,运行类型是cat
Cat cat = (Cat) animal;
cat.catchMouse();//猫抓老鼠
//2.要求父类的引用必须指向的是当前目标类型的对象
}
}
package JAVA面向对象中级部分.encapsulationAndInherit.detail_;
public class Animal {
String name = "动物";
int age = 10;
public void sleep(){
System.out.println("动物睡觉");
}
public void run(){
System.out.println("动物在跑");
}
public void eat(){
System.out.println("动物在吃");
}
public void show(){
System.out.println("hello 您好");
}
}
package JAVA面向对象中级部分.encapsulationAndInherit.detail_;
public class Cat extends Animal{
public void eat(){
System.out.println("猫吃鱼");
}
public void catchMouse(){
System.out.println("猫抓老鼠");
}
}
细节3:在多态中属性没有重写之说,属性的值看编译类型(也就是编译看左边运行看左边),静态方法和属性一样(编译看左边运行看左边)(记住)
细节4:instanceOf
比较操作符,用于判断对象的运行类型是否为XX类型或XX类型的子类型(记住)
package JAVA面向对象中级部分.encapsulationAndInherit.detail_;
public class VerPoly {
public static void main(String[] args) {
//**细节3:在多态中属性没有重写之说,属性的值看编译类型(也就是编译看左边运行看右边)
// ,静态方法和属性一样(编译看左边运行看右边)**(记住)
Father father = new Father();
//属性的值编译看左边,运行看左边和静态方法一样
System.out.println(father.n1);//100
Son son = new Son();
//注意说明:属性没有重写之说
System.out.println(son.n1);//200
//**细节4:``instanceOf``比较操作符,
// 用于判断对象的运行类型是否为XX类型或XX类型的子类型**(记住)
System.out.println(father instanceof Object);//true
System.out.println(father instanceof Father);
System.out.println(son instanceof Father);
//说明是XX类型本身或者是XX类型的子类类型就可以,否则编译通不过,报错
}
}
class Father {
int n1 = 100;
}
class Son extends Father {
int n1 = 200;
}
练习:必须完成下面两题
案例2:
class Base{
int count = 10;
public void display(){
System.out.println(this.count)
}
}
class Sub extends Base{
int count = 20;
public void display(){
System.out.println(this.count)
}
}
Public class PolyExercise02{
public static void main(String[] args){
Sub s = new Sub();
System.out.println(s.count);//20
s.display();//20
System.out.println(b==s);//true,两者都是指向SOn对象
System.out.println(b.count);//10
b.display();//20
}
}
一定要理解编译和运行两个阶段,编译是看左边,并且不能使用子类特有的方法,运行看右边,从子类开始向上查找(就近原则)