目录
多态
1.什么是多态?
多态:多种形态,就是去完成某个行为,不同对象去完成时会产生不同的状态。
2.多态的实现条件
- 必须在继承体系下(继承);
- 子类必须要对父类中的方法进行重写(重写);
- 通过父类的引用调用重写的方法(向上转型)。
多态体现:当代码运行时,当传递不同类对象时,会调用对应类中的方法。
可能刚开始看这张图片不是很能理解,接下来会进行对向上转型和重写的讲解,看完在回头看这张代码,我相信你能更清晰的理解他们。
3.多态的优缺点
优点:
- 能够降低代码的“圈复杂度”,避免大量使用if—else
- 扩展能力强,改动成本低
缺点:
- 代码的运行效率低
向上转型和向下转型
向上转型
1.向上转型的理解
向上转型:实际就是创建一个子类对象,将其当成父类对象来使用
2.向上转型格式
父类类型 对象名 =new 子类类型
3.向上转型的实现方式
1.直接赋值
public class Test {
public static void main(String[] args) {
Animal animal=new Dog(2,"小乖");
}
2.方法的传参
public static void main(String[] args) {
Dog dog = new Dog(2,"小乖");
func(dog);
}
public static void func(Animal animal){
}
3.返回值
public static void main(String[] args) {
Animal animal=func2();
}
public static Animal func2(){
Dog dog = new Dog(2,"小乖");
return dog;
}
4.向上转型的优缺点
优点:让代码实现更简单灵活;
缺点:通过父类的引用,调用父类是无法直接调用的,只能调用父类自己的。
向下转型
1.向下转型的理解
向下转型:将父类引用在还原为子类对象
2.向下转型的格式
子类类型 类型名=(子类类型)父类类型名;
3.向下转型的实现方式
先通过向上转型,在进行强制类型转换
由上述代码,可以得出,向上转型是向下转型的基础。
如果我在加一个父类的子类猫类。先将狗向上转型为动物,再将其向下转型为猫类,这样是否可以?
结果告诉我们不是所有的向下转型都可以的
上面两种的区别就是,一个本身就是Dog,而另一个是Dog不是Cat。
为了预防这种情况,我们可以使用instanceof来验证
instanceof是引用的意思
public static void main(String[] args) {
Animal animal1=new Dog(3,"小黄");
if(animal1 instanceof Cat){
System.out.println("引用了猫");
}
else{
System.out.println("没有引用");
}
重写
1.重写的概念
重写:也称为覆盖。重写是子类对父类非静态、非private修饰,非final修饰,非构造方法等的实现过程进行重新编写。可以理解为子类对父类方法的覆盖。
2.重写的作用
- 子类能够根据需要实现父类的方法
3.重写的格式
- 子类和父类的两个方法名相同;
- 子类和父类的参数列表相同(个数,顺序,类型);
- 子类和父类的返回值要相同;
如果我们调用animal的fun,其结果又是什么?
我们发现他调用的是子类的重写的方法,这种叫做动态绑定。
什么是动态绑定?
动态绑定:在继承的情况下,当代码运行的时候,通过父类引用,调用父类和子类的重写的方法,结果实际调用了子类的方法。
注意事项:
- 虽然说,重写的时候,子类和父类的方法返回值类型、方法名、参数列表要完全一致,但是被重写的方法返回值类型也可以不同,但是其返回值类型要保持父子关系;
既然有了动态绑定,那么应该也有静态绑定吧。
什么是静态绑定?
静态绑定:也称为前期绑定(早绑定),即在编译时,根据用户所传递实参类型就确定了具体调用方法。典型代表函数重载。
public static void main(String[] args) {
int a=1;
int b=2;
int c=3;
int ret=add(a,b);
System.out.println(ret);//3
}
public static int add(int a,int b){
return a+b;
}
public static int add(int a,int b,int c){
return a+b+c;
}
调用几个参数,你就知道用确定了调用的是那个方法
- 重写的时候,可以使用@Override注解来起提示作用,如果你的重写方法是错的,那么编译器会报错;注意这里的@Override是写在子类重写父类的方法上面,而不是父类方法上方。
- 在重写中,子类的重写方法的访问权限不能比父类中被重写的方法低,并且不能使用private访问权限符。如果你在子类的访问权限符是protected,而父类却是public,那么将会报错,可以将public改成比protected访问权限低的default。
- 不能被final修饰,如果被final修饰,这意味着该父类不能被重写(密封方法)
- 不能使用静态方法
4.重写与重载的区别
所以方法重载是一个类的多态性表现,而方法重写是子类和父类的一种多态性表现。
什么时候使用重写?
对于已经投入使用的类,进行不要修改。最好的方式就是:重新定义一个新的类,来重复利用其中共性的内容并添加或改动新的内容。例如:软件的功能的改进,我们不在原来老的类进行改进,因为原来的软件,用户还在使用。于是我们新建一个类,对这个改进的方面进行重写就好了
5.避免在构造方法中调用重写方法
为什么这么说呢?
1.首先我构建了一个父类Animal和子类Dog,在父类和子类写了fun()重写方法并在父类的构造方法中调用了子类和父类的重写方法。实例化后,我得出的结果是父类的被重写方法还是子类对父类的重写方法?我在父类的构造方法中调用父类的方法,结果应该是调用父类的方法吧。
但结果是子类的构造方法
2.既然我都知道了,在父类构造方法中调用子类和父类的重写方法会发生动态绑定,调用的是子类的重写方法,那么我将子类的代码修改成下面的代码
public class Dog extends Animal{
int num=1;
public void fun() {
System.out.println(num);
}
}
其实例化后结果会是什么?1吗?
但结果是0,为什么?
这就考察到了代码块的顺序,我们知道代码块的顺序是先执行父类和子类静态代码块顺序,在父类的实例和构造在子类的实例和构造。