☕ 第十五章 综合项目:客户关系管理系统

15.3 客户信息

📄 一句话概括

把三层蓝图里的"货物"造出来——Customer 客户实体类。五个字段(姓名/性别/年龄/电话/邮箱)+ 全套 getter/setter + 两种构造器,全部是 4.3 封装和 3.4 构造方法的老手艺,本节没有新知识,只有老规矩在真项目里的第一次正式上岗。写完它,CRM 的地基就三块齐了两块。


一、Customer 是三层里的哪一层

15.1 定的分工口诀:脸管喊话,库管货,实体管数据,工具管打字。Customer 就是那个"实体管数据"——它自己不干任何业务,只负责把一个客户的所有信息装在一起。

为什么要专门造个类装数据,而不是用五个散变量?因为客户是"一个"客户:姓名、电话、邮箱是长在同一个人身上的。散变量一转身就分家,Customer 对象走到哪,这个人完整的信息就跟到哪——这就是 3.1 面向对象"把数据和行为打包"的思想第一次派上真用场。

二、核心:五个字段 + 官方通道

public class Customer {
    private String name;      // 姓名
    private char gender;      // 性别(男/女,单字符用 char 恰好)
    private int age;          // 年龄
    private String phone;     // 电话
    private String email;     // 邮箱

    // 全参构造:new 的同时五个全给
    public Customer(String name, char gender, int age, String phone, String email) {
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.phone = phone;
        this.email = email;
    }

    // 无参构造:先要空壳,回头逐项 set
    public Customer() {
    }

    // 五对 getter / setter —— 数据只走官方通道
    public String getName() { return name; }
    public void setName(String name) { this.name = name; }
    // gender / age / phone / email 同款,一式五份……

    // 调试利器:show() 把客户信息一行打出来
    public String show() {
        return name + "\t" + gender + "\t" + age + "\t" + phone + "\t" + email;
    }
}

三处讲究全是老账:

  1. 字段全 private(3.3 封装性):外人直接 c.age = -5 的野路子一律堵死,想动数据必须走 setAge()——将来要在 setter 里加校验(比如年龄不能是负数),一处加锁全局生效。
  2. this. 区分(3.5 this):参数名和字段名撞车,this.name = name 左边是字段、右边是参数——这个老朋友现在天天见面了。
  3. 两种构造都留(3.4 构造方法):写了全参构造,无参构造会被顶掉,必须手动补上——因为两条造货路线都要用(见下)。
🧪 实验 1:两种造货路线各走一遍

路线 A:Customer c = new Customer(); 然后 c.setName("波仔"); c.setAge(28); 逐项填空。路线 B:Customer c2 = new Customer("小明", '男', 25, "13800000000", "[email protected]"); 一步到位。两条路各 new 一个,用 show() 打出来对比——字段值一样,殊途同归。

🧪 实验 2:亲眼看 private 挡路

在 CustomerTest 里写 c.name = "黑客"——编译报错,name has private access。换 c.setName("黑不了") 立刻通过。4.3 说的"封装保护数据",今天亲眼验证。

🧪 实验 3:删掉无参构造试试

把 public Customer() {} 注释掉,再写 new Customer()——编译报错。原因:一旦手写了全参构造,编译器就不再白送无参构造。这个坑后面 Spring 等框架里会反复遇到,现在就刻进肌肉记忆。

三、show():实体类的"自我介绍"

实体类不该有业务,但打印自己不算业务——show() 返回一行格式化好的客户信息,列表页直接调用,五个字段用 \t 隔开对齐。这是给 15.5 的"客户列表"功能提前铺路:库房把每个客户的 show() 拼起来,就是一张表格。

四、Demo33:造三个客户试跑

public class Demo33 {
    public static void main(String[] args) {
        // 路线 B:全参一步到位
        Customer c1 = new Customer("小明", '男', 25, "13800000001", "[email protected]");
        // 路线 A:无参 + 逐项 set
        Customer c2 = new Customer();
        c2.setName("小红");
        c2.setGender('女');
        c2.setAge(23);
        c2.setPhone("13800000002");
        c2.setEmail("[email protected]");

        System.out.println("姓名\t性别\t年龄\t电话\t\t邮箱");
        System.out.println(c1.show());
        System.out.println(c2.show());
    }
}

运行看到两行整齐的客户信息,Customer 就算正式下线合格——注意 main 里没有任何数据被乱摸,全走 getter/setter 官方通道。

五、五个常见误区

⚠️ 误区 1:字段忘了 private

图省事直接 public String name——封装形同虚设,将来没法统一加校验。实体类字段默认全 private,条件反射级别。

⚠️ 误区 2:写了全参构造不补无参

编译器白送的无参构造在你手写全参那一刻就没了,回头 new Customer() 直接报错。规矩:写了带参构造,必补无参。

⚠️ 误区 3:setter 里写业务逻辑

比如在 setName() 里顺手打印"欢迎新客户"——实体类是纯货物,不干业务,业务是 View 的活。setter 将来只加校验(格式对不对),不加业务。

⚠️ 误区 4:性别用 String

'男' 是单字符,char gender 恰好够用;写成 String 也不算错,但要记住传的是 "男" 带双引号。本教材用 char,跟着教材走,重点是类型和字面量要配对(char 用单引号)。

⚠️ 误区 5:在 Customer 里存"下一个客户是谁"

把链表思维混进来,实体类越写越复杂。Customer 只管一个客户的数据;"客户们怎么摆"是 CustomerList 库房的职责——分工口诀再念一遍。

六、自测题

❓ Q1:Customer 的五个字段分别是什么类型?为什么性别用 char?

name/phone/email 是 String,age 是 int,gender 是 char——性别是单字符,char 用单引号 '男' 恰好。

❓ Q2:为什么字段必须 private?直接 public 省事不好吗?

不好。private 堵死野路子赋值,所有数据进出都走 setter/getter,将来加校验(如年龄非负)一处生效;public 一散出去就收不回来了。

❓ Q3:写了全参构造后 `new Customer()` 报错,为什么?怎么救?

手写带参构造后编译器不再赠送无参构造。手动补一个空的无参构造即可。

❓ Q4:路线 A(无参+set)和路线 B(全参)各适合什么场景?

造新客户两路皆可;修改功能只能用 A 的单个 set(只动一个字段);B 适合数据齐全时一步到位。

❓ Q5:(毕业题)给 Customer 加一个"只改电话"的需求,写出最小改动方案。

用路线 A:拿到已有对象后只调 c.setPhone("新号码"),其余字段不动——这正是"修改客户"功能(15.5)的核心一行。顺带体会:没有 setter 的话,这个需求就得拆开重建整个对象。

七、下一步学什么


相关笔记