多态
多态就是指程序中定义的引用变量所指向的具体类型和通过该引用变量发出的方法调用在编程时并不确定,而是在程序运行期间才确定,即一个引用变量倒底会指向哪个类的实例对象,该引用变量发出的方法调用到底是哪个类中实现的方法,必须在由程序运行期间才能决定。因为在程序运行时才确定具体的类,这样,不用修改源程序代码,就可以让引用变量绑定到各种不同的类实现上,从而导致该引用调用的具体方法随之改变,即不修改程序代码就可以改变程序运行时所绑定的具体代码,让程序可以选择多个运行状态,这就是多态性。
Animal a = new Cat();
这里定义了一个Animal类型的a,它指向Cat对象实例。由于Cat是继承与Animal,所以Cat可以自动向上转型为Animal,所以a是可以指向Cat实例对象的。这样做存在一个非常大的好处,在继承中我们知道子类是父类的扩展,它可以提供比父类更加强大的功能,如果我们定义了一个指向子类的父类引用类型,那么它除了能够引用父类的共性外,还可以使用子类强大的功能。
但是向上转型存在一些缺憾,那就是它必定会导致一些方法和属性的丢失,而导致我们不能够获取它们。所以父类类型的引用可以调用父类中定义的所有属性和方法,对于只存在与子类中的方法和属性它就望尘莫及了---1。
public class test {
public static void main(String[] args) {
// TODO Auto-generated method stub
Cat cat = new Cat();
cat.eat();
Animal a = new Cat();
a.eat();
}
}
class Animal{
public void eat() {
System.out.println("动物吃饭");
}
}
class Cat extends Animal{
public void eat() {
System.out.println("猫吃饭");
}
}
多态中成员访问特点
成员变量
编译看左边(父类),运行看左边(子类)
成员方法
编译看左边(父类),运行看右边(子类)
public class test1 {
public static void main(String[] args) {
Father f = new Father();
System.out.println(f.num);
f.print();
Son s = new Son();
System.out.println(s.num);
s.print();
}
}
class Father{
int num = 10;
public void print() {
System.out.println("father");
}
}
class Son extends Father{
int num = 20;
public void print() {
System.out.println("son");
}
}
output:
10
father
20
son
静态成员变量
public class test1 {
public static void main(String[] args) {
Father father = new Father();
father.method();
Father f = new Son();
f.method();
Son s = new Son();
s.method();
}
}
class Father{
int num = 10;
public void print() {
System.out.println("father");
}
public static void method() {
System.out.println("fatheer static method ");
}
}
class Son extends Father{
int num = 20;
public void print() {
System.out.println("son");
}
public static void method() {
System.out.println("son static method ");
}
}
output:
fatheer static method
fatheer static method
son static method
总结:编译看左边(父类),运行看左边(子类)
静态和类相关,算不上重写,所以,访问还是左边
只有非静态成员变量,编译看左边,运行看右边
多态之向上转型和向下转型
向上转型:
子类引用的对象转换为父类类型称为向上转型。通俗地说就是是将子类对象转为父类对象。此处父类对象可以是接口。
public class Animal {
public void eat(){
System.out.println("animal eatting...");
}
}
public class Cat extends Animal{
public void eat(){
System.out.println("我吃鱼");
}
}
public class Dog extends Animal{
public void eat(){
System.out.println("我吃骨头");
}
public void run(){
System.out.println("我会跑");
}
}
public class Main {
public static void main(String[] args) {
Animal animal = new Cat(); //向上转型
animal.eat();
animal = new Dog();
animal.eat();
}
}
//结果:
//我吃鱼
//我吃骨头
这就是向上转型,Animal animal = new Cat();将子类对象Cat转化为父类对象Animal。这个时候animal这个引用调用的方法是子类方法。
转型过程中需要注意的问题
- 向上转型时,子类单独定义的方法会丢失。比如上面Dog类中定义的run方法,当animal引用指向Dog类实例时是访问不到run方法的,
animal.run()
会报错。 - 子类引用不能指向父类对象。
Cat c = (Cat)new Animal()
这样是不行的。
向上转型的好处
- 减少重复代码,使代码变得简洁。
- 提高系统扩展性。
举个例子:比如我现在有很多种类的动物,要喂它们吃东西。如果不用向上转型,那我需要这样写:
public void eat(Cat c){
c.eat();
}
public void eat(Dog d){
d.eat();
}
//......
eat(new Cat());
eat(new Cat());
eat(new Dog());
//......
一种动物写一个方法,如果我有一万种动物,我就要写一万个方法,写完大概猴年马月都过了好几个了吧。好吧,你很厉害,你耐着性子写完了,以为可以放松一会了,突然又来了一种新的动物,你是不是又要单独为它写一个eat方法?开心了么?
那如果我使用向上转型呢?我只需要这样写:
public void eat(Animal a){
a.eat();
}
eat(new Cat());
eat(new Cat());
eat(new Dog());
//.....
这样写代码是不是简洁了许多?而且这个时候,如果我又有一种新的动物加进来,我只需要实现它自己的类,让他继承Animal就可以了,而不需要为它单独写一个eat方法。是不是提高了扩展性?
向下转型
与向上转型相对应的就是向下转型了。向下转型是把父类对象转为子类对象。(请注意!这里是有坑的。)
//还是上面的animal和cat dog
Animal a = new Cat();
Cat c = ((Cat) a);
c.eat();
//输出 我吃鱼
Dog d = ((Dog) a);
d.eat();
// 报错 : java.lang.ClassCastException:com.chengfan.animal.Cat cannot be cast to com.chengfan.animal.Dog
Animal a1 = new Animal();
Cat c1 = ((Cat) a1);
c1.eat();
// 报错 : java.lang.ClassCastException:com.chengfan.animal.Animal cannot be cast to com.chengfan.animal.Cat
为什么第一段代码不报错呢?因为a本身就是Cat对象,所以它理所当然的可以向下转型为Cat,也理所当然的不能转为Dog,你见过一条狗突然就变成一只猫这种现象?
而a1为Animal对象,它也不能被向下转型为任何子类对象。比如你去考古,发现了一个新生物,知道它是一种动物,但是你不能直接说,啊,它是猫,或者说它是狗。
向下转型注意事项
- 向下转型的前提是父类对象指向的是子类对象(也就是说,在向下转型之前,它得先向上转型)
-
向下转型只能转型为本类对象(猫是不能变成狗的)。
经典案例分析:
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{
}
public class Demo {
public static void main(String[] args) {
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println("1--" + a1.show(b));
System.out.println("2--" + a1.show(c));
System.out.println("3--" + a1.show(d));
System.out.println("4--" + a2.show(b));
System.out.println("5--" + a2.show(c));
System.out.println("6--" + a2.show(d));
System.out.println("7--" + b.show(b));
System.out.println("8--" + b.show(c));
System.out.println("9--" + b.show(d));
}
}
//结果:
//1--A and A
//2--A and A
//3--A and D
//4--B and A
//5--B and A
//6--A and D
//7--B and B
//8--B and B
//9--A and D