1.继承语法
为了表示继承关系,Java提供了一个关键字,也就是extends。
java
class 父类 {
// 父类的成员变量和方法
}
class 子类 extends 父类 {
// 子类特有的成员变量和方法
}
2.继承------子类对父类成员的访问
重新定义一对父子类,即Base类和Derived类,为方便理解,这里都在同一个java文件实现
2.1 对父类成员变量的访问
1)父类和子类没有同名变量情况:
java
//定义父类 Base
class Base {
int a = 10;
double b = 3.14;
char c = 'A';
}
//子类[Derived]继承父类[Base]
class Derived extends Base {
int d = 20;
public void method() {
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
System.out.println("d = " + d);
}
}
//程序主方法
public class Main {
public static void main(String[] args) {
//创建Derived对象
Derived derived = new Derived();
derived.method();
}
}
运行结果:

结果说明:当子类中没有和父类同名成员变量时,对其进行编译输出,子类访问父类中继承下来的,并且访问子类自身新增加的成员。
2)父类和子类有同名变量情况:
java
class Base {
int a = 10;
double b = 3.14;
char c = 'A';
}
class Derived extends Base {
int a = 20;//与父类同名且同类型
int c = 52;//与父类同名但不同类型
public void method() {
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
//System.out.println("d = " + d);没有d,编译失败
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();//创建Derived对象
derived.method();
}
}
运行结果:

结果说明: 当子类中有和父类同名成员变量时,不论是否同类型,对其进行编译输出,子类会优先访问自己的成员。如果父类子类中都没有的成员,则会编译失败。
以上可以得出:成员变量访问遵循就近原则,子类自己有的,就优先访问子类自己,否则再访问父类成员,如果都没有,则会编译失败。
2.2 对父类成员方法的访问
1)父类和子类没有同名方法情况:
java
class Base {
public void baseMethodA() {
System.out.println("这是父类方法A");
}
public void baseMethodB() {
System.out.println("这是父类方法B");
}
}
class Derived extends Base {
public void derivedMethod() {
System.out.println("这是子类方法");
}
public void method() {
baseMethodA();//访问父类成员方法A
baseMethodB();//访问父类成员方法B
derivedMethod();//访问子类自身成员方法
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();//创建Derived对象
derived.method();
}
}
运行结果:

结果说明:当子类中没有和父类同名成员方法时,对其进行编译输出,子类访问父类中继承下来的,并且访问子类自身新增加的成员。
2)父类和子类有同名方法情况:
java
class Base {
public void baseMethodA() {
System.out.println("这是父类方法A");
}
public void baseMethodB() {
System.out.println("这是父类方法B");
}
}
class Derived extends Base {
public void baseMethodA() { //同名
System.out.println("这是子类方法A........");
}
public void method() {
baseMethodA();//访问父类成员方法A
baseMethodB();//访问父类成员方法B
//derivedMethod();//访问子类自身成员方法-->没有这个方法,编译失败
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();//创建Derived对象
derived.method();
}
}
输出结果:

结果说明: 当子类中有和父类同名成员方法时,对其进行编译输出,子类会优先访问自己的成员。如果父类子类中都没有的成员,则会编译失败。
以上可以得出:成员方法访问遵循就近原则,子类自己有的,就优先访问子类自己,否则再访问父类成员,如果都没有,则会编译失败。
看到这里,也许会有这么一个疑惑:如果子类和父类出现相同成员(包括变量和方法)名时,能不能精确访问到父类的成员呢?
在这里,Java提供了一个关键字,也就是super。
3.super关键字
为了解决上面提出的疑问,**Java提供了super关键字,它的作用是在子类中访问父类的成员。**为了可以更好地区分子类和父类的成员,在子类访问自己的成员中,也可以给自己加上this关键字。
拿上述成员变量同名的情况来举例:
java
class Base {
int a = 10;
char c = 'A';
}
class Derived extends Base {
int a = 20;
int c = 52;
public void method() {
System.out.println("父类a = " + super.a);
System.out.println("子类a = " + this.a);
System.out.println("父类c = " + super.c);
System.out.println("子类c = " + this.c);
//System.out.println("d = " + d);没有d,编译失败
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();//创建Derived对象
derived.method();
}
}
运行结果:

说明:想要在子类中精确地访问父类成员,可以借助super关键字来完成。
4.子类构造方法
在继承关系里,如果有构造方法,父类和子类的执行顺序又是怎样的呢?
java
class Base {
//构造Base方法
public Base() {
System.out.println("这是父类的构造方法Base()");
}
}
class Derived extends Base {
//构造Derived方法
public Derived() {
System.out.println("这是子类的构造方法Derived()");
}
}
public class Main {
public static void main(String[] args) {
Derived derived = new Derived();//创建Derived对象
}
}
运行结果:

可以看出:在子类的构造方法中,并没有任何调用父类构造方法的代码,但是从运行结果来看,代码是先执行父类的构造方法,再执行子类的构造方法。 这是为什么呢?
这是因为:在了解继承的概念并通过例子发现,在子类对象中的成员实际上是由两部分组成,继承父类的成员和自己新增加的成员,所以在构造子类对象的时候,先要调用父类的构造方法,讲父类继承下来的成员构造完整,然后再调用子类自己的构造方法,将子类自己新增加的成员初始化完整。
为什么会先调用父类的构造方法呢?这背后就藏着一个super关键字,在我们没有定义任何的构造方法的时候,编译器会自动添加一个无参的父类的构造方法,即"super();",值得注意的是,在子类的构造方法中调用父类的构造方法时,"super(); "必须是子类构造方法的第一条语句,这样说明了在该子类构造方法中,super和this这两个关键字是不能同时存在的。即以下写法是错误的:

5.super和this对比
super和this都是对类的访问,二者区别这里笔者整理了一部分表格:

6.代码块
在Java中,代码块是一段用来{}括起来的代码,用于控制变量的作用域或实现特定的初始化逻辑。代码块主要分为以下几种类型:
6.1普通代码块
作用:限制变量的作用范围(局部变量)
位置:方法内或者语句块中
例如:以下代码
java
public class Main {
public static void main(String[] args) {
int x = 10;
System.out.println("x = " + x); // 可以访问 x
{ // 普通代码块
int y = 20;
System.out.println("y = " + y); // 可以访问 y
}
// System.out.println("y = " + y); // 编译错误,y 超出作用域
}
}
说明:y 的作用域仅限于 {} 内部,外部无法访问。
6.2实例代码块
作用:用于初始化实例变量,在每次创建对象时执行
位置:类中,方法外
执行时机:每次创建对象时,在构造方法之前执行。
例如以下代码:
public class Main {
public Main() {
System.out.println("构造方法执行...");
}
{ // 实例代码块
System.out.println("实例代码块执行...");
}
public static void main(String[] args) {
new Main(); // 创建对象
}
}
运行结果:

说明:每次 new 一个新对象时都会先执行实例代码块,再执行构造方法
6.3 静态代码块(static 代码块)
作用:用于初始化静态变量,在类加载时执行
位置 :类中,方法外,用 static 修饰
执行时机 :类加载时执行,且只执行一次
例如以下代码:
public class Main {
public static void main(String[] args) {
System.out.println("main方法执行...");
}
static { // 静态代码块
System.out.println("静态代码块执行...");
}
}
运行结果:

说明:静态代码块在类加载时执行,比 main() 方法更早。
6.4在继承中的代码块
在了解以上代码块后,回到继承里,这些代码块的会如何执行呢?
class Base {
public int a;
public double b;
public Base(int a , double b) {
this.a = a;
this.b = b;
System.out.println("父类的构造代码块...");
}
{
System.out.println("父类的实例代码块...");
}
static{
System.out.println("父类的静态代码块");
}
}
class Derived extends Base {
public Derived(int a , double b) {
super(a, b);
System.out.println("子类的构造方法");
}
{
System.out.println("子类的实例代码块...");
}
static{
System.out.println("子类的静态代码块");
}
}
public class Main {
public static void main(String[] args) {
Derived derived1 = new Derived(5,3.14);//第一次创建Derived对象
System.out.println("-----------分割线-----------");
Derived derived2 = new Derived(6,2.78);//第二次创建Derived对象
}
}
运行结果:

说明:
静态代码块优先执行,且先执行父类静态代码块再执行子类静态代码块。
实例代码块比构造代码块先执行,且执行完父类的实例代码块和构造代码块后,才执行子类的实例代码块和构造代码块。
在第二次实例化对象的时候,父类和子类的静态代码块都不再执行,也就是说静态代码块最先执行,且只执行一次。
7.访问限定修饰符------protected关键字
在继承里,Java提供了一个访问限定修饰符,即protected关键字。可以在不同的包中的子类使用。protected 成员的访问权限比 private 大,比 public 小。 也就是说,当我们在封装和扩展之间取得一个平衡------即:既可以保护父类的实现细节,又不想全部暴露在外,又允许子类进行必要的访问和扩展,那么我们就可以使用protected关键字来限定访问修饰。
private < protected < public 访问权限由小到大
在这里借助不同的包和类实现代码来理解protected关键字:
1)同一个包中的子类:

从结果来看,父类中protected修饰的成员可以在子类中被访问,其中没有限定修饰符的int c系统默认被default(同一包中的同一类用)修饰。
2)不同包中的子类

3)不同包中的非子类

说明:**protected既可以保护父类的实现细节,又不想全部暴露在外,又允许子类进行必要的访问和扩展,**这为Java继承体系提供了精妙的访问控制设计。
8.继承方式与final关键字
8.1继承方式
在本篇文章开头引出继承时,用猫和狗两种动物引出,但在现实生活中,猫和狗还可以分为很多类,比如猫类还可以分为:中华田园猫、苏格兰折耳猫、布偶猫、英国短毛猫等,其中中华田园猫又可以分为:山东狮子猫、狸花猫、橘猫,这些猫的外貌特征又不一样,比如山东狮子猫是鸳鸯眼、头大脸圆等,狸花猫杏核形眼、头部圆润等,用一个关系图来表示就是:
因此,在Java中,继承方式就可以有以下几种表示:

需要注意的是:**Java中并不支持多继承。**也就是说,一个子类不能拥有两个父类。
8.2final关键字
在Java中,当定义一个变量并且初始化,在以后的应用中并希望它会被修改,这个时候就可以使用final关键字。
例如:

因此,在Java的继承关系里,如果不希望某一类会继承,那么就可以使用final关键字来修饰。
例如(为方便理解,这里也只在同一个java文件中展示):

例中,当定义的猫类继承动物时,用final修饰,在后面的田园猫便再也不能书写继承关系。这就说明: 使用final修饰类后,表示该类就不能再被继承。
9.总结
继承是Java面向对象编程的三大基本特性(其他两个是封装、多态)之一,允许子类复用父类的属性和方法,同时扩展自身功能。通过extends关键字建立继承关系,子类可以访问父类的非私有成员,并通过super关键字精确调用父类成员。
继承的关键规则包括:
成员访问:子类优先访问自身成员,如果父子类有同名成员,访问父类成员,需使用super。
构造方法:子类构造时默认先调用父类构造方法(隐式super()),确保父类成员正确初始化。
代码块执行顺序:静态代码块→ 实例代码块→ 构造方法。
访问控制:protected允许子类跨包访问父类成员,平衡封装与扩展性。
继承限制:Java不支持多继承,final修饰的类不可被继承。