class Animal{
public String name;
public int age;
public void eat(){
System.out.println(name+"在吃饭");
}
}
class Dog extends Animal{
public void wang(){
System.out.println(name+"在汪汪叫");
}
}
class Bird extends Animal{
public String wing;
public void fly(){
System.out.println(name+"在飞");
}
}
public class test {
public static void main(String[] args) {
Dog dog=new Dog();
dog.name="旺财";
dog.wang();
}
}
向上转型:因为所有的子类都是继承父类的,
把子类对象赋给父类对象叫向上转型。
理论上说等号两边变量类型一定要相同,而这两个类型不一样,因为它俩之间构成父子类关系,
下面这样向上转型也是可以的。
看个例子。
我们发现下面这样调用子类方法会报错。这是为什么?
因为animal1中没有wang(),通过引用调用时,只能调用animal1类包含的方法,类中没有的方法是调用不了的。
当发生向上转型后,父类的引用只能访问父类自己的成员,不能访问子类特有的成员。
下面这种函数传参也属于向上转型。
下面这种返回值也属于向上转型。
重写:
1.方法名称相同
2.参数列表相同
3.返回值相同
@Override可以检查是否进行了重写。
class Animal{
public String name;
public int age;
public void eat(){
System.out.println(name+"在吃饭");
}
}
class Dog extends Animal{
public void wang(){
System.out.println(name+"在汪汪叫");
}
@Override
public void eat(){
System.out.println(name+"正常吃狗粮");
}
}
class Bird extends Animal{
public String wing;
public void fly(){
System.out.println(name+"在飞");
}
@Override
public void eat(){
System.out.println(name+"正常吃鸟粮");
}
}
public class test {
public static void func(Animal animal) {
}
public static Animal func2(Animal animal)
{
return new Dog();
}
public static void main(String[] args) {
Animal animal1=new Dog();
animal1.name="旺柴";
animal1.eat();
// animal1.wang();
Animal animal2=new Bird();
animal2.name="财旺";
animal2.eat();
func(animal1);
}
}
我们发现此时调用了子类的eat(),之前说父类只能调用父类的方法,这里为什么调用了子类方法?
这个叫作动态绑定。
编译时还是调用animal中的eat(),运行时绑定到了子类的eat().(其实是把要调用的方法地址改变了)
动态绑定:
1.向上转型
2.重写
3.父类引用调用父类和子类重写的方法
静态绑定:编译时知道调用哪个方法了。比如重载。
super是个关键字,不代表父类引用,就是在书写上能一目了然,知道这是父类的。
下面再看个例子。
对于此时的animal来说,它不知道自己将来引用哪个对象,但是我们能确定animal将来指向的对象不同,执行的方法就不一样,这个也就是多态。
父类引用引用的对象不一样的时候,表现出的行为是不一样的,这也就是多态。
注意:
1.private修饰的方法不能重写。
2.static修饰的方法不能被重写。
3.子类的访问修饰限定权限要大于大于父类的权限。
4.被final修饰的方法不能被重写。此时这个方法叫密封方法。
重写原则
例如:若干年前的手机,只能打电话,发短信,来电显示只能显示号码,而今天的手机在来电显示的时候,不仅仅 可以显示号码,还可以显示头像,地区等。在这个过程当中,我们不应该在原来老的类上进行修改,因为原来的类,可能还在有用户使用,正确做法是:新建一个新手机的类,对来电显示这个方法重写就好了,这样就达到了我们当今的需求了。
向上转型优点:让代码实现更灵活。
向上转型缺点:不能调用子类特有的方法。
把父类对象给子类。
向下转型非常不安全。
下面就错了
因为实际给bird的是Dog的对象,里面没有fly()。
animal1范围大于bird这样会出错。
为了解决这个问题我们可以判断一下。
这样就不报错了。这里是判断animal1是否引用了Bird对象。
我们看个例子。
class Shape{
public void draw(){
System.out.println("画图形");
}
}
class Rect extends Shape{
@Override
public void draw() {
System.out.println("画矩形");
}
}
class Cycle extends Shape{
@Override
public void draw() {
System.out.println("画圆");
}
}
public class draw {
public static void drawmap(Shape shape)
{
shape.draw();
}
public static void main(String[] args) {
Rect rect=new Rect();
Cycle cycle=new Cycle();
drawmap(rect);
drawmap(cycle);
}
}
使用多态的好处:
1. 能够降低代码的 " 圈复杂度 ", 避免使用大量的 if - else什么叫 " 圈复杂度 " ?圈复杂度是一种描述一段代码复杂程度的方式 . 一段代码如果平铺直叙 , 那么就比较简单容易理解 . 而如果有很多的条件分支或者循环语句 , 就认为理解起来更复杂 .因此我们可以简单粗暴的计算一段代码中条件语句和循环语句出现的个数 , 这个个数就称为 " 圈复杂度 ".如果一个方法的圈复杂度太高 , 就需要考虑重构 .不同公司对于代码的圈复杂度的规范不一样 . 一般不会超过 10 .例如我们现在需要打印的不是一个形状了 , 而是多个形状 . 如果不基于多态 , 实现代码如下 :如果使用使用多态 , 则不必写这么多的 if - else 分支语句 , 代码更简单 .2. 可扩展能力更强如果要新增一种新的形状 , 使用多态的方式代码改动成本也比较低 .对于类的调用者来说 (drawShapes 方法 ), 只要创建一个新类的实例就可以了 , 改动成本很低 .而对于不用多态的情况 , 就要把 drawShapes 中的 if - else 进行一定的修改 , 改动成本更高.多态缺陷:代码的运行效率降低 。1. 属性没有多态性当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性2. 构造方法没有多态性
下面例子中,为什么Shape数组可以存不同类型的值?
这个过程发生了向上转型。Shape里面的每一个类型就是Shape类型,
那么就相当于这样Shape shape=new Shape().
父类类型的数组能存放子类类型的对象。
父类构造方法中如果调用子类和父类重写的方法,那么会调用子类的方法。
而且此时num等于0,因为正在调用父类构造函数,父类都没有初始化完成,因为继承中是父类先构造,所以此时num是默认值0。
所以以后不要这么写。