目录
[4.public 关键字](#4.public 关键字)
[1.final 关键字](#1.final 关键字)
内容大纲:
1.摘要:本文详细介绍了Java中的继承机制,通过猫狗动物案例展示了继承如何实现代码复用。
2.继承允许子类复用父类成员并扩展新功能,使用extends关键字建立继承关系。
3.重点讲解了super关键字的作用(访问父类成员、调用父类构造方法)、继承中的初始化顺序(父类静态→子类静态→父类实例→子类实例)以及访问修饰符protected的使用场景。
文章还对比了继承与组合的区别,指出继承表示"is-a"关系而组合表示"has-a"关系,建议优先使用组合。
4.最后介绍了final关键字限制继承的特性,并强调实际开发中应保持简洁的继承层次(不超过三层)。
一:继承
1.为什么需要继承
java中使⽤类对现实世界中实体来进⾏描述,类经过实例化之后的产物对象,则可以⽤来表⽰现实中的 实体,但是现实世界错综复杂,事物之间可能会存在⼀些关联,那在设计程序时就需要考虑
⽐如:狗和猫,它们都是**⼀个动物**
使⽤Java语⾔来进⾏描述,就会设计出这样的代码:
Dog.java
java
public class Dog {
String name;
int age;
float weight;
public void eat(){
System.out.println(name + " 正在吃饭 ");
}
public void sleep(){
System.out.println(name + " 正在睡觉 ");
}
public void Bark(){
System.out.println(name + " 汪汪汪 ~~~");
}
}
Cat.java
java
public class Cat {
String name;
int age;
float weight;
public void eat(){
System.out.println(name + " 正在吃饭 ");
}
public void sleep(){
System.out.println(name + " 正在睡觉 ");
}
public void Bark(){
System.out.println(name + " 喵喵苗 ~~~");
}
}
通过观察上述代码会发现,猫和狗的类中存在⼤量重复,如下所⽰:

那能否将这些共性抽取呢?⾯向对象思想中提出了继承的概念,专⻔⽤来进⾏共性抽取,实现代码复⽤
2.继承概念
继承(inheritance)机制:是⾯向对象程序设计使代码可以复⽤的最重要的⼿段 ,它允许程序员在保持 原有类特性的基础上进⾏扩展,增加新功能,这样产⽣新的类,称派⽣类。继承呈现了⾯向对象程序 设计的层次结构,体现了由简单到复杂的认知过程。继承主要解决的问题是:共性的抽取,实现代码复⽤。
例如:狗和猫都是动物 ,那么我们就可以将共性的内容进⾏抽 取,然后采⽤继承的思想来达到共⽤

上述图⽰中,Dog和Cat都继承了Animal类 ,其中:Animal类称为⽗类/基类或超类,Dog和Cat可以称 为Animal的⼦类/派⽣类,继承之后,⼦类可以复⽤⽗类中成员,⼦类在实现时只需关⼼⾃⼰新增加的 成员即可。
从继承概念中可以看出继承最⼤的作⽤就是:实现代码复⽤,还有就是来实现多态(后序讲)
3.继承的语法
在Java中如果要表⽰类之间的继承关系,需要借助extends关键字,具体如下:
修饰符 class ⼦类 extends ⽗类 {
// ...
}
注意:
1.修饰符就是我们的public private ,,,,,这些修饰符
2.子类就是我们的猫猫狗狗
3.父类就是我们共有的特性即都是动物
对刚刚开始的猫狗代码中场景使⽤继承⽅式重新设计:
Dog.java(子类)
java
public class Dog extends Animal{
public void Bark(){
System.out.println(name + " 汪汪汪 ~~~");
}
}
Cat.java(子类)
java
public class Cat extends Animal{
public void Bark(){
System.out.println(name + " 喵喵苗 ~~~");
}
}
Animal.java(父类)
java
public class Animal {
String name;
int age;
float weight;
public void eat(){
System.out.println(name + " 正在吃饭 ");
}
public void sleep(){
System.out.println(name + " 正在睡觉 ");
}
}
Test,java(测试上面的)
java
public class Test {
public static void main(String[] args) {
Dog dog = new Dog();
//dog 类中并没有定义任何成员变量,name 和 age 属性肯定是从⽗类 Animal 中继承下来的
System.out.println(dog.name);
System.out.println(dog.age);
//dog 访问的 eat() 和 sleep()⽅法也是从 Animal 中继承下来的
dog.eat();
dog.sleep();
}
}
输出:

**注意:**这里我没有实例化对象,所以返回各自所对应的值
从上述代码我们可以看到Dog类和Cat类的部分代码重复使⽤了Animal中的代码。
注意:
1.⼦类会将⽗类中的成员变量或者成员⽅法继承到⼦类中
2.⼦类继承⽗类之后,必须要新添加⾃⼰特有的成员,体现出与父类的不同,否则就没有必要继承了
4.⽗类成员访问
在继承体系中,⼦类将⽗类中的⽅法和字段(成员变量)继承下来了,那在⼦类中能否直接访问⽗类中继承下来的 成员呢?
1.⼦类中访问⽗类的成员变量
⼦类和⽗类不同名成员变量
Derived.java(子类)
java
public class Derived extends Base {
public static int c;
public static void method(){
a = 10; //访问从⽗类中继承下来的a
b = 20; //访问从⽗类中继承下来的b
c = 30; //访问⼦类⾃⼰的c
}
public static void main(String[] args) {
Derived.method();
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
Base.java(父类)
java
public class Base {
public static int a;
public static int b;
}
输出:

⼦类和⽗类成员变量同名
Derived.java
java
public class Derived extends Base {
public static int a; //与⽗类中成员 a同名,且类型相同
public static char b; //与⽗类中成员 b同名,但类型不同
public static void method(){
a = 100; //访问⽗类继承的a,还是⼦类⾃⼰新增的a?
b = 101; //访问⽗类继承的 b,还是⼦类⾃⼰新增的 b?
c = 102; //⼦类没有c,访问的肯定是从⽗类继承下来的c
}
public static void main(String[] args) {
Derived.method();
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
Base.java
java
public class Base {
public static int a;
public static int b;
public static int c;
}
输出:

注意:上面的e就是101的字符形式,因为我们是char类型
在 Derived 类重点 method ⽅法中访问a和b访问的都是优先访问⼦类⾃⼰的!
在⼦类⽅法中或者通过⼦类对象访问成员时:
• 如果访问的成员变量⼦类中有,优先访问⾃⼰的成员变量。
• 如果访问的成员变量⼦类中⽆,则访问⽗类继承下来的,如果⽗类也没有定义,则编译报错。
• 如果访问的成员变量与⽗类中成员变量同名,则优先访问⾃⼰的。
成员变量访问遵循就近原则,⾃⼰有优先⾃⼰的,如果没有则向⽗类中找。
2.⼦类中访问⽗类的成员⽅法
成员⽅法名字不同
Derived.java
java
public class Derived extends Base {
public static void methodB(){
System.out.println("Derived 中的 methodB()⽅法 ");
}
public static void methodC(){
methodB(); //访问⼦类⾃⼰的methodB()
methodA(); //访问⽗类继承的methodA()
}
public static void main(String[] args) {
Derived.methodC();
}
}
Base.java
java
public class Base {
public static void methodA(){
System.out.println("Base 中的 methodA()");
}
}
输出:

总结:成员⽅法没有同名时,在⼦类⽅法中或者通过⼦类对象访问⽅法时,则优先访问⾃⼰的,⾃⼰ 没有时再到⽗类中找,如果⽗类中也没有则报错
成员⽅法名字相同
Derived.java
java
public class Derived extends Base {
public static void methodA() {
System.out.println("Derived 中的 method()⽅法 ");
}
public static void methodB(){
System.out.println("Derived 中的 methodB()⽅法 ");
}
public static void methodC(){
methodA(); //优先访问⼦类中的methodA()
methodB(); //直接访问,则永远访问到的都是⼦类中的methodB(),父类无法访问
}
public static void main(String[] args) {
Derived.methodC();
}
}
Base.java
java
public class Base {
public static void methodA(){
System.out.println("Base 中的 methodA()");
}
public static void methodB(){
System.out.println("Base 中的 methodB()");
}
}
输出

【说明】
通过⼦类对象访问⽗类与⼦类中**不同名⽅法时,优先在⼦类中找,**找到则访问,否则在⽗类中找, 找到则访问,否则编译报错。
通过子类对象访问⽗类与⼦类同名⽅法时,如果⽗类和⼦类同名⽅法的参数列表不同(重载),根据调⽤⽅法适传递的参数选择合适的⽅法访问,如果没有则报错
问题:如果⼦类中存在与⽗类中相同的成员时,那如何在⼦类中访问⽗类相同名称的成员呢?
5.super关键字
由于设计不好,或者因场景需要,⼦类和⽗类中可能会存在相同名称的成员,如果要在⼦类⽅法中访 问⽗类同名成员时,该如何操作?直接访问是⽆法做到的,Java提供了super关键字,该关键字主要 作⽤:在⼦类⽅法中访问⽗类的成员。
Derived.java
java
public class Derived extends Base {
public static int a; // 子类自己的静态变量 a(隐藏了父类的 a)
public static int b; // 子类自己的静态变量 b(隐藏了父类的 b)
public static void methodA(int a) {
System.out.println("Derived 中的 methodA(int a) 方法,接收到的参数是:" + a);
}
public static void methodB() {
System.out.println("Derived 中的 methodB() 方法");
}
public void methodC() {
// 1. 通过 super 给父类的静态变量赋值
super.a = 200;
super.b = 201;
// 2. 【核心修改】加上 super 的对比打印,直观体现 super 的作用
System.out.println("直接访问子类的 a: " + a);
System.out.println("通过super访问父类的 a: " + super.a);
System.out.println("直接访问子类的 b: " + b);
System.out.println("通过super访问父类的 b: " + super.b);
// 3. 原有的方法调用
methodA(); // 没有传参,访问父类中的 methodA()
methodA(20); // 传递了参数,访问子类中的 methodA(int)
methodB(); // 直接访问,永远访问到的都是子类中的 methodB()
super.methodB(); // 借助 super,明确访问基类的 methodB()
}
public static void main(String[] args) {
Derived d = new Derived();
d.methodC();
}
}
Base.java
java
public class Base {
public static int a; // 父类的静态变量 a
public static int b;
public static void methodA() {
System.out.println("Base 中的 methodA()");
}
public static void methodB() {
System.out.println("Base 中的 methodB()");
}
}
输出:
直接访问子类的 a: 0
通过super访问父类的 a: 200
直接访问子类的 b: 0
通过super访问父类的 b: 201
Base 中的 methodA()
Derived 中的 methodA(int a) 方法,接收到的参数是:20
Derived 中的 methodB() 方法
Base 中的 methodB()
在⼦类⽅法中,如果想要明确访问⽗类中成员时,借助super关键字即可。
注意事项
只能在⾮静态⽅法中使⽤
只能在⼦类⽅法中,访问⽗类的成员变量和⽅法
总结:
结合我们刚刚的实战代码,super 关键字的作用其实非常直观。你可以把它简单地理解为**"子类手里的一把专属钥匙"**,专门用来在子类中打开和访问父类的成员。
在 Java 继承中,super 主要有以下三大核心用途:
1. 访问父类被隐藏的成员变量 (super.变量名)
当子类和父类定义了同名的变量时,直接写变量名默认访问的是子类自己的(就近原则)。此时如果想访问父类的变量,就必须用 super。
2. 调用父类被重写的方法 (super.方法名())
当子类重写(Override)了父类的方法时,直接调用会默认执行子类的版本。如果想在子类里复用父类原本的逻辑,就必须用 super。
3. 调用父类的构造方法 (super() 或 super(参数))
在创建子类对象时,必须先初始化父类部分。super() 专门用于在子类构造方法的第一行,调用父类的构造器。
一句话总结:
super 就是为了解决"子类想用自己的东西,但又偶尔需要用到父类原版"的需求而存在的。只要加上 super.,Java 就会直接跳过子类,去父类里找对应的变量、方法或构造器!
6.⼦类构造⽅法
我们先来看⼀段代码:
Derived.java
java
public class Derived extends Base {
public Derived(){
System.out.println("Derived()");
}
}
Base.java
java
public class Base {
public Base(int a) {
System.out.println("Base(): " + a);
}
}
Test.java
java
public class Test {
public static void main(String[] args) {
Derived d = new Derived();
}
}
输出:

问题:该代码为什么会编译错误呢?
事实上,当我们在构造⼦类对象的时候,需要先调⽤父类构造⽅法,然后执⾏⼦类的构造⽅法。所 以,我们需要在⼦类对象构造完成之前 ,先帮助⽗类对其中的成员进⾏初始化。具体⽅式如下:
我们只需更改:
Derived.java
java
public class Base {
public Base(int a) {
super(10);//此时通过super(参数)的形式
System.out.println("Base(): " + a);
}
}
输出:

【注意事项】
通过super(参数)的形式可以调⽤⽗类指定的构造⽅法
super()的形式只能出现在⼦类的构造⽅法当中且必须在第⼀⾏
问题:如下代码为什么没有报错呢?
Derived.java
java
public class Derived extends Base {
public Derived(){
System.out.println("Derived()");
}
}
Base.java
java
public class Base {
public Base() {
System.out.println("Base()");
}
}
输出:

【注意事项】:
正如注释处所说,当**⽗类的构造⽅法是不带参数的构造⽅法且只有这⼀个的情况下,默认会添加⼀ 个super()。**
【总结】
• 在⼦类构造⽅法中,并没有写任何关于父类构造的代码,但是在构造⼦类对象时,先执⾏父类的构 造⽅法,然后执⾏⼦类的构造⽅法,因为:⼦类对象中成员是有两部分组成的,父类继承下来的以 及⼦类新增加的部分。⽗⼦肯定是先有⽗再有⼦,所以在构造⼦类对象时候,先要调⽤父类 的构造⽅法,将从父类继承下来的成员构造完整,然后再调⽤⼦类⾃⼰的构造⽅法,将⼦类⾃⼰新 增加的成员初始化完整。
• 若⽗类显式定义⽆参或者默认的构造⽅法,在⼦类构造⽅法第⼀⾏默认有隐含的super()调⽤,即调⽤父类构造⽅法
• 如果⽗类构造⽅法是带有参数的,此时需要⽤⼾为⼦类显式定义构造⽅法,并在⼦类构造⽅法中选择合适的⽗类构造⽅法调⽤,否则编译失败
• 在⼦类构造⽅法中,super(...)调⽤⽗类构造时,必须是⼦类构造函数中第⼀条语句
• super(...)只能在⼦类构造⽅法中出现⼀次,并且不能和this同时出现
7.super和this
uper和this都可以在成员⽅法中⽤来访问:成员变量和调⽤其他的成员函数,都可以作为构造⽅法的第⼀条语句,那他们之间有什么区别呢?
【相同点】
都是Java中的关键字
只能在类的⾮静态⽅法中使⽤,⽤来访问⾮静态成员⽅法和字段
在构造⽅法中调⽤时,必须是构造⽅法中的第⼀条语句,并且不能同时存在
【不同点】
- this是当前对象的引⽤,当前对象即调⽤实例⽅法的对象,super相当于是⼦类对象中从⽗类继承下 来部分成员的引⽤

-
在⾮静态成员⽅法中,this⽤来访问本类的⽅法和属性,super⽤来访问⽗类继承下来的⽅法和属性
-
在构造⽅法中:this(...)⽤于调⽤本类构造⽅法,super(...)⽤于调⽤⽗类构造⽅法,两种调⽤不能同时在构造⽅法中出现
-
构造⽅法中⼀定会存在super(...)的调⽤,⽤⼾没有写编译器也会增加,但是this(...)⽤⼾不写则没有
8.继承关系上代码块等的初始化顺序
1.回顾之前的初始化顺序
我们还记得之前讲过的代码块吗?我们简单回顾⼀下⼏个重要的代码块:实例代码块和静态代码块。 在没有继承关系时的执⾏顺序
Test.java
java
public class Test {
public static void main(String[] args) {
Person person1 = new Person("bit",10);
System.out.println("============================");
Person person2 = new Person("小龙",20);
}
}
Person.java
java
public 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(" 静态代码块执⾏");
}
}
输出:

1. 静态代码块先执⾏,并且只执⾏⼀次,在类加载阶段执⾏
2. 当有对象创建时,才会执⾏实例代码块,实例代码块执⾏完成后,最后构造⽅法执⾏
2.继承关系上的执⾏顺序
Student.java(子类)
java
public 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:静态代码块执⾏ ");
}
}
Person.java(父类)
java
public 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 静态代码块执⾏");
}
}
输出:

通过分析执⾏结果,得出以下结论:
1. ⽗类静态代码块优先于⼦类静态代码块执⾏,且是最早执⾏
2. ⽗类实例代码块和⽗类构造⽅法紧接着执⾏
3. ⼦类的实例代码块和⼦类构造⽅法紧接着再执⾏
4. 第⼆次实例化⼦类对象时,⽗类和⼦类的静态代码块都将不会再执⾏
9.访问修饰限定符-protected关键字
在类和对象章节中,为了实现封装特性,Java中引⼊了访问限定符,主要限定:类或者类中成员能否 在类外或者其他包中被访问。

那⽗类中不同访问权限的成员,在⼦类中的可⻅性⼜是什么样⼦的呢?
1.private关键字
如果被 private 关键字修饰该类当中成员变量,成员⽅法等只能在当前类当中使⽤
2.不加任何的关键字(包访问权限)
如果不加任何的关键字修饰该类当中成员变量,成员⽅法等表⽰默认权限,默认权限代表的也是包访问权限,意味着只能在当前包当中使⽤
3.protected关键字
如果被 protected 关键字修饰该类当中成员变量,成员⽅法等表⽰要么只能在同⼀个包中的类中进 ⾏访问,要么在不同包中只能通过在继承关系上的⼦类对象来访问。
4.public 关键字
如果被 public 关键字修饰该类当中成员变量,成员⽅法等表⽰该成员变量和成员⽅法是公开的
什么时候下⽤哪⼀种呢?
1.我们希望类要尽量做到"封装",即隐藏内部实现细节,只暴露出必要的信息给类的调⽤者.
2.因此我们在使⽤的时候应该尽可能的使⽤⽐较严格的访问权限.例如如果⼀个⽅法能⽤private,就尽量不要⽤public.
3.另外,还有⼀种简单粗暴的做法:将所有的字段设为private,将所有的⽅法设为public.不过这种⽅ 式属于是对访问权限的滥⽤,还是更希望同学们能写代码的时候认真思考,该类提供的字段⽅法到底 给"谁"使⽤(是类内部⾃⼰⽤,还是类的调⽤者使⽤,还是⼦类使⽤).
10.继承⽅式
在Java中只⽀持以下⼏种继承⽅式:

注意:Java中不⽀持多继承
1.时刻牢记,我们写的类是现实事物的抽象.⽽我们真正在公司中所遇到的项⽬往往业务⽐较复杂,可能 会涉及到⼀系列复杂的概念,都需要我们使⽤代码来表⽰,所以我们真实项⽬中所写的类也会有很多. 类之间的关系也会更加复杂
2.但是即使如此,我们并不希望类之间的继承层次太复杂.⼀般我们不希望出现超过三层的继承关系.如 果继承层次太多,就需要考虑对代码进⾏重构了.
3.如果想从语法上进⾏限制继承,就可以使⽤final关键字
1.final 关键字
final关键可以⽤来修饰变量、成员⽅法以及类。
修饰变量或字段,表⽰常量(即不能修改)
final int a = 10;
a = 20;
// 编译出错
修饰类:表⽰此类不能被继承
//父类
final public class Animal {
...
}
//子类
public class Bird extends Animal {
...
}
我们平时是⽤的String字符串类,就是⽤final修饰的,不能被继承
修饰⽅法:表⽰该⽅法不能被重写(后序介绍)
11.继承与组合
和继承类似,组合也是⼀种表达类之间关系的⽅式,也是能够达到代码重⽤的效果。组合并没有涉及到 特殊的语法(诸如extends这样的关键字),仅仅是将⼀个类的实例作为另外⼀个类的字段。
继承表⽰对象之间是is-a的关系,⽐如:狗是动物,猫是动物
组合表⽰对象之间是has-a的关系,⽐如:汽⻋
汽⻋和其轮胎、发动机、⽅向盘、⻋载系统等的关系就应该是组合,因为汽⻋是有这些部件组成的。
//轮胎类
class Tire{
...
}
// 发动机类
class Engine{
...
}
// ⻋载系统类
class VehicleSystem{
...
}
//汽车
class Car{
private Tire tire; // 可以复⽤轮胎中的属性和⽅法
private Engine engine; // 可以复⽤发动机中的属性和⽅法
private VehicleSystem vs; // 可以复⽤⻋载系统中的属性和⽅法
...
}
class Benz extend Car{
// 将汽⻋中包含的:轮胎、发送机、⻋载系统全部继承下来
}
组合和继承都可以实现代码复⽤,应该使⽤继承还是组合,需要根据应⽤场景来选择,⼀般建议:能 ⽤组合尽量⽤组合。
以上就是我们的全部内容了!!!!!!(大大的重点)