不会吧不会吧,还有人不懂java多态? 今天我温习面试题,多态是个什么只记得可以用父类接收充当返回值了,哎,这里回顾记录一下

多态的概念

面向对象三大特性之一,用来解决程序的扩展性问题,可以简单理解为一种事物的多种表现形式,比如:

一个学生既是学生,同时也是个人类,那我们聊天的时候,会说他是个人类吗?不会我们通常说他是个学生。(说人类的不觉得很奇怪吗)

大家先搞懂几个名词,接下来才会明白我说的专业术语

比如我们创建一个实体类A 、一个实体类B,以B继承A,这个时候,你创建新对象的时候有两种方式定义返回值:

1
2
A a = new A(); //基类就是A类     
B b = new A(); //基类就是B类

我们定义的参数名(上面的小a 小b)就是引用变量,好了,现在我们知道了基类是什么,引用变量是什么。

先总结出个特点:

  • 代码体现就是 父类引用变量可以指向子类
  • 达成这个效果的前提条件是,子类继承了父类,或者实现了父类
  • 调用父类的引用方法会调用子类改写后的方法(这也是我们继承了别的方法之后,重写了却调用我们写的方法而不是人家父类原本方法的原因)

多态例子

A类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.czxy.duotai;

public class A {
public A() {
c();
}


public void b() {
System.out.println("我是a的b");
}

public void c() {
System.out.println("我是a的c");
}
}

B类

1
2
3
4
5
6
7
8
9
10
11
package com.czxy.duotai;

public class B extends A {
public void b(){
System.out.println("我是b的b");
}

public void c(){
System.out.println("我是b的c");
}
}

测试类:

1
2
3
4
5
6
7
8
9
package com.czxy.duotai;

public class tttest {
public static void main(String[] args) {
B b = new B();
b.b();
b.c();
}
}

我们这个时候在测试类中运行会发生什么?猜一猜

我是b的c
我是b的b
我是b的c

这就是结果,你会不会诧异,为什么跟我想的不一样?

解释:

先执行了一个 B类中的c方法,然后B类中的b方法,然后B类中的c方法

可是我们只执行了一次b方法,一次c方法啊,这是因为,在执行程序的时候,先执行了父类A的构造方法,但是A调用的构造方法中的方法看起来是A自己的,实际上因为绑定的是引用变量指向的具体实例对象的方法,也就是内存中正在运行的那个对象的方法,而不是引用变量的类型中定义的方法,也就是说,会调用子类的方法。

所以,我们的结果第一条打印的是B类的方法的原因是 A类的构造方法调用了内存中实例对象的A的子类同名方法 b。

有人可能不清楚为什么我创建的基类是B类,实例对象也是B类,为何还会调用到A的构造函数? 这就是另一个问题了,构造函数的执行顺序,这里不讲

多态扩展

刚刚我们是自己接收自己,现在我们来看看真正的多态怎么写,我用的还是上面的类

测试类:

1
2
3
4
5
6
7
8
9
package com.czxy.duotai;

public class tttest {
public static void main(String[] args) {
A b = new B();
b.b();
b.c();
}
}

我们这个时候在测试类中运行会发生什么?你再猜一猜

我是b的c
我是b的b
我是b的c

这就是结果,你会不会诧异,为什么还是这个!

其实,多态本身是子类类型向父类类型向上转型的过程, 这是默认转型模式,隐藏了子类类型,提高了代码的扩展性,你也可以将父类强转为子类作为基类来接收:

1
B a = (B) (new A());

但是这有个缺点,将方法强转为B为基类接收,就会使我们只能调用B的方法了,而无法调用A的其他继承类的方法了,而且我们有些方法自带一些特殊方法,也无法调用了

举个例子:

新增扩展Cat类:

cat类中有自己新增的方法,我们如何调用(你继承了A是不假,但是并不是只能调用C与A都有的方法,而是实现了谁就调用谁的方法,只不过多态提供了你可以调用默认父类方法,和你用父类方法不爽还可以自己重写的选择)

我cat 只会 叫 喵喵喵喵喵

1
2
3
4
5
6
7
8
package com.czxy.duotai;

public class Cat extends A {

public void jiao(){
System.out.println("我tm超级喵喵喵喵喵喵喵喵喵喵");
}
}

新增扩展Dog类

我dog只会叫 汪汪

1
2
3
4
5
6
7
8
package com.czxy.duotai;

public class Dog extends A {

public void jiao(){
System.out.println("cnm汪汪汪汪汪");
}
}

升级父类

1
2
3
4
5
6
7
8
9
10
11
package com.czxy.duotai;

public class A {
public A() {
c();
}

public void jiao(){

}
}

疑问

程序动态调用的时候如何确定调用猫是猫叫,狗是狗叫?

可以用instanceof

测试类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package com.czxy.duotai;

public class A {
public A() {
c();
}

public void jiao(A a){
if (a instanceof Cat){
((Cat)a).jiao();
}
if (a instanceof Dog){
((Dog)a).jiao();
}
}
}

好了,这样便实现了是狗类掉狗叫,猫类掉猫叫

我们用实体类的继承关系就实现了java 接口的作用!!!!!(没错,这其实就是人家接口的工作)