继承
方法重写
this/super关键字
抽象类
第一章 继承
1.1 继承的概念【重点】
假如我们要定义如下类: 讲师类,班主任类和就业指导类,分析如下:
1. 讲师类 属性:姓名,年龄,工资 行为:展示信息,讲课
2. 班主任类 属性:姓名,年龄,工资 行为:展示信息,管理班级
3. 就业指导类 属性:姓名,年龄,工资 行为:吃饭,展示信息,辅导就业
如果我们定义三个类,每个类都有姓名,年龄,工资,展示信息,代码重复了
,需要把重复的代码单独定义到一个类中
在一个已知类A的基础上,创建新类B的过程,称之为继承
这里类A,称为父类,基类,超类
这里类B,称为子类,派生类
1.继承的概念:
向上抽取共性,把相同的内容定义在父类中
2.什么情况下使用继承(不能随便继承)?
(1)必须满足 is a 的关系
(2)xxx是yyy的一种
(3)讲师是员工的一种
(4)班主任是员工的一种
(5)兔子是 动物的一种
3.继承的好处
(1)提高了代码的复用性
(2)让类与类之间差生了关系(父子类的关系)
(3)是多态的前提
(4)子类可以使用父类的除private修饰的和构造方法以外的内容
1.2 继承的格式和代码体现【重点】01
格式:
public class 父类 {
//成员变量
//成员方法
//构造方法
}
public class 子类 extends 父类 {
//成员变量
//成员方法
//构造方法
}
public class A extends B {
//...
}
注意:
1.B是父类,又叫做基类/超类,A是子类
2.子类可以使用父类中除了private修饰的和构造方法以外的内容
代码体现:
1.3 继承中的成员变量访问特点【重点】
/*
继承中的成员变量访问特点
1.不重名的成员变量
(1)子类自己有: 优先使用子类自己的
(2)子类没有: 找父类
(3)注意: 子类可以找父类,但是父类不能找子类
2.重名的成员变量
(1)方法内部,直接写变量名
从方法内部开始向上找
方法内部有: 直接使用
方法内部没有: 向上找,本类的成员位置
本类的成员位置有: 直接使用
本类的成员位置没有: 向上找,父类的成员位置
(2)方法内部,直接写this.变量名
从本类的成员位置开始向上找
本类的成员位置有: 直接使用
本类的成员位置没有: 向上找,父类的成员位置
(3)方法内部,直接写super.变量名
从父类成员位置开始向上找
父类的成员位置有: 直接使用
父类的成员位置没有: 继续向上找
(4)总结:就近原则
*/
/*
继承中成员方法的访问特点
1.子类自己有: 优先使用子类自己的
2.子类没有: 向上找父类
3.重名的方法(方法重写): 优先使用子类自己的
方法重写
1.概念:
子类中出现与父类一模一样的方法时(返回值类型,方法名和参数列表都相同),
会出现覆盖效果,也称为重写或者复写。声明不变,重新实现。
必要条件:方法名和参数列表必须相同
可选条件:返回值类型可以不一致(后面讲)
2.最简单的方法重写形式:
子类方法声明(定义方法的第一行)和父类方法声明一模一样
3.@Override注解:用来检测子类方法,是否是对父类方法的覆盖重写
4.与方法重载区分开:
方法重载作用:是节约命名空间
方法重载要求: 方法名称相同,参数列表不同(类型不同,数量不同,多个不同类型的顺序不同)
5.方法重写快捷键:
ctrl + o --> 选择要重写的方法 --> ok
*/
public class Demo03Extends {
public static void main(String[] args) {
//创建子类对象
Zi03 zi = new Zi03();
//使用子类对象调用成员方法
zi.methodZi();//调用子类自己的methodZi方法
//调用的是父类的methodFu方法
zi.methodFu();//子类没有,找父类
//子父类中重名的方法(方法重写): 优先使用子类自己的
zi.drive();
zi.show("张三",18);
}
}
方法重写的注意事项【了解】
/*
1. 子类方法覆盖父类方法,必须要保证权限大于等于父类权限。
权限:
public > protected > 默认(什么都不写) > private
2. 子类方法覆盖父类方法,
返回值类型、函数名和参数列表都要一模一样。
必要条件:
函数名和参数列表都要一模一样
可选条件:
返回值类型可以不一样
子类覆盖重写后的方法的返回值类型 <= 父类方法返回值类型
3. 私有方法不能被重写(父类私有成员子类是不能继承的)
*/
//父类
public class Fu05 {
public void a() {
}
//默认权限
void b() {
}
public void c() {
}
public Object d(){
return new Object();
}
public String e() {
return "Hello";
}
private void f() {
}
}
//子类
public class Zi05 extends Fu05{
//子父类方法权限相同
@Override
public void a() {
}
//子类重写后的方法的权限>父类方法权限
@Override
public void b() {
}
//错误: 子类重写后的方法的权限 < 父类方法权限
//不允许
/*void c() {
}*/
//正确: 子类重写后的方法的返回值类型 < 父类方法的返回值类型
//子类重写后的方法的返回值类型 是 父类方法的返回值类型的子类
@Override
public String d() {
return "hello";
}
//错误: 子类重写后的方法的返回值类型 > 父类方法的返回值类型
//子类重写后的方法的返回值类型 是 父类方法的返回值类型的父类
/*public Object e() {
return new Object();
}*/
//父类方法时private修饰的,子类无法继承,更无法覆盖重写
//不加@Override是没错的,因为此时f属于子类自己的方法,和父类方法无关
//@Override
public void f() {
}
}
//测试类
public class Demo05OverrideNotice {
public static void main(String[] args) {
}
}
1.9 继承中构造方法的访问特点【重点】 07
/*
1.构造方法的名字是与类名一致的。所以子类是无法继承父类构造方法的
2.子类继承父类,是为了使用父类的内容,所以子类创建对象调用构造方法时,必须先调用父类的构造方法,
完成父类成员的初始化动作,子类才可以使用父类的成员,super()表示调用父类的空参构造
3.子类的构造方法中如果没有手动给出super调用父类构造,
编译器默认提供一个super()调用父类的空参构造
4.super调用父类构造,只能写在第一句
5.构造方法可以重载,所以:
super(...):调用父类带参数的构造方法
super的用法:
1.super.成员变量名: 代表父类的成员变量
2.super.成员方法(参数列表...): 代表调用父类的成员方法
3.super(参数列表...): 代表调用父类的构造方法
this的用法:
1.this.成员变量名: 代表本类自己的成员变量(自己没有找父类)
2.this.成员方法(参数列表...): 代表本类自己的成员方法(自己没有找父类)
3.this(参数列表...): 代表调用本类自己的其它构造方法 ---后面讲
//Java中的类可以多层继承,但是只能继承一个
*/
//父类
public class Fu06 {
int num;
//父空参构造
public Fu06(){
System.out.println("父空参...");
}
//父有参构造
public Fu06(int num) {
System.out.println("父有参");
this.num = num;
}
}
//子类
public class Zi06 extends Fu06{
//子空参构造
public Zi06(){
super();//调用父类空参构造,不写也有
//super();//错误: 只能调用一次
System.out.println("子空参...");
//super();//错误: 必须写在第一行,先调用父类空参构造
}
//子有参构造
public Zi06(int num) {
/*
子类所有构造方法中,
只要没有手动给出super调用父类构造,
JVM会隐藏提供super()调用父类空参构造
*/
//super();
super(num);
System.out.println("子有参...");
}
}
//测试类
public class Demo06Extends {
public static void main(String[] args) {
//空参构造创建对象
Zi06 zi = new Zi06();
System.out.println("----");
//有参构造创建对象
Zi06 zi2 = new Zi06(100);
System.out.println(zi2.num);
//调用子类的成员方法
zi.show();
System.out.println("-------------------");
zi.method();
System.out.println("-------------------");
//输出子类的成员变量
System.out.println(zi.numZi);//子类的: 2000
System.out.println(zi.numFu);//父类的: 200
//调用成员方法
zi.methodZi();//子类的
zi.methodFu();//子类没有,找父类的
zi.driving();//子父类都有,优先使用子类的
//创建子类对象: 满参构造
Zi05 zi2 = new Zi05(100);
System.out.println(zi2.getNum());
}
}
1.10 继承中构造方法的访问内存图解【了解】
1.11继承综合案例 08
/*
继承综合案例--员工类
实现步骤:
1.定义父类员工Employee类
(1)成员变量: 姓名,年龄
(2)成员方法: 展示信息
(3)所有成员变量private修饰,提供空参/满参/set/get方法
2.定义子类讲师Teacher类 继承 父类员工Employee类
(1)根据父类生成空参/满参构造方法
(2)定义子类特有功能: 讲课
3.定义子类班主任Manager类 继承 父类员工Employee类
(1)根据父类生成空参/满参构造方法
(2)定义子类特有功能: 管理班级
*/
public class Demo07Test {
public static void main(String[] args) {
//4.创建子类班主任Manager类的对象(空参构造)
Manager m = new Manager();
//(1)调用set方法给成员变量赋值
m.setName("宝姐");
m.setAge(18);
m.setSalary(88888);
//(2)调用成员方法完成展示信息和管理班级
m.showInfo();
m.managerClass();
System.out.println("------------");
//5.创建子类讲师Teacher类的对象(满参构造)
Teacher t = new Teacher("响哥", 16, 66666);
//(1)调用成员方法完成展示信息和讲课
t.showInfo();
}
}
/*
定义子类讲师Teacher类 继承 父类员工Employee
根据父类生成空参构造方法:
根据父类生成满参构造构造方法:
快捷键: alt + insert
*/
public class Teacher extends Employee {
int numZi = 2000;
int num = 100;
//子类的成员方法
public void method(){
//子类不用自己重新实现了,直接调用父类的功能
super.show();
int num = 10;
//输出10
System.out.println(num);
//输出100
System.out.println(this.num);
//输出10000
System.out.println(super.num);
}
//空参构造
public Zi05() {
super();//调用父类空参构造,不写也有
//super();//错误: 只能写一次
System.out.println("子空参...");
//super();//错误: 只能写第一行
}
//满参构造
public Zi05(int num) {
/*
如果没有自己写super调用父类构造,
隐藏提供super()调用父类的空参构造
*/
//但是建议: 子空参 调用 父类空参
//但是建议: 子满参 调用 父类满参
super(num);
System.out.println("子满参...");
//super(num);//错误: 调用父类构造必须写在第一行
}
}
/*
1.定义父类员工Employee类
(1)成员变量(必须使用private修饰): 姓名,年龄,工资
(2)成员方法: 展示信息
(3)提供空参构造方法/满参构造方法
(4)提供get/set方法
*/
public class Employee {
//(1)成员变量(必须使用private修饰): 姓名,年龄,工资
private String name;
private int age;
private int salary;
//(2)成员方法: 展示信息
public void showInfo() {
System.out.println("姓名: " + name + ", 年龄: " + age + ", 薪资: " + salary);
}
//(3)提供空参构造方法/满参构造方法
public Employee() {
}
public Employee(String name, int age, int salary) {
this.name = name;
this.age = age;
this.salary = salary;
}
//(4)提供get/set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
}
/*
2.定义子类讲师Teacher类 继承 父类员工Employee类
(1)根据父类生成空参/满参构造方法
(2)定义子类特有功能: 讲课
*/
public class Teacher extends Employee {
//(1)根据父类构造方法生成子类构造方法
public Teacher() {
}
public Teacher(String name, int age, int salary) {
super(name, age, salary);
}
//(2)特有功能: 讲课
public void teaching() {
System.out.println("讲师: " + getName() + ", 正在讲解面向对象");
}
}
/*
3.定义子类班主任Manager类 继承 父类员工Employee类
(1)根据父类生成空参/满参构造方法
(2)定义子类特有功能: 管理班级
*/
public class Manager extends Employee{
//(1)根据父类构造方法生成子类构造方法
public Manager() {
}
public Manager(String name, int age, int salary) {
super(name, age, salary);
}
//(2)特有功能: 管理班级
public void managerClass() {
System.out.println("班主任老师: "+getName()+" ,正在严格的管理班级...");
}
}
1.12 继承的特点【了解】
/*
1.Java只支持单继承,不支持多继承。
2.Java支持多层继承(继承体系)。
3.所有的类都直接或者间接继承了Object类,Object类是所有类的最终父类。
*/
//父类
public class Fu0801 {
}
//父类
public class Fu0802 extends Fu0803 {
public void a() {
System.out.println("a....");
}
}
//父类
public class Fu0803 extends Object {
public void b() {
System.out.println("b....");
}
}
//一个类只能有一个直接父类
public class Zi0801 /*extends Fu0801 ,Fu0802*/ {
}
//子类
public class Zi0802 extends Fu0802 {
public void c() {
System.out.println("c....");
}
}
public class Demo08ExtendsTeDian {
public static void main(String[] args) {
//创建子类对象
Zi0802 zi0802 = new Zi0802();
zi0802.c();
zi0802.a();
zi0802.b();
zi0802.hashCode();
}
}
2.2 抽象类的定义和使用【重点】
/*
3.抽象类的定义格式:
public abstract class 类名 {
...
}
4.抽象方法的定义格式:
修饰符 abstract 返回值类型 方法名称(参数列表...);
注意:
(1)和以前定义方法一样,但是去掉{},添加abstract关键字
(2)返回值类型和参数列表根据需求确定
(3)含有抽象方法的类,必须定义为抽象类,但是抽象类中不一定含有抽象方法
5.抽象类的使用
(1)不能直接创建抽象类的对象
(2)定义子类,继承抽象父类
(3)子类中覆盖重写抽象父类中的所有抽象方法
去掉abstract关键字,添加{}
快捷键: ctrl + i
(4)创建子类对象
(5)子类对象调用方法
2.3 抽象类的注意事项【了解】
/*
1.抽象类不能创建对象,如果创建,编译无法通过而报错。只能创建其非抽象子类的对象
2.抽象类中,必须有构造方法,是供子类创建对象时,初始化父类成员使用的
3.抽象类中,不一定包含抽象方法,但是有抽象方法的类必定是抽象类 看: MyAbstractClass
4.抽象类的子类,必须重写抽象父类中所有的抽象方法,否则,编译无法通过而报错。除非该子类也是抽象类
看: Cat10类,BoSiCat10类
*/ (3)含有抽象方法的类,必须定义为抽象类,但是抽象类中不一定含有抽象方法
//抽象类中,不一定包含抽象方法
//意义: 不让别人直接创建抽象类的对象,必须创建子类使用
//没有抽象方法的类,也可以定义为抽象类
//目的: 不让你创建该类的对象,必须通过子类对象调用方法
/*
抽象父类和之前普通类的定义的区别?
1.不能直接创建对象
2.内部可以定义抽象方法
(2)定义子类,继承抽象父类
(3)子类中覆盖重写抽象父类中的所有抽象方法
去掉abstract关键字,添加{}
快捷键: ctrl + i
(4)创建子类对象
(5)子类对象调用方法
*/
public abstract class MyAbstractClass {
public void method() {
System.out.println("method....");
}
}
//定义抽象父类
public abstract class Animal10 {
private String name;//名字
//抽象方法
public abstract void eat();
public abstract void sleep();
//空参构造方法
public Animal10() {
}
//满参构造方法
public Animal10(String name) {
this.name = name;
}
//get和set方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//定义子类,继承抽象父类
public class Dog10 extends Animal10 {
//空参构造
public Dog10() {
super();
}
@Override
public void eat() {
System.out.println(getName()+"....狗吃骨头....");
}
@Override
public void sleep() {
System.out.println(getName()+"....狗瞪着眼珠子睡觉....");
}
}
//定义抽象子类继承抽象父类
public abstract class Cat10 extends Animal10 {
//空参构造
public Cat10() {
}
//满参构造
public Cat10(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(getName()+"....猫吃鱼....");
}
/*
抽象父类Animal中有两个抽象方法eat和sleep
子类只重写了一个抽象方法eat,相当于子类自己内部还有一个抽象方法
所以子类必须定义为抽象类
public abstract void sleep();
*/
}
//定义子类继承Cat10
public class BoSiCat10 extends Cat10 {
//空参构造
public BoSiCat10() {
}
//满参构造
public BoSiCat10(String name) {
super(name);
}
@Override
public void sleep() {
System.out.println(getName()+"....波斯猫...蹬着腿睡...");
}
}
//测试类
public class Demo10AbstractTeDian {
public static void main(String[] args) {
//错误: 不能直接创建抽象类的对象
//Animal a = new Animal();
//创建Dog10类的对象: 空参构造
Dog10 dog = new Dog10();
//调用set方法给成员变量赋值
dog.setName("哈士奇");
//调用成员方法
dog.eat();
dog.sleep();
//创建BoSiCat10类的对象: 满参构造
BoSiCat10 cat =
new BoSiCat10("小花");
cat.eat();
cat.sleep();
}
}