ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java自学第12天:搞懂类与对象,迈进面向对象编程大门

Java自学第12天:搞懂类与对象,迈进面向对象编程大门 如果你也在自学 Java大概率已经进入这种状态前十天围着变量、运算符、if/else、for 循环打转第十一天刚把数组理出点头绪第十二天一睁眼突然撞上四个大字——面向对象。我当时对着“对象”两个字懵了好一阵脑子里全是“对象哪来的对象new 是谁”后来才明白第 12 天恰恰是 Java 学习路上真正的分水岭。前面学的是“用工具”后面学的是“造工具”前面是语法后面是思维。这一天的内容决定了你以后是能读懂框架源码还是永远卡在“能跑但看不懂”的阶段。这篇就记录一下我在第 12 天是怎么扛过面向对象入门这一关的包括类与对象的理解方式、当天的实操代码、踩过的坑以及这些内容在面试八股里的真实分量。1. 第12天Java学习走到哪一步了1.1 前11天踩过的路先快速对一下进度表看看第 12 天的坐标到底在哪。如果你是按常见的 Java 自学路线图走前 11 天大概覆盖了这些东西JDK 安装与环境变量配置搞懂了JAVA_HOME、PATH、CLASSPATH的基本用法注释规范、标识符、关键字8 种基本数据类型byte / short / int / long / float / double / char / boolean与包装类型的初步认识变量的作用域、常量的使用运算符系列算术、赋值、关系、逻辑、三元、位运算流程控制if/else、switch、for、while、do-while数组的声明、创建、遍历、常见异常数组越界简单方法定义与调用、参数传递、return 的使用。这个阶段最典型的学习成果就是能写一个“考试管理”或者“学生成绩统计”的控制台程序。比如用数组存 10 个人的成绩用 for 循环算平均分用 if 判断及格率。写得多烂不重要重要的是你已经开始用编程语言表达逻辑了。但坦白讲前 11 天写出来的代码本质上是“C 语言风格的 Java”。全是 top-down 的顺序执行一个 main 方法从头写到尾数据散落在各个变量里。程序长度一旦超过 200 行改一处逻辑就要翻半天非常痛苦。第 12 天的意义就是把这套“平面”的编程方式升级成“立体”的思维方式。1.2 为什么第12天必须啃面向对象面向对象编程OOP不是 Java 发明的Java 只是把这件事贯彻得特别彻底。从语法层面看Java 里任何一个有意义的程序代码基本都要定义类然后创建对象再通过对象去调用方法。就算是简单的Hello World本质上也包裹在一个类里面。面向对象的核心逻辑是把现实世界映射到代码世界里。现实世界里有“人”“车”“订单”“商品”这些概念代码里就有对应的“类”现实世界里有一个具体的“张三”“那辆 red 的本田”“昨天那笔 99 元的订单”代码里就有对应的“对象”。类是一张图纸对象是按图纸批量造出来的实物。试想一下如果没有面向对象你要描述一个图书馆系统就得维护十几个数组书名数组、作者数组、价格数组、库存数组、借出数量数组……下标一错位数据全乱。但如果定义一个Book类把书名、作者、价格、库存都封装在一个对象里再用一个Book[]数组去装数据结构的清晰程度完全是两个维度。所以第 12 天的目标很明确理解“类与对象”这对核心概念能自己定义一个类能创建对象能调用对象的方法。一旦这个坎迈过去后面学集合、学框架、学源码都是顺水推舟的事。2. 面向对象第一课类与对象到底怎么落地2.1 类就是图纸对象就是按图纸造出来的实物很多人第一次接触“类”和“对象”总觉得抽象。我后来找到一个比较顺的类比类 饼干的模具对象 用模具压出来的一块饼干。模具本身不能吃它是用来“定义饼干长什么样”的圆的方的上面有没有花纹模具一旦定好你可以压出无数块饼干每块饼干的形状一致但每块饼干又是独立存在的实体——这一块碎了不影响下一块。对应到 Java 代码// 类定义就像设计了一款模具 public class Cookie { String shape; String flavor; void showInfo() { System.out.println(这是一块 flavor 味的 shape 饼干); } }// 创建对象就像用模具压出来两块不同的饼干 public class TestCookie { public static void main(String[] args) { Cookie c1 new Cookie(); c1.shape 圆形; c1.flavor 抹茶; Cookie c2 new Cookie(); c2.shape 方形; c2.flavor 巧克力; c1.showInfo(); c2.showInfo(); } }这段代码干了三件事定义了Cookie类图纸在 main 方法里new了两个对象压了两块饼干然后分别调用showInfo()看看饼干状态。这个阶段不用去死记“类是抽象的概念对象是具体的实例”你就想着模具和饼干、图纸和房子、菜谱和菜。等代码写多了理论自然就通了。2.2 构造方法对象的出生仪式光靠new Cookie()创建对象对象还是“空白”的成员变量都是默认值还得逐行赋值很麻烦。Java 提供了构造方法专门用于对象创建时的初始化。构造方法有三个核心特征方法名必须和类名完全一致没有返回值类型连void都不能写创建对象时通过new自动调用不能像普通方法那样手动调。我把上面Cookie类加上构造方法public class Cookie { String shape; String flavor; // 无参构造可以不写编译器会自动补一个 public Cookie() { } // 有参构造创建对象时直接把属性值准备好 public Cookie(String shape, String flavor) { this.shape shape; this.flavor flavor; } void showInfo() { System.out.println(这是一块 flavor 味的 shape 饼干); } }于是 main 方法可以变成这样Cookie c1 new Cookie(圆形, 抹茶); Cookie c2 new Cookie(方形, 巧克力);一行代码就完成了“生产并初始化”比之前那个两行赋值干净多了。这里有个细节必须说清楚如果你在类里一个构造方法都没写编译器会自动给你生成一个无参的public Cookie() {}。但只要你写了任意一个有参构造系统就不再自动补无参构造了。也就是说你写了Cookie(String, String)之后想用new Cookie()就会编译报错。这个坑我第 12 天当天就踩了后文“常见问题”部分专门讲。2.3 封装把成员变量关起来再开扇窗第 12 天真正需要建立的一个概念是“封装”。封装是面向对象的三大特性之一还有继承、多态后面几天才会遇到。封装的核心诉求很简单类的成员变量不对外直接暴露外部必须通过公共方法去访问和修改。话句话说一个类的内部数据怎么组织那是我自己的事你们外部调用者别插手也别直接乱改。想读数据我给你getter。想改数据我给你setter。我在setter里做检查拦掉非法数据。改造Cookie类public class Cookie { private String shape; private String flavor; public Cookie() {} public Cookie(String shape, String flavor) { this.setShape(shape); this.setFlavor(flavor); } public String getShape() { return shape; } public void setShape(String shape) { if (shape null || shape.length() 0) { System.out.println(形状不能为空已使用默认值); this.shape 圆形; } else { this.shape shape; } } public String getFlavor() { return flavor; } public void setFlavor(String flavor) { this.flavor flavor; } void showInfo() { System.out.println(这是一块 flavor 味的 shape 饼干); } }注意看setShape里面的判断如果外部传入空字符串我就拒绝并且给一个默认值兜底。这就是封装最实际的价值——数据的合法性由类自己把关而不是依赖调用者的自觉。这一组getter / setter是 Java 面试八股里的高频考题。很多初学者觉得“这不是脱裤子放屁吗直接cookie.shape 圆形多方便”。等你做多了项目就懂了一旦你允许外部直接改属性将来想加校验、加日志、加权限控制就得把所有调用点全部翻出来改而有了setter只需要动一个方法里的代码。3. 第12天的代码实操做一个图书管理模型3.1 环境自检先确认JDK和IDE没问题动手写代码之前我习惯先做一轮环境自检。这个习惯救了我很多次尤其是按教程走但总是“明明一样却不运行”的时候。打开命令行依次输入java -version javac -version如果输出里版本号一致比如都是17.0.x说明 PATH 配置基本正常。再看一眼JAVA_HOMEecho %JAVA_HOME% # Windows 系统 echo $JAVA_HOME # Linux / macOS 系统如果这个变量为空建议先补上。JAVA_HOME指向 JDK 安装根目录比如C:\Program Files\Java\jdk-17。很多 IDE比如 IDEA和构建工具Maven、Gradle都要靠这个变量找 JDK。这个配置在第 1 天就该完成但我见过太多人装完 JDK 不配环境变量直接靠 IDE 内嵌的 JBR结果一到命令行编译就傻眼。IDE 方面我个人一直推荐 IntelliJ IDEA 社区版。倒不是别的编辑器不能用而是 IDEA 对面向对象的代码结构有很直观的提示比如它能自动帮你生成getter/setter、构造方法、toString让初学阶段能集中精力理解概念而不是跟打字速度搏斗。3.2 从零写一个Book类第 12 天下午我用 “图书馆图书管理模型”当练习。需求控制在 30 分钟内能完成的程度但又能把当天学的类、对象、构造、封装全部串起来。先定义Book类public class Book { private String title; // 书名 private String author; // 作者 private double price; // 价格 private int totalCount; // 库存总册数 private int borrowedCount; // 已借出册数 public Book() {} public Book(String title, String author, double price, int totalCount) { this.title title; this.author author; this.price price; this.totalCount totalCount; this.borrowedCount 0; } public String getTitle() { return title; } public void setTitle(String title) { this.title title; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author author; } public double getPrice() { return price; } public void setPrice(double price) { if (price 0) { System.out.println(价格不能为负数已设置为 0); this.price 0; } else { this.price price; } } public int getTotalCount() { return totalCount; } public void setTotalCount(int totalCount) { this.totalCount totalCount; } public int getBorrowedCount() { return borrowedCount; } public int getAvailableCount() { return totalCount - borrowedCount; } public boolean borrow() { if (getAvailableCount() 0) { System.out.println(《 title 》暂时没有可借的库存了); return false; } borrowedCount; System.out.println(成功借出《 title 》剩余可借 getAvailableCount() 册); return true; } public boolean returnBook() { if (borrowedCount 0) { System.out.println(《 title 》没有可归还的记录); return false; } borrowedCount--; System.out.println(归还成功剩余可借 getAvailableCount() 册); return true; } public void showInfo() { System.out.println(《 title 》 作者 author 价格 price 库存 totalCount 已借出 borrowedCount 可借 getAvailableCount()); } }这个类里用到的知识点基本都是第 12 天的核心private成员变量、两个构造方法、getter/setter、成员方法中调用另一个成员方法比如borrow()内部用getAvailableCount()、在setPrice做参数校验。注意showInfo()方法里输出用了一长串字符串拼接看起来有点笨重。我当时是用直接拼的教程里通常会告诉你更优的方式是用String.format或StringBuilder这个改进点后面单独讲。3.3 对象数组与简单业务逻辑有了Book类再写一个Library类来演示对象数组的定义和使用public class Library { public static void main(String[] args) { // 1. 创建一个长度为3的Book对象数组 Book[] books new Book[3]; // 2. 每个元素都需要逐个 new 出来 books[0] new Book(Java 核心技术卷一, Cay Horstmann, 149.0, 10); books[1] new Book(深入理解 Java 虚拟机, 周志明, 129.0, 6); books[2] new Book(Java 并发编程的艺术, 方腾飞, 99.0, 4); // 3. 遍历对象数组调用每个对象的 showInfo for (int i 0; i books.length; i) { books[i].showInfo(); } // 4. 业务模拟借出和归还 System.out.println(--- 开始借书 ---); books[0].borrow(); books[0].borrow(); books[1].borrow(); System.out.println(--- 当前库存状态 ---); for (int i 0; i books.length; i) { books[i].showInfo(); } System.out.println(--- 归还操作 ---); books[0].returnBook(); } }运行结果大致如下《Java 核心技术卷一》 作者Cay Horstmann 价格149.0 库存10 已借出0 可借10 《深入理解 Java 虚拟机》 作者周志明 价格129.0 库存6 已借出0 可借6 《Java 并发编程的艺术》 作者方腾飞 价格99.0 库存4 已借出0 可借4 --- 开始借书 --- 成功借出《Java 核心技术卷一》剩余可借 9 册 成功借出《Java 核心技术卷一》剩余可借 8 册 成功借出《深入理解 Java 虚拟机》剩余可借 5 册 --- 当前库存状态 --- 《Java 核心技术卷一》 作者Cay Horstmann 价格149.0 库存10 已借出2 可借8 《深入理解 Java 虚拟机》 作者周志明 价格129.0 库存6 已借出1 可借5 《Java 并发编程的艺术》 作者方腾飞 价格99.0 库存4 已借出0 可借4 --- 归还操作 --- 归还成功剩余可借 9 册这个逻辑非常基础但价值在于它第一次让你完整体验了“定义类 → 封装状态 → 对象之间交互接近了 → 模拟真实业务逻辑”的完整链路。借书能成功因为borrowedCount被正确维护库存不足时能拒绝借出因为borrow()里做了判断。这就是面向对象程序设计的好处——业务规则被写进对象自己的方法里main 方法只负责“调度”。这个Book[]对象数组也特别值得多说一句很多新手愣在这里因为new Book[3]只是创建了一个“能放 3 本书的数组”数组里一开始全是null必须对每个元素new一下才算真的把书放进去了。这个null问题在后面做项目时会反复折磨你先用这个例子把概念钉死。4. 新手最容易踩的坑与排查实录4.1 数组越界和空指针的经典现场第 12 天到第 13 天最常见的两个异常可以说是ArrayIndexOutOfBoundsException数组越界和NullPointerException空指针。数组越界很好理解比如int[] arr new int[5]; for (int i 0; i arr.length; i) { System.out.println(arr[i]); }循环到i 5时arr[5]并不存在因为下标范围是 0 ~ 4。排查思路千篇一律先打印一下数组的length再检查循环边界尤其在for (int i 0; i arr.length; i)和for (int i 0; i arr.length - 1; i)之间反复折腾时最容易写混后面那个。空指针在第 12 天有一个高发地带就是对象数组里的每个元素没有newBook[] books new Book[3]; books[0].showInfo(); // 报错NullPointerException刚写完 Book 类很容易犯这个错因为上一节课的数据数组直接赋值就能用比如int[] scores {90, 85, 99};换成对象数组就忘了每个元素得单独new。记住一句话基本类型数组是连续的数值对象数组是连续的“遥控器”遥控器只有指向了真正的对象才能按键。排查空指针的另一类典型场景是方法返回值。比如public Book findBook(String title) { // 没找到时返回了 null return null; }调用方拿到null后又去调用book.showInfo()就会炸。一开始可以把返回值调整成OptionalBook或者至少在方法内和调用处都写下明确的null判断日志。我习惯的口诀是谁可能返回 null谁就必须处理 null不处理 null 的地方就不要出现 null。4.2 String和StringBuilder别再用加号拼循环了第 12 天写showInfo()的时候我用的是这种写法System.out.println(《 title 》 作者 author 价格 price);这种写法在编译后会转换成StringBuilder的append操作所以少量拼接没问题。但如果在循环里大规模拼接问题就大了String result ; for (int i 0; i 10000; i) { result result i ,; }每次循环都会生成一个新的字符串对象旧的字符串被丢弃等于是反复创建、反复复制。数据量一大又慢又占内存。面试题里“String vs StringBuilder vs StringBuffer”就是从这个场景引出来的。规范化写法是StringBuilder sb new StringBuilder(); for (int i 0; i 10000; i) { sb.append(i).append(,); } String result sb.toString();StringBuilder内部是一个可变字符数组append只是在数组后面追加不会每操作一次就新建一个对象。至于StringBuffer它和StringBuilder的 API 基本一样只是加了同步方法线程安全单线程场景下用StringBuilder性能更好多线程场景下再用StringBuffer。这个知识点初级开发面试经常一问一个准。4.3 源发行版17警告到底怎么回事“java: 警告: 源发行版 17 需要目标发行版 17”这条警告在编译环境不匹配时非常高频。我经常看到初学者编译后编译成功了但控制台报一长串警告一脸茫然。这个警告的根源是编译器的--release或 source/target 版本设置和当前 JDK 版本、项目 SDK 版本不一致。比如你电脑上装的是 JDK 17但项目语言级别设置成了 8或者反过来你设置了语言级别 17但用的 IDE 实际解析的 JDK 是 8。在 IDEA 中解决步骤就三步File → Project Structure快捷键 CtrlAltShiftS在Project标签页把SDK选成本机安装的 JDK 17同页把Language level设为 17再检查Settings → Build, Execution, Deployment → Compiler → Java Compiler确保 bytecode version 和项目语言级别保持一致。从命令行直接编译时我建议养成使用--release参数的习惯javac --release 17 Main.java--release N是“目标版本 N并且 JDK N 的 API 可用”比单独设置-source和-target更省心因为-target只控制生成的字节码版本但你还是能用高版本 JDK 才有的 API最后容易把环境搞乱。5. 第12天的面试视角这些八股先混个脸熟5.1 面向对象基础高频题学完第 12 天很多经典的 Java 面试八股题已经有资格看第一眼了。这里不是说让初学者立刻背题而是提前知道“今天学的这些将来考什么”心里有个锚点。这个阶段能听懂、能口头讲清楚的有这几道什么是面向对象和你以前写的面向过程有什么区别什么是类什么是对象它们的关系是什么构造方法能不能重载如果类里只定义了有参构造还能new ClassName()吗private 修饰的成员变量外部能访问吗setter 里到底能干什么额外的事static 修饰的方法能不能直接访问非 static 的成员变量为什么不能重载Overload的核心判断标准是什么前四个题第 12 天的知识储备基本够回答了。第五个题要展开讲讲static的本质static方法是属于类的在类加载阶段就准备好了它不依赖任何对象实例而非 static 的成员变量每个对象都有一份你都没创建对象它哪知道该访问哪个对象的字段所以static方法里不能直接访问非 static 成员如果想访问只能先new一个对象再通过这个对象去访问。第六个题“重载”今天也能说上方法名相同、参数列表不同类型、个数、顺序和返回值类型无关。重载最容易记混的一点是“这两个方法只有返回值不同”这是不允许的因为 Java 区分重载只看方法签名方法名 参数列表返回值不算签名的一部分。你写两个只有返回值不同的同名同参方法编译器直接报错。5.2 后面几天的学习路线怎么安排第 12 天只是面向对象的开始后面还有一大串主线任务。我给当时自己规划的顺序也是现在比较常用的 Java 自学路线图第 13-14 天继承、super关键字、方法重写Override、Object类第 15 天抽象类和接口学会用接口约束行为第 16 天多态理解向上转型和动态绑定第 17-18 天常用 API重点啃String、StringBuilder、Arrays工具类第 19-20 天集合框架ArrayList、LinkedList、HashMap一定要亲手动起来第 21 天包装类、自动装箱拆箱、基本类型和引用类型对比第 22-23 天异常体系会写try-catch-finally和自定义异常第 24-25 天泛型和常用数据结构开始刷简单算法题冒泡排序、二分查找。等这条线走完后Java 基础基本闭环就可以进入 IO 流、多线程、网络编程这些更深的部分。但无论后面的路多长第 12 天这个“类与对象”的关卡都非常关键因为它决定了你对整个 Java 世界的组织方式是否有感觉。如果这一关过得不踏实后面继承、多态、集合那一堆东西会像盖在流沙上的楼越学越晃。这块如果后续有空我再写一篇“Java 第 15 天继承与重写的进阶笔记”把super的细节和重写方法的访问权限限制整理一下。如果现在就急着看建议先重点做两件事一是把Book类的例子自己从零敲一遍二是把“构造方法重载”和“static 方法不能直接访问非 static 字段”这两句话用自己的话写进笔记里。我个人在第 12 天结束时的真实体会是面向对象不是靠看视频看懂的是把Cookie改出十个版本之后突然悟的。第一次写Book类时满脑子“为什么要这么绕”写到第五遍把price、borrowedCount这些字段的校验逻辑塞进setter看到 main 方法因为违反业务规则而自动报错误提示时才真正理解了“封装不是限制是保护”。所以第 12 天不用急着背八股先保证自己能把一个简单的类设计得干净、能用、能扛住非法数据这比什么都重要。
返回列表