前言
http://t.csdnimg.cn/0CAuc
在上一篇我们已经详讲了继承特性,在这我们将进行最后一个也是最重要的特性讲解——多态
在讲解之前我们需要具备对向上转型以及方法重写的初步了解,这有助于我们对多态的认识
1.向上转型
即实际就是创建一个子类对象,将其当成父类对象来使用
语法格式:父类类型 对象名 = new 子类类型()
Animal animal1=new Dog();
Animal是父类类型,但可以引用一个子类对象,因为是从小范围向大范围的转换。
class Animal {
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(this.name +" 正在吃!");
}
}
class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);//alt +回车
}
public void bark() {
System.out.println(this.name + " 旺旺叫!");
}
@Override
public void eat() {
System.out.println(this.name +" 正在吃狗粮!");
}
}
public class Test3{
public static void main(String[] args) {
Animal animal1 = new Dog("小黄", 10);
animal1.eat();
animal1.bark();
}
}
在这我们对bark引用会报错,而eat不会,原因是我们只能访问父类特有的
那为什么对eat的访问结果是"小黄 正在吃狗粮!",这里我们就要提到方法重写了
2.方法重写
重写(override):也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!
重写的好处在于子类可以根据需要,定义特定于自己的行为。也就是说子类能够根据需要实现父类的方法。
方法的重写:
1.方法名相同 2.方法的参数列表相同(个数、顺序、类型) 3.方法的返回值相同
规则:
1.静态方法不能被重写
2.被private修饰的方法不能被重写
3.被final修饰的方法(密封方法)不能被重写
4.如果方法被重写,那么子类的访问权限要大于等于父类的权限
在上面的代码里对于eat的结果是"小黄 正在吃狗粮!"我们就可以进行解释
public void eat() {
System.out.println(this.name +" 正在吃!");//父类中
}
public void eat() {
System.out.println(this.name +" 正在吃狗粮!");//子类中方法重写
}
1.什么是多态
有人可能会认为一种事物,多种形态,这样的认识是不正确的
通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。即同一件事情,发生在不同对象身上,就会产生不同的结果
2.多态的实现
class Animal {
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(this.name +" 正在吃!");
}
}
class Dog extends Animal {
public Dog(String name, int age) {
super(name, age);//alt +回车
}
public void bark() {
System.out.println(this.name + " 旺旺叫!");
}
@Override
public void eat() {
System.out.println(this.name +" 正在吃狗粮!");
}
}
class Bird extends Animal {
public Bird(String name, int age) {
super(name, age);
}
public void qiqi() {
System.out.println(this.name +" 吱吱叫!");
}
public void fly() {
System.out.println(this.name+" 正在飞!");
}
@Override
public void eat() {
System.out.println(this.name+" 正在吃鸟粮!");
}
}
public class Test3 {
public static void main(String[] args) {
Animal animal1=new Dog("小黄",10);//向上转型
Animal animal2=new Bird("小黑",5);//向上转型
animal1.eat();//实际执行就是Dog类中重写的方法
animal2.eat();//实际执行就是Bird类中重写的方法
}
}
这里输出的结果就是应用了上述两种方法,向上转型与方法重写
注意:向上转型的缺点就是不能调用子类特有的方法
3多态的优缺点
优:1.能够降低代码的 "圈复杂度", 避免使用大量的 if - else
什么叫 "圈复杂度" ? 圈复杂度是一种描述一段代码复杂程度的方式. 一段代码如果平铺直叙, 那么就比较简单容易理解. 而如 果有很多的条件分支或者循环语句, 就认为理解起来更复杂. 因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数, 这个个数就称为 "圈复杂度". 如果一个方法的圈复杂度太高, 就需要考虑重构. 不同公司对于代码的圈复杂度的规范不一样. 一般不会超过10
2. 可扩展能力更强
缺:代码的运行效率降低
1. 属性没有多态性 当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性
2. 构造方法没有多态性
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 Test3 {
public static void main(String[] args) {
D d = new D();
}
}
//结果是D.func() 0
//在子类第一条会先进行super();所以先是父类构造方法的调用,然后触发动态绑定调用子类的func(),此时的num还未初始化,即为0
结论:
"用尽量简单的方式使对象进入可工作状态", 尽量不要在构造器中调用方法(如果这个方法被子类重写, 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题
4.向下转型
在讲完向上转型之后,还有一个向下转型
将一个子类对象经过向上转型之后当成父类方法使用,再无法调用子类的方法,但有时候可能需要调用子类特有的方法,此时:将父类引用再还原为子类对象即可
如果我们仅仅像向上转型那样进行写的话会报错
Bird bird1=(Bird)animal2;//这里需要进行强制类型转换,否则会报错
对此
向下转型用的比较少,而且不安全,万一转换失败,运行时就会抛异常。Java中为了提高向下转型的安全性,引入了instanceof关键字,如果该表达式为true,则可以安全转换。
public class Test3 {
public static void main(String[] args) {
Animal animal1=new Dog("小黄",10);
Animal animal2=new Bird("小黑",5);//向上转型
if(animal1 instanceof Bird){
Bird bird2=(Bird)animal1;
}else{
System.out.println("animal1 instanceof Bird not!");
}
Bird bird1=(Bird)animal2;
bird1.fly();
}
}
如果上述内容对您有帮助,希望给个三连谢谢