跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
Javajava

从 C 到 Java:面向对象编程核心概念实战

Java 面向对象编程核心概念实战教程。针对有 C 语言基础的开发者,对比过程式与面向对象思维差异。涵盖类与对象、封装(访问修饰符、getter/setter)、继承(extends、super、重写)、多态(向上转型、接口、抽象类)。通过银行账号、图形类等实例演示代码复用与扩展性,强调构造方法规则及 final 关键字应用,帮助快速掌握 Java 核心特性。

清酒独酌发布于 2026/3/16更新于 2026/6/1720 浏览

第一课:从'过程'到'对象'

1.1 回顾:C 语言的过程式思维

对于有 C 语言基础的开发者,理解 Java 的第一步是意识到编程思维的转变。在 C 中,我们关注的是步骤和流程。

// 过程式思维:思考'步骤'
#include <stdio.h>
#include <string.h>

// 定义数据结构(数据与操作分离)
struct Student {
    char name[20];
    int age;
    float score;
};

// 定义操作函数
void printStudentInfo(struct Student s) {
    printf("姓名:%s,年龄:%d,成绩:%.1f\n", s.name, s.age, s.score);
}

void updateScore(struct Student* s, float newScore) {
    s->score = newScore;
}

int main() {
    // 创建数据
    struct Student stu1;
    strcpy(stu1.name, "张三");
    stu1.age = 20;
    stu1.score = 85.5;

    // 调用函数操作数据
    printStudentInfo(stu1);
    updateScore(&stu1, 90.0);
    printStudentInfo(stu1);
    return 0;
}

C 语言的特点:

  • 数据(struct)是被动的,像一堆零件
  • 函数是的,像工人去操作这些零件
主动
  • 数据和函数分离,需要你手动'传递'数据给函数
  • 适合解决线性、步骤清晰的问题
  • 1.2 走进:Java 的面向对象世界

    同样的需求,在 Java 中是如何处理的?我们把数据和操作打包在一起。

    // 面向对象思维:思考'对象'
    public class Student {
        // 这是'蓝图',叫做'类'
        // 数据(称为'属性'或'成员变量')
        String name; // 注意:没有 char 数组,Java 有 String 类型!
        int age;
        double score; // 注意:Java 用 double 更多,而不是 float
    
        // 操作数据的方法(函数在类中就叫'方法')
        // 这是一个普通方法,属于 Student 对象
        public void printInfo() {
            // this 指'当前对象自己',类似 C 语言结构体指针
            System.out.println("姓名:" + this.name + ",年龄:" + this.age + ",成绩:" + this.score);
        }
    
        public void updateScore(double newScore) {
            this.score = newScore;
        }
    
        // 主方法:程序的入口
        public static void main(String[] args) {
            // 根据 Student 类'蓝图',创建一个具体的'对象'
            // new 是关键!它会在内存中开辟空间,创建一个真实的 Student 对象
            Student stu1 = new Student();
    
            // 给对象的属性赋值
            stu1.name = "张三";
            stu1.age = 20;
            stu1.score = 85.5;
    
            // 调用对象的'方法'(让对象自己做事)
            stu1.printInfo(); // 输出:姓名:张三,年龄:20,成绩:85.5
            stu1.updateScore(90.0);
            stu1.printInfo(); // 输出:姓名:张三,年龄:20,成绩:90.0
        }
    }
    
    1.3 核心比喻:从'菜谱'到'餐厅'
    C 语言(过程式)Java(面向对象)现实比喻
    数据(结构体)属性(成员变量)食材
    函数方法厨师的技能
    main() 函数调用所有对象调用自己的方法顾客点餐,厨师做菜
    编程思维编程思维管理思维
    '第一步做什么,第二步做什么''这个对象有什么能力,那个对象有什么能力''服务员负责接待,厨师负责烹饪,收银员负责结账'

    关键理解:

    • 在 Java 中,Student stu1 = new Student(); 就像:
      • 在 C 语言中:struct Student* stu1 = (struct Student*)malloc(sizeof(struct Student));
      • 但 Java 帮你管理内存,不需要 free()!
    • stu1.printInfo() 就像:
      • 在 C 语言中:printStudentInfo(stu1);
      • 但现在是'让 stu1 自己打印自己的信息',更自然!
    1.4 为什么要有这种转变?

    想象你要开发一个'校园管理系统':

    • C 语言方式:你需要管理很多函数:addStudent(), addTeacher(), addCourse(), printStudent(), printTeacher()... 函数越来越多,关系越来越乱。
    • Java 方式:
      • Student 类:负责所有与学生相关的数据和操作
      • Teacher 类:负责所有与老师相关的数据和操作
      • Course 类:负责所有与课程相关的数据和操作

    每个对象都是一个小型的'自治系统',知道自己该做什么。你的 main() 方法只需要协调这些对象合作即可。


    实践小练习

    在你给出习题前,我们先做个小练习,巩固这个概念:

    public class BankAccount {
        // 属性
        String owner; // 账户持有人
        double balance; // 余额
    
        // 方法 1:存款
        public void deposit(double money) {
            balance = balance + money;
            System.out.println(owner + "存入" + money + "元,当前余额:" + balance);
        }
    
        // 方法 2:取款
        public void withdraw(double money) {
            if (money <= balance) {
                balance = balance - money;
                System.out.println(owner + "取出" + money + "元,当前余额:" + balance);
            } else {
                System.out.println("余额不足!");
            }
        }
    
        // 方法 3:查询余额
        public void checkBalance() {
            System.out.println(owner + "的账户余额为:" + balance + "元");
        }
    
        public static void main(String[] args) {
            // 创建第一个账户对象
            BankAccount account1 = new BankAccount();
            account1.owner = "小明";
            account1.balance = 1000.0;
    
            // 创建第二个账户对象
            BankAccount account2 = new BankAccount();
            account2.owner = "小红";
            account2.balance = 500.0;
    
            // 操作账户
            account1.deposit(500); // 小明存入 500
            account1.withdraw(200); // 小明取出 200
            account1.checkBalance(); // 查询小明余额
            account2.deposit(1000); // 小红存入 1000
            account2.checkBalance(); // 查询小红余额
        }
    }
    

    输出结果: 小明存入 500.0 元,当前余额:1500.0 小明取出 200.0 元,当前余额:1300.0 小明的账户余额为:1300.0 元 小红存入 1000.0 元,当前余额:1500.0 小红的账户余额为:1500.0 元

    看到面向对象的魅力了吗?

    • account1 和 account2 是两个独立的对象
    • 它们有相同的结构(都有 owner、balance,都能 deposit、withdraw)
    • 但数据完全独立(小明的操作不会影响小红的余额)
    • 每个对象都封装了自己的状态和行为

    💡 本课要点总结

    1. 类(Class):是'蓝图'或'模板',定义了一类对象应该有什么属性(数据)和方法(操作)。
    2. 对象(Object):是根据类创建出来的具体实例。new 关键字就是创建对象的'魔法咒语'。
    3. 属性(Field):对象内部的数据,相当于 C 语言结构体的成员。
    4. 方法(Method):对象能执行的操作,相当于 C 语言的函数,但属于某个对象。
    5. 核心思想:把相关的数据和对这些数据的操作打包在一起,形成一个独立的、自包含的单元。

    第二课:封装 —— 给你的数据穿上'防护服'

    上节课我们从'做菜步骤'(C 语言)进入了'开餐厅'(Java)的世界,建立了'类'和'对象'的基本概念。今天我们要学习封装(Encapsulation),这是面向对象的第一个重要特性,也是实际编程中最实用的技能。


    2.1 从现实问题说起

    先看一个上节课的例子,但这次我们发现了一个安全隐患:

    public class BankAccount {
        // 问题:所有属性都是公开的
        public String owner;
        public double balance;
    
        public static void main(String[] args) {
            BankAccount account = new BankAccount();
            account.owner = "小明";
            account.balance = 1000.0;
    
            // 问题来了:外部可以直接修改余额,甚至设置为负数!
            account.balance = -5000.0; // 这合理吗?
            account.balance = 9999999.0; // 这合法吗?
            System.out.println("余额:" + account.balance); // 输出完全不合理的值
        }
    }
    

    发现了什么?

    1. 任何人都能随意修改 balance
    2. 可以设置为负数(现实世界不允许)
    3. 可以设置为天文数字(不真实)

    这就像:把你的钱包放在桌子上,任何人都可以随便拿钱或放钱,没有密码、没有验证、没有记录。


    2.2 封装的解决方案:黑盒子理论

    封装就像给你的数据穿上防护服,或者给你的对象加上操作手册:

    public class SafeBankAccount {
        // 关键步骤 1:使用 private 关键字,将属性设为'私有'
        // private 的意思是:只有这个类内部的方法可以访问
        private String owner;
        private double balance;
    
        // 关键步骤 2:提供'公开'的方法来访问和修改私有属性
        // 这些方法叫做 getter(获取)和 setter(设置)
    
        // getter 方法:用于获取属性值
        public String getOwner() {
            return owner;
        }
    
        // setter 方法:用于设置属性值(可以加入验证逻辑)
        public void setOwner(String owner) {
            this.owner = owner;
        }
    
        // getter for balance
        public double getBalance() {
            return balance;
        }
    
        // 注意:我们不给 balance 提供直接的 setter
        // 而是提供专门的存款和取款方法,这样才能加入业务逻辑
    
        // 存款方法:只能增加余额
        public void deposit(double money) {
            if (money > 0) {
                balance += money;
                System.out.println("成功存款:" + money + "元");
            } else {
                System.out.println("存款金额必须大于 0!");
            }
        }
    
        // 取款方法:有余额验证
        public void withdraw(double money) {
            if (money <= 0) {
                System.out.println("取款金额必须大于 0!");
            } else if (money <= balance) {
                balance -= money;
                System.out.println("成功取款:" + money + "元");
            } else {
                System.out.println("余额不足!当前余额:" + balance);
            }
        }
    
        // 展示账户信息
        public void displayAccountInfo() {
            System.out.println("账户持有人:" + owner);
            System.out.println("账户余额:" + balance + "元");
        }
    
        public static void main(String[] args) {
            SafeBankAccount account = new SafeBankAccount();
    
            // 设置账户持有人
            account.setOwner("小明");
    
            // 存款(通过我们设计的安全接口)
            account.deposit(1000);
            account.deposit(500);
    
            // 取款(有安全检查)
            account.withdraw(300);
            account.withdraw(2000); // 这里会失败!
    
            // 尝试非法操作:不能直接修改 balance 了!
            // account.balance = -5000; // 编译错误!因为是 private 属性
    
            // 只能通过 getter 获取余额
            System.out.println("当前余额:" + account.getBalance());
            account.displayAccountInfo();
        }
    }
    

    运行结果: 成功存款:1000.0 元 成功存款:500.0 元 取款 300.0 元 余额不足!当前余额:1200.0 当前余额:1200.0 账户持有人:小明 账户余额:1200.0 元


    2.3 封装的核心概念:访问修饰符

    Java 提供了 4 种访问级别,从最严格到最宽松:

    public class AccessExample {
        // 1. private:只有本类内部可以访问
        private String secret = "这是秘密,只有本类能看到";
    
        // 2. 默认(不写修饰符):同包内可以访问
        String packagePrivate = "同包内的类可以看到我";
    
        // 3. protected:同包内或子类可以访问
        protected String familySecret = "家人和同住的人可以看到";
    
        // 4. public:所有地方都可以访问
        public String publicInfo = "我是公开信息,谁都能看";
    
        // 访问私有属性的方法
        public String getSecret() {
            return secret; // 类内部可以访问 private
        }
    }
    
    // 另一个类(可能在另一个文件)
    class TestAccess {
        public void test() {
            AccessExample example = new AccessExample();
            // System.out.println(example.secret); // 错误!private 不能访问
            // System.out.println(example.packagePrivate); // 如果不在同包,错误!
            // System.out.println(example.familySecret); // 如果不是子类或同包,错误!
            System.out.println(example.publicInfo); // 正确!public 可以访问
            System.out.println(example.getSecret()); // 通过 public 方法间接访问 private
        }
    }
    

    记忆技巧:

    • private(私有):像你的日记,只有自己能看
    • 默认:像家庭内部通知,家里人能看到
    • protected(受保护):像家族秘密,家人和亲戚能看到
    • public(公开):像公告栏,所有人都能看到

    2.4 封装的好处:为什么我们要这么做?

    好处 1:安全性
    // 没有封装
    account.balance = -10000; // 可以直接设为负数
    
    // 有封装
    account.withdraw(10000); // 方法内部会检查:if(money <= balance)... // 余额不足时会拒绝操作!
    
    好处 2:可控性
    public void setAge(int age) {
        if (age >= 0 && age <= 150) { // 年龄合理性检查
            this.age = age;
        } else {
            System.out.println("年龄不合法!");
        }
    }
    
    public void setName(String name) {
        if (name != null && !name.trim().isEmpty()) { // 非空检查
            this.name = name.trim(); // 去掉前后空格
        }
    }
    
    好处 3:维护性

    如果需要修改内部实现,只要保持接口不变,外部代码完全不受影响:

    // 版本 1:用普通变量存储余额
    private double balance;
    
    // 版本 2:改为从数据库读取(外部调用代码完全不变!)
    private double getBalanceFromDatabase() {
        // 连接数据库,查询余额...
        return dbBalance;
    }
    
    public double getBalance() {
        // 外部还是调用 getBalance(),不知道内部已经变了
        return getBalanceFromDatabase();
    }
    
    好处 4:隐藏复杂度
    // 外部只需要知道:调用 transfer() 可以转账
    public boolean transfer(BankAccount target, double amount) {
        // 内部可能有:验证账户、检查余额、记录日志、更新数据库...
        // 但外部调用者完全不需要知道这些细节
        if (this.withdraw(amount)) {
            target.deposit(amount);
            logTransaction(this, target, amount);
            return true;
        }
        return false;
    }
    

    2.5 标准做法:getter 和 setter

    在 Java 中,为私有属性提供 getter 和 setter 是标准做法。IDE(如 Eclipse、IntelliJ)可以自动生成:

    public class Student {
        // 私有属性
        private String name;
        private int age;
        private String id;
    
        // 标准 getter 格式:get + 属性名(首字母大写)
        public String getName() {
            return name;
        }
    
        // 标准 setter 格式:set + 属性名(首字母大写)
        public void setName(String name) {
            this.name = name; // this.name 指类的属性,name 是参数
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            if (age >= 6 && age <= 60) { // 加入业务逻辑
                this.age = age;
            }
        }
    
        // 有些属性可能只有 getter,没有 setter(只读属性)
        public String getId() {
            return id;
        }
    
        // 没有 setId 方法,id 一旦设置就不能修改
        public void setId(String id) {
            if (this.id == null) { // 只能设置一次
                this.id = id;
            }
        }
    }
    

    this 关键字的理解:

    • this 代表'当前对象自己'
    • this.name 指的是类的属性 name
    • 不加 this 的 name 指的是方法的参数 name
    • 当参数名和属性名相同时,必须用 this 来区分

    2.6 实践练习:改造一个 C 语言风格的程序

    假设你有这样的 C 语言代码:

    struct Rectangle {
        double width;
        double height;
    };
    
    double calculateArea(struct Rectangle r) {
        return r.width * r.height;
    }
    
    void setSize(struct Rectangle* r, double w, double h) {
        r->width = w;
        r->height = h;
    }
    

    用 Java 封装思想重写:

    public class Rectangle {
        // 私有属性
        private double width;
        private double height;
    
        // 构造方法(下一节详细讲)
        public Rectangle(double width, double height) {
            setWidth(width);
            setHeight(height);
        }
    
        // getter
        public double getWidth() {
            return width;
        }
    
        public double getHeight() {
            return height;
        }
    
        // setter(加入验证)
        public void setWidth(double width) {
            if (width > 0) {
                this.width = width;
            } else {
                System.out.println("宽度必须大于 0!");
            }
        }
    
        public void setHeight(double height) {
            if (height > 0) {
                this.height = height;
            } else {
                System.out.println("高度必须大于 0!");
            }
        }
    
        // 计算面积(对象自己的方法)
        public double calculateArea() {
            return width * height;
        }
    
        // 计算周长
        public double calculatePerimeter() {
            return 2 * (width + height);
        }
    
        public static void main(String[] args) {
            // 创建矩形对象
            Rectangle rect = new Rectangle(5.0, 3.0);
            System.out.println("宽度:" + rect.getWidth());
            System.out.println("高度:" + rect.getHeight());
            System.out.println("面积:" + rect.calculateArea());
            System.out.println("周长:" + rect.calculatePerimeter());
    
            // 修改尺寸
            rect.setWidth(10.0);
            rect.setHeight(5.0);
            System.out.println("新面积:" + rect.calculateArea());
    
            // 尝试非法设置
            rect.setWidth(-5.0); // 会输出错误信息,宽度不变
        }
    }
    

    💡 本课要点总结

    1. 封装是什么:把对象的属性和方法包装在一起,隐藏内部细节,只暴露必要的接口。
    2. 如何实现:
      • 使用 private 关键字保护属性
      • 提供 public 的 getter 和 setter 方法
      • 在方法中加入业务逻辑验证
    3. 访问修饰符:
      • private:只有本类
      • 默认:同包内
      • protected:同包内 + 子类
      • public:所有地方
    4. 封装的好处:
      • 安全性:防止不合理的数据
      • 可控性:可以加入验证逻辑
      • 维护性:内部修改不影响外部
      • 简洁性:隐藏复杂实现

    第三课:继承 —— 家族的智慧传承

    同学真棒!已经坚持到第三课了。封装就像给每个对象穿上防护服,让它们安全又自律。今天我们要学习继承(Inheritance),这是面向对象最强大的特性之一,它能让我们写出优雅、简洁、可复用的代码。


    📚 本章学习目标

    1. 理解继承的概念和意义
    2. 掌握 extends 关键字的用法
    3. 理解 super 关键字的三种用法
    4. 掌握方法重写(Override)的规则
    5. 掌握构造方法的继承规则(考试重点!)
    6. 了解 final 关键字的作用

    3.1 从现实问题说起:代码重复的烦恼

    假设我们要为学校开发系统,有学生和老师:

    // 学生类
    class Student {
        private String name;
        private int age;
        private String studentId;
        private double gpa;
        // getter 和 setter...
        public void study() {
            System.out.println(name + "正在学习...");
        }
        public void takeExam() {
            System.out.println(name + "正在参加考试");
        }
    }
    
    // 教师类
    class Teacher {
        private String name;
        private int age;
        private String teacherId;
        private String subject;
        // getter 和 setter...(和学生类几乎一样!)
        public void teach() {
            System.out.println(name + "正在教授" + subject);
        }
        public void gradeExam() {
            System.out.println(name + "正在批改试卷");
        }
    }
    

    发现问题了吗?

    1. Student 和 Teacher 都有 name、age、id
    2. 都有相似的 getter/setter
    3. 都要写 displayInfo() 方法

    这就像:每次造汽车,都要重新发明轮子、方向盘、发动机...


    3.2 继承的解决方案:家族的智慧

    在现实世界中:孩子会继承父母的特征(眼睛颜色、身高...),也会继承父母的一些能力(语言、文化...),还可以发展自己的独特能力。

    在 Java 中,我们可以创建'父类'(基类/超类)来存放公共特征:

    // 第一步:创建父类(基类)
    class Person {
        // 人,作为父类
        // 公共属性
        private String name;
        private int age;
        private String id; // 身份证号
    
        // 公共的 getter 和 setter
        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
        public int getAge() { return age; }
        public void setAge(int age) {
            if (age > 0 && age < 150) { this.age = age; }
        }
        public String getId() { return id; }
        public void setId(String id) { this.id = id; }
    
        // 公共方法
        public void introduce() {
            System.out.println("大家好,我是" + name + ",今年" + age + "岁");
        }
        public void eat() { System.out.println(name + "正在吃饭"); }
        public void sleep() { System.out.println(name + "正在睡觉"); }
    }
    
    // 第二步:创建子类,使用 extends 关键字
    class Student extends Person {
        // Student 继承 Person
        // 子类特有的属性
        private String studentId;
        private double gpa;
        private String major;
    
        // 子类特有的 getter 和 setter
        public String getStudentId() { return studentId; }
        public void setStudentId(String studentId) { this.studentId = studentId; }
        public double getGpa() { return gpa; }
        public void setGpa(double gpa) {
            if (gpa >= 0 && gpa <= 4.0) { this.gpa = gpa; }
        }
        public String getMajor() { return major; }
        public void setMajor(String major) { this.major = major; }
    
        // 子类特有的方法
        public void study() {
            System.out.println(getName() + "正在学习" + major);
        }
        public void takeExam() {
            System.out.println(getName() + "正在参加" + major + "考试");
        }
    
        // 可以重写(覆盖)父类的方法
        @Override // 注解:表示这是重写父类方法
        public void introduce() {
            // 先调用父类的 introduce()
            super.introduce(); // super 指父类对象
            // 再添加子类特有的信息
            System.out.println("我是" + major + "专业的学生,学号:" + studentId);
        }
    }
    
    // 另一个子类
    class Teacher extends Person {
        // Teacher 也继承 Person
        // 子类特有的属性
        private String teacherId;
        private String subject;
        private String title; // 职称
    
        // 子类特有的 getter 和 setter
        public String getTeacherId() { return teacherId; }
        public void setTeacherId(String teacherId) { this.teacherId = teacherId; }
        public String getSubject() { return subject; }
        public void setSubject(String subject) { this.subject = subject; }
        public String getTitle() { return title; }
        public void setTitle(String title) { this.title = title; }
    
        // 子类特有的方法
        public void teach() {
            System.out.println(getName() + "(" + title + ") 正在教授" + subject);
        }
        public void gradeExam() {
            System.out.println(getName() + "正在批改" + subject + "试卷");
        }
    
        // 重写父类方法
        @Override
        public void introduce() {
            super.introduce();
            System.out.println("我是" + subject + "老师,职称:" + title);
        }
    }
    
    测试类
    public class InheritanceDemo {
        public static void main(String[] args) {
            // 创建学生对象
            Student stu = new Student();
            stu.setName("张三");
            stu.setAge(20);
            stu.setId("110101200001011234");
            stu.setStudentId("20230001");
            stu.setMajor("计算机科学");
            stu.setGpa(3.8);
    
            // 调用从父类继承的方法
            stu.eat(); // 继承自 Person
            stu.sleep(); // 继承自 Person
            stu.introduce(); // 调用重写后的 introduce()
    
            // 调用子类特有的方法
            stu.study();
            stu.takeExam();
    
            System.out.println("-------------------");
    
            // 创建教师对象
            Teacher tea = new Teacher();
            tea.setName("李教授");
            tea.setAge(45);
            tea.setId("110101197801011234");
            tea.setTeacherId("T001");
            tea.setSubject("面向对象程序设计");
            tea.setTitle("教授");
            tea.introduce(); // 教师的重写版本
            tea.teach();
            tea.gradeExam();
        }
    }
    

    运行结果: 张三正在吃饭 张三正在睡觉 大家好,我是张三,今年 20 岁 我是计算机科学专业的学生,学号:20230001 张三正在学习计算机科学 张三正在参加计算机科学考试 ------------------- 大家好,我是李教授,今年 45 岁 我是面向对象程序设计老师,职称:教授 李教授 (教授) 正在教授面向对象程序设计 李教授正在批改面向对象程序设计试卷


    3.3 继承的核心概念详解

    3.3.1 继承的语法
    class 子类 extends 父类 {
        // 子类特有的属性和方法
    }
    
    3.3.2 三个重要关键字
    1. extends:表示继承关系
    2. super:指代父类对象
      • super.属性:访问父类属性
      • super.方法 ():调用父类方法
      • super():调用父类构造方法(重要!)
    3. @Override:注解,表示重写父类方法(可选但建议)
    3.3.3 继承了什么?不能继承什么?

    可以继承:

    • 父类的 public 和 protected 属性和方法
    • 父类的默认访问权限属性和方法(同包时)

    不能继承:

    • 父类的 private 属性和方法
    • 父类的构造方法(但可以通过 super() 调用)
    • 父类中被 final 修饰的方法

    3.4 方法重写(Override)vs 方法重载(Overload)

    这两个概念容易混淆,但非常重要:

    方法重写(Override):子类重新定义父类的方法
    class Animal {
        public void makeSound() {
            System.out.println("动物发出声音");
        }
    }
    
    class Dog extends Animal {
        @Override // 重写:方法名、参数列表、返回类型必须相同
        public void makeSound() {
            System.out.println("汪汪汪!");
        }
    }
    
    class Cat extends Animal {
        @Override
        public void makeSound() {
            System.out.println("喵喵喵!");
        }
    }
    
    方法重载(Overload):同一个类中有多个同名但参数不同的方法
    class Calculator {
        // 重载:方法名相同,参数列表不同
        public int add(int a, int b) { return a + b; }
        public double add(double a, double b) { return a + b; }
        public int add(int a, int b, int c) { return a + b + c; }
    }
    

    对比表格:

    特性重写(Override)重载(Overload)
    发生位置父子类之间同一个类内部
    方法名必须相同必须相同
    参数列表必须相同必须不同
    返回类型必须相同或兼容可以不同
    访问权限不能比父类更严格可以不同
    异常声明不能抛出更宽泛的异常可以不同

    3.5 构造方法的继承规则(考试重点!)

    规则 1:子类构造方法必须先调用父类构造方法
    class Animal {
        public Animal() {
            System.out.println("Animal 构造方法");
        }
    }
    
    class Dog extends Animal {
        public Dog() {
            // 即使不写,编译器也会自动加:super();
            System.out.println("Dog 构造方法");
        }
    }
    
    public class Test1 {
        public static void main(String[] args) {
            Dog d = new Dog();
        }
    }
    

    输出: Animal 构造方法 Dog 构造方法

    规则 2:super() 必须在构造方法的第一行
    class Animal {
        private String name;
        public Animal(String name) {
            this.name = name;
            System.out.println("Animal: " + name);
        }
    }
    
    class Dog extends Animal {
        private String breed;
        public Dog(String name, String breed) {
            super(name); // 必须放在第一行!
            this.breed = breed;
            System.out.println("Dog 品种:" + breed);
        }
        // 错误示例
        /*
        public Dog() {
            System.out.println("先做些事情"); // ❌ 错误!
            super("默认"); // super() 必须在第一行
        }
        */
    }
    
    规则 3:如果父类没有无参构造,子类必须显式调用 super(参数)
    class Parent {
        // 只有有参构造,没有无参构造
        public Parent(String name) {
            System.out.println("Parent: " + name);
        }
    }
    
    class Child extends Parent {
        // 错误:编译器想自动加 super(),但父类没有无参构造
        /*
        public Child() {
            // 隐含的 super() 会失败!
        }
        */
        // 正确:必须显式调用 super(参数)
        public Child(String name) {
            super(name); // 必须提供参数
            System.out.println("Child: " + name);
        }
        // 也可以这样
        public Child() {
            super("默认名字"); // 提供默认值
            System.out.println("Child 无参构造");
        }
    }
    
    规则 4:this() 和 super() 不能同时存在
    class A {
        public A() {
            System.out.println("A()");
        }
        public A(String msg) {
            System.out.println("A: " + msg);
        }
    }
    
    class B extends A {
        public B() {
            this("调用本类其他构造"); // 调用 B(String)
            System.out.println("B()");
        }
        public B(String msg) {
            super(msg); // 调用 A(String)
            System.out.println("B: " + msg);
        }
    }
    
    public class Test4 {
        public static void main(String[] args) {
            B b = new B();
        }
    }
    

    输出: A: 调用本类其他构造 B: 调用本类其他构造 B()


    3.6 final 关键字在继承中的应用

    1. final 类:不能被继承
    final class FinalClass {
        public void show() {
            System.out.println("这是 final 类");
        }
    }
    // class SubClass extends FinalClass { } // ❌ 编译错误
    
    2. final 方法:不能被重写
    class Parent {
        public final void cannotOverride() {
            System.out.println("这是 final 方法");
        }
        public void canOverride() {
            System.out.println("这个方法可以重写");
        }
    }
    
    class Child extends Parent {
        // ❌ 错误!不能重写 final 方法
        // @Override
        // public void cannotOverride() { }
        // ✅ 正确!可以重写普通方法
        @Override
        public void canOverride() {
            System.out.println("子类重写了这个方法");
        }
    }
    
    3. final 属性:常量
    class Constants {
        public final int MAX_VALUE = 100; // 声明时初始化
        public final String NAME; // 构造方法中初始化
    
        public Constants(String name) {
            this.NAME = name; // 可以在构造方法中初始化
        }
    }
    

    3.7 继承的好处与注意事项

    好处:
    1. 代码复用:不用重复写相同的代码
    2. 易于维护:修改公共代码只需改父类
    3. 逻辑清晰:类之间的关系明确
    4. 为多态打下基础
    注意事项:
    1. 不要滥用继承:只有'是一种'关系时才用继承
      • 正确:Dog 是一种 Animal
      • 错误:Student 是一种 Teacher
    2. 继承层次不宜过深:一般不超过 3 层

    Java 是单继承:一个类只能有一个父类

    class A extends B, C { // ❌ 错误!不支持多继承 }
    

    3.8 实践:设计图形类家族

    // 父类:图形
    class Shape {
        private String color;
        public Shape(String color) {
            this.color = color;
        }
        // 计算面积(父类提供默认实现)
        public double getArea() {
            return 0;
        }
        public void display() {
            System.out.print(color + "的图形");
        }
    }
    
    // 子类:圆形
    class Circle extends Shape {
        private double radius;
        public Circle(String color, double radius) {
            super(color); // 调用父类构造方法
            this.radius = radius;
        }
        @Override
        public double getArea() {
            return Math.PI * radius * radius;
        }
        @Override
        public void display() {
            super.display();
            System.out.println(" - 圆形,半径:" + radius + ",面积:" + getArea());
        }
    }
    
    // 子类:矩形
    class Rectangle extends Shape {
        private double width;
        private double height;
        public Rectangle(String color, double width, double height) {
            super(color);
            this.width = width;
            this.height = height;
        }
        @Override
        public double getArea() {
            return width * height;
        }
        @Override
        public void display() {
            super.display();
            System.out.println(" - 矩形,宽:" + width + ",高:" + height + ",面积:" + getArea());
        }
    }
    
    public class ShapeDemo {
        public static void main(String[] args) {
            Shape[] shapes = new Shape[3];
            shapes[0] = new Circle("红色", 5.0);
            shapes[1] = new Rectangle("蓝色", 4.0, 6.0);
            shapes[2] = new Circle("绿色", 3.0);
            for (Shape shape : shapes) {
                shape.display();
            }
        }
    }
    

    💡 本课要点总结

    1. 继承是什么:子类自动获得父类的属性和方法
    2. extends 关键字:建立继承关系
    3. super 关键字:
      • super.属性/方法 ():访问父类成员
      • super():调用父类构造方法
    4. 方法重写(Override):子类重新实现父类方法
    5. 构造方法规则(考试重点!):
      • 子类构造方法必须先调用父类构造方法
      • super() 必须在第一行
      • 父类没有无参构造时,必须显式调用 super(参数)
    6. final 关键字:
      • final class:不能被继承
      • final method:不能被重写
      • final field:常量

    补充:构造方法(Constructor)是什么?

    1. 什么是构造方法?

    构造方法是创建对象时自动调用的特殊方法,用于初始化对象的属性。

    2. 构造方法的特点
    public class Student {
        private String name;
        private int age;
    
        // 这就是构造方法!
        // 1. 方法名必须与类名完全相同
        // 2. 没有返回值类型(连 void 都没有!)
        // 3. 创建对象时自动调用
        public Student(String name, int age) {
            this.name = name; // 初始化属性
            this.age = age; // 初始化属性
            System.out.println("Student 对象被创建了!");
        }
    
        public static void main(String[] args) {
            // 创建对象时,会自动调用构造方法
            Student stu = new Student("张三", 20); // 执行结果:Student 对象被创建了!
        }
    }
    
    3. 构造方法的几种形式
    形式 1:无参构造方法
    class Person {
        private String name;
        // 无参构造方法
        public Person() {
            name = "未知";
            System.out.println("无参构造方法被调用");
        }
    }
    // 使用
    Person p = new Person(); // 调用无参构造
    
    形式 2:有参构造方法
    class Person {
        private String name;
        private int age;
        // 有参构造方法
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
            System.out.println("有参构造方法被调用:" + name);
        }
    }
    // 使用
    Person p = new Person("张三", 20); // 传入参数
    
    形式 3:多个构造方法(重载)
    class Person {
        private String name;
        private int age;
    
        // 构造方法 1:无参
        public Person() {
            this("未知", 0); // 调用另一个构造方法
        }
    
        // 构造方法 2:只有名字
        public Person(String name) {
            this(name, 0); // 调用有参构造
        }
    
        // 构造方法 3:全参数
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }
    // 使用
    Person p1 = new Person(); // 调用无参构造
    Person p2 = new Person("张三"); // 调用一个参数构造
    Person p3 = new Person("张三", 20); // 调用两个参数构造
    
    4. 默认构造方法

    如果你不写任何构造方法,Java 会自动提供一个无参构造方法:

    class Person {
        private String name; // 没有写构造方法!
    }
    // 仍然可以创建对象
    Person p = new Person(); // 使用默认的无参构造方法
    // 等价于:
    class Person {
        private String name;
        // 编译器自动添加
        public Person() { // 空实现
        }
    }
    

    但是! 如果你写了任何一个构造方法,Java 就不再提供默认构造方法:

    class Person {
        private String name;
        // 只写了有参构造
        public Person(String name) {
            this.name = name;
        }
    }
    // 错误!
    // Person p = new Person(); // ❌ 没有无参构造方法了!
    // 正确:
    Person p = new Person("张三"); // ✅ 必须提供参数
    

    📝 构造方法 vs 普通方法

    特性构造方法普通方法
    方法名必须与类名相同任意合法标识符
    返回值没有返回值类型有返回值类型(或 void)
    调用时机创建对象时自动调用通过对象显式调用
    作用初始化对象属性执行具体功能
    重载可以重载可以重载
    继承不能继承可以继承

    第四课:多态 —— 同一指令,不同反应

    前面我们学了:

    • 封装:给数据穿上防护服
    • 继承:建立家族关系,代码复用

    今天我们要学习面向对象的第三大支柱:多态(Polymorphism)。这是最灵活、最神奇的特性,也是面向对象编程的精髓!


    📚 本章学习目标

    1. 理解多态的概念和意义
    2. 掌握多态的两种实现方式
    3. 理解向上转型和向下转型
    4. 掌握 instanceof 运算符的使用
    5. 理解抽象类和接口

    4.1 什么是多态?

    多态:同一个方法调用,由于对象不同,可能有不同的行为。

    现实世界的多态例子:
    1. 动物叫声:都是'叫',但狗叫是'汪汪',猫叫是'喵喵'
    2. 交通工具:都是'启动',但汽车是'引擎启动',电动车是'静音启动'
    3. 绘画:都是'画',但画家画的是'油画',程序员画的是'流程图'
    代码示例:没有多态的世界
    class Dog {
        public void makeSound() {
            System.out.println("汪汪汪!");
        }
    }
    
    class Cat {
        public void makeSound() {
            System.out.println("喵喵喵!");
        }
    }
    
    public class NoPolymorphism {
        public static void main(String[] args) {
            Dog dog = new Dog();
            Cat cat = new Cat();
            // 需要分别调用
            dog.makeSound();
            cat.makeSound();
            // 如果要处理很多动物,代码会很繁琐
            // 无法写一个统一的方法处理所有动物
        }
    }
    
    代码示例:有多态的世界
    // 父类
    class Animal {
        public void makeSound() {
            System.out.println("动物发出声音");
        }
    }
    
    // 子类
    class Dog extends Animal {
        @Override
        public void makeSound() {
            System.out.println("汪汪汪!");
        }
    }
    
    class Cat extends Animal {
        @Override
        public void makeSound() {
            System.out.println("喵喵喵!");
        }
    }
    
    class Bird extends Animal {
        @Override
        public void makeSound() {
            System.out.println("叽叽喳喳!");
        }
    }
    
    public class WithPolymorphism {
        // 统一处理所有动物的方法
        public static void letAnimalSound(Animal animal) {
            animal.makeSound(); // 神奇的地方!
        }
    
        public static void main(String[] args) {
            Animal dog = new Dog();
            Animal cat = new Cat();
            Animal bird = new Bird();
            // 同一个方法调用,不同结果!
            letAnimalSound(dog); // 输出:汪汪汪!
            letAnimalSound(cat); // 输出:喵喵喵!
            letAnimalSound(bird); // 输出:叽叽喳喳!
        }
    }
    

    神奇之处:letAnimalSound() 方法接收 Animal 类型,但实际传入的是 Dog、Cat、Bird 对象,却能调用它们各自重写的 makeSound() 方法!


    4.2 多态的实现原理

    核心概念:向上转型
    Animal animal = new Dog(); // 这就是向上转型!
    

    向上转型的特点:

    1. 编译时类型(左边):Animal
    2. 运行时类型(右边):Dog
    3. 编译看左边,运行看右边
    详细示例:
    class Animal {
        public void eat() {
            System.out.println("动物吃东西");
        }
    }
    
    class Dog extends Animal {
        @Override
        public void eat() {
            System.out.println("狗吃骨头");
        }
        public void watchHouse() {
            System.out.println("狗看家");
        }
    }
    
    public class PolymorphismDemo {
        public static void main(String[] args) {
            // 情况 1:正常创建
            Dog dog1 = new Dog();
            dog1.eat(); // 狗吃骨头
            dog1.watchHouse(); // 狗看家
    
            // 情况 2:向上转型(多态的核心!)
            Animal animal = new Dog(); // 向上转型
            // 编译时:animal 是 Animal 类型
            // 运行时:animal 实际是 Dog 对象
            animal.eat(); // ✅ 输出:狗吃骨头(运行看右边)
            // animal.watchHouse(); // ❌ 编译错误!
            // 为什么?因为编译时 animal 是 Animal 类型
            // Animal 类中没有 watchHouse() 方法
            // 编译器只认左边的类型!
        }
    }
    

    关键理解:

    • 编译时:Java 编译器只看引用类型(左边的类型)
    • 运行时:JVM 实际调用的是对象类型(右边的类型)的方法

    4.3 多态的两大体现

    体现 1:方法重写(Override)
    class Shape {
        public void draw() {
            System.out.println("绘制形状");
        }
    }
    
    class Circle extends Shape {
        @Override
        public void draw() {
            System.out.println("绘制圆形");
        }
    }
    
    class Rectangle extends Shape {
        @Override
        public void draw() {
            System.out.println("绘制矩形");
        }
    }
    
    public class DrawingBoard {
        public static void main(String[] args) {
            Shape[] shapes = new Shape[3];
            shapes[0] = new Circle(); // 向上转型
            shapes[1] = new Rectangle(); // 向上转型
            shapes[2] = new Circle(); // 向上转型
            // 统一的接口,不同的行为
            for (Shape shape : shapes) {
                shape.draw(); // 多态!
            }
        }
    }
    

    输出: 绘制圆形 绘制矩形 绘制圆形

    体现 2:父类作为方法参数
    class Employee {
        public void work() {
            System.out.println("员工工作");
        }
    }
    
    class Programmer extends Employee {
        @Override
        public void work() {
            System.out.println("程序员写代码");
        }
    }
    
    class Manager extends Employee {
        @Override
        public void work() {
            System.out.println("经理管理团队");
        }
    }
    
    class Company {
        // 多态的威力:一个方法处理所有员工
        public void letWork(Employee emp) {
            emp.work(); // 自动调用具体员工的工作方法
        }
        // 如果没有多态,需要多个方法:
        /*
        public void letProgrammerWork(Programmer p) {
            p.work();
        }
        public void letManagerWork(Manager m) {
            m.work();
        }
        */
    }
    
    public class CompanyDemo {
        public static void main(String[] args) {
            Company company = new Company();
            // 创建不同类型的员工
            Employee emp1 = new Programmer(); // 向上转型
            Employee emp2 = new Manager(); // 向上转型
            Employee emp3 = new Employee(); // 普通员工
            // 统一调用,不同结果
            company.letWork(emp1); // 程序员写代码
            company.letWork(emp2); // 经理管理团队
            company.letWork(emp3); // 员工工作
        }
    }
    

    4.4 向下转型与 instanceof 运算符

    问题:向上转型后,如何调用子类特有方法?
    Animal animal = new Dog(); // animal.watchHouse(); // ❌ 编译错误,Animal 没有这个方法
    
    解决方案 1:向下转型(有风险)
    Animal animal = new Dog(); // 向下转型:把父类引用转回子类类型
    if (animal instanceof Dog) {
        Dog dog = (Dog) animal; // 向下转型
        dog.watchHouse(); // ✅ 现在可以调用了
    }
    // 危险示例:
    Animal animal2 = new Cat();
    // Dog dog2 = (Dog) animal2; // ❌ 运行时异常:ClassCastException
    // Cat 不是 Dog,不能强制转换!
    
    解决方案 2:instanceof 运算符

    instanceof 用于判断对象的实际类型:

    public class TypeCheckDemo {
        public static void main(String[] args) {
            Animal animal1 = new Dog();
            Animal animal2 = new Cat();
            Animal animal3 = new Animal();
    
            System.out.println(animal1 instanceof Dog); // true
            System.out.println(animal1 instanceof Animal); // true
            System.out.println(animal1 instanceof Cat); // false
            System.out.println(animal2 instanceof Cat); // true
            System.out.println(animal2 instanceof Animal); // true
            System.out.println(animal3 instanceof Dog); // false
            System.out.println(animal3 instanceof Animal); // true
    
            // 安全地进行向下转型
            if (animal1 instanceof Dog) {
                Dog dog = (Dog) animal1;
                dog.watchHouse(); // 安全调用
            }
    
            // 处理多种类型
            if (animal1 instanceof Dog) {
                System.out.println("这是一只狗");
            } else if (animal1 instanceof Cat) {
                System.out.println("这是一只猫");
            } else {
                System.out.println("这是普通动物");
            }
        }
    }
    

    4.5 多态的应用:抽象类

    问题:Animal 类的 makeSound() 方法应该怎么实现?
    class Animal {
        public void makeSound() {
            // 问题:动物怎么叫?不知道!
            // 空实现?打印'动物叫'?都不合适
        }
    }
    
    解决方案:抽象类
    // 抽象类:不能创建对象,只能被继承
    abstract class Animal {
        // 抽象方法:只有声明,没有实现
        // 子类必须重写抽象方法
        public abstract void makeSound();
    
        // 普通方法:可以有具体实现
        public void eat() {
            System.out.println("动物吃东西");
        }
    }
    
    // 具体子类必须实现抽象方法
    class Dog extends Animal {
        @Override
        public void makeSound() {
            System.out.println("汪汪汪!");
        }
    }
    
    class Cat extends Animal {
        @Override
        public void makeSound() {
            System.out.println("喵喵喵!");
        }
    }
    
    public class AbstractDemo {
        public static void main(String[] args) {
            // Animal a = new Animal(); // ❌ 错误!抽象类不能实例化
            Animal dog = new Dog(); // ✅ 向上转型
            Animal cat = new Cat(); // ✅ 向上转型
            dog.makeSound(); // 汪汪汪!
            cat.makeSound(); // 喵喵喵!
    
            // 多态数组
            Animal[] animals = {new Dog(), new Cat()};
            for (Animal animal : animals) {
                animal.makeSound(); // 多态调用
            }
        }
    }
    
    抽象类的特点:
    1. 用 abstract 修饰:abstract class 类名
    2. 可以包含抽象方法:public abstract 返回类型 方法名 ();
    3. 不能创建对象:只能被继承
    4. 子类必须实现所有抽象方法(除非子类也是抽象类)
    5. 可以有构造方法:用于子类初始化

    4.6 多态的终极形式:接口

    接口是什么?

    接口是纯粹的抽象,定义了一组规范(方法声明),但不提供实现。

    为什么需要接口?

    Java 只支持单继承,但可以通过接口实现'多继承'的效果。

    // 定义接口:用 interface 关键字
    interface Flyable {
        // 接口中的方法默认是 public abstract
        void fly(); // 等价于:public abstract void fly();
    
        // JDK 8 以后,接口可以有默认方法
        default void takeoff() {
            System.out.println("准备起飞");
        }
    
        // 接口可以有静态方法
        static void showInfo() {
            System.out.println("这是一个飞行接口");
        }
    }
    
    interface Swimmable {
        void swim();
    }
    
    // 类实现接口:用 implements 关键字
    class Duck extends Animal implements Flyable, Swimmable {
        @Override
        public void makeSound() {
            System.out.println("嘎嘎嘎!");
        }
    
        @Override
        public void fly() {
            System.out.println("鸭子飞");
        }
    
        @Override
        public void swim() {
            System.out.println("鸭子游泳");
        }
    }
    
    class Airplane implements Flyable {
        @Override
        public void fly() {
            System.out.println("飞机飞行");
        }
    }
    
    public class InterfaceDemo {
        public static void main(String[] args) {
            // 接口引用指向实现类对象
            Flyable flyable1 = new Duck(); // 多态
            Flyable flyable2 = new Airplane(); // 多态
            flyable1.fly(); // 鸭子飞
            flyable2.fly(); // 飞机飞行
    
            // 调用默认方法
            flyable1.takeoff(); // 准备起飞
    
            // 调用静态方法
            Flyable.showInfo(); // 这是一个飞行接口
    
            // 鸭子还可以游泳
            Duck duck = new Duck();
            duck.swim(); // 鸭子游泳
        }
    }
    
    接口 vs 抽象类
    特性接口抽象类
    定义关键字interfaceabstract class
    方法只有抽象方法(JDK8 前)可以有抽象和具体方法
    变量只能是常量(public static final)可以是普通变量
    继承可以实现多个接口只能继承一个类
    构造方法没有有
    设计理念'能做什么'(功能)'是什么'(本质)

    4.7 多态的好处

    好处 1:提高代码的可扩展性
    // 现有系统
    class PaymentSystem {
        public void processPayment(Payment payment) {
            payment.pay(); // 多态调用
        }
    }
    
    interface Payment {
        void pay();
    }
    
    // 现有支付方式
    class CreditCardPayment implements Payment {
        @Override
        public void pay() {
            System.out.println("信用卡支付");
        }
    }
    
    // 未来新增支付方式(无需修改 PaymentSystem!)
    class WeChatPayment implements Payment {
        @Override
        public void pay() {
            System.out.println("微信支付");
        }
    }
    
    class AlipayPayment implements Payment {
        @Override
        public void pay() {
            System.out.println("支付宝支付");
        }
    }
    
    // PaymentSystem 完全不用改,就能支持新支付方式!
    
    好处 2:提高代码的灵活性
    // 统一的绘图方法
    public void drawShapes(List<Shape> shapes) {
        for (Shape shape : shapes) {
            shape.draw(); // 自动调用具体的 draw 方法
        }
    }
    
    // 可以传入任何 Shape 子类
    List<Shape> shapes = new ArrayList<>();
    shapes.add(new Circle());
    shapes.add(new Rectangle());
    shapes.add(new Triangle());
    
    // 新增图形类型
    drawShapes(shapes); // 代码完全不用改!
    
    好处 3:降低耦合度
    // 高层模块不依赖具体实现
    class Computer {
        // 不依赖具体的 USB 设备
        public void connectUSB(USB usb) {
            usb.connect(); // 多态调用
        }
    }
    
    interface USB {
        void connect();
    }
    
    // 各种 USB 设备
    class Mouse implements USB { /* ... */ }
    class Keyboard implements USB { /* ... */ }
    class Printer implements USB { /* ... */ }
    
    // Computer 类完全不知道具体的 USB 设备是什么
    

    💡 本课要点总结

    1. 多态是什么:同一方法调用,不同对象有不同行为
    2. 多态的实现:
      • 方法重写(Override)
      • 父类引用指向子类对象(向上转型)
    3. 向上转型:父类 引用 = new 子类 ();
      • 编译看左边,运行看右边
      • 只能调用父类声明的方法
    4. 向下转型:子类 引用 = (子类) 父类引用 ;
      • 需要先用 instanceof 判断
    5. 抽象类:
      • 用 abstract 修饰
      • 不能创建对象
      • 可以有抽象方法和具体方法
    6. 接口:
      • 用 interface 定义
      • 实现多继承效果
      • 定义规范,不关注实现
    7. 多态的好处:
      • 提高扩展性
      • 提高灵活性
      • 降低耦合度

    目录

    1. 第一课:从“过程”到“对象”
    2. 1.1 回顾:C 语言的过程式思维
    3. 1.2 走进:Java 的面向对象世界
    4. 1.3 核心比喻:从“菜谱”到“餐厅”
    5. 1.4 为什么要有这种转变?
    6. 实践小练习
    7. 💡 本课要点总结
    8. 第二课:封装 —— 给你的数据穿上“防护服”
    9. 2.1 从现实问题说起
    10. 2.2 封装的解决方案:黑盒子理论
    11. 2.3 封装的核心概念:访问修饰符
    12. 2.4 封装的好处:为什么我们要这么做?
    13. 好处 1:安全性
    14. 好处 2:可控性
    15. 好处 3:维护性
    16. 好处 4:隐藏复杂度
    17. 2.5 标准做法:getter 和 setter
    18. 2.6 实践练习:改造一个 C 语言风格的程序
    19. 💡 本课要点总结
    20. 第三课:继承 —— 家族的智慧传承
    21. 📚 本章学习目标
    22. 3.1 从现实问题说起:代码重复的烦恼
    23. 3.2 继承的解决方案:家族的智慧
    24. 测试类
    25. 3.3 继承的核心概念详解
    26. 3.3.1 继承的语法
    27. 3.3.2 三个重要关键字
    28. 3.3.3 继承了什么?不能继承什么?
    29. 3.4 方法重写(Override)vs 方法重载(Overload)
    30. 方法重写(Override):子类重新定义父类的方法
    31. 方法重载(Overload):同一个类中有多个同名但参数不同的方法
    32. 3.5 构造方法的继承规则(考试重点!)
    33. 规则 1:子类构造方法必须先调用父类构造方法
    34. 规则 2:super() 必须在构造方法的第一行
    35. 规则 3:如果父类没有无参构造,子类必须显式调用 super(参数)
    36. 规则 4:this() 和 super() 不能同时存在
    37. 3.6 final 关键字在继承中的应用
    38. 1. final 类:不能被继承
    39. 2. final 方法:不能被重写
    40. 3. final 属性:常量
    41. 3.7 继承的好处与注意事项
    42. 好处:
    43. 注意事项:
    44. 3.8 实践:设计图形类家族
    45. 💡 本课要点总结
    46. 补充:构造方法(Constructor)是什么?
    47. 1. 什么是构造方法?
    48. 2. 构造方法的特点
    49. 3. 构造方法的几种形式
    50. 形式 1:无参构造方法
    51. 形式 2:有参构造方法
    52. 形式 3:多个构造方法(重载)
    53. 4. 默认构造方法
    54. 📝 构造方法 vs 普通方法
    55. 第四课:多态 —— 同一指令,不同反应
    56. 📚 本章学习目标
    57. 4.1 什么是多态?
    58. 现实世界的多态例子:
    59. 代码示例:没有多态的世界
    60. 代码示例:有多态的世界
    61. 4.2 多态的实现原理
    62. 核心概念:向上转型
    63. 详细示例:
    64. 4.3 多态的两大体现
    65. 体现 1:方法重写(Override)
    66. 体现 2:父类作为方法参数
    67. 4.4 向下转型与 instanceof 运算符
    68. 问题:向上转型后,如何调用子类特有方法?
    69. 解决方案 1:向下转型(有风险)
    70. 解决方案 2:instanceof 运算符
    71. 4.5 多态的应用:抽象类
    72. 问题:Animal 类的 makeSound() 方法应该怎么实现?
    73. 解决方案:抽象类
    74. 抽象类的特点:
    75. 4.6 多态的终极形式:接口
    76. 接口是什么?
    77. 为什么需要接口?
    78. 接口 vs 抽象类
    79. 4.7 多态的好处
    80. 好处 1:提高代码的可扩展性
    81. 好处 2:提高代码的灵活性
    82. 好处 3:降低耦合度
    83. 💡 本课要点总结
    • 免费图片AI生成工具免费生成了解详情
    • Magick API 一键接入全球大模型注册送1000万token查看
    • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
    • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
    • 100+免费在线小游戏爽一把
    极客日志微信公众号二维码

    微信扫一扫,关注极客日志

    微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

    更多推荐文章

    查看全部
    • AI + 鸿蒙游戏:会是下一个爆点吗?
    • Linux Shell 与脚本基础实战指南
    • CLIP-GmP-ViT-L-14 日志分级输出与错误码标准化设计
    • 麒麟 Wine 助手:利用 AI 自动配置 Windows 应用
    • RRT快速扩展随机树算法详解与Python实现
    • 通义万相 2.1 文生视频模型部署及性能测试
    • RRT 算法详细介绍(Python)
    • 高校计算机课程改革:引入 AIGC 实操教学
    • 游戏 Hacknet:零基础体验 Web 黑客攻防与 Linux 命令操作
    • Dynamic A*(D*)算法原理及 Python 实现
    • 如何成为一名黑客:态度、技术与文化指南
    • Git 代码推送与团队协作指南(基于 IDEA)
    • 八大经典排序算法原理与代码实现
    • 大模型算法面试指南:核心问题与答案解析
    • 七大主流 AIGC 测试工具横向评测与选型建议
    • 基于 n8n 与 Web Unlocker 的自动化资讯抓取与推送系统
    • 七大主流 AIGC 测试工具横向评测与选型指南
    • C++26 契约编程新特性:利用静态与动态检查提升代码健壮性
    • 七大 AIGC 测试工具横向评测与选型指南
    • 汽车雷达多径幽灵目标检测:GLRT 与压缩感知方案

    相关免费在线工具

    • Keycode 信息

      查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online

    • Escape 与 Native 编解码

      JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online

    • JavaScript / HTML 格式化

      使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online

    • JavaScript 压缩与混淆

      Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online

    • Base64 字符串编码/解码

      将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

    • Base64 文件转换器

      将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online