目录
[3.1 继承中成员变量的特点(成员变量的访问顺序)](#3.1 继承中成员变量的特点(成员变量的访问顺序))
[3.2 继承中成员方法的特点(调用遵循就近原则,子类可重写父类方法)](#3.2 继承中成员方法的特点(调用遵循就近原则,子类可重写父类方法))
[3.3 继承中构造方法的特点](#3.3 继承中构造方法的特点)
[3.4 带有继承结构的标准 Javabean 类(继承的练习)](#3.4 带有继承结构的标准 Javabean 类(继承的练习))
[4.1 继承结构的内存图解](#4.1 继承结构的内存图解)
[4.2 子类继承父类时,父类中的构造方法、成员变量、成员方法到底哪些能被子类继承,哪些不能直接使用?](#4.2 子类继承父类时,父类中的构造方法、成员变量、成员方法到底哪些能被子类继承,哪些不能直接使用?)
[1 构造方法不能被继承,但可以用super关键字调用](#1 构造方法不能被继承,但可以用super关键字调用)
[2 成员变量可以被子类继承](#2 成员变量可以被子类继承)
[3 成员方法要看修饰符](#3 成员方法要看修饰符)
面向对象三大特征:封装、继承、多态
一.什么是继承?继承的好处?
继承:
继承 是类与类之间的一种父子关系 ,Java中提供关键字 extends,用于建立类与类之间的关系。
继承的好处:
- 可以把多个子类中重复的代码抽取到父类中 了,子类可以得到父类的属性和行为,提高代码的复用性
- 子类可以在父类的基础上,增加其他的功能,使子类更强大
继承的格式:
public class 子类 extends 父类 { }
如何设计继承结构:
当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就可以考虑使用**继承,**来优化代码。
- 第一步:利用画图法解决
- 第二步:分类
- 第三步:抽取共性的内容不断往上抽取(从下往上画)
- 第四步:写代码(从上往下写)
二.继承的特点
Java只支持单继承,不支持多继承,但支持多层继承
**单继承:**一个子类只能继承一个父类
**不能多继承:**子类不能同时继承多个父类
多层继承:子类 直接继承的父类叫做直接父类 ,间接继承的爷爷叫做间接父类
Java中的顶级父类 为:**Object,**每一个类都直接或者间接的继承于 Object
三.继承中的成员特点
3.1 继承中成员变量的特点(成员变量的访问顺序)
在 Java 继承中,如果子类和父类中出现了同名成员变量 ,访问时遵循**"就近原则"**。
继承中成员变量的访问特点:就近原则
- 先在局部位置找,本类成员位置找,父类成员位置找,逐级往上(类似于js中的原型链)
this、super关键字
| 写法 | 访问位置 |
|---|---|
| name | 就近原则,先从局部变量位置找,再找本类成员变量,再找父类成员变量,逐级往上 |
| this.name | 从本类成员变量位置找,再找父类成员变量,逐级往上 |
| super.name | 从**父类成员变量找,**逐级往上 |
其中this关键字 代表当前对象自己,通常用在本类里,super关键字代表父类里的东西,通常用在子类继承父类时。
this内存的角度:表示当前方法调用者 的地址值
this代码的角度:利用this可以直接调用本类成员(比如:成员变量,成员方法,构造方法等)
super关键字:代表使用父类中的内容
|-----------|-----------------------------|---------------------------------|-----------------------------|
| 关键字 | 成员变量 | 成员方法 | 构造方法 |
| this | this.成员变量 访问本类成员变量 | this.成员方法(..) 访问本类成员方法 | this(...) 访问本类构造方法 |
| super | super.成员变量 访问父类成员变量 | super.成员方法(..) 访问父类成员方法 | super(...) 访问父类构造方法 |
java
class Fu {
String name = "Fu";
}
class Zi extends Fu {
String name = "Zi";
public void show() {
String name = "show";
System.out.println(name); // 访问局部变量
System.out.println(this.name); // 访问子类成员变量
System.out.println(super.name); // 访问父类成员变量
}
}
public class Test {
public static void main(String[] args) {
Zi z = new Zi();
z.show(); //输出 show Zi Fu
}
}
3.2 继承中成员方法的特点(调用遵循就近原则,子类可重写父类方法)
书写规则:
- 把多个子类中共性的成员方法抽取到父类当中。(抽取共性)
调用规则:
- 遵守就近原则:子类对象调用方法时,先在子类中找,子类没有再去父类中找。
方法重写:
什么是方法重写?
在继承 体系中,子类出现了和父类中一模一样的方法声明。我们就称子类的这个方法是重写的方法
重写的方法上需要加**@override 注解**,校验重写的语法是否正确。
在子类中,重写父类方法需要保证方法申明保持一致:
- 方法名相同;
- 参数列表相同,包括参数个数、参数类型、参数顺序;
- 返回值类型相同,或者返回值类型满足兼容关系;
- 子类方法访问权限不能比父类更小
- 方法体可以不同。
子类重写父类方法后,可以通过**super.方法名()**调用父类方法。
方法重写使用场景 :
如果父类的方法不能满足子类的要求了,子类中可以把该方法再重写一遍
方法重写的要注意:
- 如果父类里面的代码,我一行都不想用,此时把子类中的方法体重新完整写一遍即可
- 如果父类里面的代码我还想用,此时我只是在父类的基础上再加其他的逻辑。此时可以先通过super关键字调用父类的方法得到一个结果,再对这个结果进行操作即可
关于 final 关键字修饰的重写:
1. final 修饰类
final 修饰的类不能被继承,也就是说该类不能作为父类。(不能被继承就谈不上重写了)
2. final 修饰方法
final 修饰的方法可以被子类继承和调用,但不能被子类重写。
3. final 修饰变量
final 修饰的成员变量在子类可访问的情况下可以被继承使用,但不能被重新赋值。
final 修饰的局部变量不能被重新赋值,并且局部变量不存在继承问题。
不能被重写的方法总结:1. private 私有方法不能被重写
private 方法只能在本类内部访问,子类无法直接访问父类的 private 方法,因此谈不上重写。如果子类中定义了一个同名方法,它只是子类自己的新方法,不是方法重写。
2. static 静态方法不能被重写
static 方法属于类,不属于对象,而方法重写是基于对象的行为。
如果子类中定义了和父类同名的静态方法,这种情况叫方法隐藏,不叫方法重写。
3. final 最终方法不能被重写
final 方法表示最终方法,子类可以继承和调用它,但不能修改它的实现。
举个例子:
java
//父类智能设备
public class SmartDevice {
private String name;
private int price;
public double totalPrice(){
if(price>=0 && price <1000){
return price;
} else if (price < 5000) {
return price*0.9;
} else if (price < 10000) {
return price*0.8;
} else {
return price*0.7;
}
}
....//get/set方法
}
//子类手机
public class Phone extends SmartDevice{
//重写注解
@Override
public double totalPrice(){
//用super关键字拿到父类的结果再操作
return super.totalPrice()*0.9;
}
}
//测试类主函数
public class Test {
public static void main(String[] args) {
Phone p1 = new Phone();
p1.setName("oppo");
p1.setPrice(3999);
System.out.println(p1.totalPrice()); //输出:3239.19
}
}
3.3 继承中构造方法的特点
- 子类不能继承 父类的构造方法,只能通过super关键字调用。
- 子类的构造方法第一行有一个默认的super(),先访问父类的无参构造。
- 如果想要访问父类的带参构造,super必须手动写上,不能省略。子类的带参构造函数的参数是父类加子类的参数 ,用super写父类,子类自身的参数自己赋值。
- 再创建对象 的时候,先执行父类 的构造方法,再执行子类的构造方法。
可以使用快捷键生成子类构造函数,此时会弹出两次弹框,第一次询问写父类构造,第二次询问子类本身构造。
java
//父类
public class Person {
String name;
int age;
//父类无参构造
public Person() {
System.out.println("父类的空参构造执行了~");
}
//父类带参构造
public Person(String name, int age) {
System.out.println("父类的带参构造执行了~");
this.name = name;
this.age = age;
}
}
//子类
public class Student extends Person{
String grade;
//子类不会继承父类的构造函数,需要自己写构造函数,用super继承父类
//子类构造方法
// 1.空参构造
public Student() {
System.out.println("子类student的空参构造执行了");
}
//2.带全部参数构造(父+子)
public Student(String name,int age,String grade){
//父类中的属性:通过super(参数)的形式传递给父类的构造方法赋值
super(name,age);
//子类中的属性:自己赋值
this.grade = grade;
System.out.println("子类student的带参构造执行了");
}
}
//主函数
public class Test {
public static void main(String[] args) {
Student s = new Student("李华",18,"7");
}
}
3.4 带有继承结构的标准 Javabean 类(继承的练习)
书写一个完整的继承体系,要求私有化成员变量、get/set方法、构造方法、其他的成员方法
本科学生:
属性:姓名、年龄、年级
行为:吃饭、睡觉、学习(攻读学士学位)
专业课老师:
属性:姓名、年龄、学科
行为:吃饭、睡觉、教书(教专业课知识)
硕士研究生:
属性:姓名、年龄、年级
行为:吃饭、睡觉、学习(攻读硕士学位)
通识课老师:
属性:姓名、年龄、学科
行为:吃饭、睡觉、教书(教通识课知识)
过了一段时间,硕士研究生住宿条件升级,在豪华版学生公寓睡觉
java
//Person父类
public class Person {
//属性
private String name;
private int age;
//行为
public void eat(){
System.out.println(name + "要吃饭");
}
public void sleep(){
System.out.println(name + "要睡觉");
}
//构造函数
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;
}
}
//Student子类
public class Student extends Person {
//属性
private int grade;
//行为
public void study(){
System.out.println(super.getName()+"要学习");
}
//构造函数
public Student() {
}
public Student(String name, int age, int grade) {
super(name, age);
this.grade = grade;
}
//get/set
public int getGrade() {
return grade;
}
public void setGrade(int grade) {
this.grade = grade;
}
}
//本科类
public class Benke extends Student{
public Benke() {
}
public Benke(String name, int age, int grade) {
super(name, age, grade);
}
@Override
public void study(){
System.out.println(super.getName()+"学习本科知识");
}
}
//测试类
public class Test {
public static void main(String[] args) {
Benke s1 = new Benke("李华",18,2);
s1.study();
s1.study();
s1.sleep();
}
}
四.继承的内存结构,继承的底层原理
子类能调用 不等于 子类能继承
4.1 继承结构的内存图解

程序在刚开始运行的时候,把测试类的字节码文件(class文件)加载到方法区 当中,然后执行Main方法,此时main方法进入栈内存,开始执行第一行代码。
- 第一行代码用到了 Student 类创建对象 ,这时本来应该把 Student 的字节码文件放到内存当中,但是它发现 Student 继承了父类 Person ,在加载class文件时,他是先加载父类,再加载子类到方法区。
- 然后开始创建对象,先执行等号左边 表示在栈里声明一个变量,变量名字叫stu,数据类型是Student,他表示:这个变量以后只能存储 Student 这个对象 的内存地址。
- 再执行等号右边, 有new 关键字,代表在堆 里开辟一块空间,这块空间就是对象,他有内存地址。 这个对象还会记录下面字节码文件里方法的内存地址。
- 在继承的子类对象 中,堆里开辟的空间会分成两块 ,一块用来存父类继承 下来的数据 ,一块用来存本类自身的数据。然后进行数据初始化(0,null)
- 最后进行等号赋值 时,就是把右边堆里的对象地址 赋值给左边栈里的 stu 变量。
- 第二行代码打印 stu 变量打印的是对象的内存地址。
- 第三四行代码给对象里的属性进行赋值 :先找子类 里面有没有自己独有的属性,有就给子类里的属性赋值。没有再找继承下来的父类里的属性进行赋值。
注意:
1.加载字节码文件的顺序不同:
- 没有继承时,加载字节码文件是创建谁就加载谁的字节码文件。
- 有了继承之后,加载字节码文件是先加载父类字节码文件,再加载子类字节码文件。
2.对象内部的数据不同:
- 有了继承之后,子类对象的内存被一分为二,一半存父类继承下来的属性,一半存自身独有的属性。
4.2 子类继承父类时,父类中的构造方法、成员变量、成员方法到底哪些能被子类继承,哪些不能直接使用?
1 构造方法不能被继承,但可以用super关键字调用
构造方法名字必须和类名一致。
- 子类不能继承父类的构造方法。
- 但是子类创建对象时,会先调用父类构造方法,再执行子类构造方法。
2 成员变量可以被子类继承
父类的私有成员变量会随着父类部分存在于子类对象中,但子类不能直接访问调用,需要通过 public 的 get/set 方法间接访问。
3 成员方法要看修饰符
虚方法可以被继承。
虚方法:
虚方法就是普通的成员方法,非final、非static、非private修饰的方法。
Java中的顶级类是Object类,Object类有5个虚方法:clone(),equals(),finalize(),hashCode(),toString()
虚方法的继承规则
在一个继承结构中,顶级父类会把自己的虚方法 都写到一张虚方法表中,在继承中会把自己的虚方法表交给自己的子类,子类会在继承的虚方法表上加上自己独有的虚方法。
当创建子类的对象时,调用这个对象里的show方法,就是在这个虚方法表里找show方法的内存地址,然后再把show方法加载到栈里运行。
如果在A类里重写了show方法,**方法重写本质上时替换虚方法中的地址,**以后再调用show方法,调用的就是新地址里的方法。

通过虚方法表继承
先在方法区保存父类的字节码文件,字节码文件里包含父类的所用成员方法信息,虚方法表里的方法可以被继承 ,通过虚方法表去继承父类。当访问子类里没有的虚方法,会一级一级向上找虚方法表。像private修饰的方法不在父类虚方法表中,但是是存在于父类的类里。

| 内容 | 子类能不能用 | 说明 |
|---|---|---|
| 构造方法 | 不能继承 | 但可以用 super() 调用 |
| public 成员变量 | 可以继承可以访问 | 正常使用 |
| private 成员变量 | 可以继承,不能直接访问 | 可以通过 get/set 间接访问 |
| public 普通方法/虚方法 | 可以继承 | 子类可以直接调用 |
| private 私有方法 | 不能被继承,不能被调用,不能重写 | 子类看不见 |
| static 静态方法 | 不能被继承,可以被调用,不能重写 | 叫方法隐藏 |
| final 最终方法 | 不能被继承,可以被调用,不能重写 | 不能重写 |
五.Java中的权限修饰符
权限修饰符作用范围从小到大(private < 默认权限 < protected < public)
| 修饰符 | 同一个类 | 本包中其他类 | 不同包下的子类 | 不同包下的无关类 |
|---|---|---|---|---|
| private | √ | |||
| 空着 / 缺省 / 默认 | √ | √ | ||
| protected | √ | √ | √ | |
| public | √ | √ | √ | √ |