多态
Java学习之多态
狗 x = new 狗
动物 y = new 狗//这里就是多态性的体现
狗是需要继承动物的才可以这样
多态性:在程序中的体现就是,父类或者接口的引用只想自己的子类对象。
多态的概念
多态==晚绑定。
不要把函数重载理解为多态。
因为多态是一种运行期的行为,不是编译期的行为。
多态:父类型的引用可以指向子类型的对象。
比如 Parent p = new Child();
当使用多态方式调用方法时,首先检查父类中是否有该方法,如果没有,则编译错误;
如果有,再去调用子类的该同名方法。
(注意此处,静态static方法属于特殊情况,静态方法只能继承,不能重写Override,如果子类中定义了同名同形式的静态方法,它对父类方法只起到隐藏的作用。调用的时候用谁的引用,则调用谁的版本。)
(参考学习链接:http://docs.oracle.com/javase/tutorial/java/IandI/override.html)
如果想要调用子类中有而父类中没有的方法,需要进行强制类型转换,如上面的例子中,将p转换为子类Child类型的引用。
因为当用父类的引用指向子类的对象,用父类引用调用方法时,找不到父类中不存在的方法。这时候需要进行向下的类型转换,将父类引用转换为子类引用。
结合实例说明
下面举个例子(有问题的代码已注释):
主要讲讲两种类型转换和两种编译时候的错误。
多态示例代码 public class PolyTest { public static void main(String[] args) { //向上类型转换 Cat cat = new Cat(); Animal animal = cat; animal.sing(); //向下类型转换 Animal a = new Cat(); Cat c = (Cat)a; c.sing(); c.eat(); //编译错误 //用父类引用调用父类不存在的方法 //Animal a1 = new Cat(); //a1.eat(); //编译错误 //向下类型转换时只能转向指向的对象类型 //Animal a2 = new Cat(); //Cat c2 = (Dog)a2; } } class Animal { public void sing() { System.out.println("Animal is singing!"); } } class Dog extends Animal { public void sing() { System.out.println("Dog is singing!"); } } class Cat extends Animal { public void sing() { System.out.println("Cat is singing!"); } public void eat() { System.out.println("Cat is eating!"); } }
public class PolyTest { public static void main(String[] args) { //向上类型转换 Cat cat = new Cat(); Animal animal = cat; animal.sing(); //向下类型转换 Animal a = new Cat(); Cat c = (Cat)a; c.sing(); c.eat(); //编译错误 //用父类引用调用父类不存在的方法 //Animal a1 = new Cat(); //a1.eat(); //编译错误 //向下类型转换时只能转向指向的对象类型 //Animal a2 = new Cat(); //Cat c2 = (Dog)a2; } } class Animal { public void sing() { System.out.println("Animal is singing!"); } } class Dog extends Animal { public void sing() { System.out.println("Dog is singing!"); } } class Cat extends Animal { public void sing() { System.out.println("Cat is singing!"); } public void eat() { System.out.println("Cat is eating!"); } }
例子的执行结果:
这段代码:
Cat类中定义了eat()方法,但是Animal类中没有这个方法,a1引用是Animal类的,所以找不到,编译时出错:
两种类型的类型转换
(1)向上类型转换(Upcast):将子类型转换为父类型。
对于向上的类型转换,不需要显示指定,即不需要加上前面的小括号和父类类型名。
(2)向下类型转换(Downcast):将父类型转换为子类型。
对于向下的类型转换,必须要显式指定,即必须要使用强制类型转换。
并且父类型的引用必须指向子类的对象,即指向谁才能转换成谁。
不然也会编译出错:
因为父类引用指向的是Cat类的对象,而要强制转换成Dog类,这是不可能的。
多态:可以理解为事物存在的多种体现形态,比如说人:男人、女人;动物:猫,狗。
多态:
多种形态。
这里要重点讲的就是对象的多态性。
狗 x = new 狗();
动物 y = new 狗();//对象的多态性,狗对象即是狗类型,又是动物形态。
必须要明确一点:狗是动物中的一种。
意味着狗是需要继承动物的才可以这样指向。
多态性:在程序中的体现。
就是:父类或者接口的引用指向自己的子类对象。
好处:提高了代码的扩展性。
弊端:前期建立父类的引用虽然可以接收后期所有该类的子类对象。
但是只能使用父类中的功能,不能使用子类中的特有功能,
因为前期的程序无法知道后期的子类的特有内容的。
但是前期的程序可以使用子类覆盖了父类的方法的内容。
前提:
1,必须存在着继承关系。
2,通常要有覆盖操作。
<span style="font-size:18px;">*/ abstract class Animal { abstract void eat(); } class Dog extends Animal { public void eat() { System.out.println("啃骨头"); } public void lookHome() { System.out.println("看家"); } } class Cat extends Animal { public void eat() { System.out.println("吃鱼"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Pig extends Animal { public void eat() { System.out.println("饲料"); } public void gongDi() { System.out.println("拱地"); } } class DuoTaiDemo { public static void main(String[] args) { </span>
Animal a = new Dog();//子类对象的类型提升。向上转型[类似于基本类型的类型提升,只不过这里是引用类型数据,]。
a.eat();
Dog d = (Dog)a;//向下转型。 转成子类型。 d.eat();
d.lookHome();
可以看出,向上转型为自动提升,向下转型必须强制
public static void function(Ainimal a){ a.eat(); if (a instanceof Cat){ { Cat c=(Cat)a; c.catchMouse(); } else if (a instanceof Dog) { Dog d=(Dog)a; d.lookHome(); } }//<span style="color:#ff0000;">the above method showed one point should be careful in using the 多态</span>
多态中函数成员的特点:
在编译时期:参阅引用类型变量所属的类中是否有调用的方法,如果有,编译通过,如果没有编译失败。
在运行时期:参阅对象所属的类中是否有调用的方法:------编译看引用,运行得看对象
总的来说在多态中,成员函数在多态调用时,编译看左边,运行看右边。
成员变量的特点,无论编译和运行,都参考左边(引用型变量所属的类)。
多态的出现在成员调用上的特点:
1,成员变量
编译时期:参考的是引用型变量所属的类中是否有调用的成员变量,如果有,编译通过,如果没有编译失败。
运行时期:调用的也是引用型变量所属类中的成员变量。
简单说:编译和运行都看等号的左边。
其实这种情况不多见。
2,成员函数。
编译时期;参考的是引用型变量所属的类中是否有调用的方法。有,编译通过,没有编译失败。
运行时期:参考的是对象所属的类中是否有调用的方法,如果有运行子类自己的方法,如果没有就父类的方法。
简答说:编译时期看左边,运行时期看右边。
因为函数有一个覆盖的特性。
非静态方法需要和当期运行的对象进行动态绑定,哪个对象调用了这个方法,这个方法就所属于哪个对象。
就会运行哪个对象的中的方法。
3,静态函数。
编译时期:参考的是引用型变量所属的类中是否该方法。
运行时期:参考的也是引用型变量所属的类中的方法。
简单说:编译和运行都看左边。所谓的看左看右是指根据左边或右边的属性或者方法来完成调用
因为静态方法是不所属于对象的,是所属于类的,
它会类加载的时候,静态的绑定到所属的类上。
*/ class Fu { int num = 4; public void show()// 因为覆盖的特性, { System.out.println("fu show run"); } public static void staticMethod() { System.out.println("fu static method run"); } } class Zi extends Fu { int num = 5; public void show()// 因为覆盖的特性, { System.out.println("zi show run"); } public static void staticMethod() { System.out.println("zi static method run"); } } class DuoTaiTest { public static void main(String[] args) { Fu f = new Zi(); // System.out.println(f.num);//4 // f.show();//zi show run 因为函数覆盖的特性,运行的子类对象在调用show方法, //覆盖了父类中的方法,运行的是子类自己的show方法。 f.staticMethod(); Zi z = new Zi(); z.staticMethod(); Fu.staticMethod(); Zi.staticMethod(); } }
关于instanceof的用法
package myPackage; /** * instanceof运算符用法 * 运算符是双目运算符,左面的操作元是一个对象,右面是一个类.当 * 左面的对象是右面的类创建的对象时,该运算符运算的结果是true,否则是false * * 说明:(1)一个类的实例包括本身的实例,以及所有直接或间接子类的实例 * (2)instanceof左边操作元显式声明的类型与右边操作元必须是同种类或右边是左边父类的继承关系, * (3)不同的继承关系下,编译出错 */ class Person { } class Student extends Person { } class Postgraduate extends Student { } class Animal { } public class Ex_instanceOf { public static void main(String[] args) { instanceofTest(new Student()); } /** * 这个程序的输出结果是:p是类Student的实例 * * Person类所在的继承树是:Object<--Person<--Student<--Postgraduate。 * * 这个例子中还加入一个Animal类,它不是在Person类的继承树中,所以不能作为instanceof的右操作数。 * * @param p */ public static void instanceofTest(Person p) { // p 和 Animal类型不一样,彼此之间没有继承关系,编译会出错 // 提示错误:Incompatible conditional operand types Person and Animal // if(p instanceof Animal){ // System.out.println("p是类Animal的实例"); // } //下面代码的除了第一行都会输出 if (p instanceof Postgraduate) System.out.println("p是类Postgraduate的实例"); if (p instanceof Person) System.out.println("p是类Person的实例"); if (p instanceof Student) System.out.println("p是类Student的实例"); if (p instanceof Object) System.out.println("p是类Object的实例"); } }
interface A{ } class B implements A{ } class C extends B{ } public class Iinstanceof { public static void main(String[] args) { A ab=new B(); A ac=new C(); B bc=new C(); B bb=new B(); C cc=new C(); //对象实现一个接口,用这个对象和这个接口进行instanceof判断,都为true。 System.out.println("ab instanceof A="+(ab instanceof A)); System.out.println("ac instanceof A="+(ac instanceof A)); System.out.println("bc instanceof A="+(bc instanceof A)); System.out.println("bb instanceof A="+(bb instanceof A)); System.out.println("cc instanceof A="+(cc instanceof A)); //对象和父类进行instanceof判断,都为true System.out.println("ab instanceof B="+(ab instanceof B)); System.out.println("ac instanceof B="+(ac instanceof B)); System.out.println("bc instanceof B="+(bc instanceof B)); System.out.println("bb instanceof B="+(bb instanceof B)); System.out.println("cc instanceof B="+(cc instanceof B)); //对象和他的子类进行instanceof判断为false System.out.println("ab instanceof C="+(ab instanceof C)); System.out.println("ac instanceof C="+(ac instanceof C)); System.out.println("bc instanceof C="+(bc instanceof C)); System.out.println("bb instanceof C="+(bb instanceof C)); System.out.println("cc instanceof C="+(cc instanceof C)); } }
//程序输出: ab instanceof A=true ac instanceof A=true bc instanceof A=true bb instanceof A=true cc instanceof A=true ab instanceof B=true ac instanceof B=true bc instanceof B=true bb instanceof B=true cc instanceof B=true ab instanceof C=false ac instanceof C=true bc instanceof C=true bb instanceof C=false cc instanceof C=true
Java编程:多态和向上转型
2{
3 public static void main(String [] args)
4 {
5 A a = new B(); //父类A引用子类B的实例
6 if(a instanceof B)
7 System.out.println("对象 a 是类 B 的实例");
8 B b = (B)a; //向下转型
9 if(b instanceof B)
10 System.out.println("对象 b 是类 B 的实例");
11 C c = new D(); //父类C引用子类D的实例
12 if(c instanceof D)
13 System.out.println("对象 c 是类 D 的实例");
14 D d = (D)c; //向下转型
15 if(d instanceof D)
16 System.out.println("对象 d 是类 D 的实例");
17
18 }
19}
20
21class A
22{
23}
24
25class B extends A{}
26class C extends B{}
27class D extends C{}
向上转型:
2{
3 abstract void method();
4}
2{
3 public void method()
4 {
5 System.out.println("子类B的方法调用");
6 }
7}
2{
3 public static void main(String [] args)
4 {
5 B b = new B();
6 A a = (A)b; //向上转型
7 a.method();
8 }
9}
第7行,引用变量a与实例方法method()动态绑定,并实现(覆盖)该方法。
package day6; class Fu { int num = 5; void method1() { System.out.println("fu method_1"); } void method2() { System.out.println("fu method_2"); } static void method4() { System.out.println("fu method_4"); } } class Zi extends Fu { int num = 8; void method1() { System.out.println("zi method_1"); } void method3() { System.out.println("zi method_3"); } static void method4() { System.out.println("zi method_4"); } } public class DuoTai3 { public static void main(String []args) { Fu f = new Zi(); f.method1(); //zi method_1 f.method2();//fu method_1 // f.method3(); //error f.method4();//fu method_4 System.out.println(f.num);//5 Zi z = new Zi(); System.out.println(z.num);//8 z.method1();//zi method_1 z.method2();//fu method_2 z.method3();//zi method_3 z.method4();//zi method_4 /* 在多态中成员函数的特点:(针对非静态成员函数) 在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有,编译失败 在运行时期,参阅对象所属的类中是否有调用的方法 简单总结就是:成员函数在多态中调用,编译看左边,运行看右边 在多态中,成员变量的特点: 无论编译还是运行,都参考左边(引用型变量所属的类) 在多态中,静态成员函数的特点: 无论编译还是运行,都参考左边 */ } }
在多态中成员函数的特点:
在编译时期:参阅引用型变量所属的类中是否有调用的方法。如果有,编译通过,如果没有,编译失败。
在运行期间;参阅对象所属类中是否有调用的方法。
简单总结就是:成员函数在多态调用时,编译看左边,运行看右边。
在多态中,成员变量的特点:
无论编译和运行,都参考左边(引用类型变量所属的类)。
在多态中,静态成员函数的特点;
无论编译和运行,都参考左边(引用类型变量所属的类)。
Fu f= new zi();
f(左边).method(右边)4();
Java多态性理解
Java中多态性的实现
什么是多态
- 面向对象的三大特性:封装、继承、多态。从一定角度来看,封装和继承几乎都是为多态而准备的。这是我们最后一个概念,也是最重要的知识点。
- 多态的定义:指允许不同类的对象对同一消息做出响应。即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)
- 实现多态的技术称为:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
- 多态的作用:消除类型之间的耦合关系。
- 现实中,关于多态的例子不胜枚举。比方说按下 F1 键这个动作,如果当前在 Flash 界面下弹出的就是 AS 3 的帮助文档;如果当前在 Word 下弹出的就是 Word 帮助;在 Windows 下弹出的就是 Windows 帮助和支持。同一个事件发生在不同的对象上会产生不同的结果。
下面是多态存在的三个必要条件,要求大家做梦时都能背出来!
多态存在的三个必要条件
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
多态的好处:
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3 所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。
Java中多态的实现方式:接口实现,继承父类进行方法重写,同一个类中进行方法重载。
一个小题目:
(一)相关类
class A ...{ public String show(D obj)...{ return ("A and D"); } public String show(A obj)...{ return ("A and A"); } } class B extends A...{ public String show(B obj)...{ return ("B and B"); } public String show(A obj)...{ return ("B and A"); } } class C extends B...{} class D extends B...{}
(二)问题:以下输出结果是什么?
A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println(a1.show(b)); ① System.out.println(a1.show(c)); ② System.out.println(a1.show(d)); ③ System.out.println(a2.show(b)); ④ System.out.println(a2.show(c)); ⑤ System.out.println(a2.show(d)); ⑥ System.out.println(b.show(b)); ⑦ System.out.println(b.show(c)); ⑧ System.out.println(b.show(d)); ⑨
(三)答案
① A and A ② A and A ③ A and D ④ B and A ⑤ B and A ⑥ A and D ⑦ B and B ⑧ B and B ⑨ A and D