【JavaSE】Java入门六(面向对象3——不懂多态?看这篇就够了)

目录

多态

1.多态实现的条件

2.类型比较运算符instanceof

3.多态使用的优缺点

4.多态使用时的注意事项


多态

多态的概念:通俗来说,就是多种形态, 具体点就是去完成某个行为(调用同一个方法),当不同的对象去完成时会产生出不同的状 态。

1.多态实现的条件

在Java中要实现多态,必须同时满足下面几个条件:

  1. 必须在继承体系下。
  2. 子类必须要对父类中的方法进行重写。
  3. 通过父类的引用调用重写的方法。

下面是一个多态实现的案例:

public class Animal {
String name;
int age;
public Animal(String name, int age){
this.name = name;
this.age = age;
}
public void eat(){
System.out.println(name + "吃饭");
}
}
public class Cat extends Animal{
public Cat(String name, int age){
super(name, age);
}
@Override
public void eat(){
System.out.println(name+"吃鱼~~~");
}
}
public class Dog extends Animal {
public Dog(String name, int age){
super(name, age);
}
@Override
public void eat(){
System.out.println(name+"吃骨头~~~");
}
}
public class TestAnimal {
// 编译器在编译代码时,并不知道要调用Dog 还是 Cat 中eat的方法
// 等程序运行起来后,形参a引用的具体对象确定后,才知道调用那个方法
// 注意:此处的形参类型必须时父类类型才可以
public static void main(String[] args) {
Animal cat = new Cat("元宝",2);
Animal dog = new Dog("小七", 1);
cat.eat();
dog.eat();
}
}

       原理解释:

       我们可以看到,在声明cat,dog对象时均是使用其父类类型声明,而在初始化时调用子类构造方法,从而使父子类产生了一种联系,因为该对象还是父类类型,所以只能调用父类方法(编译层面限制),不能调用子类特有的方法,但是因为此时的父类方法如果在子类中进行的重写,编译器在执行时会按照继承的规则,优先从子类中寻找方法,找不到再去父类找(运行层面实现,也叫做动态绑定),同时又因为不同子类可以同时重写同一父类的方法,从而实现再类型相同并且调用同一方法时出现不同的状态。

向上转型: 即上述将父类类型初始化为子类对象的过程。

                   语法格式:父类类型 对象名 = new 子类类型()

2.类型比较运算符instanceof

向下转型:通过强转的方法将对象的类型从父类类型再还原为子类类型,即向下转换。

向下转型的使用比较少,而且不安全,如果转型失败,运行时就会抛出异常。Java中为了提高安全性引入了instanceof(类型比较运算符)。

instanceof(类型比较运算符):该运算符两边必须是引用类型或者null,如果该表达式结果为true,则表示两边的类型可以安全转换。

下面是代码实例:

public class TestAnimal {
public static void main(String[] args) {
Cat cat = new Cat("元宝",2);
Dog dog = new Dog("小七", 1);
// 向上转型
Animal animal = cat;
animal.eat();
animal = dog;
animal.eat();
if(animal instanceof Cat){
cat = (Cat)animal;
cat.mew();
}
if(animal instanceof Dog){
dog = (Dog)animal;
dog.bark();
}
}
}

3.多态使用的优缺点

使用多态的好处:

  1. 能够降低代码的“圈复杂度”,使代码更简洁,避免大量使用if-else语句。
  2. 增强程序的可扩展性。

多态的缺陷:

  • 一定程度影响代码的运行效率。

4.多态使用时的注意事项

不要再构造方法中调用重写的方法

class B {
public B() {
// do nothing
func();
}
public void func() {
System.out.println("B.func()");
}
}
class D extends B {
private int num = 1;
@Override
public void func() {
System.out.println("D.func() " + num);
}
}
public class Test {
public static void main(String[] args) {
D d = new D();
}
}
// 执行结果
D.func() 0

原因如下:

       在初始化D对象的同时会调用其父类B的构造方法,而在执行其构造方法中的func()时,如果该方法被子类重写,会发生动态绑定执行子类D中的func()方法,但是此时子类对象的初始化还未完成,num可能还未被初始化,可能会出现各种隐藏的问题。

你可能感兴趣的:(java,开发语言)