1. 继承
1.1 什么是继承
什么是继承?为什么需要继承 ?
观察下面两个类定义
通过观察发现,这Dog cat这两个类具有一些共性
比如 有两个相同的成员变量name age 和一个成员方法eat
那么 是否可以将这些共性抽取出来 放到另一个类中?然后 让Dog和Cat类与另一个类共用成员变量和方法 ? 结论是可以 观察下面这个例子
class Animal {
public String name;
public int age;
public void eat() {
System.out.println(this.name + "在吃");
}
}
class Dog extends Animal {
String color;
public void bark() {
System.out.println(this.name + "在狗叫");
}
}
class Cat extends Animal {
public void miao() {
System.out.println(this.name + "在喵叫");
}
}
public class TestDemo {
public static void main(String[] args) {
Dog d1 = new Dog();
d1.name = "abc";
d1.eat();
}
}
再来总结 上面这个例子中出现的重点
1. 如何表示两个类的继承关系 ?
使用extends关键字:
class 子类名 extends 父类名
2. 子类继承父类什么 ?
子类继承父类的成员变量和方法
注意:子类无法继承父类的静态成员变量
3. 子类实例化出的对象是如何存储的 ?
回到最初的问题 什么是继承?为什么需要继承 ?
继承 从实现上回答 使用extends关键字定义类之间的继承关系,子类继承父类的成员变量和方法,从而 实现对代码的复用
1.2 访问父类成员
接下来需要讨论的问题是 子类与父类存在同名的变量的情况下 this引用访问的是 子类自己的成员变量还是访问继承自父类的成员 ?
首先回顾 子类与父类不存在同名的变量,如图
this.a 访问子类自己的成员变量a
this.b 访问父类继承的成员变量b
this.c 访问父类继承的成员变量c
如果 子类与父类存在同名变量 会是什么情况
this.a 访问子类自己的成员变量a
this.b 访问子类自己的成员变量b
this.c 访问子类自己的成员变量c
根据上面两个例子 可得出结论: 当子类与父类存在同名变量时,优先访问子类的成员变量,如果访问的成员变量子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
1.3 super关键字
子类中访问父类成员变量
如何指定访问父类的成员变量 ?
可以使用super关键字 对父类成员变量指定访问 如图
为什么super关键字能指定访问父类的成员 ?
子类中访问父类成员方法
与访问父类成员变量相同,当子类与父类存在同名成员函数时,优先访问子类的成员函数,如果访问的成员方法子类中无,则访问父类继承下来的,如果父类也没有定义,则编译报错。
如果子类与父类存在同名成员函数 需要用super指定访问父类成员方法
子类与父类存在同名成员函数时,优先访问子类的成员函数
如果访问的成员方法子类中无,则访问父类继承下来的
子类与父类存在同名成员函数时 需要用super指定访问父类成员方法
需要注意的是 super关键字不能出现在静态方法中
1.4 子类调用构造方法
问题引入
代码为什么会报错 ?
子类继承父类后 在子类初始化自己成员之前,必须先使用super() 调用父类的构造方法 对父类的成员进行初始化,如下例
class Animal {
public String name;
public int age;
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
public void eat() {
System.out.println(this.name + "在吃");
}
}
class Dog extends Animal {
String color;
public Dog(String name, int age, String color) {
super(name,age);
this.color = color;
}
public void bark() {
System.out.println(this.name + "在狗叫");
}
}
public class TestDemo {
public static void main(String[] args) {
Dog d1 = new Dog("大黄",2,"黄色");
d1.eat();
}
}
接下来的问题是 为什么不写构造方法 程序没有报错 ?
已知 如果自己没有写构造方法 编译器会自己生成一个无参的默认构造方法 (默认构造中不会有内容) 现在Dog子类继承于父类Animal 如果默认构造中没有内容 不会对父类成员进行初始化 为什么不会报错 ?
实际上 编译器也会在父类中生成默认构造 然后在子类成员初始化前 调用父类构造首先对父类成员进行初始化
1.5 super关键字总结与注意事项
总结:
1. super用来在子类中 指定访问父类成员变量和方法 因为this引用优先访问子类自己的成员
2. super加点号 访问父类成员变量和方法
3. super(..) 访问父类的构造方法
注意事项:
1. super只能在非静态方法中使用
2. 在子类构造方法中调用super(...)初始化父类成员,必须放在第一行
1.6 super与this 相同点和不同点
相同点:
1. 都是Java中的关键字
2. 只能在类的非静态方法中使用
3. 在构造方法中调用时,必须是构造方法中的第一条语句,并且不能同时存在
不同点:
1. this是当前对象的引用 —— super 是从父类继承下的部分成员变量的引用 如图
2. 在非静态成员方法中,this优先访问子类自己的成员变量和属性 如果找不到子类成员 再去访问父类成员 —— super 指定访问父类的成员
3. 在子类构造方法中,this(...)用于调用子类自己的构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造方法中出现 因为this() super()都需要占用第一行
4. 子类构造方法中一定会存在super(...)的调用,用户没有写编译器也会增加,但是this(...)调用用户不写则没有
1.7 protected 关键字
protected是一个访问修饰限定符 可以让不同包的子类可以访问父类成员
但是 不能在类外使用子类对象的引用去访问父类protected修饰成员
必须在子类中 使用this/super 进行访问
package a;
public class Test {
protected int a = 1;
}
import a.Test;
// 2. 复习protected 关键字
class Test2 extends Test {
// Test2子类继承了 Test父类中的成员变量a
// Test父类中的成员变量a由protected关键字修饰 表示不同包的子类可以访问父类成员
public void print() {
System.out.println(this.a);
System.out.println(super.a);
}
}
public class Main {
public static void main(String[] args) {
Test2 test2 = new Test2();
System.out.println(test2.a);
//test2.print();
}
}