Java的3大特性是封装,继承和多态,今天就介绍下多态.
java引用变量有两个类型:一个是编译时类型,一个是运行时类型.编译是类型由申明该变量时使用的类型决定,运行时类型由实际赋值给该变量的对象决定.如果编译时类型和运行时类型不一致,就可能出现所谓的多态.
多态用一句话概括就是:父类的引用指向了子类的对象.
多态可以使代码具有扩展性
下面是不使用多态的一个例子
public class AnimalTest {
public static void main(String[] args) {
Dog dog = new Dog();
dogPlay(dog);
Cat cat = new Cat();
catPlay(cat);
}
private static void dogPlay(Dog dog) {
dog.play();
}
private static void catPlay(Cat cat) {
cat.play();
}
}
public class Cat extends Animal {
@Override
public void play() {
System.out.println("Cat play方法执行了");
}
}
public class Dog extends Animal {
@Override
public void play() {
System.out.println("Dog play方法执行了");
}
}
public class Animal {
public void play() {
System.out.println("animal play方法执行了");
}
}
该例子没有使用多态,如果后期增加了其他的新类,需要继续往Test类里面增加类似XXXPlay()的方法,不便于扩展.
如果使用多态,则完全不一样,只需改动很少的代码即可实现.
public class AnimalTest2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Dog dog = new Dog();
animalPlay(dog);
Cat cat = new Cat();
animalPlay(cat);
}
private static void animalPlay(Animal animal) {
animal.play();
}
}
后续有新类产生时,只需要在main方法中改动代码即可.
多态应该注意的知识点:
1.多态时,如果子类复写了父类的方法,在调用时执行的就是子类的方法.
2.如果子类没有复写父类的方法,在调用时执行的就是父类的方法.
3.多态时如果调用子类特有的方法就会报错.
public class AnimalTest {
public static void main(String[] args) {
Dog dog = new Dog();
dogPlay(dog);
Cat cat = new Cat();
catPlay(cat);
}
private static void dogPlay(Dog dog) {
dog.play();
}
private static void catPlay(Cat cat) {
cat.play();
}
}
public class Cat extends Animal {
@Override
public void play() {
System.out.println("Cat play方法执行了");
}
}
上面例子的执行结果如下:
Cat play方法执行了
animal sing方法执行了
4.构造函数,这一点需要特别注意
多态中,虽然只是创建了子类的对象,但是由于子类可以访问父类的一些方法,成员变量,所以默认会执行父类对应的构造器.
并且执行顺序是:父类构造器方法→子类构造器方法
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Cat();
}
}
public class Animal {
public Animal() {
System.out.println("Animal 构造器方法执行了");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("Cat 构造器方法执行了");
}
}
上面的例子执行的结果如下:
Animal 构造器方法执行了
Cat 构造器方法执行了
但是有一点需要注意,如果构造器中调用了被子类复写的方法时,可能会出现隐藏性的错误.
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Cat();
}
}
public class Animal {
public Animal() {
System.out.println("Animal 构造器方法执行了");
play();
}
public void play() {
System.out.println("animal play方法执行了");
}
}
public class Cat extends Animal {
public Cat() {
System.out.println("Cat 构造器方法执行了");
play();
}
@Override
public void play() {
System.out.println("Cat play方法执行了");
}
}
上面的例子执行的结果如下:
Animal 构造器方法执行了
Cat play方法执行了
Cat 构造器方法执行了
Cat play方法执行了
从结果中发现,Animal的构造器方法调用了play方法,而play方法恰好被子类复写了,所以执行的时候父类和子类中都执行的是子类中的paly方法.这样就可能出现意向不到的问题,所以不建议这种用法.
5.多态中成员变量
先看个例子
public class AnimalTest {
public static void main(String[] args) {
Animal animal = new Cat();
System.out.println("num:"+animal.num);
}
}
public class Animal {
public int num = 30;
}
public class Cat extends Animal {
public int num = 20;
}
运行结果如下:
num:30
由运行结果可以发现,打印是父类的成员变量的值.
所以如果子类和父类的成员变量是分别处理时,最好二者的名字不要取相同的名字,要加以区分.