继承
1.1 继承概述
代码解释:
创建一个学生类
public class Student {
private String sid;
private String name;
public void study(){ System.out.println(“努力学习”); }
// Alt+insert 一键生成get/set方法
public String getSid() { return sid; }
public void setSid(String sid) { this.sid = sid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
创建一个老师类
public class Teacher {
private String sid;
private String name;
public void teach(){ System.out.println(“教书育人”); }
// Alt+insert 一键生成get/set方法
public String getSid() { return sid; }
public void setSid(String sid) { this.sid = sid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
学生类和老师类的相同特征:
绿色区别属性相同:String sid,String name
成员变量相同 —> 该两个类的属性相同
灰色区域方法相同:get和set方法相同
提取相同特征代码后:
public class Student { public void study(){ System.out.println(“努力学习”); } }
public class Teacher { public void teach(){ System.out.println(“教书育人”); } }
将相同的特征使用一个类表示,让学生类和教师类和定义的类产生关系,这中产生的关系就是继承:
public class xxx{
private String sid;
private String name;
// Alt+insert 一键生成get/set方法
public String getSid() { return sid; }
public void setSid(String sid) { this.sid = sid; }
public String getName() { return name; }
public void setName(String name) { this.name = name; } }
继承概述:继承是面向对象三大特征之一。可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法
格式:public class 子类名 extends 父类名{ }
范例:public class Zi extends Fu { }
Fu: 是父类,也被称为基类,超类
Zi:是子类,也被称为派生类
代码实现:
// 父类
public class Fu { public void show(){ System.out.println(“父类被调用”); }}
// Zi需要使用extends关键字,继承父类
public class Zi extends Fu{ public void methods(){ System.out.println(“子类被调用”); } }
// 公共类
public class ExtendsDemo {
public static void main(String[] args) {
// 调用该方法需要创建该类的对象,没有构造方法默认一个无参构造方法
Fu fu = new Fu();
fu.show();
// 调用该方法需要创建该类的对象,没有构造方法默认一个无参构造方法
Zi zi = new Zi();
zi.methods();
// zi没法直接调用fu类的方法,需要继承父类
zi.show(); } }
继承中子类的特点
1、子类可以有父类的内容
2、子类可以有自己特有的内容
1.2 继承的好处和弊端
继承的好处:
1、提高了代码的复用性(多个类相同的成员—子类,可以放到同一个类中—父类)
2、提高了代码的维护性(如果方法的代码需要更改,只需要改一处即可)、
继承的弊端:
1、继承让类与类之间产生联系,类的耦合性增强了。
2、当父类发生改变时子类也要改变,削弱了子类的独立性。
何时使用继承?
继承体现的关系: is a —> 什么是什么的一种
假设法:我有两个类A和B,如果他们满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,就可以使用继承,相反类直接不存在关系就不能使用。
举例:苹果和水果,猫和动物,猫和狗(不可使用)
1.3 继承中变量的访问特点
代码实现:
// 父类:age父类定义的变量
public class Fu { public int age =40; }
// 子类
public class Zi extends Fu{
// 子类定义的变量height
public int height =175;
// 子类定义的变量age,子类中有的变量不在调用父类变量
public int age = 20;
public void show(){
// 子类内部定义的变量age
int age = 30;
// 子类继承父类定义的变量
System.out.println(age);
System.out.println(height); } }
// 1、输出: 40,175
// 2、输出: 20,175
// 2、输出: 30,175
// 公共类调用子类方法
public class ExtendsDemo {
public static void main(String[] args) {
// 调用该方法需要创建该类的对象
Zi zi = new Zi();
zi.show(); } }
子类继承父类变量顺序:
1、先在子类查询局部范围:内部变量(方法内的变量)
2、在查找子类成员范围:全局变量(方法外的变量)
3、最后查询父类成员范围
4、如果都没有变量就会报错(不考虑父类继承的父类)
1.4 继承中super关键字
代码实现:
// 父类:age父类定义的变量
public class Fu { public int age =40; }
// Zi需要使用extends关键字,继承父类
public class Zi extends Fu{
public int age = 20;
public void show(){
int age = 30;
System.out.println(age);
// 如何实现访问本类的成员变量age=20
// 使用this方法调用本类的成员变量age
System.out.println(this.age);
// 如何实现访问父类的成员变量age
// 使用super方法调用父类的成员变量age的
System.out.println(super.age); } }
// 公共类调用子类方法
public class ExtendsDemo {
public static void main(String[] args) {
// 调用该方法需要创建该类的对象
Zi zi = new Zi();
zi.show(); } }
super关键字总结:
super关键字的用法和this关键字的用法相似
this:代表本类对象的引用
super:代表父类对象的引用,可以访问父类的成员变量
关键字 访问成员变量 访问构造方法 访问成员方法
this this.成员变量 this(可带参数) this.成员方法(可带参数)
访问本类成员变量 访问本类构造方法 访问本类成员方法
super super.成员变量 super(可带参数) super.成员方法(可带参数)
访问父类成员变量 访问父类构造方法 访问父类成员方法
1.5 继承中构造方法的访问特点
// 父类
public class Fu {
public Fu(){ System.out.println(“Fu中的无参构造方法”); }
public Fu(int age){ System.out.println(“Fu中的带参构造方法”); } }
// 子类
public class Zi extends Fu{
public Zi(){ System.out.println(“Zi中的无参构造方法”); }
public Zi(int age){ System.out.println(“Zi中的带参构造方法”); } }
// 公共类
public class ExtendsDemo {
public static void main(String[] args) {
// 调用该方法需要创建该类的对象,无参构造方法
// 调用该无参构造方法时候,同时访问子类继承父类的无参构造方法
// 输出:Fu中的无参构造方法,Zi中的无参构造方法
Zi zi = new Zi();
// 带参构造方法
// 调用该带参构造方法时候,始终访问子类继承父类的无参构造方法
// 输出:Fu中的无参构造方法,Zi中的带参构造方法
Zi zi1 = new Zi(20); } }
子类中所有的构造方法默认都会访问父类中无参的构造方法
1、因为子类会继承父类中的数据,可能还会使用父类的数据。所以,子类初始化之前,一定要先完成父类数据的初始化,所以先访问父类的构造方法就是为了先完成父类初始化
2、每一个子类构造方法的第一条语句默认都是:super()
// Zi需要使用extends关键字,继承父类
public class Zi extends Fu{
public Zi(){
super();
System.out.println(“Zi中的无参构造方法”);
}
public Zi(int age){
super();
System.out.println(“Zi中的带参构造方法”);
} }
如果父类没有无参方法只有带参方法,子类没法初始化父类
1、可以子类直接通过super(…)加上参数
2、自己给出父类的无参构造方法
// 父类
public class Fu { public Fu(int age){ System.out.println(“Fu中的带参构造方法”); } }
// 子类
public class Zi extends Fu{
public Zi(){
super(20);
System.out.println(“Zi中的无参构造方法”); }
public Zi(int age){
super(20);
System.out.println(“Zi中的带参构造方法”); } }
public class ExtendsDemo {
public static void main(String[] args) {
// 调用该方法需要创建该类的对象,无参构造方法
Zi zi = new Zi();
// 带参构造方法
Zi zi1 = new Zi(20);
// 输出:Fu中的带参构造方法,Zi中的无参构造方法,Fu中的带参构造方法,Zi中的带参构造方 } }
1.6 继承中成员方法的访问特点
通过子类对象访问一个方法:
1、子类成员范围找
2、父类成员范围找
3、如果都没有就报错(不考虑父亲的父亲)
// 父类
public class Fu { public void show() { System.out.println(“Fu中的show()方法被调用”); } }
// Zi需要使用extends关键字,继承父类
public class Zi extends Fu{
public void method() { System.out.println(“Zi中的show()方法被调用”); }
public void show() {
// 通过super关键字调用父类show()
super.show();
System.out.println(“Zi中的show()方法被调用”); ;} }
// 公共类
public class ExtendsDemo {
public static void main(String[] args) {
// 调用该方法需要创建该类的对象,无参构造方法
Zi zi = new Zi();
// 调用方法zimethod方法
zi.method();
// 子类继承父类 ,Fu中的show()方法被调用
zi.show(); } }
// 输出:Zi中的show()方法被调用;Fu中的show()方法被调用;Zi中的show()方法被调用
1.7 super内存图
java的堆栈内存Java把内存分成两种,一种叫做栈内存,一种叫做堆内存
在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配。当在一段代码块中定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后,java会自动释放掉为该变量分配的内存空间,该内存空间可以立刻被另作他用。
堆内存用于存放由new创建的对象和数组。在堆中分配的内存,由java虚拟机(JVM)自动垃圾回收器来管理。在堆中产生了一个数组或者对象后,还可以在栈中定义一个特殊的变量,这个变量的取值等于数组或者对象在堆内存中的首地址,在栈中的这个特殊的变量就变成了数组或者对象的引用变量,以后就可以在程序中使用栈内存中的引用变量来访问堆中的数组或者对象,引用变量相当于为数组或者对象起的一个别名或者代号。
// 父类
public class Fu {—> 堆内存;子类空构造方法被访问,子类默认super
public int age = 40;—> 堆内存,存储父类初始化数据
public Fu(){ System.out.println(“Fu中的无参构造方法”); }—> 栈内存
栈内存先输出Fu的无参构造方法,方法结束后从栈内存消失
public Fu(int age){ System.out.println(“Fu中的带参构造方法”); } }
// 子类
public class Zi extends Fu{
public int age = 20;—> 堆内存;
public Zi(){—> 栈内存
super();默认自带super访问父类的空构造方法—> 堆内存,存储父类初始化数据
System.out.println(“Zi中的无参构造方法”); }
栈内存先执行Fu无参构造方法再执行Zi类,Zi类方法结束后从栈内存消失
public Zi(int age){ System.out.println(“Zi中的带参构造方法”); } }
// 公共类
public class ExtendsDemo {
public static void main(String[] args) —> 栈内存{
// 调用该方法需要创建该类的对象,无参构造方法
Zi zi —> 栈内存= new Zi() —> 堆内存;
// 调用方法zimethod方法
zi.method();
// 子类继承父类 ,Fu中的show()方法被调用
zi.show(); } }
1.8 方法重写
方法重写概述:子类中出现和父类中一模一样的方法生命
方法重写的应用:当子类需要父类的功能,而功能主体子类有自己特有内容时,就可以重写父类中的方法,这样既可以使用父类的功能,也可以定义子类特有的内容
代码实现:
// 手机类—>父类
public class Phone { public void call(String name){ System.out.println(“给” + name + “打电话”); }}
// 新手机继承手机—>子类
public class NewPhone extends Phone {
// // 父类定义了call()方法,子类也定义了call()方法这就叫方法重写
// public void call(String name){
// System.out.println(“开启视频功能”);
//// System.out.println(“给” + name + “打电话”);
// // 直接使用super调用父类的call()方法传递name参数
// super.call(name);
// }
// 重写注解,@Override 表明下面方法的重写
@Override
public void call(String name){ System.out.println(“开启视频功能”); super.call(name); } }
// 手机测试类—>公共类
public class PhoneDemo {
public static void main(String[] args) {
// 创建对象phone
Phone phone = new Phone();
// 调用方法定义Phone的call()方法,传递name这个参数
phone.call(“李畅”);
System.out.println(“—————-“);
NewPhone newPhone = new NewPhone();
newPhone.call(“法外狂徒”); } }
@Override
1、是一个注解
2、用于帮助我们检查重写方法的方法声明的正确性
1.9 方法重写的注意事项
1、私有方法不能被重写(父类私有成员子类是不能继承的)
2、子类方法访问权限不能更改(public>默认>私有)
// 父类
public class Fu {
// private和public都是访问修饰符
private void show(){ System.out.println(“Fu中show方法调用”); }
// public比private访问权限高
void method(){ System.out.println(“Fu中method方法调用”); } }
// 子类
public class Zi extends Fu{
// 重写报错 因为父类中的show()定义的是private,所以子类不能重写
// @Override
// private void show(){
// System.out.println(“Zi中show方法调用”);
// }
// 重写报错 因为父类中的show()定义的是public,子类不定义修饰符(默认修饰符)权限低
// 同理如果子类重写方法中定义的访问修饰符高于父类就可以重写
@Override
public void method(){ System.out.println(“Fu中method方法调用”); } }
1.10 Java中重写的注意事项
1、Java中类只支持单继承,不支持多继承
报错:public class Son extends Father,Mother{ }
2、Java中类支持多层级继承(嵌套继承)
// 子类
public class Son extends Father{ }
// 父类
public class Father extends GrandFather{ public void smoke(){System.out.println(“爸爸爱抽烟”); }}
// 父类的父类
public class GrandFather { public void drink(){ System.out.println(“爷爷爱喝酒”); }}
// 另一个父类
public class Mother { public void card(){ System.out.println(“妈妈爱打牌”); }}
案例:老师和学生
需求:定义老师类和学生类,然后写代码测试,最后找到老师类和学生来当中的共性内容,抽出一个父类,用继承的方式改写代码,并进行测试
思路:
1、定义老师类(姓名,年龄,教书());
2、定义学生类(姓名,年龄,学习());
3、定义测试公共类,写测试代码
4、共性抽取父类,定义人类(姓名,年龄)
5、定义老师类继承人类,并给出自己特有的方法:教书()
6、定义学生类继承人类,并给出自己特有的方法:学习()
7、定义测试类,写代码测试
代码实现:
// 子类 —> 教师
public class Teacher extends Human{
public Teacher(){ }
public Teacher(String name, int age) {
// this.name = name;
// this.age = age;
// 方法内没有参数无法直接this调用,使用super继承传递值
super(name, age); }
public void teacher(){ System.out.println(“教书育人”); } }
// 子类 —> 学生
public class Student extends Human{ public void study(){ System.out.println(“好好学习”); } }
// 父类 —> 人类
public class Human {
private String name;
private int age;
public Human() { }
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 Human(String name, int age) {
this.name = name;
this.age = age; } }
// 公共测试类
public class Test {
public static void main(String[] args) {
// 无参构造方法创建老师对象
Teacher teacher = new Teacher();
teacher.setName(“林青霞”);
teacher.setAge(30);
System.out.println(teacher.getAge() + “ , “ + teacher.getName());
teacher.teacher();
// 无参构造方法创建学生对象
Student student = new Student();
student.setName(“李四”);
student.setAge(18);
System.out.println(student.getAge() + “,” + student.getName());
student.study();
// 带参构造方法
Teacher teacher1 = new Teacher(“张曼玉”, 30);
System.out.println(teacher1.getName() + “,” + teacher1.getAge());
teacher1.teacher(); } }
案例:猫和狗
需求:请采用继承的思想实现猫和狗的案例,在测试类中进行测试
分析:
1、猫:
成员变量:姓名,年龄
构造方法:无参,带参
成员方法:get/set方法,抓老鼠()
public class Cat extends Animals{
// 给子类一键生成无参和带参构造方法
public Cat() { }
// 生成带参构造方法会默认super给父类的带参方法赋值
public Cat(String name, int age) { super(name, age); }
public void mouse(){ System.out.println(“抓老鼠”); } }
2、狗:
成员变量:姓名,年龄
构造方法:无参,带参
成员方法:get/set方法,看家()
public class Dog extends Animals{
// 给子类一键生成无参和带参构造方法
public Dog() { }
// 生成带参构造方法会默认super给父类的带参方法赋值
public Dog(String name, int age) { super(name, age); }
public void watch(){ System.out.println(“看门狗”); } }
3、共性类:
成员变量:姓名,年龄;构造方法:无参,带参;成员方法:get/set方法
public class Animals {
// 定义变量访问修饰符用private
private String name;
private int age;
public Animals() { }
public Animals(String name, int age) {
this.name = name;
this.age = age; }
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; } }
4、共性测试类:
public class AnimalsTest {
public static void main(String[] args) {
Dog dog = new Dog();
dog.setAge(2);
dog.setName(“高菲”);
dog.watch();
System.out.println(dog.getName() + “,” + dog.getAge()); } }