继承的概念和特点
当某个类需要继承其他类的时候,在定义当前类的地方使用extends关键字,然后让这个类去继承需要继承的其他类。此时被继承的类称为父类,实现这个继承的类称为子类。子类继承父类后,可以使用父类的成员变量和成员方法(private修饰的成员不能被子类继承)。
继承的优势:让类与类之间产生关系,父类定义好成员,子类直接使用,提高了代码的复用性。
继承的弊端:打破了封装性。
继承在内存中的加载顺序:JVM在加载子类的时候,会发现子类的字节码文件里有一个extends标识,所以JVM会先将父类加载到方法区中,再将子类加载到方法区中。
Java只支持单继承,不支持多继承,即一个子类只能由一个父类。
Java支持多层继承,即子类可以有父类,父类也可以再有父类。
成员变量重名
如果子类中出现了跟父类同名的成员变量,则在子类中可以通过super关键字来访问父类中的成员变量。
在Java中super关键字有两个作用:
①super.父类成员(变量、函数),在父类中访问父类的成员;
②super(参数),访问父类的构造函数。
this和super的区别:
①this表示本类中的成员,super表示父类中的成员;
②在Java中,this是一个引用变量,它保存的是当前调用这个函数的对象的地址值;super仅仅表示父类的内存空间,但不是父类的对象,仅仅是用来标记哪个是父类;
③this表示子类对象在堆内存中开辟的整个空间,而super标记的是子类对象开辟空间中的父类所占用的空间。
当父类中的成员变量和子类中同名时,子类对象优先调用属于子类对象自己的成员变量。
子类不能直接继承父类的私有成员变量,但是可以通过get、set方法进行调用。
方法重写
如果子类和父类中出现一模一样的成员方法,访问时则会出现一种特殊的情况:方法重写。
方法重写:子类中出现跟父类一样的成员方法时(返回值类型,方法名和参数列表都相同),则会出现覆盖效果,也称为重写或复写,声明不变,重新实现。
方法重写的应用:子类可以根据自己的需要,定义特定于属于自己的行为,这样既沿袭了父类的功能名称,又根据子类的需要重新实现了父类的方法,从而进行扩展增强。
方法重写的注意事项:
①必须有继承关系;
②父类中的private修饰的成员方法,不能被重写;
③函数的重写要求函数的返回值类型、方法名和参数列表全部相同。
继承后构造函数的特点(隐式三步)
通过学习对象的创建流程,我们知道,在使用new关键字创建对象的时候,先在堆内存中给对象分配内存空间,接着给非静态成员变量进行默认初始化,开始调用对应的构造函数。而在执行构造函数中有隐式三步:super();它是在调用父类的无参构造函数:
①非静态成员变量显示赋值;
②父类的构造代码块运行;
③父类的构造函数中的代码运行。
构造代码块:直接在类中定义且没有加入static关键字修饰的代码块称为构造代码块,构造代码块在创建对象时被调用,每次创建对象时都会被调用,且构造代码块执行的优先级高于类的构造函数。
public class Fu {
int x = 3;
{
System.out.println("父类构造代码块x=" + x);
}
public Fu() {
System.out.println("父类构造函数执行");
show();
}
public void show() {
System.out.println("父类show方法执行x=" + x);
}
}
public class Zi extends Fu {
int y = 4;
{
System.out.println("子类构造代码块x=" + x);
System.out.println("子类构造代码块y=" + y);
}
public Zi() {
super();
System.out.println("子类构造函数执行");
show();
}
@Override
public void show() {
System.out.println("子类show方法执行x=" + x);
System.out.println("子类show方法执行y=" + y);
}
}
public class Demo {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
执行结果:
父类构造代码块x=3
父类构造函数执行
子类show方法执行x=3
子类show方法执行y=0
子类构造代码块x=3
子类构造代码块y=4
子类构造函数执行
子类show方法执行x=3
子类show方法执行y=4
每个类中都调用父类的构造函数,是因为子类继承了父类,子类要使用父类的成员变量,所以调用父类中的构造函数是为了父类中的成员变量显示初始化。
上述代码中由于父类的构造函数是子类对象来调用的,所以父类构造函数中的this记录着子类对象的内存地址,因此在执行show方法时JVM会优先在子类中去寻找,如果子类中没有show函数,则再返回父类中查找。
注意事项:
①为什么任何一个类(不包括Object类)的构造函数中都有一个隐式super()语句?因为任何子类继承父类之后,都会继承到父类的成员变量,这时创建子类对象的时候,会在子类的对象空间中分配出存储空间存储父类的成员变量,而父类的成员变量显示初始化动作必须由子类自己的构造函数完成,因此任何一个类的构造函数中都有一个隐式super()语句。
②如果一个类的所有构造函数全部私有,这个类就无法再有子类。
③如果一个类没有无参构造函数,那么这个类也可以有子类,但是要求在子类的构造函数中必须手写super语句,即super(具体参数),之后会根据参数去调用父类中参数类型相同的构造函数。