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方法被调用了~

核心知识点总结(看图中的注释):

  1. 成员变量 :编译看左边,运行也看左边(编译期绑定)。

    • fFu类型,所以 f.name永远取 Fu里的值。
  2. 成员方法 :编译看左边,运行看右边(运行期绑定/动态绑定)。

    • fFu类型,编译器允许调用 Fu里的方法。

    • 但运行时发现 f实际上是 Zi对象,所以执行的是 Zi里重写的方法。

  3. 子类特有方法:不能通过父类引用直接调用。

    • 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)核心规则总结

  1. 抽象方法

    • 使用 abstract关键字

    • 没有方法体(没有 {}

    • 以分号结束

  2. 抽象类

    • 使用 abstract关键字

    • 可以有抽象方法(必须用 abstract

    • 也可以有普通方法(可以有方法体)

    • 可以有属性

    • 可以有构造方法

  3. 子类的责任

    • 如果一个类继承了抽象类

    • 必须实现父类的所有抽象方法

    • 或者自己也声明为抽象类

  4. 不能创建对象

    • 抽象类不能直接 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. 注意事项

  1. 成员内部类:需要外部类对象才能创建

  2. 静态内部类:可以直接创建,不需要外部类对象

  3. 匿名内部类:必须继承一个类或实现一个接口

  4. 局部内部类:只能在方法内部使用

内部类就是"包在里面的小助手",可以直接访问主人的私有东西,但对外界是隐藏的。

相关推荐
zhangfeng113327 分钟前
openclaw skills 小龙虾技能 通讯仿真 matlab skill Simulink Agentic Toolkit,通过kimi找到,mcp通讯
开发语言·matlab·openclaw·通讯仿真
Javatutouhouduan7 小时前
2026Java面试的正确打开方式!
java·高并发·java面试·java面试题·后端开发·java编程·java八股文
chao1898447 小时前
基于 SPEA2 的多目标优化算法 MATLAB 实现
开发语言·算法·matlab
JAVA面经实录9177 小时前
Java初级最终完整版学习路线图
java·spring·eclipse·maven
赏金术士7 小时前
Kotlin 习题集 · 高级篇
android·开发语言·kotlin
Cat_Rocky8 小时前
k8s-持久化存储,粗浅学习
java·学习·kubernetes
楼兰公子8 小时前
buildroot 在编译rust时裁剪平台类型数量的方法
开发语言·后端·rust
知识领航员8 小时前
蘑兔AI音乐深度实测:功能拆解、实测表现与适用场景
java·c语言·c++·人工智能·python·算法·github
吴声子夜歌9 小时前
Go——并发编程
开发语言·后端·golang
释怀°Believe9 小时前
Spring解析
java·后端·spring