JAVA自学教程之(多态及其基本应用)。多态:
面向对象的第三个特征,定义:某类事物存在多种形态,比如,函数就具备多态性,同名的一个函数,它的参数列表不一样,它的存在性就不一样,还有同一个函数,放在父类和放在子类,它的存在性也就不一样。
对象也存在多态性。
例子:动物有猪、猫、狗等
猫这个对象对应的类是猫类
猫 x = new 猫();
同时,猫还是动物的一种,也就可以把猫成为动物
动物 y = new 猫(); 动物 z = new 狗();
动物是狗和猫集体事物中抽取出来的父类 父类引用指向了子类对象
一、概述
- //对象的多态性
- //多态的表现,父类型在指向自对象
- class animal
- {
- }
- class cat extends animal
- {
- }
- class dog extends animal
- {
- }
- public class Main
- {
- public static void main(String[] args)
- {
- //一个对象两种形态
- animal 小动物 = new cat();//小动物通过猫创建对象,动物类在指向
- /*
- * 猫这类事物具备猫的形态,又具备动物的形态。
- * 这就是事物的多态性
- * 也就是一个对象对应着不同的类型
- * 多态在代码中的体现:
- * (父类/接口)的引用指向了其子类的对象
- * */
- }
- }
//对象的多态性 //多态的表现,父类型在指向自对象 class animal { } class cat extends animal { } class dog extends animal { } public class Main { public static void main(String[] args) { //一个对象两种形态 animal 小动物 = new cat();//小动物通过猫创建对象,动物类在指向 /* * 猫这类事物具备猫的形态,又具备动物的形态。 * 这就是事物的多态性 * 也就是一个对象对应着不同的类型 * 多态在代码中的体现: * (父类/接口)的引用指向了其子类的对象 * */ } }
二、多态的优点
提供了代码的扩展性,前期定义的代码可以使用后期的内容(先有了动物,才有了猪)
以下述代码体现:
- abstract class animal
- {
- abstract void sing();//叫
- }
- class cat extends animal
- {
- void sing()
- {
- System.out.println(“喵喵叫”);
- }
- void fun()//猫的特有功能
- {
- System.out.println(“捉老鼠”);
- }
- }
- class dog extends animal
- {
- void sing()
- {
- System.out.println(“汪汪叫”);
- }
- void fun()//狗的特有功能
- {
- System.out.println(“看家”);
- }
- }
- class pig extends animal
- {
- void sing()
- {
- System.out.println(“哼哼叫”);
- }
- void fun()
- {
- System.out.println(“拱地”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- //一只猫
- cat 小猫 = new cat();
- 小猫.sing();
- //很多猫
- cat 二猫 = new cat();
- cat 三猫 = new cat();
- catqun(二猫);
- catqun(三猫);
- //….
- //多态的体现,多只动物
- dog 小狗1号 = new dog();
- cat 小猫1号 = new cat();
- catqun(小狗1号);
- catqun(小猫1号);
- catqun(new pig(););
- }
- static void catqun(animal c)//animal c = new cat()/dog()/pig();
- {
- c.sing();
- }
- }
abstract class animal { abstract void sing();//叫 } class cat extends animal { void sing() { System.out.println("喵喵叫"); } void fun()//猫的特有功能 { System.out.println("捉老鼠"); } } class dog extends animal { void sing() { System.out.println("汪汪叫"); } void fun()//狗的特有功能 { System.out.println("看家"); } } class pig extends animal { void sing() { System.out.println("哼哼叫"); } void fun() { System.out.println("拱地"); } } public class Main { public static void main(String[] args) { //一只猫 cat 小猫 = new cat(); 小猫.sing(); //很多猫 cat 二猫 = new cat(); cat 三猫 = new cat(); catqun(二猫); catqun(三猫); //.... //多态的体现,多只动物 dog 小狗1号 = new dog(); cat 小猫1号 = new cat(); catqun(小狗1号); catqun(小猫1号); catqun(new pig();); } static void catqun(animal c)//animal c = new cat()/dog()/pig(); { c.sing(); } }
三、多态的弊端和前提
1.多态的弊端:
前期定义的内容不能使用后期 子类的特有内容
- static void catqun(animal c)
- {
- c.sing();
- // c.fun();->animal里没有fun这个方法
- }
static void catqun(animal c) { c.sing(); // c.fun();->animal里没有fun这个方法 }
PS:当然可以直接使用猫static void catqun(cat c)来调用,但是我们不知道后期到底还会出现多少物种,复用性差
2.多态的前提:
⑴.必须有关系,要么继承,要么实现 ⑵.要有覆盖(父类定义了功能,子类具体实现,狗叫、狼叫,很麻烦。犬科叫,很简单) 如果不满足⑵,没有覆盖就使用多态,比如:狗看家,正常,狼看家,这不就出问题了。
保证了多态的这两个前提,就可以提高程序的扩展性
四、转型
以代码体现:
- abstract class animal
- {
- abstract void sing();//叫
- }
- class cat extends animal
- {
- void sing()
- {
- System.out.println(“喵喵叫”);
- }
- void fun()//猫的特有功能
- {
- System.out.println(“捉老鼠”);
- }
- }
- class dog extends animal
- {
- void sing()
- {
- System.out.println(“汪汪叫”);
- }
- void fun()//狗的特有功能
- {
- System.out.println(“看家”);
- }
- }
- class pig extends animal
- {
- void sing()
- {
- System.out.println(“哼哼叫”);
- }
- void fun()
- {
- System.out.println(“拱地”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {//以前指挥对象做事
- /*
- * cat 小猫 = new cat();
- 小猫.sing();
- */
- animal a = new cat();//自动类型提升,猫对象提升到了动物,类似byte x = 3;int y = x;
- a.sing();
- //PS:猫一旦提升到了动物,但是其特有功能无法访问。
- //专业说法,向上转型。目的:限制对特有功能的访问
- //如果还行用猫的特有功能
- //就可以将该对象,向下转型
- cat c = (cat)a;//将动物a,向下转型为了猫c
- c.fun();
- // 向下转型的目的:是为了使用子类中特有的方法
- /*animal d = new animal();
- * animal f = new dog();
- * cat g = (cat)f;
- cat e = (cat)d;这种类型不允许,小动物就一定是猫么*/
- }
- }
abstract class animal { abstract void sing();//叫 } class cat extends animal { void sing() { System.out.println("喵喵叫"); } void fun()//猫的特有功能 { System.out.println("捉老鼠"); } } class dog extends animal { void sing() { System.out.println("汪汪叫"); } void fun()//狗的特有功能 { System.out.println("看家"); } } class pig extends animal { void sing() { System.out.println("哼哼叫"); } void fun() { System.out.println("拱地"); } } public class Main { public static void main(String[] args) {//以前指挥对象做事 /* * cat 小猫 = new cat(); 小猫.sing(); */ animal a = new cat();//自动类型提升,猫对象提升到了动物,类似byte x = 3;int y = x; a.sing(); //PS:猫一旦提升到了动物,但是其特有功能无法访问。 //专业说法,向上转型。目的:限制对特有功能的访问 //如果还行用猫的特有功能 //就可以将该对象,向下转型 cat c = (cat)a;//将动物a,向下转型为了猫c c.fun(); // 向下转型的目的:是为了使用子类中特有的方法 /*animal d = new animal(); * animal f = new dog(); * cat g = (cat)f; cat e = (cat)d;这种类型不允许,小动物就一定是猫么*/ } }
注意:对于转型,自始至终都是子类对象在做着类型的转化:猫对象一会转型为动物,一会转型为猫 PS:转型是有目的性
练习:
- /*
- * BLF和BLF2的故事
- * BLF2是BLF的儿子
- * */
- class BLF
- {
- void 功能()
- {
- System.out.println(“用C++写程序”);
- }
- void 说英语()
- {
- System.out.println(“hello,world”);
- }
- }
- class BLF2 extends BLF
- {
- void 功能()
- {
- System.out.println(“用java写程序”);
- }
- void 说汉语()
- {
- System.out.println(“你好,世界”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- BLF x = new BLF2();//一天BLF2冒充BLF
- x.功能();//只能是用java写程序,因为BLF2只会java
- x.说英语();//可以,让BLF2像BLF一样,说英语
- //x.说汉语();//不可以,BLF2已经向上转型为BLF,禁止了BLF2特有功能的使用
- BLF2 Z = (BLF2)x;//变回来
- Z.说汉语();
- }
- }
/* * BLF和BLF2的故事 * BLF2是BLF的儿子 * */ class BLF { void 功能() { System.out.println("用C++写程序"); } void 说英语() { System.out.println("hello,world"); } } class BLF2 extends BLF { void 功能() { System.out.println("用java写程序"); } void 说汉语() { System.out.println("你好,世界"); } } public class Main { public static void main(String[] args) { BLF x = new BLF2();//一天BLF2冒充BLF x.功能();//只能是用java写程序,因为BLF2只会java x.说英语();//可以,让BLF2像BLF一样,说英语 //x.说汉语();//不可以,BLF2已经向上转型为BLF,禁止了BLF2特有功能的使用 BLF2 Z = (BLF2)x;//变回来 Z.说汉语(); } }
五、类型判断:
- instanceof 用法:
- import java.lang.reflect.Method;
- abstract class animal
- {
- abstract void sing();
- }
- class cat extends animal
- {
- void sing()
- {
- System.out.println(“喵喵叫”);
- }
- void fun()
- {
- System.out.println(“抓老鼠”);
- }
- }
- class dog extends animal
- {
- void sing()
- {
- System.out.println(“汪汪叫”);
- }
- void fun()
- {
- System.out.println(“看家”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- animal BLF = new cat();//猫向上转型为了动物
- method1(BLF);
- animal BLF2 = new dog();//猫向上转型为了动物
- method2(BLF2);
- }
- public static void method1(animal a)
- {
- a.sing();
- //a.fun();我们知道向上转型后,就无法使用子类特有功能
- //所以想要使用就必须再向下转型
- cat c = (cat)a;
- c.fun();//只是可以的,但是假如我们传一只狗呢
- }
- public static void method2(animal a)
- {
- a.sing();
- if(a instanceof cat)//instancdof用于判断a的具体类型
- //通常是在向下转型前用于健壮性的判断
- {
- cat c = (cat)a;
- c.fun();
- }
- else if(a instanceof dog)//当然一个父类,有n多子类,不可能写n多if
- {
- dog c = (dog)a;
- c.fun();
- }
- else if(a==null){System.out.println(“类型错误”);}
- }
- }
instanceof 用法: import java.lang.reflect.Method; abstract class animal { abstract void sing(); } class cat extends animal { void sing() { System.out.println("喵喵叫"); } void fun() { System.out.println("抓老鼠"); } } class dog extends animal { void sing() { System.out.println("汪汪叫"); } void fun() { System.out.println("看家"); } } public class Main { public static void main(String[] args) { animal BLF = new cat();//猫向上转型为了动物 method1(BLF); animal BLF2 = new dog();//猫向上转型为了动物 method2(BLF2); } public static void method1(animal a) { a.sing(); //a.fun();我们知道向上转型后,就无法使用子类特有功能 //所以想要使用就必须再向下转型 cat c = (cat)a; c.fun();//只是可以的,但是假如我们传一只狗呢 } public static void method2(animal a) { a.sing(); if(a instanceof cat)//instancdof用于判断a的具体类型 //通常是在向下转型前用于健壮性的判断 { cat c = (cat)a; c.fun(); } else if(a instanceof dog)//当然一个父类,有n多子类,不可能写n多if { dog c = (dog)a; c.fun(); } else if(a==null){System.out.println("类型错误");} } }
instaceof 后面可以是类,也可以是接口,且,它只适用于引用数据类型判断
六、多态的成员的特点:(面试。。。。。。)
1.成员变量 2.成员函数 3.静态函数
1.成员变量
编译时,参考引用型变量所属类中是否有调用成员变量,有,编译通过,没有,编译失败 { 为什么编译失败? 假如动物类中没有sing这个方法 animal c = new cat();//把猫提升为动物 c.sing();//猫现在是动物了,但是动物不会sing,所以编译失败 }
运行时,参考引用型变量所属类中是否有调用成员变量,并运行该所属类中的成员变量 简单说:就是编译和运行都参考等号(“=”)左边,fu f = new zi();
见代码://本问题不会在开发中出现,只会在面试时。。。
- class fu
- {
- int num = 3;
- }
- class zi extends fu
- {
- int num = 4;
- }
- public class Main
- {
- public static void main(String[] args)
- {
- /* zi z = new zi();
- * System.out.println(z.num);//普通继承是先找子类,子类有的直接覆盖
- * */
- //多态形式
- fu f = new zi();//向上转型为了父类
- System.out.println(f.num);//3
- //想要打印子类的成员变量,向下转型
- zi ff = (zi)f;
- System.out.println(ff.num);//4
- }
- }
class fu { int num = 3; } class zi extends fu { int num = 4; } public class Main { public static void main(String[] args) { /* zi z = new zi(); * System.out.println(z.num);//普通继承是先找子类,子类有的直接覆盖 * */ //多态形式 fu f = new zi();//向上转型为了父类 System.out.println(f.num);//3 //想要打印子类的成员变量,向下转型 zi ff = (zi)f; System.out.println(ff.num);//4 } }
2.成员函数(非静态函数,重点)
编译时,参考引用型变量所属类中是否有调用的函数,有,编译通过,没有,编译失败 运行时,参考对象所属类中是否有调用的函数,并运行该所属类中的函数 简单说:编译看左边,运行看右边 (非静态函数,需要用对象调用,所以运行时看右边)
- class fu
- {
- void show()
- {
- System.out.println(“父”);
- }
- }
- class zi extends fu
- {
- void show()
- {
- System.out.println(“子”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- fu f = new zi();//向上转型将子类型隐藏
- f.show();//打印“子”,如果把子类show注释,打印父
- }
- }
class fu { void show() { System.out.println("父"); } } class zi extends fu { void show() { System.out.println("子"); } } public class Main { public static void main(String[] args) { fu f = new zi();//向上转型将子类型隐藏 f.show();//打印“子”,如果把子类show注释,打印父 } }
3.静态函数(可以直接用类名调用,比较特殊)
实际上多态性应该没有静态函数,对象的多态性,而静态函数,可以直接用类名调用,创建对象来调用静态方法,这个对象其实就是垃圾
编译时,参考引用型变量所属类中是否有调用的静态方法,有,编译通过,没有,编译失败 运行时,参考引用型变量所属类中是否有调用的函数,并运行该所属类中的函数
简单说:就是编译和运行都看左边
- class fu
- {
- static void my()
- {
- System.out.println(“父sta”);
- }
- }
- class zi extends fu
- {
- static void my()
- {
- System.out.println(“子sta”);
- }
- }
- public class Main
- {
- public static void main(String[] args)
- {
- fu f = new zi();
- f.my();//打印“父sta”,如果删掉父类中的my方法,编译失败
- }
- }