类和对象(3)——继承:extends关键字、super关键字、protected关键字、final关键字

server/2025/1/21 16:00:41/

目录

1. 继承

1.1 继承的概念

1.2 extends关键字

1.3 继承方式

2.继承类的成员访问

2.1 成员变量

2.2 成员方法

3. super关键字

4. super、this 与 构造方法

4.1 子类中的super()调用

4.1.1 父类只有无参构造方法

4.1.2 父类有带参构造方法时

4.2 super 与 this 的异同与注意事项

5. 再谈初始化:类中代码的执行顺序

5.1 无继承关系

5.2 有继承关系

6. protected的包访问权限

7. final关键字

7.1 修饰变量

7.2 修饰类

8. 组合与继承


1. 继承

1.1 继承的概念

继承机制:它允许程序员在保持原有类特性的基础上进行扩展,增加新功能,这样产生新的类,称为派生类继承呈现了面向对象程序设计的层次结构, 体现了由简单到复杂的认知过程。

继承主要解决的问题是:共性的抽取,实现代码复用。

上述图示中,Dog和Cat都继承了Animal类,其中:Animal类称为父类/基类或超类,Dog和Cat可以称为Animal的 子类/派生类继承之后,子类可以复用父类中成员,子类在实现时只需关心自己新增加的成员即可。

1.2 extends关键字

在Java中如果要表示类之间的继承关系,需要借助extends关键字,具体如下:

修饰符 class 子类名 extends 父类名 {

         // ...  

}

子类继承父类之后,建议要新添加自己特有的成员,体现出与基类的不同,否则就没有必要继承

例如:

// Animal.java
public class Animal {public String name = "小小";public int age = 3;public void sleep(){System.out.println(name+"正在睡觉");}
}// Dog.java
public class Dog extends Animal{public void bark(){System.out.println(name+"在汪汪叫");}
}// Test.java
public class Test {public static void main(String[] args) {Dog dog = new Dog();System.out.println("小狗叫"+dog.name);dog.sleep();dog.bark();}
}

这里的Dog(狗类)继承了Animal(动物类),所以dog的名字也继承了父类Animal的名字“小小”。

同时父类能做的事情,子类继承过来也能做,比如这里的dog可以使用父类的sleep()方法。

而且子类添加了自己特有的成员——bark()成员方法。

1.3 继承方式

在现实生活中,事物之间的关系是非常复杂,灵活多样,比如:

类似的,java中有这3种继承关系:(箭头端表示子类

1. 单继承

2. 多层继承

3. 不同类继承自同个类(一个父类可以有多个子类) 

注意java中不存在继承的关系,即一个子类不能有多个父类

2.继承类的成员访问

在刚刚的例子中,我们通过dog.name的方式访问到父类的name叫“小小”,也通过dog.sleep的方式访问到父类的sleep方法。那如果Dog类中也有自己的name成员变量和sleep成员方法又会发生什么?

(子类的成员与父类的成员名字相同时,系统会访问子类的还是父类的?)

2.1 成员变量

//父类
public class Dad {public int a = 10;
}//子类
public class Child extends Dad{public int a = 20;{System.out.println("在代码块中输出a:"+ this.a);}public void printa(){System.out.println("使用成员方法输出a:"+ this.a);}
}//测试类
public class Test {public static void main(String[] args) {Child child = new Child();//访问的是父类的a还是子类的a?System.out.println("通过子类对象访问a:"+child.a);child.printa();}
}

结果都是20,而不是父类的10,说明访问的是子类的a。

继承类中的成员变量访问规则:子类优先,父类其次。

  1. 子类无,父类无:报错。
  2. 子类有,父类无:访问子类的成员变量。
  3. 子类无,父类有:访问父类的成员变量。(向上寻找)
  4. 子类有,父类有 [重名]优先访问子类的成员变量。

2.2 成员方法

//父类public class Dad {public void A(){System.out.println("Dad中的方法A()");}public void B(){System.out.println("Dad中的方法B()");}
}//子类
public class Child extends Dad{public void A(int a) {System.out.println("Child中的方法A(int)");}public void B(){System.out.println("Child中的方法B()");}public void method(){A();      // 没有传参,访问父类中的A()A(20);    // 传递int参数,访问子类中的A(int)B();      // 直接访问,则永远访问到的都是子类中的B(),父类的无法访问到}
}//测试类
public class Test {public static void main(String[] args) {Child child = new Child();        child.method();}
}

由这个例子我们可以总结出访问子类的成员方法的规则。

继承类中的成员方法访问规则:子类优先,父类其次;重载看参数,重写用子类。

  1. 子类无,父类无:报错。
  2. 子类有,父类无:访问子类方法。
  3. 子类无,父类有:访问父类方法。(向上寻找)
  4. 子类有,父类有 [重名]
    a. 参数列表不同(构成方法重载):根据输入的参数来决定使用哪一个方法
    b. 参数列表相同(构成方法重写):优先使用子类的方法。

3. super关键字

由于设计不好,或者因场景需要,子类和父类中可能会存在相同名称的成员,如果要在子类方法中访问父类同名成 员时,该如何操作?

直接访问是无法做到的,Java提供了super关键字,该关键字主要作用:在子类方法中访问父类的成员。

父类
public class Dad {public int a = 10;public void A(){System.out.println("Dad中的方法A()");}}子类
public class Child extends Dad{public int a = 20;public void A() {System.out.println("Child中的方法A()");}public void field(){System.out.println(a);       //子类的变量aSystem.out.println(super.a); //父类的变量a}public void method(){A();        //子类的方法Asuper.A();  //父类的方法A}
}测试
public class Test {public static void main(String[] args) {Child child = new Child();child.field();child.method();}
}

在子类方法中,如果想要明确访问父类中成员时,借助super关键字即可。

注意:super关键字不能用于静态代码块、静态变量和静态方法。

因为super是对子类对象的父类的引用,需要子类对象的创建,而静态的成员不依赖于对象。

4. super、this 与 构造方法

4.1 子类中的super()调用

4.1.1 父类只有无参构造方法

当父类只有 无差的构造方法 或 无构造方法,子类构造方法中默认会调用父类的无参构造方法:super()。【super()默认是子类构造方法的第一条语句。】

在子类构造方法中,并没有写任何关于父类构造的代码,但是在构造子类对象时,先执行父类的构造方法,然后执行子类的构造方法。

因为:子类对象中成员是由两部分组成的,基类继承下来的以及子类新增加的部分 。父子父子,肯定是先有父再有子,所以在构造子类对象时候 ,先要调用基类的构造方法,将从基类继承下来的成员构造完整 ,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整 。

例如:

4.1.2 父类有带参构造方法时

如果父类构造方法是带有参数的,此时需要:

  1. 用户要为子类显式定义构造方法,并在子类构造方法中选择合适的父类构造方法调用。
  2. 该子类构造方法的第一句是super(...)

例如:

4.2 super 与 this 的异同与注意事项

super和this都可以在成员方法中用来访问:成员变量和调用其他的成员函数,都可以作为构造方法的第一条语 句,那他们之间有什么区别呢?

【相同点】

  1. 不能用于静态变量、静态方法 和 静态代码块
  2. 显式使用时,必须是构造方法中的第一条语句

【不同点】

  1. this用来访问本类的方法和属性,super用来访问父类继承下来的方法和属性。
  2. 构造方法中一定会存在super(...)的调用,用户不写编译器也会增加,但是this(...)用户不写则没有

【注意】

this(...)用于调用本类构造方法,super(...)用于调用父类构造方法,两种调用不能同时在构造 方法中出现

因为super和this要显式使用时,都必须是构造方法的第一句,但两者又不可能同时是第一句。

例如:

5. 再谈初始化:类中代码的执行顺序

我们知道,静态代码块、实例代码块和构造方法是在类加载时或者实例对象创建时执行的。那么它们执行的顺序是怎样的呢?

5.1 无继承关系

观察下面的代码,猜测一下在没有继承关系时的执行顺序

class Person {public String name;public int age;//构造方法public Person(String name, int age) {this.name = name;this.age = age;System.out.println("构造方法执行");}//实例代码块{System.out.println("实例代码块执行");}//静态代码块static {System.out.println("静态代码块执行");}
}public class Test {public static void main(String[] args) {System.out.println("第一次:");Person person1 = new Person("小明",10);//第一次触发类加载和静态代码块System.out.println("============================");System.out.println("第二次:");Person person2 = new Person("大明",20);//第二次无类加载,不再执行静态代码块的内容}
}

由这个结果可以总结出无继承关系时的执行顺序:

继承关系时:

【类未加载】

  静态代码块 --> 实例代码块 --> 构造方法

【类已加载】

  实例代码块 --> 构造方法

补充:

  1. 静态变量的初始化、静态代码块和静态方法的代码都是同一个时期执行的。(执行的顺序由代码文本的上下顺序决定)
  2. 静态成员执行完后,接下来就是成员变量、实例代码块和成员方法的执行时期。(执行的顺序也是由代码文本的上下顺序决定)
  3. 构造方法最后执行

5.2 有继承关系

当存在继承关系时,子类的执行顺序是怎么样的?

父类
class Person {public String name;public int age;public Person(String name, int age) {this.name = name;this.age = age;System.out.println("Person:父类构造方法执行");}{System.out.println("Person:父类实例代码块执行");}static {System.out.println("Person:父类静态代码块执行");}
}
子类
class Student extends Person{public Student(String name,int age) {super(name,age);System.out.println("Student:子类构造方法执行");}{System.out.println("Student:子类实例代码块执行");}static {System.out.println("Student:子类静态代码块执行");}
}public class Test {public static void main(String[] args) {System.out.println("第一次:");Student student1 = new Student("小明",19);System.out.println("===========================");System.out.println("第二次:");Student student2 = new Student("大明",20);}
}

由该例子可以得出以下执行顺序:

继承关系时:(创建子类对象

【父类子类均未加载】

父类静态 --> 子类静态 --> 父类实例 --> 父类构造方法 --> 子类实例 --> 子类构造方法

【父类已加载、子类未加载】

子类静态 --> 父类实例 --> 父类构造方法 --> 子类实例 --> 子类构造方法

【父类子类都已加载】

父类实例 --> 父类构造方法 --> 子类实例 --> 子类构造方法

图示:

6. protected的包访问权限

类和对象章节中,为了实现封装特性,Java中引入了访问限定符,主要限定:类或者类中成员能否在类外或者其 他包中被访问。

我们来回忆一下这些关键字:

如果把不同的包比作不同的村子,把子类和父类比作家族成员的话,那么:

public:可以理解为一个人的外貌和声誉,谁都可以看得到。(不同包中的不同类都可以访问)

protected:对于同一个村庄的村民当然知道你的外面特征和声誉(个同一包中的不同类可以访问);而对于其他地方或城市,也是你的亲戚和家族成员比较了解你一些不同包中的子类和父类)。

无修饰(default):只有同一个村庄的人才知道你。(同一包中的不同类可以访问)

private:只有自己知道,其他人都不知道。

举例说明:

[同一个包]

其他类(子类+非子类):可使用的权限是public、protected、default

[不同包]

继承:可使用的权限是public、protected

继承:可使用的权限是public

private修饰的,只能父类使用。

7. final关键字

final关键字可以修饰变量、类和成员方法。

7.1 修饰变量

final修饰变量或字段时,表示常量不能修改

(final修饰的范围包括成员变量局部变量

例如:

public class Test {final static int a = 1;public static void main(String[] args) {final int b = 2;a = 2;b = 3;}
}

此时 成员变量a 和 局部变量b 都是常量,常量的值不能修改,所以会运行报错:

  • 基本数据类型变量:当 final 修饰基本数据类型变量时,该变量一旦被赋值后就不能再次改变其值。

  • 引用数据类型变量:对于引用数据类型变量,final 关键字表示该变量的引用不能再指向其他对象,但对象本身的内容是可以修改的

7.2 修饰类

final修饰类时,表示此类不能被继承

例如:

package demo1;
final public class Animal {      //Animal类被final修饰}class Dog extends Animal{        //继承Animal类会报错}

我们平时是用的 String 字符串类, 就是用 final 修饰的, 不能被继承


7.3 修饰方法:表示该方法不能被重写【放在下一篇文章中介绍】

8. 组合与继承

组合的思想:

继承类似,组合也是一种表达类之间关系的方式。它允许我们将对象组合成树形结构以表示部分-整体的层次结构。

继承表示对象之间是is-a的关系,比如:狗是动物,猫是动物

组合表示对象之间是have-a的关系,比如:汽车中有方向盘、发动机、前照灯…

组合的实现:

组合并没有涉及到特殊的语法 (诸如 extends 这样的关键字),仅仅是一个类的实例作为另外一个类的字段

汽车和其轮胎、发动机、方向盘、车载系统等的关系就应该是组合,因为汽车是有这些部件组成的:

// 轮胎类
class Tire{// ...
}// 发动机类
class Engine{// ...
}// 车载系统类
class VehicleSystem{// ...
}
——————————————————————————————————————————————————【组合】
// 汽车类 将上述所有类组合起来
class Car{private Tire tire;          // 可以复用轮胎中的属性和方法private Engine engine;      // 可以复用发动机中的属性和方法private VehicleSystem vs;   // 可以复用车载系统中的属性和方法// ...
}
——————————————————————————————————————————————————【继承】// 奔驰汽车类 继承自汽车类
class Benz extend Car{// 将汽车中包含的:轮胎、发送机、车载系统全部继承下来
}

这里的轮胎实例变量发动机实例变量车载系统实例变量 都作为 汽车类的成员变量,这就是组合。


本期分享完毕,感谢大家的支持Thanks♪(・ω・)ノ


http://www.ppmy.cn/server/160222.html

相关文章

牛客周赛Round 76 F同位序列

F-同位序列_牛客周赛 Round 76 Round76比较简单,最后一题不涉及到什么算法,就是道模拟题,wa了几发最后还是让我混过去了🤭。其实就是一个规律:将整数二进制中第一次出现零的位置pos0且在这个位置之前出现了1(位置为pos…

AI发展困境:技术路径与实践约束的博弈

标题:AI发展困境:技术路径与实践约束的博弈 文章信息摘要: AI技术发展路径主要受实践约束驱动,而非纯理论优势。大型AI实验室的成功更依赖优质执行力和资源优势,而非独特技术创新。当前AI发展面临评估体系与实际应用脱…

Spring Cloud 微服务

一、什么是微服务? 先说说什么是微服务。想象一下,你有一个超大的乐高积木,里面有很多小零件,每个小零件都有自己的功能。要是其中一个零件坏了,你只需要换掉那个小零件,而不用把整个乐高都扔掉。微服务就…

腾讯云AI代码助手评测:如何智能高效完成Go语言Web项目开发

腾讯云AI代码助手评测:如何智能高效完成Go语言Web项目开发 ?? 文章目录 腾讯云AI代码助手评测:如何智能高效完成Go语言Web项目开发 ?? 背景引言开发环境介绍腾讯云AI代码助手使用实例 1. 代码补全2. 技术对话3. 代码优化4. 规范代码5. Bug处理 获得…

mysql学习笔记-索引优化与查询优化

1.索引失效的几种情况 1.1 全值匹配我最爱 1.2 最佳左前缀法则 1.3 主键插入顺序,主键不是递增,可能造成页分裂、性能损耗 1.4 计算、函数、类型转换(自动或手动)导致索引失效 1.5 类型转换导致索引失效 1.6 范围条件右边的列索引失效 CREATE INDEX idx_age_classI…

Spring Boot安全加固:基于Spring Security的权限管理

引言 在当今数字化时代,随着企业信息化程度的不断提高,应用程序的安全性成为了一个至关重要的问题。Spring Boot 作为 Java 生态系统中广泛使用的开发框架,以其简洁、高效的特点深受开发者的喜爱。然而,仅仅依靠 Spring Boot 的默…

ACL基础理论

ACL ——访问控制列表 ACL属于策略的一种 ACL访问控制列表的作用: 访问控制:在路由器流量流入或流出的接口上,匹配流量,然后执行设定好的动作:permit(允许)、deny(拒绝&#xff…

某国际大型超市电商销售数据分析和可视化

完整源码项目包获取→点击文章末尾名片! 本作品将从人、货、场三个维度,即客户维度、产品维度、区域维度(补充时间维度与其他维度)对某国际大型超市的销售情况进行数据分析和可视化报告展示,从而为该超市在弄清用户消费…