文章目录
一、什么是继承
把对象的共性抽取出来,放在父类中,子类可以继承父类的内容,这种方式称为 “继承”
二、继承的使用
1.继承了父类中的内容
下面给一个情境:
假如,我们要创建一个两个对象,一个是狗,一个是猫,那么就需要写出一个狗类,一个猫类,而猫类和狗类有一些属性/方法是相同的,为了避免繁琐,我们可以把这些共性抽取出来,把这些共性放在一个名为Animal中,然后猫和狗都去继承Animal的内容。
class Aminal{
public String name;
public int age;
public void eat(){
System.out.println(this.name + "正在吃饭");
}
}
class Dog extends Aminal{
private String color;
public void bark(){
System.out.println(this.name + " 正在汪汪叫");
}
}
class Cat extends Aminal{
public void bark(){
System.out.println(this.name + " 正在喵喵叫");
}
}
public class Main{
public static void main(String[] args) {
Dog dog = new Dog();
Cat cat = new Cat();
dog.name = "旺财";
cat.name = "咪咪";
dog.bark();
cat.bark();
dog.eat();
}
}
1.解析
由于 brak 方法不是在 Animal 类中定义的,所以属于 Animal 类的对象不能使
用它。然而, 尽管在 Cat、Dog 类中没有显式地定义 eat()方法, 但属于Dog、Cat 类的对象却可以使用它们,这是因为 Dog、Catr 类自动地继承了超类 Animal中的这些方法
2.内部图
父类中private成员变量虽然在子类中不能直接访问,但是也继承到子类中了
利用继承,人们可以基于已存在的类构造一个新类。继承已存在的类就是复用(继承)这些类的方法和域。在此基础上,还可以添加一些新的方法和域, 以满足新的需求
二、父类的成员访问(super)
原理:
Dogr 类不能够直接地访问超类的私有域。也就是说,尽管每个 Dog 对象都拥有一个名为 name 的域, 但在 Dog 类的getName方法中并不能够直接地访问 name 域。只有 Animal 类的方法才能够访问私有部分。如果 Dog 类的方法一定要访问私有域, 就必须借助于公有的接口, Animal 类中的公有方法 getName 正是这样一个接口。
2.1 访问父类的成员变量
- 父类的成员名和子类的成员名不一致
- 子类会继承父类的内容
- 父类的成员名和子类的成员名一致
- 会优先使用子类成员
- 如果想要调用的是父类成员,就需要用super
- 概念:super只是一个关键字,指的是子类继承父类的那块空间,
- 作用:在子类方法中访问父类的成员或方法
- 注意:
(1)只能在非静态方法中使用
(2)必须在子类当中使用,这样才能调用父类的成员或方法
class Father{
public String name = "父类方法";
public int age;
}
class Son extends Father{
public String name = "子类方法";
public void methodA(){
System.out.println("调用的是:" + name);
System.out.println("调用的是:" + super.name);
}
}
public class Main {
public static void main(String[] args) {
Son son = new Son();
son.methodA();
}
}
成员变量访问遵循就近原则,自己有优先自己的,如果没有则向父类中找
2.2 访问父类的成员方法
- 操作与成员变量类似
class Father{
public String name;
public int age;
public void methodA(){
System.out.println("调用的是父类中的methodA");
}
public void methodB(int a){
System.out.println("调用的是父类中的methodB");
}
}
class Son extends Father{
public void methodA(){
System.out.println("调用的是子类中的methodA");
}
public void methodB(){
System.out.println("调用的是子类中的methodB");
}
public void methodC(){
super.methodA();
}
}
public class Main {
public static void main(String[] args) {
Son son = new Son();
son.methodB(5);
son.methodB();
son.methodA();
son.methodC();
}
}
- 注意方法重载
2.3 访问父类的构造方法
- 因为子类是继承于父类产生的,所以在初始化子类成员前,需要先给父类成员初始化 ----------->使用super给父类初始化
class Father{
public String name;
public int age;
public Father(String name, int age) {
this.name = name;
this.age = age;
}
}
class Son extends Father{
public Son() {
super("aaa", 19);
}
}
- 如果没有主动写构造方法,编译器会自动生成
public Father() {
}
class Son extends Father{
public Son() {
super();
}
}
- 无法与this()一起用,因为都要放在第一行
❤️this 和 super 的区别:
- 相同点
- 都是Java中的关键字
- 都不能在静态方法中使用,因为都是依赖于对象的,只能访问非静态的成员 / 方法
- 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
- 不同点
- 构造方法中一定会存在super(…)的调用,用户没有写编译器也会增加,但是this(…)用户不写则没有
- this指的是引用当前对象中的成员,super相当于是子类对象中从父类继承下来部分成员的引用
三、执行顺序问题
class Father{
public String name;
public int age;
static{
System.out.println("这是Father的静态代码块");
}
{
System.out.println("这是Father的构造代码块");
}
public Father() {
System.out.println("这是Father的构造方法");
}
}
class Son extends Father{
static{
System.out.println("这是Son的静态代码块");
}
{
System.out.println("这是Father的构造代码块");
}
public Son(){
System.out.println("这是Son的构造方法");
}
}
public class Main{
public static void main(String[] args) {
Son son1 = new Son();
System.out.println("===============");
Son son2 = new Son();
}
}
- 静态代码块先执行,并且只执行一次,在类加载阶段执行
- 父类静态代码块优先于子类静态代码块执行,且是最早执行
- 第二次实例化子类对象时,父类和子类的静态代码块都将不会再执行
- 当有对象创建时,才会执行实例代码块,实例代码块执行完成后,最后构造方法执行
- 先执行父类实例代码块和父类构造方法
- 再执行子类的实例代码块和子类构造方法
四、继承方式 + final
4.1 继承方法
但是如果出现多个继承关系后,改代码就变成了一件很麻烦的事,所以一般当出现三层继承关系时,我们就可以设置让这个类无法再被继承了。而 final 可以实现这个效果
4.2 final
❤️修饰变量:使得这个变量无法被修改,相当于变成常量,但是需要注意的是,即使是常量,也可以有初始值,只是之后不能再赋值了而已
final class Father{
}
/*class Son extends Father{ 无法继承,会报错
}*/
public class Main{
public static void main(String[] args) {
final int a = 0;
//a = 100; //会报错
}
}
❤️修饰类:使得这个无法被继承
final类中的方法,自动变为final方法
❤️修饰方法: 使得方法不能被重写
五、继承与组合
- 继承是 is - a 的关系,组合是has - a 的关系
- 子类继承父类,就意味着子类要依赖于父类,灵活性会降低。而且多层继承下来,代码的可维护性会降低。相比之下,组合的灵活性更高,每个对象之间是独立的
- 一般情况下,遵循【能用组合先用组合】,但是还是要考虑实际情况
- 面向对象的三种特性是封装、继承、多态,组合不是特性,只是一种实现方式,和继承一样,可以实现代码的复用