1.为什么需要继承
说起继承大家会想起什么,想到子女继承了父母的哪些优秀品质。那就说明子女和父母一样拥有这些品质。
Java中的类是对现实事物的描述,通过对类的实例化就会产生对象。世间事物千千万万,那么有些事物一定会存在一定的联系。
比如有一个狗类,有一个猫类:
//狗类 class Dog { public String name; public int age; public void dogCall() { System.out.println(name+"汪汪汪"); } public void eat() { System.out.println(name+"吃饭饭"); } } //猫类 class Cat { public String name; public int age; public void catCall() { System.out.println(name+"喵喵喵"); } public void eat() { System.out.println(name+"吃饭饭"); } }
通过观察上述的狗类和猫类,我们可以发现它们有许多相同的成员变量和成员方法
为了避免大量的重复:面向对象思想中提出了继承的概念,继承就是专门用来共性抽取,实现代码复用
2.继承的概念
继承机制:是面向对象程序设计使代码达到复用的最重要手段,它允许类在保持原有的特性上加以扩展,增加新功能,这样产生的新类就叫做派生类。
将 Dog 类 和 Cat类 的相同的成员放入一个类中 ,那么这个类就是动物类因为动物都有这些特征,狗和猫也都是动物。动物类在保持原有的特性上加以扩展,增加 Dog 的新功能,就有了 Dog 类,那么 Dog类需要继承动物类,这样 Dog 类才能继承动物类的一些成员,也就达到了代码的复用。Cat 类也是同样的道理。
Dog类 和 Cat类 都继承了 Animal类
Animal类 就称为父类 / 基类 / 超类
Dog类 和 Cat类 就称为子类 / 派生类
继承之后子类可以复用父类的成员,子类在实现时只需关心自己新增加的成员
继承:共性的抽取,实现代码复用
3.继承的语法
子类只有继承了父类,才可以复用父类的成员,那么子类应该如何继承父类呢?
通常借助 extends 关键字,来实现类之间的继承
class Animal { public String name; public int age; public void eat() { System.out.println(name+"吃饭"); } } class Dog extends Animal { public void dogCall() { System.out.println(name+"汪汪汪"); } }
这样 Dog类 也就成功继承了 Animal类
我们知道实例化一个类的时候,实例化而成的对象在堆上存放的是成员变量
当我们执行这句代码时:
Dog dog = new Dog();
实例化出来的 dog 对象在堆上存放的又该是什么咧?
我们可以通过这个解析图可以理解,当我们实例化一个 Dog 类是,dog 对象在堆里面不仅会存放自己的成员变量,还会存放继承父类的成员变量
子类会将父类中的成员变量或成员方法继承到子类中了
子类继承父类之后,必须要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承
4.父类成员访问
子类将父类的成员继承了下来,那么子类是否可以访问继承下来的父类成员
4.1子类中访问父类的成员变量
①访问父类中与子类不同名的成员变量:直接访问
class Animal { public String name; public int age; public void eat() { System.out.println(name+"吃饭"); } } class Dog extends Animal { public String colour; public void dogCall() { name = "阿黄"; System.out.println(name+"汪汪汪"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.dogCall(); } }
子类如果访问父类不同名的成员变量可以直接访问
注:
当父类的成员变量为 private 修饰时,我们知道 private 修饰的成员变量只能在同一个包中同一个文件中同一个类中使用,那么此时子类如何访问父类的成员变量了?
因为 age 成员变量是 private 修饰的,所以 age 只能在 Animal 类中使用。即使 Dog 继承了Animal 类,也不能使用
private 修饰的成员变量可以继承,但不能访问
如果想要使用 private 修饰 age 成员变量,可以在 Animal 里面写一个返回 age 的成员方法:
class Animal { public String name; public int age; public void eat() { System.out.println(name+"吃饭"); } public int setAge(int age) { this.age = age; return this.age; } } class Dog extends Animal { public String colour; public void dogCall() { name = "阿黄"; System.out.println(name+setAge(1)+"汪汪汪"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.dogCall(); } }
写一个返回 age 的成员方法,我们便可以使用 age 里面的值了
②访问父类中与子类同名的成员变量
class Animal { public String name = "阿黄"; public void eat() { System.out.println(name+"吃饭"); } } class Dog extends Animal { public String name = "阿福"; public void dogCall() { System.out.println(name+"汪汪汪"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.dogCall(); } }
打印结果:
上述代码中父类有一个 name 成员变量,子类也有一个 name 成员变量,且它们的类型都相同。我们通过子类中的成员方法打印可以发现,打印的是子类中的 name 成员变量的值
在子类成员方法中或者通过子类对象访问成员变量时:
如果访问的成员变量子类中有,优先访问子类自己的成员变量
如果访问的成员变量子类中没有,就访问父类继承下来的成员变量。如果父类也没有,则编译报错
如果访问的成员变量与父类的成员变量同名,则优先访问子类自己的成员变量
注:成员变量的访问遵循就近原则,自己有则优先访问自己的,如果没有则向父类中找
4.2子类中访问父类的成员方法
①访问父类中与子类不同名的成员方法
class Animal { public String name = "阿黄"; public void eat() { System.out.println(name+"吃饭"); } } class Dog extends Animal { public String name = "阿福"; public void dogCall() { eat(); System.out.println(name+"汪汪汪"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.dogCall(); } }
运行结果:
当通过 子类的成员方法 或者 通过子类的对象 访问父类中与子类不同名的成员方法,则直接访问
②访问父类中与子类同名的成员方法
class Animal { public String name = "阿黄"; public void eat() { System.out.println(name+"吃饭"); } } class Dog extends Animal { public String name = "阿福"; public void eat() { System.out.println(name+"吃狗粮"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.eat(); } }
运行结果:
当通过 子类的成员方法 或者 通过子类的对象 访问父类中与子类同名的成员方法,则会优先访问子类的成员方法
通过子类对象访问父类与子类中不同名方法时,优先在子类中找,找到则访问,否则在父类中找,找到则访问,否则编译报错。
通过子类对象访问父类与子类同名方法时,如果父类和子类同名方法的参数列表不同(重载),根据调用方法传递的参数选择合适的方法访问,如果没有则报错
那么现在就有这样一个问题了:
如果子类和父类的成员变量或成员方法相同,我们如何通过子类的成员方法,去访问与父类中与子类相同的成员变量或者成员方法呢?
5.super关键字
上面我们留下了这样一个问题,当子类与父类的成员相同时,我们如果通过子类的方法去访问,一定会访问子类的成员。 那么我们应当如何通过子类的方法去访问父类与子类相同的成员呢?
我们可以通过 super 关键字去访问父类的成员不论与子类相不相同,都可以用 super 去访问
super关键字主要作用:在子类方法中访问父类的成员
如果我们在子类中看到了 super 关键字,那么它一定是在访问父类中的成员
class Animal { public String name = "阿黄"; public void eat() { System.out.println(name+"吃饭"); } } class Dog extends Animal { public String name = "阿福"; public void eat() { System.out.println(super.name+"吃狗粮"); } } public class Test { public static void main(String[] args) { Dog dog = new Dog(); dog.eat(); } }
运行结果:
我们可以看到上述的代码中在 Dog 类中的 eat 方法里面,我们打印的时候用来 super.name。那么打印的时候一定用的是父类中的 name,通过运行结果我们也可以看出来的确打印的是父类中的 name
super 关键字只能在非静态方法中使用
super 关键字在子类方法中,访问父类的成员变量和方法