直接讲重点封装、继承、多态并不是三个孤立的知识点它们是在解决同一个问题——如何把现实世界的复杂性映射到代码里并且让代码可以被维护、被扩展、被复用。零基础看这三块的时候最容易犯的毛病是背概念、背定义觉得“封装就是private继承就是extends多态就是重写”背完了写代码还是不知道该在什么场景用。这篇文章我不讲虚的直接按我平时带新人时的思路来把一个类从设计到扩展、再到灵活调用的完整过程拆开每一步都告诉你为什么这么做以及在真实项目和面试里这些特性到底怎么考察。先给大家吃个定心丸Java的三大特性学起来不难但想用得好必须理解它们背后的设计逻辑。封装解决的是“东西坏了不知道从哪儿修”的问题继承解决的是“大量重复代码无从下手”的问题多态解决的是“代码写死了一改就崩”的问题。你把这三个问题记住了再看JVM源码也好、看Spring源码也好都有一个抓手。1. 封装给数据装上一道闸门而不是设一堵墙1.1 封装到底在保护什么封装的第一步很好理解用private把字段藏起来然后用public方法暴露操作入口。但很多新手问过我“字段直接public不也能用吗搞这么多getter/setter代码还更长了。”这个问题的本质是没有搞清楚封装保护的不是数据本身而是数据变化的规则。举个例子。你做一个用户管理系统User类里有age字段。如果age是public的调用方可以直接写user.age -100程序也不会报错。等你后面统计平均年龄的时候结果一下子变成负数你还得满项目排查是谁在哪儿把数据弄脏了。但如果age是private的所有外部代码都只能通过setAge(int age)来改在这个方法里你就可以加一道校验public class User { private int age; public void setAge(int age) { if (age 0 || age 150) { throw new IllegalArgumentException(年龄范围不合法: age); } this.age age; } public int getAge() { return this.age; } }看到区别了吗public字段等于把修改数据的权力下放给了所有调用方而private字段加方法是把修改规则收拢到类内部。数据还是那个数据但修改它的每一笔操作都变得可控了。这就是“闸门”和“墙”的区别——墙是禁止通行闸门是登记放行。1.2 为什么不建议无脑写getter/setter这里要聊聊很多人踩过的坑。网上大量课程的代码风格是“类里字段一写IDEA快捷键一按getter/setter全生成”看起来规范实际上这种操作根本没理解封装的意图。我见过一个项目订单类Order里有十几个字段每个字段都配了一对getter/setter。结果调用方想改订单状态的时候直接调setStatus(已取消)但没人去校验这个订单是不是已经支付、是不是已经发货。最后出现了“已发货的订单被改成已取消”这种bug。问题的根源在于没有业务语义的setter等于把类内部的逻辑完全暴露在了外部。真正合理的做法是把业务动作建模成方法。比如同样是改状态你应该提供cancel()而不是setStatus()public class Order { private OrderStatus status; private boolean paid; private boolean shipped; public void cancel() { if (paid) { throw new IllegalStateException(订单已支付不能直接取消请走退款流程); } if (shipped) { throw new IllegalStateException(订单已发货不能取消); } this.status OrderStatus.CANCELED; } }这段代码才是封装的意义类对外暴露的不是数据而是一组服务。调用方不需要知道内部有status、paid、shipped这些字段只需要告诉订单“把你自己取消了”剩下的规则判断全在类内部完成。1.3 封装在工程中的四个实用层次封装并不只是语法层面的private/public我习惯把封装的运用场景分成四个层次方便大家对照检查自己的代码第一层是字段封装也就是getter/setter或业务方法保证数据合法性和变更可控性第二层是方法封装把一段有完整业务含义的操作提炼成一个方法外部只关心输入输出不关心内部实现第三层是类的封装通过构造方法和静态工厂方法控制对象的创建方式保护不变量第四层是包的封装通过default访问权限控制同一个包内可见、跨包不可见把一组成员类打包成一套对外接口。在实际项目里包的封装这个层次往往被忽略。比如你做一个支付模块内部有许多实现细节类正常情况下它们不应该被外部模块引用。用包级私有类或者内部类把这些实现藏起来只暴露一个PayService给外部这个就是更高级的封装设计。从面试角度来看面试官问封装的时候最喜欢问的就是“为什么需要封装”和“封装和抽象的区别”。如果你只回答“隐藏细节、提高安全性”很容易被追问“那你觉得setter滥用算不算封装”。所以我建议零基础的朋友从一开始就往“封装是控制变化”这个方向上理解而不是简单记一个“属性私有化”。2. 继承代码复用的同时也继承了约束2.1 继承解决的核心问题写代码写到一定量你一定会发现一个让人头大的问题两个类之间有大量重复字段和方法。比如猫和狗都有名字、年龄都会吃饭、睡觉但叫声不一样。最粗暴的做法是复制粘贴但后面改需求就麻烦了——比如要给所有动物增加一个“寿命”属性你得在两个类里各改一遍。如果项目里有十种动物你就要改十遍漏改一个就是线上bug。继承就是为了解决这个问题的把公共的字段和方法提取到父类Animal里子类Cat和Dog通过extends继承下来然后只写自己特有的部分public class Animal { protected String name; protected int age; public void eat() { System.out.println(name 正在进食); } public void sleep() { System.out.println(name 正在睡觉); } } public class Dog extends Animal { public void bark() { System.out.println(name 汪汪叫); } } public class Cat extends Animal { public void meow() { System.out.println(name 喵喵叫); } }继承的本质是is-a关系。子类是父类的一种特殊形态比如Dog is-a Animal。所以继承不仅仅是拿代码它还在语义上承诺了“凡是能对Animal做的操作也一定能对Dog做”。这个承诺就是后面讲多态的基础。2.2 super关键字和构造链的调用规则继承之后马上会遇到的一个问题是父类有构造方法子类构造的时候怎么处理Java的规定是子类的构造函数第一行会隐式调用父类的无参构造方法super()。如果父类没有无参构造方法那子类就必须显式调用super(参数)。很多新人在写子类的时候会遇到编译报错“There is no default constructor available in class Animal”就是因为父类写了带参构造方法后无参构造方法消失了子类没有显式调用父类的带参构造。解决办法也很简单要么父类保留一个无参构造要么子类显式调用父类构造public class Animal { protected String name; public Animal(String name) { this.name name; } } public class Dog extends Animal { public Dog(String name) { super(name); // 必须显式调用否则编译不过 } }在实际项目里我更推荐“父类必须有带核心参数的构造方法子类通过super把参数传上去”这种做法。因为这样能保证一个对象一创建出来关键属性就是完整的避免了先创建对象再慢慢set属性的“半成品对象”问题。2.3 方法重写的铁律和坑子类继承了父类的方法之后如果对父类的实现不满意可以重写Override这个方法。但重写有四个铁律面试经常考我直接列出来第一方法名必须完全一致。第二参数列表必须完全一致这叫重写参数列表不同的情况属于重载不叫重写。第三返回值类型可以相同也可以是父类返回值的子类这叫协变返回类型。第四子类方法的访问权限不能比父类更严格比如父类是public子类就不能改成protected。最后一条是很多人容易踩的坑。为什么访问权限不能更严格因为继承的is-a关系里只要父类能调用这个方法的地方换成子类对象也都得能调用。如果子类把public方法改成private那就违背了“子类能完全替代父类”的承诺。实际写代码时还有一个隐藏的坑重写方法时忘了加Override注解。加了注解之后编译器会帮你检查方法签名是否真的覆盖了父类的方法。我见过有人本意是重写equals方法结果参数写成了自己的类型导致根本不是重写而是重载程序运行后行为完全不符合预期。这个问题加上Override注解后一瞬间就能发现。3. 多态运行时才决定调用的是哪个版本3.1 重载和重写到底有什么区别多态包含两块内容编译时多态和运行时多态。编译时多态就是方法重载Overload同一个类里多个同名方法参数不同调用时编译器根据参数类型决定调用哪一个。运行时多态就是方法重写Override父类引用指向子类对象运行时会动态调用子类的实现。这两个概念几乎每个面试都会考。我做个对比表方便大家记忆对比维度方法重载方法重写发生位置同一个类中父类和子类之间方法名相同相同参数列表必须不同必须相同返回值可相同可不同相同或协变返回类型访问权限无要求不能比父类更严格异常范围无要求不能抛出父类没有的更大异常绑定时机编译期运行期3.2 向上转型用父类的眼睛看子类多态的核心用法是向上转型声明为父类类型实际指向子类对象。可能有人不理解既然Dog有自己独特的bark()方法为什么不直接用Dog类型引用非要写成Animal类型答案是写代码的时候面向父类编程运行的时候才让JVM去动态绑定到子类实现。举个例子动物园系统里有一只猫一只狗还有一个动物园管理员ZooKeeper。如果不使用多态喂食方法可能要写两个public void feed(Dog dog) { dog.eat(); } public void feed(Cat cat) { cat.eat(); }如果动物有100种这个feed方法就要重载100次。而用多态的话只需要一个参数为Animal的方法所有Animal的子类对象都能传进来public void feed(Animal animal) { animal.eat(); } // 使用完全不用关心具体是狗还是猫 Animal a1 new Dog(旺财); Animal a2 new Cat(咪咪); zooKeeper.feed(a1); zooKeeper.feed(a2);看到没有真正扩展性强的代码依赖的是父类和接口而不是具体的子类。这也是设计模式里“依赖倒置原则”的体现上层模块不该依赖底层模块的细节大家都依赖抽象。3.3 运行时绑定到底发生了什么讲多态的时候如果只讲语法不讲原理很多人会“会写但想不通”。这里稍微展开一下JVM层的大致机制。Java里每个类在JVM的方法区中都有一个方法表Method Table记录了该类所有方法的信息。子类重写了父类的方法之后在子类的方法表中这个方法指向的是子类的实现而不是父类的实现。当代码执行到animal.eat()时JVM看到animal变量是Animal类型但实际对象是Dog于是去Dog的方法表中查找eat()方法最终跳转到Dog类的实现代码。这个过程叫动态分派它是多态能成立的底层前提。特别注意方法重写才是动态分派实例字段和静态方法不具备多态性。如果你用父类引用访问一个子类和父类都有的同名字段拿到的是父类的字段如果你用父类引用调用静态方法调用的也是父类的版本。这是我见过很多人在笔试中错的点。3.4 多态在实战里的一个典型案例很多零基础的朋友觉得上述例子太简单。这里给一个真实点的项目场景支付系统。假设系统里有支付宝、微信、银行卡三种支付方式每种支付方式的处理流程不同但都涉及参数校验、发起支付、回调处理这几个步骤。如果用if-else判断支付方式if (payType ALIPAY) { alipayService.pay(order); } else if (payType WECHAT) { wechatService.pay(order); } else if (payType BANKCARD) { bankcardService.pay(order); }以后每加一种支付方式这段if-else就得加一行而且和具体实现类耦合得死死的。如果用多态设计定义一个统一的PayStrategy接口public interface PayStrategy { void pay(Order order); void onCallback(Order order, CallbackResult result); } public class AlipayStrategy implements PayStrategy { Override public void pay(Order order) { // 调用支付宝SDK发起支付 } Override public void onCallback(Order order, CallbackResult result) { // 支付宝回调处理 } } // 微信、银行卡同理调用方的代码只面向PayStrategy具体用哪个策略由工厂或者Spring容器注入。之后再加新支付方式只需要新增一个类不需要改动任何已有代码。这就是多态带来的维护性提升。4. 三大特性不是孤岛面向对象设计实战拆解4.1 从零设计一个员工管理系统为了把三大特性串起来我拿一个绝大多数人都接触过的业务场景来做全流程演示员工管理系统。需求很明确公司有普通员工和经理都有工号、姓名、基本工资经理还能管理团队。每个月的工资都要单独计算普通员工就是基本工资经理是基本工资加团队绩效。先看不用三大特性的写法——两个类各写各的大量重复字段和方法public class Employee { public String id; public String name; public double baseSalary; public double calculateSalary() { return baseSalary; } } public class Manager { public String id; public String name; public double baseSalary; public ListEmployee team; public double calculateSalary() { return baseSalary teamPerformanceBonus(); } }如果项目只有这两个类好像也不觉得有什么问题但一旦加需求——所有员工都有社保扣除、有打卡记录、有调薪记录——重复代码就会疯涨。这时候继承就该上场了。4.2 用继承抽象公共部分第一轮重构把公共字段和方法提到父类。工号、姓名、基本工资、获取信息的方法都放到Employee里Manager只需要extends Employee再用super调用父类构造方法public class Employee { protected String id; protected String name; protected double baseSalary; public Employee(String id, String name, double baseSalary) { this.id id; this.name name; this.baseSalary baseSalary; } public double calculateSalary() { return baseSalary; } public void printInfo() { System.out.println(工号: id , 姓名: name); } } public class Manager extends Employee { private ListEmployee team; public Manager(String id, String name, double baseSalary, ListEmployee team) { super(id, name, baseSalary); this.team team; } Override public double calculateSalary() { return baseSalary teamPerformanceBonus(); } private double teamPerformanceBonus() { return team.size() * 500.0; } }你注意看Manager类现在只保留了“团队管理”和“绩效奖金”这两个特有部分其余的姓名工号基础工资全都复用父类。代码量减少的同时字段定义也统一了。这就是继承第一个层面的价值消除重复收拢公共状态。4.3 用多态实现统一计价继承走到这里还没有体现多态的价值。现在来了新需求公司要给整个员工列表统一发工资。如果不用多态你就得这么写for (Employee e : allEmployees) { if (e instanceof Manager) { double salary ((Manager) e).calculateSalary(); // 处理经理工资 } else { double salary e.calculateSalary(); // 处理普通员工工资 } }这种写法的问题显而易见每加一个员工子类这堆if-else都要改一遍。而利用多态所有子类都是Employee统一调用calculateSalary()就完事了public class PayrollSystem { public static void main(String[] args) { ListEmployee allEmployees new ArrayList(); allEmployees.add(new Employee(E001, 张三, 8000)); allEmployees.add(new Manager(M001, 李四, 12000, List.of(new Employee(E002, 王五, 6000)))); for (Employee employee : allEmployees) { // 多态运行时会自动调用具体子类的实现 System.out.println(employee.getName() 本月工资: employee.calculateSalary()); } } }看到重点了吗这段代码完全没有出现Manager和Employee的判断逻辑但李四的工资计算会自动走Manager类的重写方法。将来再加一个“临时工”类、加一个“实习生”类这个循环一行都不用改。面向父类写代码运行时让JVM自己找正确的实现这就是多态的实际价值。4.4 封装在系统里的守护作用上面这个员工系统里salary和id都是公司核心数据。如果id是public的任何代码都可以直接改成另一个人的工号整个系统数据就乱了。正确的做法是把id和salary设计成private只提供一个按需读取的getter甚至salary字段连getter都不给只给一个calculateSalary方法让外界无法得知基本工资细节只能拿到最终结算结果。同时calculateSalary在父类里加final也是个值得考虑的设计。普通员工的工资计算逻辑相对固定不希望子类随意覆盖。但经理的工资计算需要特殊逻辑所以必须允许重写。这里的取舍在代码里可以这样表达public class Employee { private String id; protected String name; private double baseSalary; private static final double TAX_RATE 0.1; public final double getSalaryAfterTax() { return calculateSalary() * (1 - TAX_RATE); } }getSalaryAfterTax标记为final意思是说所有子类都不能重写税后工资的算法。如果某个子类重写了这个方法把税率改成0那公司的财务系统就形同虚设了。用final方法守住不可变规则这是封装思维在继承体系里的延伸。5. 继承里最隐蔽的坑组合优于继承5.1 为什么说继承不是万能的我见过太多新人学会继承后什么类都继承一下最后搞出一个深度五层的继承树改父类一个方法整个项目一半的类都受影响。继承虽然能复用代码但它同时也复用了父类的所有约束父类的修改会向下传递牵一发而动全身。举个经典的例子假设你有一个Bird类里面有fly()方法然后你打算写一个Penguin类因为企鹅是鸟呗所以你extends Bird。运行起来后发现企鹅也会飞了这显然不对。你可以在Penguin里重写fly()抛出异常但随之而来的是所有接收Bird对象并调用fly()方法的代码遇到Penguin就崩了。这种“继承来的能力不符合子类实际语义”的情况在用继承复用时非常容易出现。5.2 组合的思路和实战对比组合的思路是不要用继承去强行复用代码而是用一个类持有另一个类的引用。还是企鹅的例子应该设计一个Bird接口表示“会飞的鸟类能力”让实际会飞的类实现它。Penguin类可以有自己的name、age等属性但不需要强行拥有飞行能力。回到员工管理系统的例子。如果哪天需求变成“经理也是一个拥有打卡技能的员工”用继承就会很尴尬——Manager已经继承了Employee再想继承另一个类就做不到了因为Java不支持多继承。但用组合就很灵活public class Manager { private Employee employeeInfo; private ListEmployee team; private AttendanceManager attendanceManager; public void clockIn() { attendanceManager.clockIn(); } }Manager把员工基本信息委托给employeeInfo对象把考勤行为委托给attendanceManager对象彼此独立。将来需求变化只动对应组件不会波及整体。所以我的建议是优先使用组合只有在“is-a关系非常明确且父类有真正的模板逻辑需要收口”时才使用继承。比如JDK里ArrayList继承AbstractList、HashMap继承AbstractMap这些是因为子类确实是一种父类而业务代码里大量“继承只是为了拿两个现成方法”的情况基本都可以用组合优化。6. 深入原理从JVM层面看三大特性的本质6.1 类加载和对象的创建过程理解三大特性能不能只停留在语法层面我建议零基础的朋友至少了解一点JVM层面的东西这对后面看框架源码非常有帮助。使用new创建对象时JVM大致做这么几件事先检查类是否已经被加载如果没有就通过类加载器加载经过加载、链接、初始化三个阶段然后分配内存再设置对象头最后执行构造方法。其中和继承关系密切的是类加载阶段的初始化顺序。看下面这段代码我非常推荐大家自己在本地跑一遍并思考原因public class Parent { static { System.out.println(父类静态代码块); } { System.out.println(父类普通代码块); } public Parent() { System.out.println(父类构造方法); } } public class Child extends Parent { static { System.out.println(子类静态代码块); } { System.out.println(子类普通代码块); } public Child() { System.out.println(子类构造方法); } } public class Demo { public static void main(String[] args) { new Child(); } }输出结果是父类静态代码块、子类静态代码块、父类普通代码块、父类构造方法、子类普通代码块、子类构造方法。这个顺序很多人背过但没想过为什么。静态代码块在类加载阶段执行先加载父类再加载子类所以父类静态块在前创建对象时子类构造方法必须先让父类的实例变量和构造方法执行完所以父类的普通代码块和构造方法会先执行然后才轮到子类的普通代码块和构造方法。掌握这个顺序对排查对象初始化相关的bug非常有帮助。6.2 动态分派多态背后的核心机制前面提到多态依赖动态分派。这里可以再深入一点JVM解析方法调用时invokevirtual指令是运行时动态绑定的关键。它首先在调用者所引用的实际对象的类中查找目标方法找不到再到它的父类中查找一直向上直到找到为止。这个机制解释了为什么子类重写父类方法后即使你用父类类型的引用去调用最终执行的还是子类的版本。它也解释了为什么Java的方法默认不是final——因为有了动态分派系统才能在不修改调用方代码的前提下通过新增子类来完成功能扩展。System.out.println之所以能打印任何对象底层也是把Object的toString()做动态分派每个类都重写了它。从面试角度来说能把这个机制讲清楚的候选人面试官一般都会觉得他基础扎实。早几年有面试官喜欢问“方法重写和重载在JVM中的指令有什么区别”重载对应invokestatic或invokevirtual的编译期解析而重写对应运行期动态分派。现在虽然这么细的问题不太常考了但理解这个机制对后面学Spring AOP的代理原理也很有帮助。6.3 final关键字在三大特性中的角色final在继承和多态里是个容易混淆的点我专门列一下final修饰类表示这个类不能被继承。比如java.lang.String就是final类这是为了保证字符串操作的不可变性和安全性。final修饰方法表示方法不能被子类重写。final修饰变量如果是基本类型值不能变如果是引用类型引用不能变但对象内部状态仍然可改。这三个“不能”分别对应着类结构上的禁止继承、行为上的禁止重写、状态上的禁止修改。在设计API的时候这些都是限控继承和多态的关键工具。比如你封装了一套模板方法核心的算法骨架用final方法守住把可变的步骤设计成抽象方法交给子类实现——这就是经典的模板方法设计模式底层就靠final和abstract的配合。7. 面试高频考点与实践经验速查7.1 常见的面试追问和答题思路很多八股文背得很熟的人一被追问就露馅。这里我把自己平时面试别人常问的几个问题列出来并给一个比较稳的回答思路。第一个问题“说说Java的三大特性。”如果只是背出封装继承多态的含义只能及格。我会建议按“问题-方案-效果”的逻辑回答先说开发中会遇到数据易被改坏、代码重复多、扩展不灵活这三类问题然后分别用封装、继承、多态解决最后指出三者是协同工作而不是割裂的。第二个问题“重写和重载的区别。”先说定义再说发生的类位置、参数列表、返回类型、访问权限和绑定时机。如果能把“重载是编译期静态绑定重写是运行期动态分派”讲出来这段就出彩了。第三个问题“构造方法能不能重写为什么”不能。重写的前提是有继承关系且子类方法签名和父类一致。但构造方法名必须和类名一样子类类名和父类不可能一样所以构造方法不存在重写的概念。构造方法只能重载。第四个问题“为什么Java不支持多继承”这是典型的开放题。可以从菱形继承问题回答如果两个父类有相同签名的方法子类不知道该继承哪一个会带来歧义和复杂性。Java用接口多实现来替代多继承接口没有状态只定义行为契约从设计上规避了冲突。7.2 实际项目中我推荐的代码设计习惯在最后给大家几条我个人踩坑多年总结出来的编码习惯写封装继承多态相关的代码时非常实用。第一类字段默认private对外提供的方法必须有业务语义。一个类如果全是getter和setter大概率是贫血模型。把行为放到类里而不是在外面判断来判断去。第二父类里尽量设计稳定的模板方法用final方法收口核心流程把可变的细节用protected抽象方法开放给子类。这样既留了扩展点又不会让子类破坏主流程。第三接口优先于抽象类。一个业务能力可以建模成接口就先用接口。抽象类适合有共性状态和部分通用实现的时候用但它毕竟还是单继承的用了就挤占了唯一的继承名额。第四判断一个继承设计是否合理试着大声把这句话念一遍“子类是一种父类吗”如果为了复用两个方法而强行说“是”那就改成组合。8. 从一个系统看三大特性的协作闭环8.1 多态和封装的组合策略模式的最小实现最后用一个最小化的支付系统把三大特性的协作关系完整串一遍。这个场景在电商、外卖、教育等很多系统里都存在是理解面向对象非常好的载体。需求系统要支持多种折扣策略。新用户首单立减10元满100减20节假日打八折。如果不用多态和封装一个计算价格的方法里塞满if-else每加一种活动就改一堆逻辑。用多态和封装设计后是这样public interface DiscountStrategy { double apply(double originalPrice); } public class NewUserDiscount implements DiscountStrategy { Override public double apply(double originalPrice) { return Math.max(0, originalPrice - 10); } } public class FullReductionDiscount implements DiscountStrategy { private static final double THRESHOLD 100; private static final double REDUCTION 20; Override public double apply(double originalPrice) { return originalPrice THRESHOLD ? originalPrice - REDUCTION : originalPrice; } } public class PercentageDiscount implements DiscountStrategy { private final double rate; public PercentageDiscount(double rate) { this.rate rate; } Override public double apply(double originalPrice) { return originalPrice * rate; } }接口DiscountStrategy就是多态的核心。调用方只需要持有DiscountStrategy引用具体是哪个策略由外部传入public class OrderService { private final DiscountStrategy discountStrategy; public OrderService(DiscountStrategy discountStrategy) { this.discountStrategy discountStrategy; } public double calculateFinalPrice(double originalPrice) { return discountStrategy.apply(originalPrice); } }这里的设计还能注意到百分比折扣策略是在构造方法里传入折扣率通过构造器来控制对象的状态比后续用setter修改要安全得多——这正是一种封装思维。8.2 从模仿到内化如何真正掌握三大特性有些初学者会问“看了这么多代码轮到自己写还是不知道该什么时候用继承、什么时候写接口怎么办”我的建议是先做模仿式重构。不用从零设计一个新项目就把自己写过的那些if-else代码挑一小段尝试用面向对象的方式重新实现。比如你看过这个策略模式之后把自己手头的某个业务场景改成策略模式试试。第一遍照猫画虎第二遍理解思路第三遍不看任何参考自己写出来。这个过程比看十篇文章都有用。本质上Java的三大特性是我们组织代码思维的脚手架。封装让你想清楚“哪些数据是内部机密哪些行为是对外服务”继承让你想清楚“哪些东西可以抽象成共同父类哪些东西必须各走各路”多态让你想清楚“代码该怎么面向抽象写才能让未来的扩展不用改旧代码”。这三件事想明白了你在Java这条路上就真正起步了。我自己的经验是把这三个特性融进习惯里之后再去看Spring、MyBatis这些框架源码起码不会觉得那是天书。框架里大量藏在接口、抽象类、模板方法背后的设计底层就是今天讲的这些东西。你掌握了这套面向对象的基本功后面学到哪一层都不会虚。
企业数字化 ERP 产品动态
相关推荐
安卓逆向入门:LSPosed模块开发从零到跑通全流程 最近总有人私信问我,安卓逆向到底从哪里入门。我自己折腾了一圈下来,最绕不开的就是Xposed这一套东西,尤其是现在社区里几乎一统天下的LSPosed。这篇博文是我打算写的“安卓逆向之LSPosed开发”系列第一篇,先把LSPosed是什么、模块… · 2026/9/24 21:50:00
泸州老窖跌至三成背后:估值收缩与深度回撤的周期启示 最近和一个做投资的朋友聊天,他感叹了一句话:“泸州老窖的股票已经从最高点下跌到只有30%了。” 这句话听起来没什么波澜,但稍微算一下就知道,所谓的“只有30%”,不是跌了30%,而是股价只剩下最高点的三成&a… · 2026/9/24 21:50:00
安卓PS5模拟器实测:能跑但离“口袋PS5”还有多远? 最近几天数码圈和游戏圈同时被一个消息刷了屏——有团队放出了安卓端的PS5模拟器,名字一出来群就炸了,各路主播和搞机党连夜下载试跑。我也第一时间搞到手里实测了一轮。先说结论:它能跑,但跟你心里那个“口袋PS5”还差得很远。这… · 2026/9/24 21:49:54
CSP-S必会:Dijkstra堆优化与链式前向星实战全解析 得从CSP-S考场上一个很现实的问题说起:同样是求最短路,为什么有人能用Dijkstra十分钟AC,有人却卡在SPFA的TLE里出不来,还有人连建图都写不对。这篇东西就是把我自己备考和带选手过程中,关于Dijkstra算法最核心的那套东… · 2026/9/24 22:33:25
Agent Skills:从单体Prompt到技能化,打造稳定可靠的AI Agent 我一直在琢磨怎么让AI Agent从“演示玩具”变成真正能稳定干活的工具,直到最近反复研究agent-skills这个方向,才算是摸到了门道。如果你也在做AI应用开发、自动化流程设计,或者单纯好奇为什么别人的Agent能一口气搞定复杂任务,而你… · 2026/9/24 22:33:25
Dijkstra算法在CSP-S竞赛中的核心应用与优化实战 1. CSP-S为什么绕不开Dijkstra先说结论:在信奥赛CSP-S(提高级)的图论题里,Dijkstra算法不是“考不考”的问题,而是“怎么考”的问题。最近几年的真题反复证明了这一点,比如涉及最短路径的题目,十… · 2026/9/24 22:33:12
fastEventbus4cj性能基准测试:10000事件压测与并行度调优技巧清单 fastEventbus4cj性能基准测试:10000事件压测与并行度调优技巧清单 【免费下载链接】fast-eventbus-cj 一种发布/订阅事件总线,为多线程应用程序中的高吞吐量而优化的强大事件总线。 项目地址: https://gitcode.com/Cangjie-TPC/fast-eventbus-cj
… · 2026/9/24 22:33:06
黑胶试听Mili《Miracle Milk》:转录、Hi-Res录制与听感全解析 做黑胶试听这个事儿,我前前后后折腾了快四年,拍过古典、爵士、也拍过不少独立乐队的七寸,但Mili这张《Miracle Milk/奇迹牛奶》我一直拖到最近才真正动手。原因不复杂:这张碟在粉丝心里的位置太特殊了,它几乎是Mili前半… · 2026/9/24 22:33:00
Gekko 比特币交易机器人:Node.js 技术分析交易与回测平台完全指南 金融科技后端 【免费下载链接】gekko A bitcoin trading bot written in node - https://gekko.wizb.it/ 项目地址: https://gitcode.com/gh_mirrors/ge/gekko 点击查看 免费下载 Gekko 是一款基于 Node.js 编写的免费开源比特币技术分析(TA)… · 2026/9/24 22:33:00
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44