1. 继承的使用场景
共性抽取:父亲每天吃饭睡觉,生了两个儿子继承了父亲的这两点,也每天吃饭睡觉。
特性区分:父亲是个农民,每天的工作就是种地,两个儿子出人头地,一个当了老师,一个当了医生。
2. 继承的实现
通过extends关键字,即可实现两个类之间的继承关系
public class A{}
public class B extends A{}
复制代码
3. 继承中的成员变量访问
// 父类
public class Employee{
int num = 10;
int age = 100;
}
// 子类
public class Student extends Employee{
int num = 20;
}
// 测试
public class FieldVarTest{
public static void main(String[] args){
// 父类子类都有的同名成员变量
Employee employee = new Student();
Student student = new Student();
System.out.println(employee.num); // 输出10
System.out.println(student.num); // 输出20
// 只有父类有的成员变量
System.out.println(student.age); // 向父类取,输出100
}
}
复制代码
当访问父类子类都有的同名成员变量时,取创建对象等号左边的类中定义的值;当通过子类对象访问的成员变量只存在于父类中时,向父类取值。
4. 继承中的构造方法访问
// 父类
public class Employee{
public Employee(){
System.out.println("父类构造方法");
}
public Employee(int num){
System.out.println("父类有参构造方法");
}
}
// 子类构造方法
public class Student extends Employee{
public Student(){
// TO DO
System.out.println("子类构造方法");
}
}
// 测试
public class ExtendsConstructorTest(String[] args){
Student student = new Student();
// TO DO处可能有三种情况
// 1. 不写 -> 默认调用super()
// 2. super() -> 调用super()
// 3. super(10) -> 调用父类重载构造方法
}
复制代码
需要注意的是,父类构造方法必须是子类构造方法的第一句,如果未指定父类构造方法,则默认执行super(),如果指定了,则执行重载的父类构造方法,比如super(10)。
5. 继承中的成员方法重写
重写需要注意,子类中方法名和参数列表和父类相同;子类返回类型 <= 父类返回类型;子类权限 >= 父类权限。
// 父类
public class Employee{
public void work(){
System.out.println("我去上班了");
}
}
// 子类
public class Student extends Employee{
@Override
public void work(){
System.out.println("我去上学了");
}
}
// 测试
public class SameNameVarTest{
public static void main(String[] args){
Employee employee = new Student();
employee.work(); // 输出"我去上学了"
}
}
复制代码
上面是一个重写的示例,可以看出重写的目的是根据子类的特点,改变与父类同名方法的内容。当我们有多个子类时,则可以分别重写work方法,比如再新建一个子类Teacher,那么work的内容可以改变为System.out.println("我去上学了");。在本例中,还有一个值得注意的地方是Employee employee = new Student();,我们将父类变量指向子类对象,进行work方法调用的时候,调用的是子类的方法,而在前面将继承中变量访问的时候,访问的确实父类的变量。这其实就是Java的另一个特性,多态,这篇帖子生动的描述了什么是多态:https://www.zhihu.com/question/30082151/answer/120520568。
6. 继承中的super和this关键字
// 父类
public class Employee{
int num = 10;
public Employee(){
System.out.println("父类构造方法");
}
public void work(){
System.out.println("我去上班了");
}
}
// 子类
public class Student extends Employee{
int num = 20;
public Student(){
super(); // super用法一,在子类构造方法中通过super()访问父类构造方法
}
public Student(int id){
this(); // this用法一,在子类构造方法中通过this()访问重载构造方法
}
public void superTest(){
System.out.println(super.num); // super用法二,在子类成员方法中通过super.num访问父类成员变量
super.work(); // super用法三,在子类成员方法中通过super.work()访问父类成员方法
}
public void thisTest(){
System.out.println(this.num); // this用法二,在子类成员方法中通过this.num访问子类成员变量
this.work(); // this用法三,在子类成员方法中通过this.work()访问子类成员方法
}
public void work(){
System.out.println("我去上学了");
}
}
复制代码
在super和this关键字的使用时,需要注意子类构造方法中,super()和this()都只能出现在构造方法的第一句,这也就意味着两者不能在一个构造方法中同时使用。
为了更清楚的解释super和this关键字,我们通过一段代码在运行时和内存的交互进行说明。
// 父类 (1)
public class Employee{
int num = 10;
public void work(){
System.out.println("我去上班了");
}
}
// 子类 (1)
public class Student extends Employee{
int num = 20;
public void getVariable(){
System.out.println(super.num);
System.out.println(this.num);
}
public void work(){
super.work();
}
}
// 测试类 (1)
public class SuperThisTest{
public static void main(String[] args){ // (2)
Student stu = new Student(); // (3)
stu.getVariable(); // (4)
stu.work(); // (5)(6)
}
}
复制代码
(1) 将父类,子类和测试类的.class文件保存到方法区,其中,子类中有一个super标志指向父类。
(2) 将main方法压栈,开始执行方法中的内容。
(3) 执行main方法中的创建Student对象的语句,将stu变量保存在栈中,然后先创建Employee对象,再在它的外部创建Student对象,对象中的方法指向方法区.class文件中的方法,栈中的stu变量指向堆中的new Student()对象。
(4) 执行main方法中的stu.getVariable();语句,将getVariable()方法压栈,执行方法中的内容,通过super.num找到Employee内容中的num,然后通过this.num找到Student内容中的num,getVariable()执行完毕,出栈。
(5) 执行main方法中的stu.work();语句,先找到Student对应的work()方法,并将其压栈,然后其中的语句super.work();,找到Employee对应的work()方法,并将其压栈。
(6) 执行Employee对应的work()方法中的语句System.out.println("我去上班了");,Employee对应的work()方法执行完毕,出栈;Student对应的work()方法执行完毕,出栈;main方法执行完毕,出栈。
7. 继承在Java中的特殊特性
(1) 一个类只可以有一个直接父类,也就是只支持单继承。
(2) 父类也可以有自己的父类,也就是支持多级继承。
(3) 一个父类可以有多个子类。