Java 封装
Java 封装 (Encapsulation) 详细学习笔记封装是面向对象编程OOP的第一大特性也是构建安全、健壮、可维护代码的基石。它就像给对象穿上了一层“防护服”将内部状态隐藏起来只暴露必要的接口供外部访问。1. 核心概念1.1 什么是封装封装Encapsulation是指将对象的状态属性/字段和行为方法捆绑在一起并隐藏对象的内部实现细节只对外提供受控的访问接口。通俗理解就像一台洗衣机你只需要按“启动”按钮公开接口不需要知道内部电机怎么转、水怎么流隐藏细节。就像汽车你只需要方向盘和油门公开接口不需要知道发动机内部活塞如何运动隐藏细节。就像人体你的内脏私有属性受到皮肤访问控制保护外界不能随意触碰只能通过嘴巴公开方法进食。1.2 封装的三大要素隐藏将类的成员变量属性声明为private禁止外部直接访问。暴露提供public的getter和setter方法作为访问内部数据的唯一通道。控制在setter方法中加入逻辑判断如数据校验确保数据的合法性和安全性。2. 为什么要封装核心价值价值说明安全性防止外部代码随意修改内部数据避免对象处于非法状态如年龄为负数。隐蔽性隐藏实现细节外部只需关注“能做什么”无需关心“怎么做”。可维护性内部实现逻辑修改如算法优化不影响外部调用代码降低耦合。复用性封装好的类可以像黑盒一样被多次复用无需了解内部结构。灵活性可以在不改变接口的前提下灵活调整内部逻辑如增加日志、缓存。3. 封装的实现步骤3.1 标准步骤修改访问权限将类的所有成员变量属性修饰符改为private。提供公共方法为每个属性提供public的getXxx()和setXxx()方法。添加逻辑控制在setXxx()方法中加入数据校验逻辑。3.2 代码示例学生类❌ 错误示范未封装publicclassStudent{// 直接公开外部可以随意修改甚至赋非法值publicStringname;publicintage;publicStringgender;}// 调用StudentsnewStudent();s.name张三;s.age-20;// 逻辑错误年龄不能为负但代码无法阻止s.gender未知;✅ 正确示范封装publicclassStudent{// 1. 私有化属性 (隐藏)privateStringname;privateintage;privateStringgender;// 2. 提供公共的 Getter 和 Setter 方法 (暴露)// Getter: 获取数据publicStringgetName(){returnname;}// Setter: 设置数据 (加入控制逻辑)publicvoidsetName(Stringname){if(name!null!name.isEmpty()){this.namename;}else{System.out.println(姓名不能为空);}}publicintgetAge(){returnage;}publicvoidsetAge(intage){// 数据校验年龄必须在 0-150 之间if(age0age150){this.ageage;}else{System.out.println(年龄输入错误必须在 0-150 之间);// 可以选择抛出异常throw new IllegalArgumentException(年龄错误);}}publicStringgetGender(){returngender;}publicvoidsetGender(Stringgender){if(男.equals(gender)||女.equals(gender)){this.gendergender;}else{this.gender未知;// 默认值}}// 3. 构造方法 (也是封装的一部分用于初始化)publicStudent(Stringname,intage,Stringgender){this.namename;this.ageage;this.gendergender;}// 无参构造 (通常也需要)publicStudent(){}}调用测试publicclassTest{publicstaticvoidmain(String[]args){StudentsnewStudent();s.setName(李四);s.setAge(25);// 正常s.setAge(-5);// 输出年龄输入错误...System.out.println(姓名s.getName());System.out.println(年龄s.getAge());}}4. 访问修饰符详解封装的核心在于访问控制。Java 提供了四种访问修饰符修饰符当前类同包类子类 (不同包)其他包类说明private✅❌❌❌最严格仅本类可见。用于属性封装。default(不写)✅✅❌❌同包可见。protected✅✅✅❌同包或子类可见。常用于父类成员。public✅✅✅✅最宽松所有类可见。用于 Getter/Setter。最佳实践属性一律private。方法对外暴露的接口public。内部辅助方法private或protected。包内共享default。5. 封装的高级技巧5.1 只读属性 (Immutable)如果某个属性一旦初始化就不允许修改如身份证号、创建时间可以只提供 Getter不提供 Setter。publicclassUser{privatefinalStringid;// final 保证不可变privateStringname;publicUser(Stringid,Stringname){this.idid;this.namename;}publicStringgetId(){returnid;}// 只有 Getter// 没有 setId()id 不可修改publicStringgetName(){returnname;}publicvoidsetName(Stringname){this.namename;}}5.2 只写属性 (Write-Only)极少见通常用于密码输入等场景只允许设置不允许读取明文。publicvoidsetPassword(Stringpwd){// 加密后存储this.passwordencrypt(pwd);}// 没有 getPassword()或者返回加密后的字符串5.3 计算属性 (Computed Property)属性不是直接存储的而是通过计算得出的。publicclassRectangle{privatedoublewidth;privatedoubleheight;// 没有 area 字段publicdoublegetArea(){returnwidth*height;// 实时计算}}5.4 懒加载 (Lazy Loading)在 Getter 方法中实现延迟初始化。publicclassDataSource{privateConnectionconn;publicConnectiongetConnection(){if(connnull){conncreateConnection();// 第一次调用时才创建}returnconn;}}6. 封装 vs 继承 vs 多态特性封装 (Encapsulation)继承 (Inheritance)多态 (Polymorphism)核心隐藏细节保护数据复用代码建立层次统一接口动态实现关键词private,public,getter/setterextends,superoverride,instanceof作用安全、解耦代码复用、扩展灵活性、可扩展性关系基础进阶升华关系封装是基础没有封装继承和多态会导致数据混乱。继承在封装的基础上扩展功能。多态利用封装和继承实现灵活调用。7. 常见面试题与陷阱Q1: 为什么属性要私有化答防止外部代码随意修改对象状态保证数据的安全性和完整性。如果属性公开外部可以直接obj.age -100导致对象状态非法。Q2: 如果只写private属性不提供getter/setter会怎样答该属性完全被隐藏外部无法访问和修改。通常用于内部临时变量或完全不可变的常量配合final。Q3: 构造方法属于封装吗答属于。构造方法用于在对象创建时初始化私有属性是封装的重要一环。通常提供有参构造和无参构造。Q4:final修饰的属性需要 Setter 吗答不需要。final属性只能在声明时或构造方法中赋值一旦赋值不可修改因此不需要 Setter。Q5: 封装会牺牲性能吗答理论上方法调用比直接访问字段稍慢。但在现代 JVM 优化下如内联优化这种差异几乎可以忽略不计。安全性远大于微小的性能损耗。Q6: 如何快速生成 Getter/Setter答IDEA:Alt Insert-Getter and Setter。Eclipse:Alt Shift S-Generate Getters and Setters。Lombok: 使用Data,Getter,Setter注解需引入依赖。8. 总结封装的核心口诀属性私有化方法公开化。访问加控制数据保安全。只读无 Setter只写无 Getter。内部藏细节外部调接口。最佳实践清单所有属性默认private。所有对外方法默认public。Setter 方法必须包含数据校验逻辑。不可变对象如String属性用private final不提供 Setter。利用 IDE 工具或 Lombok 自动生成样板代码保持代码整洁。封装是面向对象编程的第一道防线它让代码从“裸奔”变成了“装甲车”是编写高质量 Java 代码的起点。