一、什么是多态?
Polymorphism means “different forms.” In object-oriented programming, you have the same interface from the base class, and different forms using that interface: the different versions of the dynamically bound methods. (摘自thinking in java, Page217) polymorphism (also called dynamic bindingor late bindingor run-time binding)
例子:多态,子类-->父类 向上转型
public class PolyTest
{
public static void main(String[] args)
{
//Parent parent = new Parent();
//parent.sing();
//Child child = new Child();
//child.sing();
Parent p = new Child(); //子类即父类
p.sing();
}
}
class Parent
{
public void sing()
{
System.out.println("parent is singing");
}
}
class Child extends Parent
{
//public void sing(int i) //重载
public void sing()
{
System.out.println("child is singing");
}
}
//Note:当申明父类型的引用去指向字类型的对象时,父类调用方法原则:第一,会去
//父类中查看是否有此方法,如果有继续查找此方法是否有被子类继承,如果继承
//则继续查找此方法是否覆写,如果覆写则调用覆写的方法,如果没有被覆写则直接
//调用父类中的方法。第二,如果父类型的引用调用方法时此方法在父类中没有定义,则直接会报错,因为什么类型的引用就能调用什么类型的方法。
//(与重载无关,如果子类重载了父类的方法,调用时任然是调用父类的方法,如果有
//被覆写则调用覆写的方法)
为什么要向上转型?
a) 可以保持父类不需要修改,只需要子类继承父类,然后覆写父类的方法。
b)可以用父类型引用,接受子类型引用参数,这样父类方法不需要改变,只需要增加子类即可。(详见第二十讲最后大约10分钟)
public class PolyTest5
{
/*
public void runCar(BMW bmw) //不使用多态:子类添加,方法也必须添加。
{
bmw.run();
}
public void runCar(QQ qq) //传的都是具体的车的类型。QQ不能接受BMW
{
qq.run();
}
*/
//使用多态:无论子类增加多少类,父类方法不需要修改
public void runCar(Car car) //可接受car类型和car的子类类型
{
car.run();
}
public static void main(String[] args)
{
/*
PolyTest5 test = new PolyTest5();
BMW bmw = new BMW();
test.runCar(bmw);
QQ qq = new QQ();
test.runCar(qq);
*/
PolyTest5 test = new PolyTest5();
Car car = new BMW(); //通过多态自动向上转换实现
test.runCar(car);
QQ qq = new QQ();
test.runCar(qq); //子类引用传递给父类方法接受。向上类型转换
}
}
class Car
{
public void run()
{
System.out.println("car is running");
}
}
class BMW extends Car
{
public void run()
{
System.out.println("BMW is running");
}
}
class QQ extends Car
{
public void run()
{
System.out.println("QQ is running");
}
}
//总结:多态屏蔽掉了子类之间的差异性,用一个公共父类方法标识我的接口(方法的参数)
//不管子类怎么改变,只要增加的子类都继承了这个父类,都可以作为参数传递到我的这个
//方法里。
多态的晚绑定:
//验证晚绑定
public class PolyTest4
{
public static void main(String[] args)
{
A a = null;
if(args[0].equals("1"))
{
a = new B();
}
else if(args[0].equals("2"))
{
a = new C();
}
else if(args[0].equals("3"))
{
a = new D();
}
a.method();
}
}
class A
{
public void method()
{
System.out.println("A");
}
}
class B extends A
{
public void method()
{
System.out.println("B");
}
}
class C extends A
{
public void method()
{
System.out.println("C");
}
}
class D extends A
{
public void method()
{
System.out.println("D");
}
}
向下转型:
例子:多态,父类-->子类, 向下转型
public class PolyTest2
{
public static void main(String[] args)
{
/*
Animal animal = new Cat();
Animal animal2 = new Animal();
animal2 = animal;
animal2.sing();
*/
/*
Animal animal = new Cat();
Animal animal2 = new Animal();
animal = animal2;
animal.sing();
*/
/*
Cat cat = new Cat();
Animal animal = cat;
animal.sing();
*/
Animal animal = new Animal();
Cat cat =new Animal();
/* //向上类型转换
Cat cat = new Cat();
Animal animal = cat;
animal.sing();
//向下类型转换
Animal a = new Cat();
Cat c = (Cat)a; //向下转换的原则是:a实际指向的是什么,就可以把它转换成什么类型的引用
c.sing();
*/
}
}
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 class PolyTest3
{
public static void main(String[] args)
{
//Fruit f = new Pear();
//f.run();
//Pear p = (Pear)f;
//p.run();
//Fruit f = new Pear();
//f.grow();
Fruit f = new Pear();
Pear p = (Pear)f;
p.grow();
}
}
class Fruit
{
public void run()
{
System.out.println("fruit is running");
}
}
class Pear extends Fruit
{
public void run()
{
System.out.println("pear is running");
}
public void grow()
{
System.out.println("pear is growing");
}
}
总结:子类里面新增了自己的方法时,父类引用需要调用子类方法此时就需要向下转型。将父类引用类型转型为子类引用类型,因为什么类型的引用就能指向什么类型的对象。
1. JAVA中面向对象主要有两种提现:
(1) 方法的重载与覆写。
(2) 对象的多态性。
对象的多态性主要分为以下两种类型:
(1) 向上转型:子类对象——>父类对象。
(2) 向下转型: 父类对象——>子类对象。
对于向上转型,程序会自动完成,而对于向下转型时,必须明确地指明要转型的子类类型。
【格式 对象转型】
对象向上转型: 父类 父类对象 = 子类实例;
对象向下转型: 子类 子类对象 = (子类)父类实例;
范例:对象的向上转型
//对象的向上转型
class A{ //定义类A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子类通过extends继承父类
public void fun1(){ //覆写父类中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子类自己定义的方法
System.out.println("B --> public void fun3()");
}
};
public class PolDemo01{
public static void main(String []args){
B b = new B(); //定义子类实例化对象
A a = b; //发生了向上转型的关系,子类 --> 父类
a.fun1(); //此方法被子类覆写过
}
}
//总结:如果对象发生了向上转型关系后,所调用的方法一定是被子类覆写过的方法。
程序运行结果:
B --> public void fun1(){}
此时的对象a是无法调用B类中的fun3()方法的,因为此方法只在子类定义,而没有在父类中定义,如果想要调用子类自己的方法,则肯定要使用子类实例,所以此时可以将对象进行向下转型。
//对象的向下转型
class A{ //定义类A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子类通过extends继承父类
public void fun1(){ //覆写父类中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子类自己定义的方法
System.out.println("B --> public void fun3(){}");
}
};
public class PolDemo02{
public static void main(String []args){
A a = new B(); //发生了向上转型的关系,子类-->父类
B b = (B)a; //此时发生了向下转型关系
b.fun1(); //调用方法被覆写的方法
b.fun2(); //调用父类的方法
b.fun3(); //调用子类自己定义的方法
}
}
程序运行结果:
B --> public void fun1(){}
B --> public void fun1(){}
B --> public void fun3(){}
注意: 对象向下转型的要求
在进行对象的向下转型前,必须首先发生对象向上转型,否则将出现对象转换异常
//错误的转型
class A{ //定义类A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子类通过extends继承父类
public void fun1(){ //覆写父类中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子类自己定义的方法
System.out.println("B --> public void fun3(){}");
}
};
public class PolDemo03{
public static void main(String []args){
A a = new A(); //此时声明的是父类对象。父对象并不知道谁是子对象
B b = (B)a; //此时发生了向下转型关系
b.fun1(); //调用方法被覆写的方法
b.fun2(); //调用父类的方法
b.fun3(); //调用子类自己定义的方法
}
}
由以上程序可以发现,此时的A类对象是由A类本身进行实例化的,然后将A类的实例化对象强制转换为子对象,这样写在语法上是没有任何错误的,但是在运行时出现了异常,因为此时父类用其本身类实例化自己的对象,但它并不知道谁是自己的子类,那肯定在转换时会出现错误。只需要将连个对象建立好关系即可解决此我呢提,在声明父类对象时发生向上转型关系“A a = new B();”,这时相当于是由子类去实例化父类对象,也就是说这时父类知道有这么一个子类,所以下面在进行转换时就不会再有问题。2. instanceof 关键字:判断某个对象是否是某个类的实例。
语法形式: 引用名 instanceof 类名(接口名),返回 boolean 值。
//Instanceof 关键字作用
public class InstanceofTest{
public static void main(String args[]){
People people = new Man();
System.out.println(people instanceof People);
//因为 Man 是 People 的子类,根据继承,子类就是父类,
//因此 Man 也可以看作是 People 的实例。
}
}
class People{};
class Man extends People{};
//使用instanceof判断一个对象属于那个类的实例
class A{ //定义类A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子类通过extends继承父类
public void fun1(){ //覆写父类中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子类自己定义的方法
System.out.println("B --> public void fun3(){}");
}
};
public class InstanceofDemo01{
public static void main(String []args){
A a1 = new B(); //通过向上转型实例化A类对象
System.out.println("A a1 = new B():" + (a1 instanceof A));
System.out.println("A a1 = new B():" + (a1 instanceof B));
A a2 = new A(); //通过A类的构造实例化本类对象
System.out.println("A a2 = new A():" + (a2 instanceof A));
System.out.println("A a2 = new A():" + (a2 instanceof B));
}
}
//通过子类实例化的对象同时是子类和父类的实例
程序运行结果:
A a1 = new B():true
A a1 = new B():true
A a2 = new A():true
A a2 = new A():false
//在向下转型前进行验证
class A{ //定义类A
public void fun1(){
System.out.println("A --> public void fun1(){}");
}
public void fun2(){
this.fun1();
}
};
class B extends A{ //子类通过extends继承父类
public void fun1(){ //覆写父类中的fun1()方法
System.out.println("B --> public void fun1(){}");
}
public void fun3(){ //子类自己定义的方法
System.out.println("B --> public void fun3(){}");
}
};
class C extends A{ //子类通过extends继承父类
public void fun1(){ //覆写父类中的fun1()方法
System.out.println("C --> public void fun1(){}");
}
public void fun5(){ //子类自己定义的方法
System.out.println("C --> public void fun3(){}");
}
};
public class InstanceofDemo02{
public static void main(String []args){
fun(new B()); //传递B类实例,产生向上转型
fun(new C()); //传递C类实例,产生向上转型
}
public static void fun(A a){ //此方法可以分别调用各自子类单独定义的方法
a.fun1();
if(a instanceof B){ //判断是否是B类实例
B b = (B)a; //进行向下转型
b.fun3(); //调用子类自己定义的方法
}
if(a instanceof C){ //判断是否是C类实例
C c = (C)a; //进行向下转型
c.fun5(); //调用子类自己定义的方法
}
}
}
程序运行结果:
B --> public void fun1(){}
B --> public void fun3(){}
C --> public void fun1(){}
C --> public void fun3(){}