java——继承

分类: 日博365.tv 📅 2025-06-30 16:45:43 👤 admin 👁️ 8608 ❤️ 725
java——继承

目录

一. 继承的基本概念

二. 继承的语法

三. 继承的核心规则

1.单继承:

2.子类继承父类后,除私有的不支持继承、构造方法不支持继承。其它的全部会继承。

①访问权限:

②构造方法:

3.一个类没有显示继承任何类时,默认继承java.lang.Object类。

(1).为什么需要隐式继承Object类?

(2).验证隐式继承的示例

(3).特殊情况说明

(4).隐式继承的底层逻辑

(5).总结

四. 继承的示例

父类 Animal

子类 Cat

测试类

五、继承中变量的访问特点

1.情况 1:完整代码(子类有成员变量 + 局部变量)

2.情况 2:去掉局部变量 age=30

3.情况 3:去掉子类成员变量 age 和局部变量 age

4.总结表格

5.关键规则

六. super 关键字

1. super 关键字用法及与 this 关键字对比

1.1 this 与 super 关键字概念

1.2 this 与 super 关键字具体用法对比

1.3 代码示例及结果

七、继承中构造方法的访问特点

1.代码示例及结果

2.父类只有带参构造方法的处理办法

八、继承中成员方法的访问特点

情况一:子类有该方法,父类无该方法

情况二:子类无该方法,父类有该方法

情况三:子类和父类都有该方法(方法重写)

情况四:子类和父类都无该方法

九、继承的用途

十、方法重写(Override)

十一、 继承的注意事项

十二、 继承与接口的区别

十三. 总结

在 Java 中,继承(Inheritance) 是面向对象编程(OOP)的核心特性之一,允许一个类(子类/派生类)继承另一个类(父类/基类)的属性和方法,从而实现代码复用和逻辑分层。以下是关于 Java 继承的详细讲解:

一. 继承的基本概念

父类(Superclass):被继承的类,包含通用的属性和方法。

子类(Subclass):继承父类的类,可以扩展或修改父类的功能。

核心思想:子类“是一个(is-a)”父类,例如 Cat 是 Animal。

继承相关的术语:当B类继承A类时

A类称为:父类、超类、基类、superclass

B类称为:子类、派生类、subclass

继承是面向对象的三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。继承是指在原有类的基础上,进行功能扩展,创建新的类型。继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模。JAVA中类只有单继承,没有多继承!继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。extends的意思是“扩展”,子类是父类的扩展。

二. 继承的语法

使用 extends 关键字实现继承:

class ParentClass {

// 父类的属性和方法

}

class ChildClass extends ParentClass {

// 子类可以添加新的属性和方法,或重写父类方法

}

三. 继承的核心规则

1.单继承:

Java 不支持多继承(一个子类只能有一个直接父类)。

语法错误:java不支持多继承,不能同时直接继承多个类。只能“直接”继承1个类

重复的类:` C ` 类不能扩展多个类

解决方法:Java不支持多继承,但支持多重继承(多层继承)。

//Java 支持多层继承(链式继承)

class A {}

class B extends A {}

class C extends B {} // C 间接继承 A

2.子类继承父类后,除私有的不支持继承、构造方法不支持继承。其它的全部会继承。

①访问权限:

public 和 protected 成员:子类可以继承父类的public和protected成员(字段和方法),并直接访问它们。

private 成员:子类无法继承父类的private成员。若需操作父类的private成员,需通过父类提供的public或protected方法(如getter/setter)。

默认权限(包级私有):若子类与父类在同一个包内,子类可以继承默认权限的成员;否则不能。

示例:

class Parent {

private int privateVar; // 不可继承

protected int protectedVar; // 可继承

public int publicVar; // 可继承

private void privateMethod() {} // 不可继承

protected void protectedMethod() {} // 可继承

public void publicMethod() {} // 可继承

}

class Child extends Parent {

void accessMembers() {

// System.out.println(privateVar); // 编译错误

System.out.println(protectedVar); // 允许

System.out.println(publicVar); // 允许

// privateMethod(); // 编译错误

protectedMethod(); // 允许

publicMethod(); // 允许

}

}

②构造方法:

子类构造方法默认调用父类的无参构造方法(通过 super())。 如果父类没有无参构造方法,子类必须显式调用父类的有参构造方法(super(参数))。

3.一个类没有显示继承任何类时,默认继承java.lang.Object类。

在Java中,如果一个类没有显式继承其他类(即没有使用extends关键字),它会自动隐式继承java.lang.Object类。这是Java语言设计的核心机制,确保所有类都有一个共同的根类,形成统一的类型体系。

(1).为什么需要隐式继承Object类?

统一类型系统:所有对象(包括数组)都直接或间接继承Object,这使得Object可以作为通用类型使用(如泛型容器List)。

提供基础方法:Object类定义了对象的基本行为方法,如:

toString():返回对象的字符串表示。

equals():比较对象是否相等。

hashCode():返回对象的哈希码。

getClass():获取对象的运行时类信息。

clone():创建对象的副本。

finalize():垃圾回收前的清理操作(已弃用)。

(2).验证隐式继承的示例

示例1:自定义空类

// 没有显式继承任何类

public class MyClass {

// 空类

}

public class Main {

public static void main(String[] args) {

MyClass obj = new MyClass();

// 调用Object类的方法

System.out.println(obj.toString()); // 输出类似 MyClass@1b6d3586

System.out.println(obj.getClass()); // 输出 class MyClass

}

}

示例2:通过反射验证父类

import java.lang.reflect.Modifier;

public class Main {

public static void main(String[] args) {

Class clazz = MyClass.class;

Class superClass = clazz.getSuperclass();

System.out.println("MyClass的父类是: " + superClass.getName());

// 输出: MyClass的父类是: java.lang.Object

}

}

(3).特殊情况说明

接口不继承Object:

接口(interface)虽然不能显式继承Object,但实现接口的类仍然继承Object。

接口中声明的方法(如toString())默认是public abstract的,而Object中的同名方法是具体实现,二者不冲突。

数组类型:

所有数组类型(如int[]、String[])也隐式继承Object。

int[] arr = new int[5];

System.out.println(arr instanceof Object); // 输出 true 枚举类:

枚举类(enum)隐式继承java.lang.Enum,而Enum本身继承Object,因此枚举类间接继承Object。

(4).隐式继承的底层逻辑

当编译以下代码时:

public class MyClass { }

编译器会自动补全继承关系,等效于:

public class MyClass extends java.lang.Object { }

(5).总结

场景继承关系类未显式继承任何父类默认继承java.lang.Object类显式继承其他类直接继承指定的父类,间接继承Object(除非父类链中断)接口(interface)不继承Object,但实现类仍继承Object

通过隐式继承Object,Java确保了所有对象的行为一致性和可操作性,这是实现多态、反射、垃圾回收等高级特性的基础。

四. 继承的示例

父类 Animal

public class Animal {

protected String name;

protected int age;

public Animal(String name, int age) {

this.name = name;

this.age = age;

}

public void eat() {

System.out.println(name + " is eating.");

}

}

子类 Cat

public class Cat extends Animal {

public Cat(String name, int age) {

super(name, age); // 调用父类构造方法

}

// 子类扩展方法

public void meow() {

System.out.println(name + " says meow!");

}

// 重写父类方法(方法重写/Override)

@Override

public void eat() {

System.out.println(name + " is eating fish.");

}

}

测试类

public class Test {

public static void main(String[] args) {

Cat cat = new Cat("Tom", 2);

cat.eat(); // 输出:Tom is eating fish.

cat.meow(); // 输出:Tom says meow!

}

}

五、继承中变量的访问特点

在子类方法中访问一个变量

最先在子类局部范围找,如果没有就在子类成员范围找,最后在父类成员范围找,如果都没有就报错(不考虑父亲的父亲...)。

1.情况 1:完整代码(子类有成员变量 + 局部变量)

例如:创建一个父类Fu

public class Fu {

public int age = 10; // 父类成员变量

}

创建一个子类Zi

public class Zi extends Fu {

public int heigth = 180; // 子类独有变量

public int age = 20; // 隐藏父类的同名变量,如果没有这句,和下面那句,输出的是10

public void show() {

int age = 30; // 局部变量(优先级最高),如果没有这句,输出的是20

System.out.println(age); // 输出局部变量

System.out.println(heigth);

}

}

创建一个测试类Test

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

z.show(); // 调用方法

}

}

运行结果:

解释: 局部变量 age=30 优先级最高,直接输出;heigth 访问子类成员变量。

2.情况 2:去掉局部变量 age=30

父类 Fu

public class Fu {

public int age = 10; // 父类成员变量

}

子类 Zi

public class Zi extends Fu {

public int heigth = 180;

public int age = 20; // 隐藏父类的同名变量

public void show() {

// int age = 30; // 注释掉局部变量

System.out.println(age); // 访问子类成员变量

System.out.println(heigth);

}

}

测试类 Test(同上)

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

z.show();

}

}

运行结果:

解释: 没有局部变量时,访问子类成员变量 age=20。

3.情况 3:去掉子类成员变量 age 和局部变量 age

父类 Fu

public class Fu {

public int age = 10; // 父类成员变量

}

子类 Zi

public class Zi extends Fu {

public int heigth = 180;

// public int age = 20; // 注释掉子类成员变量

public void show() {

// int age = 30; // 注释掉局部变量

System.out.println(age); // 访问父类成员变量

System.out.println(heigth);

}

}

测试类 Test(同上)

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

z.show();

}

}

运行结果:

解释: 子类和局部都没有 age 时,向上查找父类变量 age=10。

4.总结表格

情况代码变化输出结果访问顺序子类有成员变量 + 局部变量保留所有变量30,180局部 → 子类 → 父类仅子类有成员变量去掉局部变量20,180子类 → 父类仅父类有成员变量去掉子类成员变量和局部变量10,180父类

5.关键规则

就近原则:优先访问离当前作用域最近的变量(局部 > 子类 > 父类)。

显式访问父类变量:若需要强制访问父类变量,使用 super.age。

变量隐藏:子类定义同名变量会隐藏父类变量,但不会覆盖(父类变量仍存在)。

编译报错:如果局、子类、父类都没有 age,编译报错。

六. super 关键字

作用:访问父类的成员(属性、方法、构造方法)。

1. super 关键字用法及与 this 关键字对比

1.1 this 与 super 关键字概念

this 关键字:代表本类对象的引用,指向调用该方法的对象。通常在当前类里使用,所以常说它代表本类对象的引用。super 关键字:代表父类存储空间的标识,可理解为父类对象引用。

1.2 this 与 super 关键字具体用法对比

关键字访问成员变量访问构造方法访问成员方法thisthis.成员变量:用于访问本类成员变量this(...):用于访问本类构造方法this.成员方法(...):用于访问本类成员方法supersuper.成员变量:用于访问父类成员变量super(...):用于访问父类构造方法super.成员方法(...):用于访问父类成员方法

1.3 代码示例及结果

// 父类 Fu

public class Fu {

public int age = 10;

}

// 子类 Zi

public class Zi extends Fu {

public int age = 20;

public void show() {

int age = 30;

System.out.println(age); // 输出 30,访问局部变量 age

System.out.println(this.age); // 访问本类中的成员变量 age

System.out.println(super.age); // 访问 Fu 类中的成员变量 age

}

}

// 测试类 Test

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

z.show();

}

}

运行结果:

七、继承中构造方法的访问特点

子类中所有的构造方法默认都会访问父类中无参的构造方法。原因在于子类会继承父类的数据,可能还会使用这些数据,所以在子类初始化之前,必须先完成父类数据的初始化。

每个子类构造方法的第一条语句默认都是 super()。

1.代码示例及结果

创建一个父类Fu

// 父类 Fu

public class Fu {

public Fu() {

System.out.println("Fu 中无参构造方法被调用");

}

public Fu(int age) {

System.out.println("Fu 中带参构造方法被调用");

}

}

创建一个子类Zi

// 子类 Zi

public class Zi extends Fu {

public Zi() {

// super(); 即使不写,默认也会有

System.out.println("Zi 中无参构造方法被调用");

}

public Zi(int age) {

// super(); 即使不写,默认也会有

System.out.println("Zi 中带参构造方法被调用");

}

}

测试:Test

// 测试类 Test

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

System.out.println("-------------------");

Zi zi = new Zi(18);

}

}

运行结果:

2.父类只有带参构造方法的处理办法

如果父类中没有无参构造方法,只有带参构造方法,有以下两种解决办法:

使用 super 关键字显式调用父类带参构造方法:在子类构造方法里,通过 super(参数) 的形式显式调用父类的带参构造方法。在父类中提供无参构造方法:为父类添加一个无参构造方法,这样子类构造方法就可以默认调用它。推荐采用这种方式,能避免很多不必要的错误。

例如:创建一个父类Fu

// 父类 Fu

public class Fu {

public Fu(int age) {

System.out.println("Fu 中带参构造方法被调用");

}

}

创建一个子类Zi

// 子类 Zi

public class Zi extends Fu {

public Zi() {

super(18);

System.out.println("Zi 中无参构造方法被调用");

}

public Zi(int age) {

super(18);

System.out.println("Zi 中带参构造方法被调用");

}

}

测试:Test

// 测试类 Test

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

System.out.println("-------------------");

Zi zi = new Zi(18);

}

}

运行结果:

八、继承中成员方法的访问特点

通过子类对象访问一个方法:

先子类成员范围找,如果找不到就在父类成员范围找,如果都没有就报错(不考虑父亲的父亲...)

情况一:子类有该方法,父类无该方法

当子类中定义了某个方法,而父类中没有定义该方法时,子类对象调用此方法会直接执行子类中的实现。

示例代码:

// 父类

class Fu {

// 父类没有定义 method 方法

}

// 子类

class Zi extends Fu {

public void method() {

System.out.println("Zi 中 method() 方法被调用");

}

}

// 测试类

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

z.method();

}

}

运行结果为:

解释:

因为在子类对象 z 调用 method 方法时,会先在子类成员范围查找,找到了该方法,所以直接执行子类的 method 方法。

情况二:子类无该方法,父类有该方法

若子类中没有定义某个方法,但父类中定义了,子类对象调用此方法会执行父类中的实现。

示例代码:

// 父类

class Fu {

public void show() {

System.out.println("Fu 中 show() 方法被调用");

}

}

// 子类

class Zi extends Fu {

// 子类没有定义 show 方法

}

// 测试类

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

z.show();

}

}

运行结果为:

解释:

当子类对象 z 调用 show 方法时,在子类成员范围未找到该方法,就会到父类成员范围查找,找到后执行父类的 show 方法。

情况三:子类和父类都有该方法(方法重写)

当子类和父类都定义了相同签名(方法名、参数列表和返回类型相同)的方法时,这就是方法重写,子类对象调用该方法会执行子类重写后的实现。若需要调用父类的原始方法,可以使用 super 关键字。

示例代码:

// 父类

class Fu {

public void show() {

System.out.println("Fu 中 show() 方法被调用");

}

}

// 子类

class Zi extends Fu {

@Override

public void show() {

super.show();

System.out.println("Zi 中 show() 方法被调用");

}

}

// 测试类

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

z.show();

}

}

运行结果为:

解释:

子类对象 z 调用 show 方法时,先在子类成员范围找到重写后的 show 方法。在子类的 show 方法里,通过 super.show() 调用了父类的 show 方法,所以先输出父类 show 方法的信息,接着输出子类 show 方法的信息。

情况四:子类和父类都无该方法

如果子类和父类中都没有定义某个方法,子类对象调用该方法会导致编译错误。

// 父类

class Fu {

// 父类没有定义 test 方法

}

// 子类

class Zi extends Fu {

// 子类也没有定义 test 方法

}

// 测试类

public class Test {

public static void main(String[] args) {

Zi z = new Zi();

// 下面这行代码会编译报错

// z.test();

}

}

结果及解释

由于 test 方法在子类和父类中都未定义,当尝试调用 z.test() 时,编译器会报错,提示找不到该方法。

综上所述,在继承中通过子类对象访问成员方法时,Java 会按照先子类成员范围、再父类成员范围的顺序查找方法,若都找不到则会报错。

九、继承的用途

代码复用:子类可以直接使用父类的属性和方法。

扩展功能:子类可以添加新的属性和方法。

多态实现:子类可以重写父类方法,实现多态行为。

十、方法重写(Override)

🟩回顾方法重载 overload

* 1. 什么时候考虑使用方法重载?

* 在一个类中,如果功能相似,可以考虑使用方法重载。

* 这样做的目的是:代码美观,方便编程。

*

* 2. 当满足什么条件的时候构成方法重载?

* 条件1:在同一个类中。

* 条件2:相同的方法名。

* 条件3:不同的参数列表:类型,个数,顺序

*

* 3. 方法重载机制属于编译阶段的功能。(方法重载机制是给编译器看的。)

*

* 🟦方法覆盖/override/方法重写/overwrite

* 1. 什么时候考虑使用方法重写?

* 当从父类中继承过来的方法,无法满足子类的业务需求时。

*

* 2. 当满足什么条件的时候,构成方法重写?

* 条件1:方法覆盖发生在具有继承关系的父子类之间。

* 条件2:具有相同的方法名(必须严格一样)

* 条件3:具有相同的形参列表(必须严格一样)

* 条件4:具有相同的返回值类型(可以是子类型)

*

* 3. 关于方法覆盖的细节:

* 3.1 当子类将父类方法覆盖之后,将来子类对象调用方法的时候,一定会执行重写之后的方法。

* 3.2 在java语言中,有一个注解,这个注解可以在编译阶段检查这个方法是否是重写了父类的方法。

* @Override注解是JDK5引入,用来标注方法,被标注的方法必须是重写父类的方法,如果不是重写的方法,编译器会报错。

* @Override注解只在编译阶段有用,和运行期无关。

* 3.3 如果返回值类型是引用数据类型,那么这个返回值类型可以是原类型的子类型

* 3.4 访问权限不能变低,可以变高。

* 3.5 抛出异常不能变多,可以变少。(后面学习异常的时候再说。)

* 3.6 私有的方法,以及构造方法不能继承,因此他们不存在方法覆盖。

* 3.7 方法覆盖针对法的是实例方法。和静态方法无关。(讲完多态再说。)

* 3.8 方法覆盖针对的是实例方法。和实例变量没有关系。

定义:子类重新实现父类的方法。

规则:

方法名、参数列表和返回类型必须与父类方法相同。

访问权限不能比父类方法更严格(如父类是 public,子类不能是 private)。

使用 @Override 注解显式声明重写(非强制,但推荐)。

十一、 继承的注意事项

避免过度继承:继承层次过深会导致代码复杂度增加。

优先使用组合:如果逻辑关系不是“is-a”,应使用组合(Composition)而非继承。

final 类:被 final 修饰的类不能被继承。

十二、 继承与接口的区别

特性继承接口(Interface)关系“is-a” 关系(父子关系)“can-do” 关系(能力定义)多继承不支持支持(一个类可实现多个接口)实现方式extends 关键字implements 关键字成员类型可以有属性、具体方法、构造方法只能有抽象方法和默认方法

十三. 总结

继承 是 Java 实现代码复用和多态的核心机制。

子类通过 extends 继承父类,可重写方法或扩展功能。

合理使用继承和组合,避免设计复杂的类层次结构。

相关文章