黑马学习笔记Day10

2025-11-13 黑马程序员学习笔记 41 0

Java 核心知识点总结:接口与 ArrayList

本文基于学习文档,系统梳理接口(面向对象高级规范)和ArrayList(常用动态集合)的核心特性、用法及实战案例,同时包含作业题的完整解答,助力夯实 Java 基础进阶技能。

一、接口

1. 核心概念

接口(interface)是面向对象编程的“行为规范”,仅定义对象的行为标准(如方法签名),不包含具体实现。它的核心价值是解耦代码、制定规则、支持多实现,是框架开发和团队协作的重要工具。

2. 基本语法

(1)接口定义

interface 关键字声明,内部可包含常量、抽象方法、默认方法等(无构造方法,不能实例化)。

// 订单业务接口:定义订单相关行为规范
public interface OrderService {
    // 1. 常量(默认 public static final,可省略)
    String SERVICE_NAME = "订单服务";

    // 2. 抽象方法(默认 public abstract,可省略)
    void createOrder();       // 创建订单
    void cancelOrder(String orderId); // 取消订单
    String getOrderStatus(String orderId); // 查询订单状态

    // 3. 默认方法(有方法体,实现类可直接使用或重写)
    default void printServiceInfo() {
        System.out.println("当前服务:" + SERVICE_NAME);
    }

    // 4. 静态方法(接口名调用,实现类不能重写)
    static void showTips() {
        System.out.println("订单操作请遵守业务规范");
    }
}

(2)接口实现

implements 关键字让类实现接口,必须重写所有抽象方法(否则类需声明为抽象类)。

// 订单服务实现类:实现接口的抽象方法
public class OrderServiceImpl implements OrderService {
    // 重写抽象方法:创建订单
    @Override
    public void createOrder() {
        System.out.println("创建订单:生成订单号、扣减库存");
    }

    // 重写抽象方法:取消订单
    @Override
    public void cancelOrder(String orderId) {
        System.out.println("取消订单:订单号=" + orderId + ",恢复库存");
    }

    // 重写抽象方法:查询订单状态
    @Override
    public String getOrderStatus(String orderId) {
        return "订单号=" + orderId + ",状态:已支付";
    }

    // 可选:重写默认方法
    @Override
    public void printServiceInfo() {
        System.out.println("自定义服务信息:订单服务V2.0");
    }
}

3. 接口的核心特点

成员类型规则说明
成员变量仅允许常量,默认 public static final,初始化后不可修改(如 String VERSION = "1.0")。
构造方法无,接口不能实例化(需通过实现类创建对象)。
抽象方法默认 public abstract,无方法体,实现类必须全部重写。
默认方法default 修饰,有方法体,实现类可直接调用或重写(解决接口升级问题)。
静态方法public static 修饰,通过“接口名.方法名”调用(如 OrderService.showTips())。
私有方法private 修饰,仅接口内部调用(抽取默认方法的公共逻辑,避免冗余)。

4. 接口的关系规则

  • 类与类:继承关系(extends),仅支持单继承、多层继承(如 class A extends B)。
  • 类与接口:实现关系(implements),支持单实现或多实现(如 class A implements B, C),也可“继承类+实现接口”(如 class A extends Parent implements B, C)。
  • 接口与接口:继承关系(extends),支持单继承或多继承(如 interface C extends A, B),实现类需重写所有接口的抽象方法。

5. 接口与抽象类的区别

对比维度接口抽象类
成员变量仅常量(public static final可定义变量或常量(无默认修饰符)
成员方法抽象方法、默认方法、静态方法、私有方法抽象方法、普通方法(有实现)、静态方法等
构造方法有(用于子类初始化,不能直接实例化)
继承/实现类可多实现,接口可多继承类仅支持单继承
核心作用制定行为规范,解耦代码(如服务接口)封装子类通用逻辑,强制子类实现抽象方法(如模板类)

6. 接口作业题:用户服务接口设计

题目需求

  1. 定义 UserService 接口,包含方法:void register(String username, String password)(用户注册)、boolean login(String username, String password)(用户登录)、String getUserInfo(String username)(查询用户信息)。
  2. 定义实现类 UserServiceImpl,重写所有抽象方法(模拟注册存储、登录校验逻辑)。
  3. 编写测试类,验证注册、登录、查询功能。

解答代码

// 1. 用户服务接口
public interface UserService {
    // 注册:参数为用户名和密码
    void register(String username, String password);
    // 登录:返回是否成功
    boolean login(String username, String password);
    // 查询用户信息:返回用户详情
    String getUserInfo(String username);
}

// 2. 接口实现类(模拟数据存储)
import java.util.HashMap;
import java.util.Map;

public class UserServiceImpl implements UserService {
    // 用Map存储用户信息(用户名→密码)
    private Map<String, String> userMap = new HashMap<>();

    // 重写注册方法
    @Override
    public void register(String username, String password) {
        if (userMap.containsKey(username)) {
            System.out.println("注册失败:用户名'" + username + "'已存在");
            return;
        }
        userMap.put(username, password);
        System.out.println("注册成功:用户名'" + username + "'");
    }

    // 重写登录方法
    @Override
    public boolean login(String username, String password) {
        // 校验用户名是否存在 + 密码是否匹配
        if (!userMap.containsKey(username)) {
            System.out.println("登录失败:用户名不存在");
            return false;
        }
        if (!userMap.get(username).equals(password)) {
            System.out.println("登录失败:密码错误");
            return false;
        }
        System.out.println("登录成功:欢迎'" + username + "'");
        return true;
    }

    // 重写查询用户信息方法
    @Override
    public String getUserInfo(String username) {
        if (!userMap.containsKey(username)) {
            return "查询失败:用户名'" + username + "'不存在";
        }
        return "用户信息:用户名=" + username + ",密码(加密前)=" + userMap.get(username);
    }
}

// 3. 测试类
public class TestUserService {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();

        // 测试注册
        userService.register("zhangsan", "123456");
        userService.register("zhangsan", "654321"); // 重复注册

        // 测试登录
        userService.login("zhangsan", "123456"); // 正确密码
        userService.login("zhangsan", "111111"); // 错误密码

        // 测试查询用户信息
        System.out.println(userService.getUserInfo("zhangsan"));
        System.out.println(userService.getUserInfo("lisi")); // 不存在的用户
    }
}

运行结果

注册成功:用户名'zhangsan'
注册失败:用户名'zhangsan'已存在
登录成功:欢迎'zhangsan'
登录失败:密码错误
用户信息:用户名=zhangsan,密码(加密前)=123456
查询失败:用户名'lisi'不存在

二、ArrayList

1. 核心概念

ArrayList 是 Java 中最常用的动态数组集合,底层基于数组实现,支持大小自动扩容,提供丰富的增删改查方法。适用于“有序、可重复、频繁查询、少量插入删除”的场景(如商品列表、用户列表)。

2. 核心特点

  • 长度可变:底层数组满时自动扩容,解决普通数组“长度固定”的问题。
  • 有序存储:元素添加顺序与存储顺序一致,支持通过索引快速访问(如 list.get(0))。
  • 可重复:允许存储重复元素,包括 null(如 list.add(null))。
  • 查询高效:通过索引直接访问元素(时间复杂度 O(1)),插入/删除效率低(需移动元素,时间复杂度 O(n))。

3. 构造器与常用方法

(1)构造器

构造器说明
ArrayList()创建初始容量为 0 的空集合,第一次添加元素时扩容为 10(默认无参构造)。
ArrayList(int initialCapacity)创建指定初始容量的集合(如 new ArrayList<>(20),避免频繁扩容)。

(2)常用方法

方法名说明
boolean add(E e)向集合末尾添加元素,返回是否成功(如 list.add("Java"))。
void add(int index, E e)在指定索引处插入元素,后续元素后移(如 list.add(1, "Python"))。
E get(int index)返回指定索引处的元素(索引范围:0 ~ size()-1,越界抛异常)。
int size()返回集合中元素个数(非底层数组容量,如 list.size())。
E remove(int index)删除指定索引处的元素,返回被删除元素,后续元素前移(如 list.remove(0))。
boolean remove(Object o)删除第一个匹配的元素,返回是否成功(如 list.remove("Java"))。
E set(int index, E e)修改指定索引处的元素,返回旧元素(如 list.set(0, "C++"))。
boolean contains(Object o)判断集合是否包含指定元素(如 list.contains("Java"))。
void clear()清空集合所有元素(如 list.clear())。

4. 长度可变原理(扩容机制)

ArrayList 底层通过“数组+扩容”实现动态长度,核心流程如下:

  1. 初始状态:无参构造创建的 ArrayList,底层数组初始容量为 0(空数组)。
  2. 第一次添加元素:底层创建容量为 10 的新数组,将元素存入。
  3. 触发扩容:当元素个数(size)等于底层数组容量时,触发扩容。
  4. 扩容规则:新容量 = 原容量 × 1.5(如 10→15→22→...),通过 Arrays.copyOf() 拷贝原数组元素到新数组。
  5. 后续添加:将新元素存入新数组,原数组被垃圾回收。

5. 常见问题与解决方案

问题 1:遍历删除元素“漏删”

原因:正序遍历删除时,元素前移导致后续元素索引错乱(如删除索引 2 的元素后,原索引 3 的元素变为索引 2,下一轮循环跳过该元素)。
解决方案:① 倒序遍历;② 正序遍历删除后索引自减。

问题 2:频繁扩容影响性能

解决方案:已知元素数量时,使用指定初始容量的构造器(如 new ArrayList<>(100)),减少扩容次数。

6. ArrayList 作业题:学生成绩管理

题目需求

  1. 定义 Student 类,属性:name(姓名)、score(成绩),提供构造器、getter 方法和 toString() 方法。
  2. 用 ArrayList 存储 5 个学生对象(成绩随机 60~100)。
  3. 实现功能:① 查找成绩≥90 的学生;② 删除成绩<60 的学生(本题无,模拟删除逻辑);③ 遍历打印所有学生信息。

解答代码

// 1. 学生实体类
class Student {
    private String name;
    private int score;

    public Student(String name, int score) {
        this.name = name;
        this.score = score;
    }

    // getter 方法(用于筛选)
    public int getScore() {
        return score;
    }

    // 重写 toString(),便于打印
    @Override
    public String toString() {
        return "Student{name='" + name + "', score=" + score + "}";
    }
}

// 2. 测试类(成绩管理逻辑)
import java.util.ArrayList;
import java.util.Random;

public class TestStudentScore {
    public static void main(String[] args) {
        // 初始化 ArrayList,存储 5 个学生
        ArrayList<Student> students = new ArrayList<>();
        Random random = new Random();
        String[] names = {"张三", "李四", "王五", "赵六", "钱七"};

        // 添加学生(成绩 60~100 随机)
        for (String name : names) {
            int score = random.nextInt(41) + 60; // 60~100
            students.add(new Student(name, score));
        }

        // 功能 1:遍历打印所有学生
        System.out.println("=== 所有学生信息 ===");
        for (Student s : students) {
            System.out.println(s);
        }

        // 功能 2:查找成绩≥90 的学生(优秀学生)
        ArrayList<Student> excellentStudents = new ArrayList<>();
        for (Student s : students) {
            if (s.getScore() >= 90) {
                excellentStudents.add(s);
            }
        }
        System.out.println("\n=== 优秀学生(成绩≥90) ===");
        if (excellentStudents.isEmpty()) {
            System.out.println("无优秀学生");
        } else {
            for (Student s : excellentStudents) {
                System.out.println(s);
            }
        }

        // 功能 3:模拟删除成绩<60 的学生(倒序遍历避免漏删)
        for (int i = students.size() - 1; i >= 0; i--) {
            if (students.get(i).getScore() < 60) {
                students.remove(i);
                System.out.println("\n已删除成绩<60 的学生");
            }
        }
        System.out.println("\n=== 删除后剩余学生 ===");
        for (Student s : students) {
            System.out.println(s);
        }
    }
}

运行结果(示例)

=== 所有学生信息 ===
Student{name='张三', score=85}
Student{name='李四', score=92}
Student{name='王五', score=78}
Student{name='赵六', score=95}
Student{name='钱七', score=65}

=== 优秀学生(成绩≥90) ===
Student{name='李四', score=92}
Student{name='赵六', score=95}

=== 删除后剩余学生 ===
Student{name='张三', score=85}
Student{name='李四', score=92}
Student{name='王五', score=78}
Student{name='赵六', score=95}
Student{name='钱七', score=65}

三、总结

  1. 接口:核心是“规范”,用于定义行为标准,支持多实现和解耦,是服务设计、框架开发的基础。
  2. ArrayList:核心是“动态存储”,底层数组+自动扩容,适用于有序、可重复、频繁查询的场景,需注意遍历删除的“漏删”问题。
  3. 实战结合:接口制定业务规则(如 UserService),ArrayList 负责数据存储(如用户列表),两者结合可实现规范、高效的 Java 程序。

掌握这两个知识点,能大幅提升代码的规范性和灵活性,是 Java 基础进阶的关键。

最后更新于 2025-11-15 09:30:43