JAVA_08_封装、继承和多态
01_继承是什么

02_举例子

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

04_继承中成员变量的查找顺序


05_继承成员方法
关于子类中方法的重写:
父类如下:

子类call方法重写如下:

05_继承中的构造方法


06_JAVA中的权限修饰符
07_JAVA中的多态
1. Fu.java (父类)
package com.itheima.test2; public class Fu { String name = "Fu"; public void fuShow() { System.out.println("父类的fuShow方法被调用了~"); } public void show() { System.out.println("父类的show方法被调用了~"); } }2. Zi.java (子类)
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方法被调用了~"); } }3. Test.java (测试类)
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不知道子类有这个方法。
因此多态是有弊端的,为了解决这个弊端可以使用类型转换
08_类型转换

09_抽象类和抽象方法
- 抽象方法:只有方法声明,没有方法体的方法
- 抽象类:包含抽象方法的类
(1)简单代码示例
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(); // 输出:小猫正在吃东西 // 输出:小猫喵喵叫! } }(2)核心规则总结
抽象方法:使用abstract关键字没有方法体(没有{})以分号结束抽象类:使用abstract关键字可以有抽象方法(必须用abstract)也可以有普通方法(可以有方法体)可以有属性可以有构造方法子类的责任:如果一个类继承了抽象类必须实现父类的所有抽象方法或者自己也声明为抽象类不能创建对象:抽象类不能直接new只能通过子类来创建对象
(3)和普通类的区别
区别点 | 普通类 | 抽象类 |
|---|---|---|
创建对象 | ✅ 可以 | ❌ 不可以 |
抽象方法 | ❌ 不能有 | ✅ 可以有 |
abstract关键字 | ❌ 不需要 | ✅ 必须用 |
继承 | 可以选择 | 必须被子类实现 |
(4)为什么要用抽象类?
- 强制规范:要求子类必须实现某些方法
- 代码复用:抽象类中的普通方法可以被所有子类共享
- 设计约束:比如“动物”都要会叫,但“狗”和“猫”叫的方式不同
(5)一句话理解抽象类:
"半成品" 或 "模板",它定义了一部分功能,剩下的让子类去完成。
10_接口
(1)接口 vs 抽象类主要区别总结
特性 | 抽象类 | 接口 |
|---|---|---|
关键字 | abstract class | interface |
继承 | 只能继承一个 | 可以实现多个 |
方法 | 可以有抽象方法和普通方法 | 主要是抽象方法(新版可以有默认方法) |
变量 | 可以有各种变量 | 只能是常量(public static final) |
构造方法 | ✅ 可以有 | ❌ 不能有 |
单继承 | ✅ 只能继承一个类 | ✅ 可以实现多个接口 |
(2)使用场景简单例子
一句话记住接口:接口就是"约定"或"协议",它说:"只要你实现了我的方法,你就具备了某种能力"
比如:USB接口约定好"连接"和"传输"两个方法,鼠标和键盘只要实现这两个方法,就能当USB设备用。
11_内部类
内部类:一个类定义在另一个类的内部
(1)例子
Outer.java(外部类)
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(); } }Test.java(测试类)
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(); } }(2)四种内部类
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(); } }(3)常用场景示例
情况1:成员内部类(标准写法)
// 外部类:汽车 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发动机启动了! } }情况2:静态内部类(独立性强)
// 外部类:学校 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(); // 小明在清华学习 } }情况3:匿名内部类(最常用)
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(); }情况4:局部内部类(方法里使用)
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 } }(4)实际应用场景
场景1:简化代码(事件监听器)
// 按钮类 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(); // 按钮被点击了! } }场景2:私有内部类(隐藏实现细节)
// 电脑类:对外只提供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(); // 数据传输中... } }6. 注意事项
- 成员内部类:需要外部类对象才能创建
- 静态内部类:可以直接创建,不需要外部类对象
- 匿名内部类:必须继承一个类或实现一个接口
- 局部内部类:只能在方法内部使用
内部类就是"包在里面的小助手",可以直接访问主人的私有东西,但对外界是隐藏的。