1. 继承
1.1 面向对象的三大特征
- 封装
把离散的数据/行为封装成一个整体 - 继承
- 多态
1.2 什么是继承/继承有什么好处
- 继承是类和类之间的父子关系, Java中提供关键字
extends, 用于建立类与类之间的关系
eg:
定义了一个Student类, 继承Person类
java
public class Student entends Person{
子类 父类
/派生类 /基类/超类
}
- 好处:
- 可以把多个子类中的重复代码抽取到父类中, 提高代码复用性
- 子类可以在父类的基础上, 增加其他功能, 使子类强大
eg:
Person.java
java
package oopextends.text1;
public class Person {
String name;
int age;
public void eat(){
System.out.println("吃饭");
}
}
Student.java
java
package oopextends.text1;
public class Student extends Person{
//子类特有的内容
//特有属性
String grade;
public void study(){
System.out.println("学生在学习");
}
}
Teacher.java
java
package oopextends.text1;
public class Teacher extends Person{
//子类特有的内容
//特有属性
String subject;
public void teach(){
System.out.println("老师在教书");
}
}
Text.java
java
package oopextends.text1;
public class Text {
static void main() {
Student s = new Student();
s.name = "张三";
s.age = 18;
s.grade = "高三";
s.study();
s.eat();
Teacher t = new Teacher();
t.name = "李四";
t.age = 35;
t.subject = "数学";
t.teach();
t.eat();
}
}
1.3 如何设计继承结构
- 当类与类之间, 存在形同(共性)的内容, 并满足子类是父类的一种, 就可以考虑使用继承, 来优化代码
- 父类起名要能代表所有的子类
eg:
现在有三个电子设备, 请设计他们的继承结构
- 安卓手机:
- 属性: 品牌, 价格
- 行为: 打电话, 发短信, nfc功能
- 苹果手机:
- 属性: 品牌, 价格
- 行为: 打电话, 发短信
- 笔记本电脑
- 属性: 品牌, 价格
- 行为: 编程
设计继承结构:
画图, 从下到上 -> 分类 -> 抽取共性的内容不断往上抽取(从下往上抽) -> 写代码(从下往上写)

1.4 继承的特点
-
IDEA中子类特有的是黑色加粗, 没有加粗的是父类的

-
Java只支持单继承 , 不支持多继承 , 但支持多层继承
- 支持单继承: 一个子类只能继承一个父类
- 不能多继承: 子类不能继承多个父亲
- 多层继承:
- 直接父类 与间接父类(没有爷类的概念)
- 顶级父类
Object(没有父类的父类默认为Object)
1.5 继承中成员的特点
1.5.1 成员变量的特点
-
书写规则: 把多个子类中相同的属性抽取父亲当中(抽取共性)
-
调用规则: 遵守就近原则
-
就近原则: 谁离我近, 我就用谁
-
eg:


注意: 最多到父类, 父类的父类就无法获取了
注意: 本类没有也最高只能到第一个父类

-
1.5.2 成员方法的特点
-
书写规则: 把多个子类中共性的成员方法抽取到父类中(抽取共性)
-
调用规则: 遵守就近原则

-
方法的重写: 在子类中, 把父类的方法再写一遍, 方法申明保持一致(方法的修饰符, 名字, 小括号内的参数列表 )
-
使用场景: 如果父类的方法不能满足子类的要求了, 子类可以把方法再重写一边
-
小技巧: Alt+Ins / Alt + Fn + Del 选择重写方法, 可自动生成重写方法, 或者直接输入想要重写的方法的名称,回车

-
注意:
- 如果父类里面的代码,我一行都不想用,此时把子类中的方法体重新完整写一遍即可(练习一)
- 如果父类里面的代码我还想用,此时我只是在父类的基础上再加其他的逻辑此时可以先通过super关键字调用父类的方法得到一个结果,再对这个结果进行操作即可(练习二)
-

java
package oopextends.text3;
public class FirstGenerationPhone {
public void call(){
System.out.println("打电话");
}
}
java
package oopextends.text3;
public class SecondGenerationPhone extends FirstGenerationPhone{
public void sendMessage(){
System.out.println("发短信");
}
}
java
package oopextends.text3;
public class ThirdGenerationPhone extends SecondGenerationPhone{
//注释: 给程序员看的
//注解: 给虚拟机看的
@Override//注解
public void call() {
System.out.println("视频通话");
}
public void playGame(){
System.out.println("玩游戏");
}
}
java
package oopextends.text3;
public class Text {
static void main() {
FirstGenerationPhone phone1 = new FirstGenerationPhone();
phone1.call();
SecondGenerationPhone phone2 = new SecondGenerationPhone();
phone2.call();
phone2.sendMessage();
ThirdGenerationPhone phone3 = new ThirdGenerationPhone();
phone3.call();
phone3.sendMessage();
phone3.playGame();
}
}

java
package oopextends.text4;
public class SmartDevice {
//属性
String name;
double price;
//行为
public double payment(){
if(price >= 0 && price <= 1000){
return price ;
}else if(price > 1000 && price <= 5000){
return price * 0.9;
}else if(price > 5000 && price <= 10000){
return price * 0.8;
}else if(price > 10000){
return price * 0.7;
}else{
return 0;
}
}
}
java
package oopextends.text4;
public class Phone extends SmartDevice{
@Override
public double payment() {
return super.payment()*0.9;
}
}
java
package oopextends.text4;
public class Laptop extends SmartDevice{
}
java
package oopextends.text4;
public class Pad extends SmartDevice {
}
java
package oopextends.text4;
public class Text {
static void main() {
Phone p = new Phone();
p.name = "iphone";
p.price = 6000;
System.out.println(p.payment());
Laptop l = new Laptop();
l.name = "lenovo";
l.price = 8000;
System.out.println(l.payment());
}
}
-
方法重写的注意事项和要求:
- 重写方法的名称、形参列表必须与父类中的一致,方法体按照实际需求书写
- 了解: 子类重写父类方法时,访问权限子类必须大于等于父类(空着不写<protected<public)
- 了解: 子类重写父类方法时,返回值类型子类必须小于等于父类
- 建议: 重写方法的申明和父类保持一致
- final修饰类为最终类, 里面所有的方法不能被重写
- private私有方法, static静态方法, final最终方法不能被重写
-
成员方法特点总结
- 继承中成员方法的特点:
- 书写规则: 抽取子类中的共性行为
- 调用规则: 就近原则
- this调用: 先访问本类, 再访问父亲
- super调用: 直接访问父亲
- 什么时方法重写?
- 在继承体系中, 子类出现了和父亲中一模一样的方法声明, 我们就称这个子类的这个方法是重写的方法
- 重写的方法要加上==
@Override注解==, 校验重写的语法是否正确
- 什么时候用方法重写?
当父类中的方法不能满足子类的要求, 需要进行方法的重写 - 重写方法有那些基本要求?
- 子类重写的方法申明跟父类保持一致
- private私有方法, static静态方法, final最终方法不能被重写
1.5.3 构造方法的特点
- 父类中的构造方法不会被子类继承, 只能通过super关键字调用
理解: 构造方法的方法名要与类名一致, 继承父类的名字就不一致了, 所以不能继承

小细节
- 如果子类的构造方法未写super, JVM会加一个默认的super(), 先访问父类的无参构造
- 要访问父类带参构造, super(参数)必须写上, 不能省略
- 在创建对象的时候, 先执行父类的构造方法, 再执行子类的构造方法
- 低版本super(参数)必须写在带参构造方法的第一行, 但新版本(jdk25)不用
tips
- Alt+Ins / Alt + Fn + Del 用前面的方法也可以
java
package oopextends.text5;
public class Person {
//属性
String name;
int age;
//构造方法
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
java
package oopextends.text5;
public class Student extends Person{
//属性
String grade;
//构造方法
public Student() {
}
public Student(String name, int age, String grade) {
super(name, age);
this.grade = grade;
}
}
java
package oopextends.text5;
public class Teacher extends Person{
//属性
String subject;
//构造方法
public Teacher() {
}
public Teacher(String name, int age, String subject) {
super(name, age);
this.subject = subject;
}
}
java
package oopextends.text5;
public class Text {
static void main() {
Student s = new Student("张三", 23, "高三");
System.out.println(s.name + " " + s.age + " " + s.grade);
}
}
1.6 this关键字和super关键字
-
this内存角度: 表示当前方法的调用者的地址值
-
this代码角度: 利用this可以直接调用本类的成员(比如: 成员变量, 成员方法, 构造方法等)
-
super关键字: 代表使用父类中的内容

-
this(...) 访问本类的构造方法
细节:
若子类中有多个构造方法, 不能用this()互相调用, 一定要预留一个调用父类的构造方法
若构造方法中写上了this(), 就不能再写super()了, JVM也不会自动添加super()

1.7 继承综合练习
1.7.1 学生老师

继承结构设计图:

java
package oopextends.text7;
public class Person {
//属性
private String name;
private int age;
//构造方法
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//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 void eat(){
System.out.println(name+"在吃饭");
}
public void sleep(){
System.out.println(name+"在睡觉");
}
}
java
package oopextends.text7;
public class Student extends Person{
//属性
private String grade;
//构造方法
public Student() {
}
public Student(String name, int age, String grade) {
super(name, age);
this.grade = grade;
}
//get/set
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
//行为
public void study(){
System.out.println("学生正在学习");
}
}
java
package oopextends.text7;
public class Teacher extends Person{
//属性
private String subject;
//构造方法
public Teacher() {
}
public Teacher(String name, int age, String subject) {
super(name, age);
this.subject = subject;
}
//get/set
public String getSubject() {
return subject;
}
public void setSubject(String subject) {
this.subject = subject;
}
//行为
public void teach(){
System.out.println("老师正在教书");
}
}
java
package oopextends.text7;
public class BeachelorStudent extends Student{
//没有独有属性
//构造方法
public BeachelorStudent() {
}
public BeachelorStudent(String name, int age, String grade) {
super(name, age, grade);
}
//get/set 无属性, 不用写
//重写学习方法
@Override
public void study() {
System.out.println("本科同学正在攻读学士学位");
}
}
java
package oopextends.text7;
public class MasterStudent extends Student{
//没有独有属性
//构造方法
public MasterStudent() {
}
public MasterStudent(String name, int age, String grade) {
super(name, age, grade);
}
//重写学习方法
@Override
public void study() {
System.out.println("硕士研究生同学正在攻读硕士学位");
}
//重写睡觉方法
@Override
public void sleep() {
System.out.println("硕士研究生住宿条件升级, 在豪华版学生公寓睡觉");
}
}
java
package oopextends.text7;
public class MajorTeacher extends Teacher{
//没有独有属性
//构造方法
public MajorTeacher() {
}
public MajorTeacher(String name, int age, String grade) {
super(name, age, grade);
}
//重写学习方法
@Override
public void teach() {
System.out.println("专业教师正在教授专业知识");
}
}
java
package oopextends.text7;
public class GeneralTeacher extends Teacher{
//没有独有属性
//构造方法
public GeneralTeacher() {
}
public GeneralTeacher(String name, int age, String grade) {
super(name, age, grade);
}
//重写学习方法
@Override
public void teach() {
System.out.println("通识课老师正在教授通识知识");
}
}
java
package oopextends.text7;
public class Text {
static void main() {
BeachelorStudent bs = new BeachelorStudent("张三", 18, "大一");
System.out.println(bs.getName()+","+bs.getGrade()+","+bs.getAge());
bs.study();
bs.sleep();
bs.eat();
MasterStudent ms = new MasterStudent("李四", 20, "硕士");
System.out.println(ms.getName()+","+ms.getGrade()+","+ms.getAge());
ms.study();
ms.sleep();
ms.eat();
MajorTeacher mt = new MajorTeacher("王五", 35, "教授");
System.out.println(mt.getName()+","+mt.getSubject()+","+mt.getAge());
mt.teach();
mt.sleep();
mt.eat();
GeneralTeacher gt = new GeneralTeacher("赵六", 40, "副教授");
System.out.println(gt.getName()+","+gt.getSubject()+","+gt.getAge());
gt.teach();
gt.sleep();
gt.eat();
}
}
1.7.2 黑马员工

继承结构设计图:

java
package oopextends.text8;
public class Employee {
//属性
private int id;
private String name;
private String job;
//构造方法
public Employee() {
}
public Employee(int id, String name, String job) {
this.id = id;
this.name = name;
this.job = job;
}
//set/get
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
//行为
public void work() {
System.out.println("员工正在工作");
}
}
java
package oopextends.text8;
public class Teacher extends Employee{
//无特有属性
//构造方法
public Teacher() {
}
public Teacher(int id, String name, String job) {
super(id, name, job);
}
//无set/get
//无特殊行为
}
java
package oopextends.text8;
public class AdminStaff extends Employee{
//无特有属性
//构造方法
public AdminStaff() {
}
public AdminStaff(int id, String name, String job) {
super(id, name, job);
}
//无set/get
//无特殊行为
}
java
package oopextends.text8;
public class Lecturer extends Teacher{
//无特有属性
//构造方法
public Lecturer() {
}
public Lecturer(int id, String name, String job) {
super(id, name, job);
}
//无set/get
//重写工作
@Override
public void work() {
System.out.println("讲师正在授课");
}
}
java
package oopextends.text8;
public class Maintainer extends AdminStaff{
//无特有属性
//构造方法
public Maintainer() {
}
public Maintainer(int id, String name, String job) {
super(id, name, job);
}
//无set/get
//重写工作
@Override
public void work() {
System.out.println("维护人员正在维护");
}
}
java
package oopextends.text8;
public class Buyer extends AdminStaff{
//无特有属性
//构造方法
public Buyer() {
}
public Buyer(int id, String name, String job) {
super(id, name, job);
}
//无set/get
//重写工作
@Override
public void work() {
System.out.println("采购人员正在采购");
}
}
java
package oopextends.text8;
public class Tutor extends Teacher{
//无特有属性
//构造方法
public Tutor() {
}
public Tutor(int id, String name, String job) {
super(id, name, job);
}
//无set/get
//重写工作
@Override
public void work() {
System.out.println("助教正在工作");
}
}
java
package oopextends.text8;
public class Text {
public static void main(String[] args) {
Lecturer l = new Lecturer(1, "张三", "讲师");
l.work();
Maintainer m = new Maintainer(2, "李四", "维护人员");
m.work();
Tutor t = new Tutor(3, "王五", "助教");
t.work();
Buyer b = new Buyer(4, "赵六", "采购人员");
b.work();
}
}
1.7.3 物流快递运费计算

java
package oopextends.text9;
public class Goods {
//属性
private String id;
private int weight;
private String name;
//构造方法
public Goods() {
}
public Goods(String id, int weight, String name) {
this.id = id;
this.weight = weight;
this.name = name;
}
//get/set
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//行为
public int getPrice(){
return weight*10;
}
}
java
package oopextends.text9;
public class SameCity extends Goods{
//无特殊属性
//构造方法
public SameCity() {
}
public SameCity(String id, int weight, String name) {
super(id, weight, name);
}
//无get/set
//重写快递费
@Override
public int getPrice() {
return super.getPrice()+10;
}
}
java
package oopextends.text9;
public class AirSend extends Goods {
//无特殊属性
//构造方法
public AirSend() {
}
public AirSend(String id, int weight, String name){
super(id,weight,name);
}
//无get/set
//重写快递费
@Override
public int getPrice(){
return super.getPrice()+15;
}
}
java
package oopextends.text9;
public class text {
public static void main(String[] args) {
SameCity sameCity = new SameCity("123", 10, "张三");
System.out.println(sameCity.getPrice());
AirSend airSend = new AirSend("123", 10, "李四");
System.out.println(airSend.getPrice());
}
}
1.8 (扩展)继承的内存结构/字节码文件详解
1.8.1 继承的底层原理
- 子类能调用≠子类能继承
1.8.2 子类真正能继承父类的东西
- 构造方法: 不能被子类继承, 可以用super关键字调用
- 成员变量: 可以被子类继承, private私有的也可以, 但是私有的无法直接调用
- 成员方法: 虚方法可以被继承(虚方法: 普通成员方法, 非final/static/private修饰的方法 )
- final修饰的最终方法不能被继承, 可以被调用
编译期: 一级一级往上找, 遍历整个继承链, 确定要调用的方法在A类里, 记录该方法的地址
运行期: 直接原型编译时确定的方法, 虽然降低了编译的速度, 但是运行的速度会得到保证 - static修饰的静态方法不能被继承, 可以被调用
编译期: 一级一级的往上找, 遍历整个继承链, 确定要调用的方法在A里面, 将对象调用方法直接修改为类名的调用方法
运行期: 直接运行 - private修饰的私有方法不能被继承, 不能被调用
- 方法重写: 子类替换虚方法表里方法的地址值
- final修饰的最终方法不能被继承, 可以被调用
1.8.3 继承结构的内存图解

1.9 Java中的权限修饰符
- 权限修饰符: 其实就是Java中的关键字, 用来控制一个成员被访问的范围
- 修饰的内容: 可以修饰成员变量, 成员方法, 构造方法... 等

- 作用范围从小到大(private<空着不写<protected<public)