新闻详情

新闻详情

首页 / 资讯中心 / 详情

Java面向对象核心:类与对象、封装、继承、static一次搞懂

发布时间:2026/10/1 14:05:44来源:尧图网络
Java面向对象核心:类与对象、封装、继承、static一次搞懂
Java 学到面向对象算是真正开始迈入“编程思维”的门槛。很多人把这部分概念背得滚瓜烂熟回头写代码却一头雾水问题不是脑子笨而是没把“对象”这个词和一个真实世界里你想表达的东西对上号。这篇笔记是“Java自学”系列的第二篇聚焦面向对象最核心的类与对象、封装、继承、static 这几个基础考点接口、多态、抽象类这些我会放到下一篇再展开。适合刚学完 Java 基础语法、准备正式接触面向对象的朋友也适合想快速把这块基础重新梳理一遍、应付面试基础题的开发者。今天的目标很明确把这些概念不仅看懂还要能在 IDEA 里随手敲出来。1. 为什么非要讲面向对象它到底解决了什么问题1.1 从面向过程说起在面向对象出现之前写程序的主流思路叫“面向过程”。它的核心逻辑是把一件事情拆成若干步骤每个步骤用函数实现按顺序调用。举例来说你要实现一个用户登录模块面向过程的写法可能就是“接收用户输入 - 查询数据库 - 校验密码 - 写 session - 返回结果”每一步对应一个函数数据在各个函数之间传来传去。这种方式写小工具、小脚本完全没问题但一旦项目变大问题就来了数据结构和操作数据的方法被拆散了维护成本高。今天要加一个验证码校验明天要改成短信登录你很可能需要翻遍所有相关函数改来改去还容易漏掉一处。面向对象换了一种组织方式把“用户”“会话”“校验器”都抽象成对象每个对象内部既保存自己的数据也提供操作这些数据的方法。数据和行为的绑定让代码的边界更清晰哪个对象负责什么一目了然。需求变化的时候往往改一个对象就够了其他部分不用牵连。1.2 类与对象图纸和实物的关系很多人分不清“类”和“对象”我用一个比喻来解释类是图纸对象是按照图纸造出来的房子。图纸描述了房子该有哪些房间、门窗开在哪里但它本身不能住人只有按照图纸实际盖出来的每一套房子才是对象。对应到 Java 里类就是定义“一类事物”的模板包含两个部分属性成员变量和行为方法。比如你定义一个Student类属性是姓名、学号、年龄行为是study()、exam()。而对象就是通过这个类new出来的一个个具体学生“张三”“李四”各自拥有自己的姓名、学号和年龄可以独立调用方法。这里还有一层新手最容易忽视的含义类本身不占内存空间的实例只有执行了new之后JVM 才会在堆内存里真正给这个对象分配一块空间。所以“对象是类的一个实例”不是一句空话它意味着实打实的内存分配和生命周期管理。1.3 三大特性先有个整体印象面向对象有三大特性封装、继承、多态。封装是把实现细节藏起来对外提供一个稳定接口继承是把公共的属性和行为抽取到父类子类直接复用多态是同一个行为在不同对象上表现出不同结果。这三句话现在背下来没有意义关键是要在代码里体会到它们各自解决了什么问题。这篇文章会把封装和继承讲透多态必须依赖接口和抽象类配合才能讲明白所以留到下一篇。你现在只要记住这三个词带着问题往下看就行封装为什么要把字段设成 private继承会不会带来负面影响等到学完接口和多态再把整个图景拼起来。2. 定义一个类成员变量、方法与构造方法2.1 成员变量与局部变量默认值差别怎么这么大在类里面、方法外面声明的变量叫成员变量也叫字段在方法内部声明的变量叫局部变量。两者最直观的区别是默认值成员变量即使你没赋初值也会有一个默认值局部变量不初始化就直接使用编译器会直接报错。数据类型成员变量默认值byte / short / int / long0float / double0.0booleanfalsechar\u0000引用类型String、数组、对象等null为什么 Java 要这么设计我的理解是成员变量属于对象对象在创建时 JVM 会统一做零值初始化而局部变量的生命周期极短如果也默认给个值你很可能忘记自己初始化了什么导致用了一个逻辑上错误的值。编译器强制你在使用前先赋值反而逼着你把逻辑写清楚。另外定义变量时最好顺手遵守命名规则类名用大驼峰StudentInfo变量名和方法名用小驼峰studentName、getStudentName常量全大写加下划线MAX_SIZE。这套规则不强制运行但团队协作时是必须的面试里也经常单独拿出来问。2.2 方法的定义与重载方法是对象行为的载体。方法定义由修饰符、返回值类型、方法名、参数列表和方法体组成。新手容易忽略的一点是Java 的方法重载只看方法名和参数列表是否不同跟返回值类型、访问修饰符完全无关。重载的好处是让同一个方法名能处理不同类型的参数。你看System.out.println()它能打印字符串、整数、对象其实背后就是几十个重载方法在支撑。你在自己类里也可以这样给一个Student类写一个print()方法让它分别接收不同参数输出不同格式的信息调用方不需要记一堆不一样的方法名。public class Printer { public void print(String s) { System.out.println(文本 s); } public void print(int num) { System.out.println(数字 num); } public void print(String title, int num) { System.out.println(title num); } }注意上面第二个和第三个方法参数列表不同属于重载但如果只改返回值类型参数一样那不叫重载编译器会直接报“重复定义”错误。2.3 构造方法new 的过程到底发生了什么构造方法是一种特殊方法名字和类名完全相同没有返回值类型。它负责在对象创建时完成初始化。每个类即使你不写Java 也会自动提供一个无参构造方法但这个“免费赠送”有一个陷阱一旦你自己写了任意一个有参构造方法默认的无参构造方法就失效了。很多初学者就是在这里踩坑定义了一个Student(String name)构造方法然后在别处写new Student()编译报错“找不到符号”半天排查不出来其实就是无参构造被覆盖了。new一个对象的过程大致有四步在堆内存分配空间 - 给成员变量赋默认值 - 执行构造方法体里面通常给成员变量赋真正的初始值- 把对象的引用返回给调用处。所以构造方法可以重载就是为了让你能用多种方式“创建”一个对象。public class Student { private String name; private int age; public Student() { // 什么都不写也行保留这个无参构造很重要 } public Student(String name) { this.name name; } public Student(String name, int age) { this(name); // 调用上面的单参构造 this.age age; } }上面第三个构造方法里的this(name)表示调用同类中另一个构造方法而且这行代码必须出现在构造方法的第一行否则编译报错。2.4 this它到底指的是谁this是 Java 里的一个关键字代表“当前对象”的引用。哪个对象调用了方法方法里的this就指向哪个对象。最常用的场景就是区分成员变量和局部变量public void setName(String name) { this.name name; }这里如果不加thisname name是局部变量自己给自己赋值成员变量完全没被修改程序看起来“没报错但数据丢了”。写this.name才能让编译器知道左边访问的是成员变量。this还可以用在上面提到的构造方法连调中但注意它不能出现在静态方法里因为静态方法不依赖具体对象也就没有“当前对象”这个说法。如果面试官问“this 能否用在 static 方法中”答案是不能原因就在这里。3. 封装为什么字段都要写成 private3.1 不封装会怎样如果你把类的字段直接设成public外部代码就能随意修改比如给年龄赋一个 -5给姓名赋一个空对象类里面完全没法拦截。这在小型练习里看不出来在正式项目里就是安全隐患和数据混乱的根源。封装的核心思路是字段用private私有化外部访问必须通过public的 getter/setter 方法。这样你可以在setAge()里加校验逻辑拒绝不合理的数据也可以随时改变内部实现只要 getter/setter 的签名不变调用方代码就无需改动。这就像银行不会把所有保险柜的钥匙摆在大厅客户存取款必须通过柜台窗口银行在窗口后面怎么调拨现金是内部事务客户不需要知道也不该知道。3.2 private getter/setter 的标准写法标准写法如下建议你手敲一遍而不是直接复制。手写过一遍你对“封装”的体感会和只看不写完全不同。public class Student { private String name; private int age; public String getName() { return name; } public void setName(String name) { if (name null || name.isEmpty()) { throw new IllegalArgumentException(姓名不能为空); } this.name name; } public int getAge() { return age; } public void setAge(int age) { if (age 0 || age 150) { throw new IllegalArgumentException(年龄不合法); } this.age age; } }在 IDEA 里你可以用快捷键Alt Insert选择 Getter 和 Setter 自动生成但学习阶段我更建议先手写几遍搞清楚每个方法在干什么再考虑效率问题。很多项目现在用 Lombok 的Data注解省去这些代码这个工具很好用但前提是你已经理解它帮你生成了什么否则一旦出问题就完全摸不着头脑。3.3 访问修饰符权限控制的全家桶Java 有四个访问修饰符从宽松到严格public、protected、默认什么都不写、private。修饰符同类同包子类跨包任意位置public可以可以可以可以protected可以可以可以不行默认可以可以不行不行private可以不行不行不行理解访问修饰符还要理解“包”的概念。包就是 Java 的目录组织方式它的两个作用一是分类管理类二是提供命名空间避免同名类冲突。跨包使用类需要用import导入这跟你手机通讯录里同名联系人需要区分是一个道理。平时写实体类的字段基本约定是全部private工具方法如果是给外部用的用public只给内部协助用的用private。protected在继承和重写时会遇到后面讲。3.4 JavaBean行业里的一种约定俗成JavaBean 不是语法是一种约定一个类如果有无参构造方法、所有字段私有、为每个字段提供 getter/setter就称为 JavaBean 风格的类。现在主流框架大量依赖这种约定比如 Spring 注入对象属性、MyBatis 把数据库列映射到实体类、Jackson 把对象转成 JSON它们都默认通过 getter/setter 来读写属性。我见过一个真实案例有人写了一个实体类字段都设成了 public没写 getter结果接口返回 JSON 时字段全部丢失。那是因为 Jackson 在序列化时找的是 getter 方法而不是直接读字段。所以 JavaBean 规范不是老古董而是所有 Java 开发者都应该遵守的行业默契。4. 继承让类与类之间产生血缘关系4.1 extends 基础写法与意义继承用extends关键字实现。子类继承父类之后就自动拥有父类中非private的属性和方法同时还能扩展自己独有的内容。为什么要有继承因为多个类之间往往存在公共部分把这些公共部分抽到父类里子类就能复用不用重复写。举一个最经典的例子public class Animal { protected String name; public void eat() { System.out.println(name 正在吃东西); } } public class Dog extends Animal { public void bark() { System.out.println(name 汪汪叫); } }Dog自己没有写eat()但可以调用因为它继承了Animal。这里还会引入一个重要的判断标准继承必须满足 is-a 关系。狗是一种动物没问题但是“狗”继承“汽车”就不合理虽然字段可能碰巧有很多相同但语义上说不通。4.2 方法重写与重载不是一回事重写Override和重载Overload名字像内容完全不同。重载发生在同一个类中方法名相同参数不同重写发生在子类和父类之间子类重新实现父类的方法方法签名必须完全一致。public class Dog extends Animal { Override public void eat() { System.out.println(name 正在啃骨头); } }父类Animal的eat()输出“正在吃东西”子类Dog觉得太笼统想改成“啃骨头”这就是重写。这里必须加Override注解加了之后编译器会帮你检查方法签名是否真的匹配如果写错了比如参数类型不一致编译阶段就会报错而不是运行到一半才发现。重写有两条硬性规则新手常踩一子类重写方法的访问权限不能比父类更严格父类是public子类就必须是public否则编译报错二构造方法不能被重写它只能被调用。4.3 super 与构造方法的神秘调用链super和this是两兄弟this指向当前对象super指向父类那一部分。子类构造方法中如果要调用父类带参构造第一行必须写super(参数)如果子类构造方法里没写编译器会自动加上无参的super()。这就会引出一个很容易犯的错误如果父类只写了有参构造没有显式保留无参构造那么子类构造方法默认调用的super()就不存在编译直接失败。报错信息通常是“Implicit super constructor Animal() is undefined”很多人一看就懵。正确的做法通常是父类里保留一个无参构造或者子类构造方法第一行显式super(name)。另外this(...)和super(...)都要求在第一行所以两者不可能同时出现你必须选一个。构造方法的执行顺序也要清楚先执行父类的初始化逻辑再执行子类的。你可以想象成盖房子父类是地基子类是上层建筑必须先打地基再盖楼。4.4 继承的代价类不是继承得越深越好继承虽然能复用代码但它的耦合程度非常高子类一旦继承父类父类的一举一动都会影响到所有子类。如果继承层次很深比如 A 继承 BB 继承 CC 继承 D这时候改 D 的一个方法可能牵动下面四层的所有行为排查 bug 时非常痛苦。Java 设计成“类只能单继承”其实是有意为之就是为了避免多继承带来的菱形问题。菱形问题简单说就是一个子类同时继承两个父类而这两个父类又都继承自同一个祖先调用方法时不知道该用哪一个版本。Java 躲避这个问题的方法是类单继承接口可以多实现。这个细节等下一篇讲接口的时候再展开。还有一句业界流传很广的话叫“组合优于继承”。意思是能通过持有其他对象来复用功能时优先考虑组合而不是强行构建继承树。这不是让你完全不继承而是提醒你继承是一种强关系约束用之前多想想是不是真的满足 is-a。5. static属于类不属于实例5.1 静态变量与静态方法static修饰的成员属于类本身不属于某个具体对象。所有对象共享同一份静态变量任何对象对它的修改其他对象都能看到。举个最常见的例子用静态变量统计类一共创建了多少个对象public class Student { private static int count 0; public Student() { count; } public static int getCount() { return count; } }这里count是静态变量所有Student对象共同维护它getCount()是静态方法可以直接用Student.getCount()调用不需要先创建对象。静态方法有个重要限制不能直接访问非静态成员。原因很简单——静态方法没有this它不知道当前是哪个对象在调用自然无法访问依赖具体对象的成员变量和成员方法。反过来普通方法可以随意访问静态成员因为静态成员是大家共有的。5.2 静态代码块和类加载时机除了静态变量和静态方法还有静态代码块。它会在类第一次加载到 JVM 时执行一次适合做一些类级别的初始化工作比如读取配置文件、初始化静态资源。执行顺序值得注意。如果一个类有父类顺序大概是父类静态代码块和静态变量 - 子类静态代码块和静态变量 - 父类实例变量和构造方法 - 子类实例变量和构造方法。这个顺序面试里经常作为类加载机制的问题出现但你现在不需要死记只要抓住一个原则静态优先于非静态父类优先于子类。“static 方法不能直接访问非静态成员”“静态代码块只执行一次”这两句话是面试官检验初学者是否真正理解 static 的常用题目。理解了内存里“类模板存在方法区静态成员跟随类实例成员跟随对象”这个模型后答案自然就出来了。5.3 main 方法为什么必须是 staticmain方法是程序的入口声明是public static void main(String[] args)。它必须是static因为 JVM 启动时还没有任何对象虚拟机只能通过类名直接调用一个静态方法作为入口不可能先创建某个对象再让对象来调你。args参数用来接收命令行传进来的字符串参数。日常开发很少用到但在一些启动脚本里能看到它的身影。另外很多初学者在main方法里直接写测试代码来验证类是否正确这完全正常也是学习阶段最朴素的调试手段不用觉得低级。6. 新手必经之路常见问题与排查技巧实录6.1 空指针异常最经典的“劝退师”只要写过几天 Java就一定见过NullPointerException。它出现的原因很简单一个引用变量的值是null你却去调用它的方法或访问它的字段。最典型的场景是Student s null; s.setName(张三); // 这里运行时报空指针为什么编译不报错因为编译器只检查类型s的类型是StudentsetName方法存在它不管你运行时s到底是不是 null。排查方法也很直接看报错信息中第一个 “at” 后面的代码行号定位到那一行用 IDEA 的 Debug 模式在那一行打断点查看哪个对象为 null就知道问题在哪了。预防空指针有两个好习惯一是对象用完或创建后就立刻初始化尽量不要让引用长期处于 null 状态二是方法接收参数时做防御性判断需要拒绝 null 的场景可以直接抛异常或用Objects.requireNonNull()显式校验。6.2 和 equals比较字符串这么容易翻车比较的是引用地址基本数据类型比较的是值。equals在没有重写的情况下默认也是用比较效果一样。但是String类重写了equals所以两个字符串内容相同但地址不同时返回 falseequals返回 true。String a new String(hello); String b new String(hello); System.out.println(a b); // false两个对象地址不同 System.out.println(a.equals(b)); // true内容相同新手最常犯的错就是用去比较两个字符串结果时好时坏。比如String x hello和String y hello用可能返回 true这是字符串常量池的干扰容易让你产生错误认知。一句话比较字符串内容永远用equals永远不要用。6.3 继承相关的编译报错速查继承部分的报错其实很集中我把最常见的几种整理一下现象原因处理子类构造器报错 “Implicit super constructor 未定义”父类只有有参构造没有无参构造子类构造器第一行显式super(参数)或父类补充无参构造重写方法报 “reduces visibility”子类重写后访问权限比父类更严格子类重写方法权限设为与父类相同或更宽提示找不到super()super未写在构造方法第一行把super(...)移动到第一行子类无法访问父类字段父类字段是private改成protected或通过 getter 访问这些问题本身不难真正难的是“看到报错信息能联想到继承关系”。所以建议你把每个报错都手动复现一遍亲眼看到它发生再亲手解决。6.4 提前扫个盲引用拷贝与对象深度拷贝虽然“深度拷贝”听起来是进阶内容但跟面向对象基础关系非常密切热词里也总出现这里值得提前说一嘴。一句最简单的赋值Student s1 new Student(张三); Student s2 s1;这里s2并没有复制出一个新对象只是复制了一个引用s1和s2指向堆内存中同一个对象。通过s2修改属性s1也会看到修改。如果你需要一个真正独立的新对象就叫对象拷贝。浅拷贝只复制一层新对象内部的字段如果是基本类型值真的复制了如果是引用类型新对象和旧对象仍然共享同一个内部对象。深拷贝则是把对象内部的引用对象也一起复制得到完全独立的副本。实现深拷贝有很多方式常见的有重写clone()方法、手动逐个复制字段、用序列化方式复制。这些细节今天不展开但你记住一个结论以后设计类时遇到可变引用类型字段一定要想清楚这个对象被复制时内部数据会不会被意外共享。这是很多高级 bug 的源头。7. 学完这一篇建议立刻动手做的几件事7.1 最小练习清单概念不落到代码上约等于没学。我建议你按下面这个清单练一遍每个练习不要太久但一定要完整敲出来写一个Student类私有字段name、age提供无参构造、全参构造、getter/settersetter 里加年龄校验。写三个重载构造方法用this(...)互相调用用 Debug 模式观察构造方法执行的跳转过程。写一个Animal父类和两个子类子类重写父类方法并用Override注解标注。在Student类里加一个private static int studentCount在构造方法里让count写一个静态方法返回它然后创建多个对象观察这个值的变化。把自己写的类实例化两个对象用 Debugger 查看它们在堆内存中的地址确认它们不是同一个对象。这些练习如果都能独立完成并且能跟别人解释清楚每一步在干嘛那这篇的基础就算真正掌握了。7.2 我个人的一点学习体会我带过的学生里有一个特别典型的转变是从“背定义”变成“画内存图”。你说得出“类是模板、对象是实例”这句话完全不够但如果你能在纸上画出一个类在方法区两个对象在堆区对象的字段指向各自的字符串对象引用变量在栈区指向堆里的对象——你会发现后面学集合、学框架都顺畅很多。面试里问面向对象往往不是考你背概念而是问你“怎么设计一个类”“为什么字段要私有”“继承和组合怎么选”。这些问题的答案恰恰就藏在今天这些看似基础的操作里。我这几年最大的感受是基础部分不怕慢就怕你觉得简单就跳过去。宁可多花几天把类、对象、引用、封装、继承这些概念揉碎了练也不要赶进度。下一篇我会顺着今天埋下的伏笔把接口、多态、抽象类讲清楚到那时你就能看到面向对象的全貌了。先花点时间把这篇消化掉尤其是最后一个练习清单做完再来找下一篇效果会好得多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

Python爬虫实战:抓包定位JSON接口,采集2345天气网数据 2026/10/1 17:26:11

Python爬虫实战:抓包定位JSON接口,采集2345天气网数据

刚接触Python爬虫的同学,最容易卡住的地方不是不会写代码,而是不知道"到底该爬哪个接口"。看到网页上明明有天气数据,代码里却只能拿到一堆HTML标签,或者整页都是动态加载的内容,直接 requests 根本拿不到想…

阅读更多 →
90DaysOfCyberSecurity 学习路线图全解析:90 天从 Network+ 基础到道德黑客的系统化网络安全成长计划 2026/10/1 17:26:10

90DaysOfCyberSecurity 学习路线图全解析:90 天从 Network+ 基础到道德黑客的系统化网络安全成长计划

教程网络安全 【免费下载链接】90DaysOfCyberSecurity This repository contains a 90-day cybersecurity study plan, along with resources and materials for learning various cybersecurity concepts and technologies. The plan is organized into daily tasks, covering…

阅读更多 →
ContextMenuManager:一款基于注册表的 Windows 右键菜单管理工具全解析 2026/10/1 17:26:03

ContextMenuManager:一款基于注册表的 Windows 右键菜单管理工具全解析

桌面应用系统工具 【免费下载链接】ContextMenuManager 🖱️ 纯粹的Windows右键菜单管理程序 项目地址: https://gitcode.com/gh_mirrors/co/ContextMenuManager 点击查看 免费下载 ContextMenuManager 是一个开源的 Windows 右键菜单管理程序&#xff…

阅读更多 →
读懂 remoteintech.company 公司档案:以 Brainstorm Force 为例解析远程友好公司的数据模型与站点渲染链路 2026/10/1 17:26:03

读懂 remoteintech.company 公司档案:以 Brainstorm Force 为例解析远程友好公司的数据模型与站点渲染链路

数据集 【免费下载链接】remote-jobs Source for remoteintech.company — a community-maintained directory of remote-friendly tech companies 项目地址: https://gitcode.com/GitHub_Trending/re/remote-jobs 点击查看 免费下载 导读 本文以开源仓库 remotei…

阅读更多 →
SAP Business Partner(BP)后台表与BAPI核心解析 2026/10/1 17:26:02

SAP Business Partner(BP)后台表与BAPI核心解析

1. 项目概述:这不是“BP神经网络”,而是SAP里那个天天打交道的BP主数据刚看到标题“BP-常用后台表/BAPI”时,我下意识也愣了一下——现在满屏都是“bp神经网络结构图”“bp算法”“matlab bp拟合曲线”,连搜索引擎都快把SAP里的BP…

阅读更多 →
频繁模式挖掘实战:Apriori与FP-Growth选型及Python实现 2026/10/1 17:25:56

频繁模式挖掘实战:Apriori与FP-Growth选型及Python实现

简介:面向数据仓库与数据挖掘课程设计/期末大作业场景的 Python 频繁模式挖掘完整项目,覆盖 Apriori 算法实现、多数据集应用与实验报告,适合需要提交可运行代码和说明文档的本科/高职学生。代码注释详细,新手也能跟着注释读懂事务…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉