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. 注意事项
-
成员内部类:需要外部类对象才能创建
-
静态内部类:可以直接创建,不需要外部类对象
-
匿名内部类:必须继承一个类或实现一个接口
-
局部内部类:只能在方法内部使用
内部类就是"包在里面的小助手",可以直接访问主人的私有东西,但对外界是隐藏的。