简介
继承?向上下转型?多态?,都是什么鬼,其实三者是有联系的,终于理解清楚了
什么是继承
继承是用来表示类之间的 is-a 关系:狗是动物,电脑是机器
继承的分类
1.单继承
2.多继承
一个子类可以继承多个父类,比如猫即使猫科动物,也是哺乳动物
Java是支持单继承的
继承解决了什么问题?
假如两个类有一些相同的属性和方法,我们就可以将这些相同的部分,抽取到父类中,让两个子类继承父类。
这样,两个子类就可以重用父类中的代码,避免代码重复写多遍
1.减少重复代码
2.从设计的角度来说,也有一种结构美感(艺术感)
人类认知的角度上来说,是一种 is-a 关系。我们通过继承来关联两个类,反应真实世界中的这种关系,非常符合人类的认知
继承的缺点
过度使用继承,继承层次过深过复杂,就会导致代码可读性、可维护性变差。
为了了解一个类的功能,我们不仅需要查看这个类的代码,还需要按照继承关系一层一层地往上查看“父类、父类的父类……”的代码。
还有,子类和父类高度耦合,修改父类的代码,会直接影响到子类
为什么Java只支持单继承
为什么类是单继承的?如果类是多继承的,且两个父类中都实现了init()方法,
子类在调用时存在调用的不确定性(不知道调用哪个类的init()方法)。
继承的代码探索
上面都是里面,下面是实战,根据代码来探索Java 继承的特性
父类: Animal
package p2.code.obj.lab_05_03.extendHello;
import p2.code.obj.lab_05_02.Main;
/**
* @Author: wuyuhong
* @Date: 2021/11/26 - 11 - 26 - 9:16 下午
* @Description: p2.code.obj.lab_05_03.extendHello
* @Version: 1.0
*/
public class Animal {
public String name="动物";
public boolean canRun;
public Animal(){
System.out.println("父类-->构造方法执行");
}
public Animal(boolean canRun){
this.canRun=canRun;
System.out.println("父类-->有参构造执行");
}
public void sayHi(){
System.out.println("Animal Hi");
}
private void sayHello(){
System.out.println("Animal hello");
}
public void sayOMG(){
System.out.println("Animal Oh MY GOD");
}
public static void main(String[] args) {
Animal animal=new Animal();
animal.sayHello(); // 私有方法只能在类内部能访问
}
}
子类:
package p2.code.obj.lab_05_03.extendHello;
/**
* @Author: wuyuhong
* @Date: 2021/11/26 - 11 - 26 - 9:17 下午
* @Description: p2.code.obj.lab_05_03.extendHello
* @Version: 1.0
*/
public class Dog extends Animal{
private String name = "dog"; // 当父类属性和子类属性相同时,当子类被调用,会使用子类自己的属性
// public String name = "小狗";
public Dog() {
// 不写super(); 当创建Dog对象时,默认会执行父类构造函数
// super(); // 只能执行一个父类构造函数,不能即执行super(),又执行super(true);
super(true);
}
public void helloFunc(){
super.sayHi(); // 可以使用super调用父类方法,假设sayHi被重写,则可以使用super这种方式来调用父类的方法
System.out.println(super.name); // 可以是使用super的方式访问父类属性。假设父类属性被重写
// 也可以修改父类的属性
super.name="Animal";
}
@Override
public void sayOMG() {
System.out.println("wang~ oh my god");
}
private void sayYYDS(){
System.out.println("yyds");
}
public static void main(String[] args) {
Dog dog=new Dog();
System.out.println(dog.name);
dog.helloFunc();
dog.sayHi(); // 可以直接调用父类的方法(前提是父类的方法是public 或protected),
dog.sayOMG(); // 假设sayHi被重写,那么子类调用自身被重写的方法
}
}
子类的创建
public Dog() {
// 不写super(); 当创建Dog对象时,默认会执行父类构造函数
// super(); // 只能执行一个父类构造函数,不能即执行super(),又执行super(true);
super(true);
}
当创建Dog对象时,默认会执行父类构造函数,然后拥有父类公开的属性
只能执行一个父类构造函数,不能即执行super(),又执行super(true);
super关键字
public void helloFunc(){
super.sayHi(); // 可以使用super调用父类方法,假设sayHi被重写,则可以使用super这种方式来调用父类的方法
System.out.println(super.name); // 可以是使用super的方式访问父类属性。假设父类属性被重写
// 也可以修改父类的属性
super.name="Animal";
}
super.sayHi(); 可以使用super调用父类方法,假设sayHi被重写,则可以使用super这种方式来调用父类的方法
System.out.println(super.name); 可以是使用super的方式访问父类属性。假设父类属性被重写
super.name=“Animal”; 可以修改父类的属性
子类的重写
重写属性:
private String name = "dog"; // 当父类属性和子类属性相同时,当子类被调用,会使用子类自己的属性
// public String name = "小狗";
重写方法
@Override
public void sayOMG() {
System.out.println("wang~ oh my god");
}
创建子类对象时,JVM创建了几个对象
即然子类被创建会调用父类的构造方法super(),那么当子类被创建时,JVM创建了几个对象?
答案是一个,只是在子类构造中调用了父类的构造函数,并没有同时创建父类对象
怎么验证呢?可以看字节码指令来验证
子类创建后的使用
Dog dog=new Dog();
System.out.println(dog.name);
dog.helloFunc();
dog.sayHi(); // 可以直接调用父类的方法(前提是父类的方法是public 或protected),
dog.sayOMG(); // 假设sayHi被重写,那么子类调用自身被重写的方法
dog.sayHi(); // 可以直接调用父类的方法(前提是父类的方法是public 或protected),
dog.sayOMG(); // 假设sayHi被重写,那么子类调用自身被重写的方法
向上、向下转型
// 向上转型: 子类对象转化为父类对象 向上转型是多态的基础
Animal bigDog=new Dog();
System.out.println(bigDog.canRun);
bigDog.helloFunc(); 虽然helloFunc是public的,但是向上转型后还是不能访问
总结: 因此向上转型后,对象bigDog 只能访问父类和子类公共的方法和属性
// 向下转型:父类对象转化为子类对象
安全的向下转型 :父类引用指向子类对象
Animal dog1=new Dog();
Dog animal1=(Dog) dog1;
animal1.sayHi();
// 不安全的向下转型
Animal animal2=new Animal();
//Dog dog2=(Dog)animal2;
// 编译无错,运行时报错 Exception in thread "main" java.lang.ClassCastException:
// 这样编译就不会报错
if(animal2 instanceof Dog){
Dog dog2=(Dog)animal2;
System.out.println("running ?");
dog2.sayOMG();
}
多态
多态的定义是什么
子类可以替换父类
为什么子类能替换父类?
因为子类替换父类后,其实就是向上转型,取得是子类和父类的公共部分的交集
多态的条件
1.父类可以引用子类对象(父类其实引用的是子类和父类公共的部分,包括父类的方法)
2.子类重写父类的方法
package p2.code.obj.lab_05_03.extendHello;
import java.util.Date;
/**
* @Author: wuyuhong
* @Date: 2021/11/27 - 11 - 27 - 2:30 下午
* @Description: p2.code.obj.lab_05_03.extendHello
* @Version: 1.0
*/
public class SoundCollector {
public void say(Animal animal){
animal.sayOMG();
}
public static void main(String[] args) {
Animal dog=new Dog();
Animal cat=new Cat();
SoundCollector soundCollector=new SoundCollector();
soundCollector.say(dog);
soundCollector.say(cat);
Dog dog1=new Dog();
soundCollector.say(dog1);
}
}
多态的好处
多态特性能提高代码的可扩展性和复用性
表示一类事物借助子类可以替换父类和子类可以重写父类方法的特性,所以作为封装方可以进行统一处理
for example: public void say(Animal animal)
既然子类可以替换父类(向上转型)
那么封装的方法say(Animal animal)就可以接收父类,来动态调用子类的方法
举个栗子:
say(Animal animal) 这个函数就像是面包机,你往say(Animal animal)中放水果,它就产出水果面包,
放肉松就产出肉松面包,放入的所有物品都是Food,Food为父类,你不可能放一个刀进去吧 哈哈哈