继承的概念

Java 面向对象核心:继承、多态与抽象类接口。详细讲解了 Java 继承机制,包括单继承原则、成员变量与方法查找顺序、构造方法调用及方法重写。介绍了多态的实现方式(父类引用指向子类对象)、编译期与运行期绑定规则以及类型转换。同时阐述了抽象类与接口的区别、使用场景及规范,并讲解了四种内部类(成员、静态、局部、匿名)的定义、创建方式与实际应用场景,如事件监听器与隐藏实现细节。



Java 中的继承有别于 C++,不能多继承,只能单继承,但是可以多层继承。
比如有三个类 A、B 和 C:一个子类只能继承一个父类,如 A 继承 B,不能同时继承多个父类,A 不能同时继承 B 和 C,但是可以 A 继承 B,然后 B 继承 C。
第二个特点是,假如一个 class 没有设置父类,那么默认继承 Object 类。



关于子类中方法的重写:
父类如下:

子类 call 方法重写如下:



package com.itheima.test2;
public class Fu {
String name = "Fu";
public void fuShow() {
System.out.println("父类的 fuShow 方法被调用了~");
}
public void show() {
System.out.println("父类的 show 方法被调用了~");
}
}
package com.itheima.test2;
public class Zi extends Fu {
String name = "Zi";
// 子类独有的方法
public void ziShow() {
System.out.println("子类的 ziShow 方法被调用了~");
}
// 子类重写父类的方法
@Override
public void show() {
System.out.println("子类重写的 show 方法被调用了~");
}
}
package com.itheima.test2;
public class Test {
public static void main(String[] args) {
// 多态:父类引用指向子类对象
Fu f = new Zi();
// 1. 访问成员变量
// 编译看左边 (Fu),运行也看左边 (Fu) -> 输出 Fu
System.out.println(f.name);
// 2. 访问成员方法
// 编译看左边 (Fu),运行看右边 (Zi) -> 输出 子类重写的 show 方法被调用了~
f.show();
// 3. 访问子类特有方法
// f.ziShow(); // ❌ 报错!因为编译时看左边 (Fu),Fu 类里没有 ziShow 方法
}
}
Fu 子类重写的 show 方法被调用了~
f是 Fu 类型,所以 f.name 永远取 Fu 里的值。f 是 Fu 类型,编译器允许调用 Fu 里的方法。f 实际上是 Zi 对象,所以执行的是 Zi 里重写的方法。f.ziShow() 会报错,因为父类 Fu 不知道子类有这个方法。因此多态是有弊端的,为了解决这个弊端可以使用类型转换。

Animal.java(抽象类)
// 抽象类:有抽象方法的类必须是抽象类
public abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
// 抽象方法:没有方法体,以分号结束
public abstract void makeSound();
// 普通方法:可以有具体实现
public void eat() {
System.out.println(name + "正在吃东西");
}
}
Dog.java(具体子类)
// 具体子类:必须实现父类的所有抽象方法
public class Dog extends Animal {
public Dog(String name) {
super(name);
}
// 必须实现抽象方法
@Override
public void makeSound() {
System.out.println(name + "汪汪叫!");
}
}
Cat.java(具体子类)
public class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void makeSound() {
System.out.println(name + "喵喵叫!");
}
}
Test.java(测试类)
public class Test {
public static void main(String[] args) {
// ❌ 错误:抽象类不能直接创建对象
// Animal animal = new Animal("动物");
// ✅ 正确:创建具体子类的对象
Animal dog = new Dog("小狗");
Animal cat = new Cat("小猫");
dog.eat(); // ✅ 调用普通方法
dog.makeSound(); // ✅ 调用具体实现的抽象方法
// 输出:小狗正在吃东西
// 输出:小狗汪汪叫!
cat.eat();
cat.makeSound(); // 输出:小猫正在吃东西
// 输出:小猫喵喵叫!
}
}
抽象方法:使用
abstract关键字没有方法体(没有{})以分号结束 抽象类:使用abstract关键字可以有抽象方法(必须用abstract)也可以有普通方法(可以有方法体),可以有属性,可以有构造方法 子类的责任:如果一个类继承了抽象类必须实现父类的所有抽象方法或者自己也声明为抽象类 不能创建对象:抽象类不能直接new,只能通过子类来创建对象
| 区别点 | 普通类 | 抽象类 |
|---|---|---|
| 创建对象 | ✅ 可以 | ❌ 不可以 |
| 抽象方法 | ❌ 不能有 | ✅ 可以有 |
| abstract 关键字 | ❌ 不需要 | ✅ 必须用 |
| 继承 | 可以选择 | 必须被子类实现 |
'半成品' 或 '模板',它定义了一部分功能,剩下的让子类去完成。
| 特性 | 抽象类 | 接口 |
|---|---|---|
| 关键字 | abstract class | interface |
| 继承 | 只能继承一个 | 可以实现多个 |
| 方法 | 可以有抽象方法和普通方法 | 主要是抽象方法(新版可以有默认方法) |
| 变量 | 可以有各种变量 | 只能是常量(public static final) |
| 构造方法 | ✅ 可以有 | ❌ 不能有 |
| 单继承 | ✅ 只能继承一个类 | ✅ 可以实现多个接口 |
一句话记住接口:接口就是'约定'或'协议',它说:'只要你实现了我的方法,你就具备了某种能力'
比如:USB 接口约定好'连接'和'传输'两个方法,鼠标和键盘只要实现这两个方法,就能当 USB 设备用。
内部类:一个类定义在另一个类的内部
public class Outer {
private String outerField = "我是外部类";
// 成员内部类:像外部类的成员一样
class Inner {
private String innerField = "我是内部类";
public void show() {
// 内部类可以直接访问外部类的私有成员
System.out.println("访问外部类:" + outerField);
System.out.println("访问内部类:" + innerField);
}
}
public void test() {
// 外部类创建内部类对象
Inner inner = new Inner();
inner.show();
}
}
public class Test {
public static void main(String[] args) {
// 1. 先创建外部类对象
Outer outer = new Outer();
// 2. 通过外部类对象创建内部类对象
Outer.Inner inner = outer.new Inner();
inner.show(); // 或者直接调用外部类的方法
outer.test();
}
}
public class FourTypes {
private String name = "外部类";
// 1. 成员内部类(最常用):普通类中的普通类
class MemberInner {
void show() {
System.out.println("成员内部类:" + name);
}
}
// 2. 静态内部类:普通类中的静态类
static class StaticInner {
void show() {
// System.out.println(name); // ❌ 不能访问外部非静态成员
System.out.println("静态内部类");
}
}
public void test() {
// 3. 局部内部类:方法里的类
class LocalInner {
void show() {
System.out.println("局部内部类:" + name);
}
}
LocalInner li = new LocalInner();
li.show();
// 4. 匿名内部类:没有名字的类(常用)
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类:" + name);
}
};
r.run();
}
}
// 外部类:汽车
class Car {
private String brand = "奔驰";
// 内部类:发动机
class Engine {
private String type = "V8";
public void start() {
// 可以访问外部类的私有属性
System.out.println(brand + "汽车的" + type + "发动机启动了!");
}
}
}
// 使用
public class Test1 {
public static void main(String[] args) {
Car car = new Car();
Car.Engine engine = car.new Engine();
engine.start(); // 奔驰汽车的 V8 发动机启动了!
}
}
// 外部类:学校
class School {
private static String schoolName = "清华";
// 静态内部类:学生
static class Student {
private String name;
public Student(String name) {
this.name = name;
}
public void study() {
System.out.println(name + "在" + schoolName + "学习"); // 只能访问外部类的静态成员
}
}
}
// 使用:不需要外部类对象
public class Test2 {
public static void main(String[] args) {
School.Student student = new School.Student("小明");
student.study(); // 小明在清华学习
}
}
public class Test3 {
public static void main(String[] args) {
// 场景 1:实现接口(比如按钮点击)
Runnable task = new Runnable() {
@Override
public void run() {
System.out.println("匿名内部类:执行任务");
}
};
task.run();
// 场景 2:继承抽象类
Animal cat = new Animal() {
@Override
void sound() {
System.out.println("喵喵叫");
}
};
cat.sound();
}
}
abstract class Animal {
abstract void sound();
}
public class Test4 {
public void outerMethod() {
final int localVar = 100; // 局部变量需要是 final 或事实上 final
// 局部内部类
class LocalClass {
void show() {
System.out.println("局部内部类,访问局部变量:" + localVar);
}
}
LocalClass lc = new LocalClass();
lc.show();
}
public static void main(String[] args) {
Test4 test = new Test4();
test.outerMethod(); // 局部内部类,访问局部变量:100
}
}
// 按钮类
class Button {
// 内部接口
interface OnClickListener {
void onClick();
}
private OnClickListener listener;
public void setOnClickListener(OnClickListener listener) {
this.listener = listener;
}
public void click() {
if (listener != null) {
listener.onClick();
}
}
}
// 使用(最常见)
public class App {
public static void main(String[] args) {
Button button = new Button();
// 用匿名内部类设置点击监听器
button.setOnClickListener(new Button.OnClickListener() {
@Override
public void onClick() {
System.out.println("按钮被点击了!");
}
});
button.click(); // 按钮被点击了!
}
}
// 电脑类:对外只提供 USB 接口
class Computer {
// 私有内部类:对外隐藏 USB 实现
private class USBImpl implements USB {
@Override
public void connect() {
System.out.println("USB 已连接");
}
@Override
public void transfer() {
System.out.println("数据传输中...");
}
}
// 对外提供 USB 接口
public USB getUSB() {
return new USBImpl();
}
}
interface USB {
void connect();
void transfer();
}
// 外部只能使用 USB 接口,看不到内部实现
public class User {
public static void main(String[] args) {
Computer computer = new Computer();
USB usb = computer.getUSB(); // 只能获得 USB 接口
usb.connect(); // USB 已连接
usb.transfer(); // 数据传输中...
}
}
内部类就是'包在里面的小助手',可以直接访问主人的私有东西,但对外界是隐藏的。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online