高亮:如何判断是否是多态,三个因素。
1、继承:(子类只有继承才能重写转型)
在多态中必须存在有继承关系的子类和父类。基于继承的实现机制主要表现在父类和继承该父类的一个或多个子类对某些方法的重写,多个子类对同一方法的重写可以表现出不同的行为。
2、重写:(可以重写父类方法和变量的多种形态)
子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
3、向上转型:(也就是定义的由来)
在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备技能调用父类的方法和子类的方法。
向上转型存在一些缺憾,那就是它必定会导致一些方法和属性的丢失,而导致我们不能够获取它们。所以父类类型的引用可以调用父类中定义的所有属性和方法,对于只存在与子类中的方法和属性它就望尘莫及了。
只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。
对于Java而言,多态的实现机制遵循一个原则:当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法。
一.概念:父类引用对象可以指向子类对象(三元素中的继承继承,重写,向上转型的详细描述)
必须有子父类的前提或者类实现接口关系,否则不能完成多态
父类引用变量调用方法时,调取的是子类重写后的方法
使用格式:
父类类型 变量名=new子类类型();向上转型
变量名.方法名();//父类对象变量名.调取方法名();
普通定义:类继承类,正常继承,不需要重写extends
public class fu{ public void show(){
system.out.in(“这是普通父类”); } } public class zi extends fu{ public void show(){
system.out.in(“这是不用重写的子类”);
} }
抽象类和普通方法:类继承类,需要重写,子类实现父类功能extends
public abstract class fu{ //抽象方法 public void show(); system.out.in(“这是抽象父类”); } public class zi extends fu{ //重写方法 public void show(){
system.out.in(“这是需要重写的子类”);
} }
普通类和接口:类实现接口,需要重写接口的方法implements
public interface fu{ //定义接口方法 public void show(); } //实现接口 public class zi implement fu{ //重写接口方法 public void show(){
system.out.in(“这是需要重写的子类”); } }
测试类: //父类引用对象指向引用对象
//父类 对象名=new子类类型(); fu f=new zi();
//父类对象名.子类方法名(); f.show(); 调取的都是子类重写方法
2.多态成员特点
编译在父类解析时,运行时点击运行控制台
在多态中:
成员变量的特点:
编译时看父类的变量,如果父类有,则编译成功,如果父类没有则编译失败,和子类无关系
编译运行时看父类的变量
编译运行都看左边
package com.oracle.demo01; //普通父类 public class Fu { int i=1; //父类方法 public void show(){ System.out.println("这是父类的方法"); } } package com.oracle.demo01; //普通子类 public class Zi extends Fu { int t=2;//这是子类变量 public void show(){ System.out.println("这是子类的方法"); } } 测试: public static void main(String[] args) { Fu fu=new Zi(); /* 编译在解析时,运行时点击运行控制台 * 在多态中: 成员变量的特点: 编译时看父类的变量,如果父类有,则编译成功,如果父类没有则编译失败,和子类无关系 运行时看父类的变量 编译运行都看左边 成员方法特点: 编译时看父类的方法,如果有则编译成功,如果没有,则编译失败 运行时看子类。 编译时看左边,运行时看右边 */ System.out.println( fu.i); //如果左边父类是抽象方法,则继承的右边也不对,但如果右边子类重写,则使用右边 fu.show();
解析当然是左边父类有才可以,毕竟继承。运行时是看右边
成员方法特点:
编译时看父类的方法,如果有则编译成功,如果没有,则编译失败
运行时看子类。
编译时看左边,运行时看右边
3.多态instanceof
判断某个对象是否属于某个数据类型
对象 instanceof 数据类型;
例如:
父类是抽象动物类 public abstract class Animal { //抽象独有方法eat public abstract void eat(); } 子类重写父类独有方法eat变成普通子类,并继承父类的独有方法 //普通子类 public class Cat extends Animal{ public void eat(){ //重写父类方法,变成普通子类 System.out.println("猫吃鱼"); } //子类独有的抓老鼠方法 public void catchMouse(){ System.out.println("抓老师"); } } 测试: 那我们判断一下,猫究竟是不是动物类或者狗类呢 ublic class Demo02 { public static void main(String[] args) { Animal an=new Cat(); //多态只能是子类和父类 //给an赋予即是猫又是动物类,但是和兄弟无关,父子打架 //判断某种对象属于某种类型 //判断狗是不是猫 //boolean b = 对象 instanceof 数据类型; boolean flag=an instanceof Cat; //boolean flag=an instanceof dog; System.out.println(flag); }
判断得出:
4.多态转换
向上转换:转换结束不能获取子类的特有功能
向下转换最好用if(对象 instanceof 数据类型;)判断一下
向下转换:转换结束不能获取父类特有功能
父类独有元素eat public abstract class Animal { public abstract void eat(); } 子类 public class Cat extends Animal{ public void eat(){ system.in.out(这是重写父类,猫吃鱼); } public void catchMouse(){ System.out.println("子类猫特有的抓老鼠"); } } 子类dog public class Dog extends Animal{ public void eat() { System.out.println("这是重写父类,狗吃肉"); } public void lookHome(){ System.out.println("这是子类特有方法,狗看家"); } } 测试: public static void main(String[] args) { /*//向上转型:多态 父类类型 变量名=new 子类类型(); //小类型(子类)->大类型(父类) 自动转 Animal an=new Cat(); //多态的弊端,不能调取子类独有的方法 //解决的办法: //向下转型:子类类型 变量名=(子类类型)父类变量 Cat cat =(Cat) an; //只能转小的,不能转大的 cat.catchMouse();*/ //即调用重写方法,又调用独有方法 Animal cat =new Cat(); Animal dog=new Dog(); cat.eat(); dog.eat(); //向下转型,用instances判断一下 //如果cat是cat,转cat if(cat instanceof Cat){ Cat c=(Cat) cat; c.catchMouse(); } //如果dog是Dog类,转dog子类类型 if(dog instanceof Dog){ Dog d=(Dog) dog; d.lookHome(); } }
方法中的参数传递和返回值都是多态的表现。
package com.oracle.demo02;
import com.oracle.demo01.Animal;
import com.oracle.demo01.Cat;
import com.oracle.demo01.Dog;
public class Demo01 {
public static void main(String[] args) {
Animal an=get();//相当于Animal an=new Cat();
//向下转型
if(an instanceof Cat){
Cat c=(Cat)an;
c.catchMouse();
an.eat();
}
//因为只有向上转型,所以无法调取子类方法
//向下转型
method(new Dog());
//调取方法的时候将子类类型传入
}
//多态避免了多次重写方法
//Object obj万物的父类
public static void method(Animal an){
an.eat();
if(an instanceof Dog){
Dog d=(Dog)an;
d.lookHome();
}
//父类对象调取方法名
}
public static Animal get(){
Cat c=new Cat();
return c;
}
}
封装:把对象的属性与方法的实现细节隐藏,仅对外提供一些公共的访问方式
继承:子类会自动拥有父类所有可继承的属性和方法。
多态:配合继承与方法重写提高了代码的复用性与扩展性;如果没有方法重写,则多态同样没有意义。