继承是面向对象三大特征之一。
JAVA中提供了一个关键字----extends,extends表明正在构造的新类派生于一个已存在的类。
用这个关键字,我们可以让一个类与另外一个类建立起继承关系。
public class Student extendx Person{}
public class 子类 extendx 父类{}
在这个案例中,Student被称为子类(派生类),person被称为父类(基类或者超类)
举一个例子
假如我有一个学生javabean类和一个老师javabean类,他们都有几个共同的属性和方法,比如姓名、年龄、性别、吃饭、睡觉等。
那我写完学生类中这些属性和方法,再写老师类中这些相同属性和方法就很麻烦,我想偷点懒,不想写了。
俗话说:没有偷懒,就没有进步。
所以我们能不能把他们俩个共同的方法和属性抽取到另外一个类(父类)中。
这样再创建学生类和老师类时,直接用父类中的这些方法属性就可以了。
那么我们什么时候需要用到继承呢?
当类与类之间,存在相同(共性)的内容,并满足子类是父类的一种,就可以用继承。
比如我刚刚提到的例子。
可以把学生类和老师类放在人(Person)这个类中。
单继承的意思是:一个类只能继承一个父类。通俗点来说,一个儿子只能有一个亲生爸爸,不可能有多个亲生爸爸。
多重继承的意思是:父类也可以继承别的父类。继续通俗点来说,爸爸还有他的爸爸。
这时你可能会想,讲了那么多子类到底继承父类中的哪些东西呢?
构造方法:不能继承。
成员变量:非私有和私有修饰的变量都可以继承下来。
成员方法:添加到虚方法表中的方法能够被继承,其余的方法不能被继承。
注意事项:
我们来看下代码
//创建了父类
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类中的有参构造,所以他会报错。
然后我们再来看继承的内存原理
了解完继承的内存原理后我们来看,成员变量的继承原理
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修饰的不能用而已。
虚拟机在给最顶级的父类开始设立一个虚方法表,里面放的都是经常使用的方法,他有三个要求—不能被private修饰,不能被static修饰,不能被final修饰。
B类去继承C类时,他们的虚方法表也会有所更新,B的虚方法表会把C的所有虚方法表继承过来,还添加属于自己类的虚方法。
所以理解完虚方法的原理后,我们也很快知道为什么成员方法只能在虚方法表中被继承了。
恭喜你当你看到这里就已经完全掌握继承这个知识点了,如果你有什么疑问和意见,欢迎在评论区询问或者私信我。
看完麻烦各位哥哥姐姐点点赞收收藏,你们的支持就是我分享的最大动力。
那么这一篇博客就到为止了,我们下篇再见!