云计算百科
云计算领域专业知识百科平台

计算机学习笔记 从封装、继承到多态与抽象类全景解析(附带详细代码示例)

📚 8.5 课程笔记整理:从封装、继承到多态与抽象类全景解析

1. 封装与权限控制

1.1 封装思想

定义:隐藏对象的属性和实现细节,仅对外提供公共访问方式。
核心:关注“如何调用”,不关心“内部实现”。就像使用遥控器,只需知道按键功能,无需了解内部电路。

1.2 实现方式

  • 私有化属性:使用 private 修饰成员变量,禁止外部直接访问。
  • 提供公共方法:使用 public 的 get/set 方法作为唯一访问通道。
  • public class Person {
    // 1. 私有化属性
    private String name;
    private int age;

    // 2. 提供公共的 Getter/Setter
    public String getName() {
    return name;
    }

    public void setName(String name) {
    this.name = name;
    }

    // Setter 中可以加入逻辑校验,保证数据安全
    public void setAge(int age) {
    if (age >= 0 && age <= 120) {
    this.age = age;
    } else {
    System.out.println("年龄不合法!");
    }
    }
    }

    1.3 引用类型的封装安全(深拷贝)

    问题:对于引用类型(如 Date、数组),如果 get 方法直接返回引用,外部仍可修改对象内部数据,导致封装失效。
    解决:在 get 方法中返回对象的副本(深拷贝)。

    import java.util.Date;

    public class User {
    private Date birthDate;

    // 错误的 Getter (浅拷贝) – 外部可修改 birthDate
    // public Date getBirthDate() { return birthDate; }

    // 正确的 Getter (深拷贝) – 返回副本
    public Date getBirthDate() {
    return new Date(birthDate.getTime());
    }
    }

    1.4 访问权限

    • private:仅限当前类内部访问。
    • 默认(无修饰符):当前类 + 同包。
    • protected:当前类 + 同包 + 子类。
    • public:所有地方。

    2. final 关键字

    2.1 修饰变量(常量)

    • 基本类型:数值一旦赋值,不可改变。
    • 引用类型:引用地址(指向的对象)不可改变,但对象内部的内容可以修改。

    public class FinalDemo {
    public static void main(String[] args) {
    // 1. 修饰基本类型
    final int NUM = 10;
    // NUM = 20; // 报错:无法再次赋值

    // 2. 修饰引用类型
    final int[] arr = {1, 2, 3};
    // arr = new int[5]; // 报错:引用地址不可变
    arr[0] = 99; // 正确:对象内部内容可变
    System.out.println(arr[0]); // 输出 99
    }
    }

    2.2 修饰类与方法

    • 修饰类:该类不能被继承(断子绝孙类)。
    • 修饰方法:该方法不能被重写(最终方法)。

    // final class Father { } // 报错:无法继承 final 类

    class Father {
    public final void run() {
    System.out.println("父亲在跑");
    }
    }

    class Son extends Father {
    // @Override
    // public void run() { } // 报错:无法重写 final 方法
    }


    3. 静态域与静态方法 (static)

    3.1 静态成员

    • 归属:属于类本身,所有对象共享同一份。
    • 调用:通过 类名.成员 访问,不依赖对象实例。
    • 内存:类加载时分配内存(静态常量池),优先于对象存在。

    3.2 访问规则

    • 静态方法:只能访问静态成员(静态变量、静态方法)。
    • 非静态方法:可以访问静态成员和非静态成员。
    • 原因:静态方法在对象创建前就存在,无法访问依赖对象的非静态成员。

    public class StaticDemo {
    static int staticVar = 1;
    int instanceVar = 2;

    // 静态方法
    public static void staticMethod() {
    System.out.println(staticVar); // 正确
    // System.out.println(instanceVar); // 报错:无法访问非静态变量
    }

    // 非静态方法
    public void instanceMethod() {
    System.out.println(staticVar); // 正确
    System.out.println(instanceVar); // 正确
    }
    }


    4. 参数传递机制

    4.1 值传递

    核心原则:Java 中只有值传递。

    • 基本类型:传递的是数值的副本。方法内修改不影响外部。
    • 引用类型:传递的是地址值的副本。方法内修改对象属性会影响外部,但修改引用指向(如 obj = null)不影响外部。

    public class PassByValueDemo {
    // 基本类型传递
    public static void changeValue(int x) {
    x = 100; // 修改副本,不影响 main 中的 a
    }

    // 引用类型传递
    public static void changeObj(Person p) {
    p.setName("李四"); // 修改副本指向的对象内容,影响 main 中的 person
    p = new Person(); // 修改副本的指向,不影响 main 中的 person
    p.setName("王五");
    }

    public static void main(String[] args) {
    int a = 10;
    changeValue(a);
    System.out.println(a); // 输出 10

    Person person = new Person("张三");
    changeObj(person);
    System.out.println(person.getName()); // 输出 "李四",不是 "王五"
    }
    }


    5. 方法重载 (Overload)

    5.1 定义

    在同一个类中,允许存在多个同名方法,但参数列表必须不同(类型、数量、顺序)。

    5.2 规则

    • 方法签名:方法名 + 参数列表。
    • 无关项:与返回值类型、访问修饰符无关。

    public class OverloadDemo {
    // 参数类型不同
    public void add(int a, int b) { System.out.println("整数相加"); }
    public void add(double a, double b) { System.out.println("小数相加"); }

    // 参数数量不同
    public void add(int a) { System.out.println("单个整数"); }

    // 参数顺序不同
    public void print(String s, int i) { }
    public void print(int i, String s) { }

    // public int add(int a, int b) { } // 报错:与第一个方法签名冲突
    }


    6. 构造器与初始化

    6.1 构造器特点

    • 方法名与类名相同。
    • 没有返回值类型(连 void 都没有)。
    • 创建对象时自动调用。

    6.2 默认初始化

    如果成员变量未显式赋值,系统会赋予默认值(int为0,boolean为false,引用为null)。

    6.3 this() 调用

    在构造器中调用本类的其他构造器,必须放在第一行。

    public class Person {
    private String name;
    private int age;

    // 无参构造
    public Person() {
    this("匿名", 18); // 调用有参构造,必须写在第一行
    }

    // 有参构造
    public Person(String name, int age) {
    this.name = name;
    this.age = age;
    }
    }


    7. 初始化块 (Initializer Block)

    7.1 定义

    使用 {} 包裹的代码块,用于初始化属性。

    7.2 执行顺序(面试重点)

  • 静态代码块 (static {}):类加载时执行,只执行一次。
  • 普通代码块 ({}):每次创建对象时执行,优先于构造器。
  • 构造器:最后执行。
  • public class InitDemo {
    static {
    System.out.println("1. 静态代码块");
    }

    {
    System.out.println("2. 普通代码块");
    }

    public InitDemo() {
    System.out.println("3. 构造器");
    }

    public static void main(String[] args) {
    new InitDemo();
    new InitDemo();
    }
    }
    // 输出:
    // 1. 静态代码块
    // 2. 普通代码块
    // 3. 构造器
    // 2. 普通代码块
    // 3. 构造器


    8. 继承与多态

    8.1 继承 (extends)

    • 单继承:一个类只能有一个直接父类。
    • 多层继承:A继承B,B继承C。
    • 根类:所有类默认继承 Object。
    • 限制:private 成员不可继承,final 类不可继承。

    8.2 方法重写 (Override)

    • 定义:子类定义与父类方法签名完全相同的方法。
    • 规则:子类方法覆盖父类方法。调用时执行子类版本。
    • 对比重载:重载看参数列表,重写看继承关系且签名必须一致。

    class Animal {
    public void run() { System.out.println("动物跑"); }
    }

    class Dog extends Animal {
    @Override
    public void run() { System.out.println("狗跑"); } // 重写
    }

    8.3 多态 (Polymorphism)

    • 前提:继承 + 重写。
    • 表现:父类引用指向子类对象。
    • 规则:编译看左边(父类),运行看右边(子类)。
    • 限制:父类引用只能调用父类中定义的方法,不能调用子类新增的方法。

    public class PolyDemo {
    public static void main(String[] args) {
    Animal a = new Dog(); // 多态
    a.run(); // 输出 "狗跑",执行子类重写的方法

    // a.bark(); // 报错:Animal 类中没有 bark 方法
    }
    }

    8.4 强制类型转换

    • 向上转型:自动(小碗装大碗)。
    • 向下转型:强制(大碗倒小碗),需使用 instanceof 检查,防止 ClassCastException。

    Animal a = new Dog();
    Dog d = (Dog) a; // 强制转换
    d.bark(); // 调用子类特有方法

    8.5 总结

    子类继承父类之后可以用父类的所以方法(重写过的就按照自己的方法运行)和自己的方法。父类不能用子类的方法。父类强制转化为子类时可以用自己的方法和子类重写的方法。父类可以使用子类定义的所有数据类型。


    9. 继承中的初始化顺序(面试重点)

    当涉及继承时,初始化顺序如下:

  • 父类静态代码块
  • 子类静态代码块
  • 父类普通代码块
  • 父类构造器
  • 子类普通代码块
  • 子类构造器
  • 口诀:静父 -> 静子 -> 普父 -> 构父 -> 普子 -> 构子

    class Father {
    static { System.out.println("1. 父类静态"); }
    { System.out.println("3. 父类普通"); }
    public Father() { System.out.println("4. 父类构造"); }
    }

    class Son extends Father {
    static { System.out.println("2. 子类静态"); }
    { System.out.println("5. 子类普通"); }
    public Son() { System.out.println("6. 子类构造"); }
    }

    public class Test {
    public static void main(String[] args) {
    new Son();
    }
    }


    10. 包 (Package) 与类库管理

    10.1 包的核心概念
    • 本质:包(Package)本质上就是文件系统中的文件夹层级结构。
    • 作用:
    • 避免命名冲突:就像不同文件夹下可以有同名文件一样,不同包下可以有同名类(如 com.jd.User 和 com.taobao.User)。
    • 分类管理:将功能相关的类组织在一起,便于维护。
    10.2 命名规范
    • 规则:通常采用公司域名的倒写形式,以确保全球唯一性。
    • 示例:
      • 京东:com.jd
      • 百度某部门:com.baidu.department1
    10.3 导入机制 (Import)
    • 精确导入:import java.time.LocalDate;
      • 优点:清晰,只加载需要的类。
    • 通配符导入:import java.util.*;
      • 缺点:
      • 内存消耗:会将整个包下的类都加载到内存中。
      • 打包体积:可能导致最终应用体积变大(就像把整个书架搬回家,而不仅仅是借一本书)。
    • 静态导入:import static java.lang.System.out;
      • 作用:调用静态成员时可省略类名。
      • 示例:out.println("Hello"); 代替 System.out.println("Hello");。
    10.4 类库设计权衡 (Java vs C)
    • C语言:小而多。类库功能单一,内存占用小,但数量庞大,开发者记忆负担重。
    • Java:大而全。功能打包在一起,调用方便,但可能包含未使用的功能,占用更多内存。
    • 优化:项目打包部署前,应检测并剔除未使用的类,以减小体积。

    11. 访问权限修饰符 (Access Modifiers)

    Java通过四种权限控制类成员的可见性。

    修饰符当前类同包子类其他包通俗理解
    public 公开:大门敞开,谁都能进。
    protected 受保护:同包邻居能进,不同包的亲戚(子类)也能进。
    (默认) 包级私有:仅限同包邻居,跨包即使是亲戚也不行。
    private 私有:仅限自己,谁都不让进。

    class Person {
    public String name; // 谁都能访问
    protected int age; // 同包或子类能访问
    int height; // (默认) 只有同包能访问
    private String secret; // 只有自己知道

    public void test() {
    name = "张三";
    age = 18;
    height = 170;
    secret = "密码"; // 本类内部都可以访问
    }
    }


    12. 抽象类 (Abstract Class)

    12.1 核心概念
    • 定义:使用 abstract 修饰的类。它代表一个抽象概念,不能实例化(不能 new)。
    • 类比:Animal(动物)是一个抽象概念,现实中不存在叫“动物”的具体生物,只有 Cat、Dog 等具体动物。
    • 作用:作为子类的模板,定义规范。
    12.2 抽象方法
    • 定义:没有方法体({})的方法,必须用 abstract 修饰。
    • 强制约束:
      • 包含抽象方法的类必须是抽象类。
      • 继承抽象类的具体子类,必须重写所有抽象方法。
      • 如果子类不重写,子类也必须声明为抽象类。
    • 目的:强制子类实现特定行为,避免业务逻辑遗漏(如忘记实现 run 方法)。
    12.3 抽象类的特性
    • 构造方法:抽象类可以有构造方法。
      • 作用:供子类创建对象时通过 super() 调用,用于初始化父类属性(打地基)。
    • 方法组成:可以同时包含抽象方法(强制子类实现)和具体方法(子类直接继承复用)。
    12.4 关键限制
    • abstract 和 final 不能共存:
      • abstract 要求被继承/重写,final 禁止继承/重写,语义矛盾。
    • abstract 和 static 不能共存:
      • static 方法随类加载而存在,abstract 方法无方法体无法调用,没有意义。

    abstract class Animal {
    // 抽象方法:强制子类实现
    public abstract void run();

    // 具体方法:子类可直接复用
    public void eat() {
    System.out.println("动物在吃东西");
    }
    }

    class Cat extends Animal {
    // 必须重写 run 方法,否则编译报错
    @Override
    public void run() {
    System.out.println("猫在跑");
    }
    }


    13. 接口 (Interface) – 简介

    • 定义:比抽象类更纯粹的抽象形式。
    • 特点:
      • 接口中的方法默认都是抽象方法(无需写 abstract)。
      • 实现接口的类(实现类)必须重写接口中所有方法。
    • 与抽象类的区别:
      • 抽象类:灵活,可包含具体方法和抽象方法。
      • 接口:纯粹,早期版本中所有方法必须是抽象的,侧重于定义规范。
      • // 1. 定义一个 USB 接口(规定好必须有什么功能)
        interface UsbInterface {
        // 抽象方法:只声明,不写具体实现
        void start();
        void stop();
        }

        // 2. 手机类实现 USB 接口
        class Phone implements UsbInterface {
        @Override
        public void start() {
        System.out.println("手机开始连接电脑,准备传输照片…");
        }

        @Override
        public void stop() {
        System.out.println("手机安全断开连接。");
        }
        }

        肝死我了

    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 计算机学习笔记 从封装、继承到多态与抽象类全景解析(附带详细代码示例)
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!