2. 面向对象特征二:继承(Inheritance)
2.1 继承的概述
说到继承我们会想到什么
在Java面向对象程序设计中的继承
,多个类中存在相同属性和行为时,将这些相同的内容抽取到单独一个类(父类)中,然后所有的类继承这一个类,那么这些相同的代码就不用在写多次了,只需要继承这个类这即可;同时,子类还可以扩展父类的功能,也可以使用父类的功能,但不能选择性的继承父类(超类/基类)
继承的特点
为描述和处理个人信息,定义类 Person:
为描述和处理学生信息,定义类 Student:
通过继承,简化 Student 类的定义:
说明:Student 类继承了父类 Person 的所有属性和方法,并增加了一个属性 school。Person 中的属性和方法,Student 都可以使用。
特点
使用extends关键字来表示继承关系
相当于子类把父类的功能复制了一份
Java只支持单继承
继承可以传递(爷爷/儿子/孙子这样的关系)
父类的私有成员由于私有限制访问,所以子类不能使用父类的私有资源
继承多用于功能的修改,子类可以在拥有父类功能的同时,进行功能拓展
像是is a的关系
继承的好处
• 继承的出现减少了代码冗余,提高了代码的复用性。
• 继承的出现,更有利于功能的扩展。
练习:继承入门案例
创建包: cn.tedu.oop2
创建类: TestExtends.java
package cn.tedu.oop2;
/*本类用于继承的入门案例*/
public class TestExtends {
public static void main(String[] args) {
//5.分别创建3个类的对象
Animal a = new Animal();
Cat c = new Cat();
MiaoMiao m = new MiaoMiao();
//6.利用对象调用方法进行测试
/*3.继承相当于是子类将父类的功能复制了一份
继承还具有传递性,爷爷的功能会传给爸爸,爸爸的功能会传给孙子*/
a.eat();//爷爷类使用自己的方法
c.eat();//爸爸类可以使用从爷爷类中继承过来的方法
m.eat();//孙子类也可以使用从爷爷类中继承过来的方法
}
}
/*1.我们通过extends关键字建立子类与父类的继承关系,格式:子类 extends 父类*/
/*2.Java只支持单继承,一个子类只能有一个父类,但是一个父类可以有多个子类*/
//1.创建小动物类--爷爷类
class Animal{
//4.添加爷爷类的普通方法
public void eat(){
System.out.println("小动物Animal吃啥都行~");
}
}
//2.创建小猫类--爸爸类
/*6.继承是is a的关系,比如小猫是小动物,MiaoMiao是一只小猫
* 继承要求子类必须是父类的一种下属类型,依赖性非常强,强耦合*/
class Cat extends Animal{
//7.定义爸爸类中的属性
int a = 10;//普通属性
private int b = 100;//私有属性
}
//3.创建MiaoMiao类--孙子类
class MiaoMiao extends Cat{
/*4.子类可以拥有自己独有的方法,实现了功能的拓展,青出于蓝而胜于蓝*/
//8.定义孙子类的方法
public void studyJava(){
System.out.println("正在学Java");
System.out.println(a);
/*5.子类继承了父类以后,可以使用父类的所有非私有资源
* 注意:这个私有资源由于被private修饰,所以没有访问权限*/
//System.out.println(b);//不可以,私有资源被限制访问
}
}
方法的重写
父类的所有方法都会被子类继承,但是当某个方法被继承到子类之后,子类觉得
父类原来的实现不适合于自己当前的类,该怎么办呢?子类可以对从父类中继
承来的方法进行改造,我们称为方法的重写 (override、overwrite)。也称为
方法的重置、覆盖。
在程序执行时,子类的方法将覆盖父类的方法
4.1 练习:继承中成员方法的使用
创建包: cn.tedu.oop2
创建类: ExtendsDemo3.java
package cn.tedu.oop2;
/*本类用于测试继承中方法的使用*/
public class ExtendsDemo3 {
public static void main(String[] args) {
//4.创建对象进行测试
Father f = new Father();
Son s = new Son();
f.eat();
s.eat();
f.play();
s.play();
}
}
//1.创建父类
class Father{
//3.定义父类中的普通方法
public void eat(){
System.out.println("爸爸爱吃肉");
}
public void play(){
System.out.println("爸爸爱放风筝");
}
}
//2.创建子类
class Son extends Father{
//5.如果子类对父类的方法不满意,可以重写父类的方法
/*重写的原则:两同 两小 一大
* 两同:子类方法的 方法名与参数列表 和父类方法的相同
* 一大:子类方法的 方法修饰符权限 >= 父类方法的
* 两小:子类方法的返回值类型 <= 父类方法的返回值类型
* 注意:这里所说的<=是指子类方法的返回值类型是父类返回值类型的子类
* 或者与父类的返回值类型一致,如果父类方法的返回值类型是void,子类保持一致即可
* */
@Override //注解,用来加在方法上,表示这是一个重写的方法
public void eat(){
System.out.println("儿子爱吃蔬菜");
}
@Override
public void play(){
System.out.println("儿子爱玩游戏");
}
}
@Override 使用说明:
写在方法上面,用来检测是不是满足重写方法的要求。这个注解就算
不写,只要满足要求,也是正确的方法覆盖重写。建议保留,这样编
译器可以帮助我们检查格式,另外也可以让阅读源代码的程序员清晰
的知道这是一个重写的方法。
5.2 重载Overload 与重写Override的区别
重载:在一个类中的现象:同一个类中,存在方法名相同,参数列表不同的方法
重写:是指建立了继承关系以后,子类对父类的方法不满意,可以重写,遵循两同两小一大原则
重载的意义:是为了外界调用方法时方便,不管传入什么样的参数,都可以匹配到对应的同名方法
重写的意义:在不修改源码的情况下,进行功能的修改与拓展(OCP原则:面向修改关闭,面向拓展开放)
super关键字的使用
- 关键字:super
5.1 super 的理解
super也是使用在继承中,我们可以把super看作是父类的对象:Father super = new Father();
在 Java 类中使用 super 来调用父类中的指定操作:
• super 可用于访问父类中定义的属性
• super 可用于调用父类中定义的成员方法
• super 可用于在子类构造器中调用父类的构造器
注意:
• 尤其当子父类出现同名成员时,可以用 super 表明调用的是父类中的成员
• super 的追溯不仅限于直接父类
• super 和 this 的用法相像,this 代表本类对象的引用,super 代表父类的内存空间的标
识
继承的用法
练习:super之继承中成员变量使用
创建包: cn.tedu.oopextends
创建类: TestExtends1.java
package cn.tedu.oop2;
/*本类用于测试继承中变量的使用*/
public class ExtendsDemo1 {
public static void main(String[] args) {
//7.创建子类的匿名对象,调用study()
new Son().study();
}
}
//1.创建父类
class Father{
//3.创建父类的成员变量
int sum = 1;
int count = 2;
}
//2.创建子类
class Son extends Father{
//4.创建子类的成员变量
int sum = 10;
//5.创建子类的普通方法
public void study(){
System.out.println("goog good study , day day up");
//6.创建子类的局部变量
int sum = 100;
//8.打印子类的局部变量sum
System.out.println(sum);//100
//9.打印子类的成员变量sum
System.out.println(this.sum);//10
//10.打印父类的成员变量sum
/*当父类的成员变量与子类的成员变量同名时,可以使用super指定父类的成员变量
* 我们可以把super看作是父类的对象:Father super = new Father();*/
System.out.println(super.sum);//1
System.out.println(count);
}
}
练习:super之继承中构造方法的使用
创建包: cn.tedu.oop2
创建类: ExtendsDemo2.java
package cn.tedu.oop2;
/*本类用于测试继承中构造方法的使用
* 1.子类在创建对象时,默认会先调用父类的构造方法
* 2.原因是子类构造函数中的第一行默认存在super();--表示调用父类的无参构造
* 3.当父类没有无参构造时,可以通过super(参数)调用父类的其他含参构造
* 子类必须调用一个父类的构造函数,不管是无参还是含参,选一个即可
* 4.构造方法不可以被继承!因为语法的原因:要求构造方法的名字必须是本类类名
* 不能在子类中出现一个父类名字的构造方法*/
public class ExtendsDemo2 {
public static void main(String[] args) {
//6.1通过父类的无参构造创建父类对象
//Father2 f = new Father2();
//6.2通过父类的含参构造创建父类对象
//Father2 f2 = new Father2("哈哈哈");
//7.创建子类对象
Son2 s = new Son2();
}
}
//1.创建父类
class Father2{
//3.创建父类的无参构造
// public Father2(){
// System.out.println("我是父类的无参构造");
// }
//4.创建父类的含参构造
public Father2(String s){
System.out.println("我是父类的含参构造"+s);
}
}
//2.创建子类
class Son2 extends Father2{
//5.创建子类的无参构造
public Son2(){
//super();//调用父类的无参构造
super("你好~");
System.out.println("我是子类的无参构造");
}
}
2、this 和 super 的使用格式
• this
– this.成员变量:表示当前对象的某个成员变量,而不是局部变量
– this.成员方法:表示当前对象的某个成员方法,完全可以省略 this.
– this()或 this(实参列表):调用另一个构造器协助当前对象的实例化,只能在
构造器首行,只会找本类的构造器,找不到就报错
• super
– super.成员变量:表示当前对象的某个成员变量,该成员变量在父类中声明的
– super.成员方法:表示当前对象的某个成员方法,该成员方法在父类中声明的
– super()或 super(实参列表):调用父类的构造器协助当前对象的实例化,只
能在构造器首行,只会找直接父类的对应构造器,找不到就报错
this关键字指向调用该方法的对象
一般我们是在当前类中使用this关键字
所以我们常说this代表本类对象的引用
注意:super的使用前提是继承,没有父子类关系,就没有super
注意:this调用构造方法或者super调用构造方法,都必须出现在构造方法的第一行
注意:如果父类没有无参构造,需要手动在子类构造方法的第一行调用其他的含参构造
拓展:如果子类重写了父类的方法以后,可以使用super.方法名(参数列表)来调用
注意⚠️:super和this都不能出现在静态方法和静态代码块中,因为super和this都是存在于对象中的