<继承看这一篇就够了【彻底教会你理解JAVA中的继承(包括继承的内存讲解)】>

<继承看这一篇就够了【彻底教会你理解JAVA中的继承(包括继承的内存讲解)】>

一、继承的概念(什么是继承)

继承是面向对象三大特征之一。

JAVA中提供了一个关键字----extends,extends表明正在构造的新类派生于一个已存在的类。

用这个关键字,我们可以让一个类与另外一个类建立起继承关系。

public class Student extendx Person{}
public class 子类 extendx 父类{}

在这个案例中,Student被称为子类(派生类),person被称为父类(基类或者超类)

二、继承的意义(为什么要用继承)

举一个例子

假如我有一个学生javabean类和一个老师javabean类,他们都有几个共同的属性和方法,比如姓名、年龄、性别、吃饭、睡觉等。

那我写完学生类中这些属性和方法,再写老师类中这些相同属性和方法就很麻烦,我想偷点懒,不想写了。

俗话说:没有偷懒,就没有进步。

所以我们能不能把他们俩个共同的方法和属性抽取到另外一个类(父类)中。

这样再创建学生类和老师类时,直接用父类中的这些方法属性就可以了。

继承的好处

  • 可以把多个子类中重复的代码抽取到父类中,提高代码的复用性。
  • 子类可以在父类的基础上,增加其他的功能,使子类更加强大。

那么我们什么时候需要用到继承呢?

继承的使用场景

当类与类之间,存在相同(共性)的内容,并满足子类是父类的一种,就可以用继承。

比如我刚刚提到的例子。

可以把学生类和老师类放在(Person)这个类中。

继承后子类的特点

  • 子类可以得到父类的属性和行为,子类可以使用。
  • 子类可以在父类的基础上新增其他功能,子类更加强大。

三、继承的用法(继承如何使用)

继承的特点

  1. JAVA只能单继承,不支持多继承,但支持多重继承。
  2. JAVA中所有的类都直接或者间接继承与object类。

单继承的意思是:一个类只能继承一个父类。通俗点来说,一个儿子只能有一个亲生爸爸,不可能有多个亲生爸爸。

多重继承的意思是:父类也可以继承别的父类。继续通俗点来说,爸爸还有他的爸爸。

这时你可能会想,讲了那么多子类到底继承父类中的哪些东西呢?

继承父类的内容

构造方法:不能继承。

成员变量:非私有和私有修饰的变量都可以继承下来。

成员方法:添加到虚方法表中的方法能够被继承,其余的方法不能被继承。

注意事项:

  • 子类只能访问父类中的私有成员

我们来看下代码

//创建了父类
public class Fu {
    //成员变量
     String name;
     int age;
    //父类的有参构造
    public Fu(String name, int age) {
        this.name = name;
        this.age = age;
    }
    //父类的无参构造
    public Fu() {
    }
}
//创建了子类 继承了父类
public class Zi extends Fu{
    //成员变量
    String game;
}

我们先来看看构造方法为什么不能继承。

public class Test {
    public static void main(String[] args) {
        //利用空参创建了子类的对象 z 此时z记录的只是z在堆内存中开辟一块内存的地址值
        Zi z = new Zi();
        
        //利用带参构造子类对象
        Zi z1 = new Zi("zhangsan",15);//此时的代码就已经报错了
    }
}

你可能会想为啥空参的没有报错,有参报错了,这俩都在父类中有啊。

其实不然,如果你创造一个类没有给他任何的构造方法,这时Java虚拟机会自动给他一个空参构造

所以在Zi类中就有空参构造,所以肯定不会报错,但是Zi类里并没有Fu类中的有参构造,所以他会报错。

然后我们再来看继承的内存原理

继承的内存原理

<继承看这一篇就够了【彻底教会你理解JAVA中的继承(包括继承的内存讲解)】>_第1张图片

了解完继承的内存原理后我们来看,成员变量的继承原理

public class Test {
    public static void main(String[] args) {
                //创建了子类的对象 z 此时z记录的只是z在堆内存中开辟一块内存的地址值
                Zi z = new Zi();
                //打印z对象的地址值
                System.out.println(z);
                //给z类中的成员变量赋值
                z.name = "张三";
                z.age = 18;
                z.game ="瓦罗兰特";
                //打印z对象的成员变量
                System.out.println(z.name+", "+z.age+", "+z.game);
    }
}

当你实行这段代码时,是没有问题的,因为此时的父类属性name和age都是非私有化,那么直接用Zi调用父类的属性是没有一点毛病的。

但是如果将父类的属性用private修饰,此时就会报错。

//创建了父类
public class Fu {
    //成员变量
     private String name;
     private int age;
}
public class Test {
    public static void main(String[] args) {
                //创建了子类的对象 z 此时z记录的只是z在堆内存中开辟一块内存的地址值
                Zi z = new Zi();
                //打印z对象的地址值
                System.out.println(z);
                //给z类中的成员变量赋值
                z.name = "张三";      //会报错
                z.age = 18;          //会报错
                z.game ="瓦罗兰特";   //不会报错
                //打印z对象的成员变量
                System.out.println(z.name+", "+z.age+", "+z.game);
    }
}

为什么会被报错呢,我们来结合继承的原理图和private修饰符来看。

此时的main方法先进栈,然后虚拟机将test类的字节码文件放入方法区,随后创建Zi类,虚拟机也会把他的字节码文件放到方法区中,此时还会把他继承的父类字节码文件也放到方法区中。即test,Zi,Fu,object类的字节码文件放进去。

然后给z的成员变量赋值,虚拟机会先根据z的地址,在堆内存中找到Zi类开辟的一块空间,再找有没有name和age属性,其实这些属性都被找到了,但是因为name和age都被private修饰了,不能给他们赋值,所以会报错。

为什么会找到在继承的原理图中已经说清楚了

所以成员变量都能被继承下来,只是被private修饰的不能用而已。

虚方法表的解释

<继承看这一篇就够了【彻底教会你理解JAVA中的继承(包括继承的内存讲解)】>_第2张图片

虚拟机在给最顶级的父类开始设立一个虚方法表,里面放的都是经常使用的方法,他有三个要求—不能被private修饰,不能被static修饰,不能被final修饰。

B类去继承C类时,他们的虚方法表也会有所更新,B的虚方法表会把C的所有虚方法表继承过来,还添加属于自己类的虚方法。

所以理解完虚方法的原理后,我们也很快知道为什么成员方法只能在虚方法表中被继承了。

恭喜你当你看到这里就已经完全掌握继承这个知识点了,如果你有什么疑问和意见,欢迎在评论区询问或者私信我。

看完麻烦各位哥哥姐姐点点赞收收藏,你们的支持就是我分享的最大动力。

那么这一篇博客就到为止了,我们下篇再见!

你可能感兴趣的:(java学习之路,java,开发语言)