继承是面向对象三大特征之一,可以让类跟类之间产生子父的关系。可以把多个子类中重复的代码抽取到父类中,子类可以直接使用,并且也可以在父类的基础上增加其他功能,使得子类更加强大,减少代码的冗余,提高代码的复用性
当类与类之间,存在相同(共性)的内容,并满足子类是父类中的一种,就考虑使用继承,来优化代码
extends
,用这个关键字,我们可以让一个类和另一个类建立起继承关系
public class 子类 extends 父类 {}
子类访问父类的权限:
父类中:
public
修饰的——不能访问 private
修饰的——不能访问public
修饰的——能访问 private
修饰的——能访问(但不能直接使用)public
修饰的——能访问 private
修饰的——不能访问构造方法不能继承,但可以调用
Object
类(Java内部的一个类)TestStudent
的class
文件会在方法区加载,main
方法进入栈区main
方法的第一行代码,遇到新的Zi
类,则将Zi
的class
文件在方法区加载,加载发现有父类,则会再给父类Fu
的class
文件在方法区加载(这里应该还要再加载Fu
类的默认父类Object
类,但是此处省略了),代码的左边会在栈区开辟一个存放地址值的内存空间,代码的右边new
关键字会在堆区开辟一个空间(和以前的内存不同,有继承关系,会将new
出来的这块内存一分为二,一部分记录父中的成员变量,另一部分会继承子中的成员变量),并默认初始化值,地址返回栈区sout
——打印语句的缩写,打印z
即打印该地址值z
的name
、age
和game
赋值,会先在堆区开辟的空间的存储子类的空间中查找,若没找到再去存储父类的空间查找,找到并赋值main
方法执行完了,main
方法出栈,方法出栈,原本里面的变量也就消失了,堆区的对象也就不再被调用,就会被回收处理与之前非继承的内存图的不同:
private
修饰,子类中就不能用变量名调用(子类会在堆区开辟的内存中寻找,但是是找不到的) 如果要在子类中调用它多层以上的父类中的方法时,子类不会从下而上逐级寻找父类中是否有该方法,而是父类会提前自上而下的依次传递一个可能被经常调用的方法,并放到一个表中(虚方法表)——这样会大大的提高性能
虚方法表:
private
修饰static
修饰final
修饰只有父类中的虚方法才能被子类继承
TestStudent
类的字节码文件会在方法区加载,main
方法进入栈区Zi
类,则开始在方法区加载zi
的字节码文件,发现有父类,继续加载父类Fu
的字节码文件,还有Fu
类的父类Object
类,都会被加载到方法区,同时开始自上而下依次传承并加载虚方法表,代码的左边会在栈区开辟一个存放地址值的内存空间,代码的右边new
关键字会在堆区开辟一个空间(一分为二,因为没有写成员变量,所以看着是空的)z
调用ziShow()
方法,先判断该方法在不在虚方法表中,在的话可以直接调用,发现这些方法在虚方法表中,会直接将这些方法依次加载进栈中开始执行fushow2()
不是虚方法,则不会在虚方法表里面找,会先在本类中寻找该方法,若没有找到,则继续向上父级中寻找,查找到后是私有的,则会报错 先在局部位置找,本类成员位置找,父类成员位置找,主句往上
示例代码:
public class Test {
public static void main(String[] args) {
Zi zi = new Zi();
zi.ziShow();
}
}
class Ye {
String name = "Ye";
}
class Fu extends Ye {
String name = "Fu";
}
class Zi extends Fu {
String name = "Zi";
public void ziShow() {
String name = "ziShow";
System.out.println(name); // 输出: ziShow
System.out.println(this.name); // 输出: Zi
System.out.println(super.name); // 输出: Fu
// 但不可以再访问父级之上的类了
}
}
super
调用,直接访问父类示例代码:
public class Test02 {
public static void main(String[] args) {
Student s = new Student();
s.lunch();
OverseasStudent os = new OverseasStudent();
os.lunch();
}
}
class Person {
public void eat() {
System.out.println("吃馒头");
}
public void drink() {
System.out.println("喝米汤");
}
}
class Student extends Person{
public void lunch() {
// 先在本类中查看eat和drink方法,就会调用子类的,如果没有,就会调用从父类继承下来的eat和drink方法
eat(); // 输出:吃馒头
drink(); // 输出:喝米汤
// 直接调用父类中的eat和drink方法
super.eat(); // 输出:吃馒头
super.drink(); // 输出:喝米汤
}
}
class OverseasStudent extends Person {
public void lunch() {
this.eat(); // 输出:吃意大利面
this.drink(); // 输出:喝凉水
super.eat(); // 输出:吃馒头
super.drink(); // 输出:喝米汤
}
// 方法的重写
@Override
public void eat() {
System.out.println("吃意大利面");
}
@Override
public void drink() {
System.out.println("喝凉水");
}
}
代码中还用到了方法的重写
当父类的方法不能满足子类现在的需求时,需要进行方法重写
书写格式:
在继承体系中,子类出现了和父类中一模一样的方法声明时,我们就称子类这个方法时重写的方法
@Override
重写注释:
@Override
是放在重写后的方法上,校验子类重写时语法生否正确@Override
注释,代码安全,优雅方法重写的本质:
自上而下创建虚方法表时,如果发生了重写,则会覆盖虚方法表中的内容,进而达成重写的目的
protected
< public
)示例代码:
父类:
public class Person {
String name;
int age;
public Person() {
System.out.println("父类的构造方法");
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
子类:
public class Student extends Person{
public Student() {
//子类构造方法中隐藏的super()去访问父类的无参构造——且必须写在第一行,不写系统会自动补充空参构造
super();
System.out.println("子类的无参构造");
}
//如果想调用父类的有参构造,必须手动写super进行调用
public Student(String name, int age) {
// 将参数传递给父类的有参构造
super(name, age);
}
}
测试类:
public class Test {
public static void main(String[] args) {
// 创建学生对象
Student s = new Student();
System.out.println(s.name + ", " + s.age);
// 有参构造创建
Student s1 = new Student("xiaoming", 19);
System.out.println(s1.name + ", " + s1.age);
}
}
运行结果:
访问特点:
super
调用super()
this
和super
使用总结this
:理解为一个变量,表示当前方法调用者的地址值super
:代表父类存储空间关键字 | 访问成员变量 | 访问成员方法 | 访问构造方法 |
---|---|---|---|
this | this.成员变量 访问本类成员变量 | this.成员方法(…) 访问本类成员方法 | this(…) 访问本类构造方法 |
super | super.成员变量 访问父类成员变量 | super.成员方法(…) 访问父类成员方法 | super(…) 访问父类构造方法 |
this
访问本类构造方法:
public class Student {
String name;
int age;
String school;
public Student() {
// 表示调用本类其他构造方法——且必须放在第一行
// 细节:虚拟机就不会再添加super()
this(null, 0, "java大学");
System.out.println("1234"); // 1234
}
public Student(String name, int age, String school) {
this.name = name;
this.age = age;
this.school = school;
}
}
测试类:
public class Test {
public static void main(String[] args) {
Student s = new Student();
System.out.println(s.school); // java大学
}
}
Javabean
类员工类:
public class Employee {
// 带有继承结构的标准的Javabean类
private String id;
private String name;
private int salary;
public Employee() {
}
public Employee(String id, String name, int salary) {
this.id = id;
this.name = name;
this.salary = salary;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public void work() {
System.out.println("员工正在工作");
}
public void eat() {
System.out.println("员工正在吃米饭");
}
}
经理类:
public class Manager extends Employee{
private double bouns;
public Manager() {
}
// 全参构造: 父类 + 子类
public Manager(String id, String name, int salary, double bouns) {
super(id, name, salary);
this.bouns = bouns;
}
public double getBouns() {
return bouns;
}
public void setBouns(double bouns) {
this.bouns = bouns;
}
@Override
public void work() {
System.out.println("管理其他人");
}
}
厨师类:
public class Cook extends Employee{
@Override
public void work() {
System.out.println("炒菜");
}
}
测试类:
public class Test {
public static void main(String[] args) {
Manager m = new Manager("001", "小飞", 15000, 8000.0);
System.out.println(m.getId() + ", " + m.getName() + ", " +
m.getSalary() + ", " + m.getBouns());
m.work();
m.eat();
Cook c = new Cook();
c.setId("002");
c.setName("小王");
c.setSalary(9000);
System.out.println(c.getId() + ", " + c.getName() + ", " +
c.getSalary());
c.work();
c.eat();
}
}
运行结果:
Over!