extends关键字
-
表示继承
-
概念:子类继承父类的属性及行为,使得子类的对象具有父类相同的属性及行为
-
父类别称:基类、超类
子类别称:派生类 -
继承的优点:提高代码的可重用性、可维护性及可扩展性
-
继承的关系式:is-a的关系
例 class 食肉动物 extends 动物 { }
关系式:食肉动物 是 动物
代码演示:extends关键字的基本使用
public class Test {
public static void main(String[] args) {
Student student = new Student();
student.name = "仲永";
student.run();
student.study();
System.out.println("---------------------");
Teacher teacher = new Teacher();
teacher.name = "卡卡";
teacher.run();
teacher.education();
}
}
class Person { // 父类
String name; // 共用属性
void run () { // 共用方法
System.out.println("每天运动一小时");
}
}
// 继承格式:子类 extends 父类
class Student extends Person{
// 学生特有的行为
void study() {
System.out.println(name +"学生的任务是学习");
}
}
class Teacher extends Person{
// 老师特有的行为
void education() {
System.out.println(name + "老师的任务是教书育人");
}
}
父子类的访问特点
- 子类继承非私有的属性和方法,子类对象可以直接访问
- 针对私有的属性及方法,子类对象不能直接访问,但可通过父类提供的公共方法间接访问
代码演示:子类间接访问父类的属性及行为
public class Test {
public static void main(String[] args) {
Student student = new Student();
System.out.println("名字:" + student.getName()); // 解析:间接访问父类的属性
//(student.getName()值为null)
student.getRunMethod(); // 解析:间接访问父类的行为
}
}
class Person {
// 属性行为都私有化
private String name;
private void run () {
System.out.println("每天运动一小时");
}
public String getName() {
return name;
}
public void getRunMethod() {
run();
}
}
class Student extends Person{
// 无法直接访问父类中私有的属性及行为
// 解决方式:通过父类提供的公共方法间接访问
}
代码演示:子类访问父类的属性的优先级
在继承类中,子类访问属性的规则
- 子类优先在本类中访问属性
- 当子类中无属性,到父类中调用
- 当父类中无属性,到父类的父类中调用,
- 若父类的父类中有属性则进行调用;若没有继续向父类寻找,直至到Object结束访问
注意:子类无论是访问父类的属性或方法,规则与子类访问属性的规则同理
public class Test {
public static void main(String[] args) {
Son son = new Son();
System.out.println(son.name); // 运行结果:大头
// 子类优先调用本类中的属性或方法
System.out.println(son.age); // 运行结果:28
// 当子类中无属性时,到父类中调用
System.out.println(son.hobby); // 运行结果:下棋
// 当父子类中都无属性时,到爷类中调用,直至到Object结束访问
}
}
class Grandpa { // 爷类
String name = "大头爷爷";
String hobby = "下棋";
}
class Father extends Grandpa { // 父类
String name = "大头爸爸";
int age = 28;
}
class Son extends Father { // 子类
String name = "大头";
}
父子类的构造器
继承关系中,子类必须调用父类的构造器,完成父类的初始化
- 当创建子类对象时,子类不管使用哪个构造器,默认情况下调用父类的无参构造器
- 当父类中没有无参构造器,但存在有参构造器,说明父类中的无参构造器被覆盖
子类构造器无法直接调用父类无参构造器,需手动添加一个“显式”无参构造器再调用
子类构造器中调用父类有参构造器,使用super指定的父类有参构造器,并完成父类的初始化 - super() 与 this() 必须放置构造器第一行,因此一个构造器不能两个同时存放
代码演示:子类构造器 默认访问 父类无参构造方法
public class Test {
public static void main(String[] args) {
Student student1 = new Student(); // 证明:子类构造器中有个隐式的super()构造器
Student student2 = new Student(12);
/* 运行结果:
父类无参构造方法已运行!
子类无参构造方法已运行!
父类无参构造方法已运行!
子类有参构造方法已运行!
*/
}
}
class Person {
Person() {
System.out.println("父类无参构造方法已运行!");
}
}
class Student extends Person {
Student() {
System.out.println("子类无参构造方法已运行!");
}
Student(int num) {
super(); // 使用super,必须放置子类构造器第一行。可省略不写
System.out.println("子类有参构造方法已运行!");
}
}
代码演示:子类构造器 访问 父类有参构造方法
public class Test {
public static void main(String[] args) {
Student student = new Student();
/* 运行结果:
父类有参构造方法开始运行!
子类无参构造方法开始运行!
*/
}
}
class Person {
Person(int a) {
System.out.println("父类有参构造方法开始运行!");
}
}
class Student extends Person {
Student() {
super(20); // 调用父类有参构造器,必须使用super指定的父类构造器
System.out.println("子类无参构造方法开始运行!");
}
}
代码演示:子类构造器 访问 父类构造器
子类构造器访问父类构造器,这是毋庸置疑的。但是子类构造器不能访问父类上级的构造器(子类构造器无法访问爷类的构造器)
public class Test {
public static void main(String[] args) {
C c = new C();
/* 运行结果:
爷爷构造器
爸爸构造器
儿子构造器
*/
}
}
class A {
public A() {
System.out.println("爷爷构造器");
}
}
class B extends A {
public B() {
System.out.println("爸爸构造器");
}
}
class C extends B {
public C() {
System.out.println("儿子构造器");
}
}
super 与 this 的三种用法
super的三种用法
-
在子类的构造方法中,访问父类的构造方法
-
在子类的成员方法中,访问父类的成员变量
-
在子类的成员方法中,访问父类的成员方法
-
super在访问属性或调用方法时,从父类开始查找
若父类中没有该属性或方法,则在父类的上级类继续查找
代码演示:super的三种用法
public class Test {
public static void main(String[] args) {
Son son = new Son();
son.method1();
son.method2();
}
}
class Father { // 父类
int i = 10;
public Father() {
System.out.println("父类无参构造器已执行");
}
public void work() {
System.out.println("父亲是秃头工程师");
}
}
class Son extends Father { // 子类
// 1. 访问父类的构造方法
public Son() {
super();
}
// 2. 访问父类的属性
void method1() {
System.out.println("父类属性i=" + super.i);
}
// 3. 访问父类的方法
void method2() {
super.work();
}
}
this的三种用法
-
在本类的构造方法中,访问本类的构造方法【注意:多个构造方法间不能相互调用避免死循环现象】
-
在本类的成员方法中,访问本类的成员变量
-
在本类的成员方法中,访问本类其他的成员方法
-
this在访问属性或调用方法时,从本类开始查找
若本类中没有该属性或方法,则在父类继续查找
代码演示:this的三种用法
public class Test {
public static void main(String[] args) {
Father father = new Father();
father.method2();
}
}
class Father { // 父类
int i;
public Father() {
this(66); // 1. 访问本类的构造方法
System.out.println("无参构造器已执行");
}
public Father(int i) {
System.out.println("有参构造器已执行");
}
public void method1() {
System.out.println("属性i=" + this.i); // 2. 访问本类的属性
}
public void method2() {
this.method1(); // 3. 访问本类的行为
}
}
方法重写(Override)
方法重写的规则
1. 重写发生在继承关系中
2. 子类方法的参数列表和方法名称要与父类方法的的参数列表和方法名称完全一致
3. 基本数据类型:子类成员方法的类型和父类成员方法的类型相同
引用数据类型:父类成员方法的类型可以是父类也可以是子类
父类成员方法的类型是父类,子类的重写方法的类型可以是父类也可以是子类
父类成员方法的类型是子类,子类的重写方法的类型只能是子类
4. 子类成员方法的访问权限 “大于或等于” 父类成员方法的访问权限
权限排序:public > protected > 默认 > private
特殊情况:父类的成员方法权限是private,子类的成员方法权限无法重写
父类的成员方法权限是public,子类的成员方法权限必须是public
代码演示:方法重写的应用
父子类中都有 eat方法,子类优先调用本类的eat方法
这里不能很好的体现方法重写的作用,在后面“多态、抽象类”等章节中更好的体现
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat(); // 运行结果:狗吃骨头
}
}
class Animal {
public void eat() {
System.out.println("动物吃食物");
}
}
class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃骨头");
}
}
代码演示:引用数据类型的重写
class AAA {
public BBB method() {
return null;
}
}
class BBB extends AAA {
public BBB method() {
return null;
}
}
小练习:封装+构造器+重写 三者综合应用
题目
1. 编写一个Person类,包含属性/private(name、age),构造器,方法say(返回自我介绍的字符串)
2. 编写一个Student类,继承Person类,增加id、score属性/private,以及构造器,定义say方法(返回自我介绍的字符串)
3. 在main中,分别创建Person和Student对象,调用say方法输出自我介绍
public class Test {
public static void main(String[] args) {
Person person = new Person("小黄",23);
System.out.println(person.say()); // 运行结果:小黄 年龄:23
Student student = new Student("大白",18,32,123);
System.out.println(student.say());
// 运行结果:大白 年龄:18 学号:32 分数:123
}
}
class Person {
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public String say() {
return getName() + " 年龄:" + getAge();
}
}
class Student extends Person {
public Student(String name,int age,int id,int score) {
super(name,age);
this.id = id;
this.score = score;
}
private int id;
private int score;
public int getId() {
return id;
}
public int getScore() {
return score;
}
@Override
public String say() {
return super.say() + " 学号:" + getId() + " 分数:" + getScore();
}
}
自我总结
此练习遇到的问题
1. say方法没有注意返回值类型。我本以为是void
2. private修饰词一定想到两种解决方案
a.初始化可通过构造器或set方法
b.获取可通过get方法
3. 本题提到构造器,则使用a方案
4. 本提涉及父子类构造器,而我忘记了一个知识点
父类中只有有参构造器,那么子类通过super(参数)指定调用
5. String返回值可以直接(return 字符串),这是我第一次使用