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. 局部内部类:只能在方法内部使用

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

相关推荐
键盘鼓手苏苏1 小时前
Flutter for OpenHarmony:dart_ping 网络诊断的瑞士军刀(支持 ICMP Ping) 深度解析与鸿蒙适配指南
开发语言·网络·flutter·华为·rust·harmonyos
CodeByV1 小时前
【Qt】窗口
开发语言·qt
白露与泡影1 小时前
Java 春招高级面试指南( Java 面试者必备)
java·开发语言·面试
_OP_CHEN1 小时前
【前端开发之JavaScript】(四)JS基础语法下篇:函数与对象核心要点深度解析
开发语言·前端·javascript·界面开发·前端开发·网页开发·语法基础
大尚来也2 小时前
Python 调用 Ollama 本地大模型 API 完全指南
开发语言·python
追随者永远是胜利者2 小时前
(LeetCode-Hot100)3. 无重复字符的最长子串
java·算法·leetcode·职场和发展·go
qq_24218863322 小时前
Python 春节贺卡代码
开发语言·python
Lenyiin2 小时前
《LeetCode 顺序刷题》11 -20
java·c++·python·算法·leetcode·lenyiin
wuqingshun3141592 小时前
说一下java的四种引用
java·开发语言