ARTICLE DETAIL

资讯详情

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

Java类和对象函数题解析:11道OJ题从入门到熟练

Java类和对象函数题解析:11道OJ题从入门到熟练 SDUT-Java面向对象-05标题里写着“类和对象函数题1-11题”你是不是也正在面对这11道题作为一个在OJ平台上带过不少学生刷题的人我太清楚这种题卡人卡在哪了不是题本身有多难而是很多人压根没弄明白“函数题”的玩法。它和那种让你从零写一个完整程序的编程题不一样题目会先把main函数或者一部分测试代码摆在你面前你只需要按约定好的接口把缺失的类、字段、方法补出来。看起来是在填空实际上考的是你能不能严格按照调用方的要求设计一个类。这篇文章我就把这11道题涉及的类和对象知识配合我踩过的坑、总结的套路一次性讲清楚。1. 先搞懂“函数题”到底在考什么1.1 函数题的提交规则和你想的不太一样很多第一次接触函数题的人都会犯一个同样的问题把题目里给出的完整代码原封不动复制粘贴甚至连public class Main都一起交上去。结果OJ直接给你一个编译错误类名冲突。原因很简单函数题的评测机制是把“题目自带框架”和“你提交的代码片段”合并成一个文件再编译而框架里已经有Main类了你再提交一个Main编译器当然不干了。正确的做法是先看清楚题目要求提交什么。一般题目描述里会有“注意只提交class Xxx的代码”或者“请勿提交main函数”之类的提示。做题时我习惯把“裁判测试程序样例”复制到本地IDE把它当成一个完整项目跑起来等验证通过之后再把属于自己写的类单独复制出来提交。这个流程虽然多了一步复制粘贴但能避开八成以上的低级提交错误。1.2 为什么出题人偏爱函数题函数题和编程题最大的区别在于编程题让你掌控全过程从输入到输出全是你的代码函数题则只让你实现一个“零件”零件之外的调度逻辑早就写好了。这种设计有几个明显的好处。第一评测稳定。题目方把输入输出逻辑锁死在框架里你只需要保证类本身的正确性不容易出现“整体逻辑对但输出格式飘了”的争议。第二考查知识点更聚焦。类和对象这一章的重点就是类的定义、构造方法、封装、对象使用如果让考生写完整程序很容易被Scanner、循环这些边角料分散注意力函数题刚好把火力集中在核心概念上。第三贴合真实开发场景。你在公司里写代码极少从零开发整个系统更多是“别人定义好了接口你来实现”。函数题就是在模拟这种协作关系。1.3 拿到函数题第一步永远是“读框架”不是“写类”我见过太多同学看到题目里有个“定义一个矩形类”就兴奋地埋头写写了二十行一提交全错。为什么因为题目框架里调用的是new Rectangle(4.5, 7.8)但你把构造方法写成了Rectangle(double length, double width)参数名无所谓可你如果把构造方法签名中的参数类型或数量写错框架根本调不到你的构造方法。所以拿到任何一道函数题正确操作顺序是先找到“裁判测试程序样例”这段代码一字一句读完。从main方法的调用语句反推需要定义哪些类构造方法接收什么参数方法名是什么方法返回什么类型把这些“接口信息”用注释写在代码里然后再填充方法体。这个习惯看起来简单实战中能救命。方法签名只要错一个字母整个程序直接编译失败而且报错信息经常指向main方法内部新手根本联想不到是自己定义的类出了问题。2. 11道题背后的知识地图从“定义类”到“对象数组”2.1 前几题类骨架、成员变量、无参构造这套题从1到11并不是随便排的出题人有非常明确的递进设计。前面几道题通常只要求你定义一个最简单的类字段类型可能是double、int、String再配一个无参构造方法和几个基础的getter/setter。这部分只要把握住三点就不会丢分类名和题目要求完全一致、字段名字不能随意改、getter/setter命名遵循驼峰规则。这里有个新手最容易忽略的细节无参构造方法里的默认值。题目如果要求“创建一个对象不传参数时默认宽度为1.0”很多人会想当然地在定义字段时写private double width 1.0这样做虽然也能得到正确结果但出题人真正想考的是你知不知道在无参构造方法里给字段赋初始值。两种写法结果看似一样背后的设计思路差距很大。建议养成“默认值在构造方法里设置”的习惯因为字段初始化语句只负责声明而构造方法才是对象创建时真正执行逻辑的地方。2.2 中间几题构造方法重载与this关键字大概从第4道题开始难度会上一个台阶。题目会要求一个类同时具备无参构造和有参构造这时候就涉及两个高频考点构造方法重载和this关键字。先记住一个铁律构造方法绝对不能写void。public Rectangle(double w, double h)是构造方法public void Rectangle(double w, double h)只是一个普通方法。你一旦写了voidnew Rectangle(...)时就找不到对应的构造方法编译直接报错。这个错误出现的频率极高很多人半天找不到原因。this有两种用法单靠概念记忆很容易混。第一种是this.成员变量用来区分形参和成员变量比如this.width width左边的width是对象的字段右边的width是方法参数没有this的话width width只是自己给自己赋值对象的字段永远是0。第二种是this(...)用于在另一个构造方法里调用本类其他构造方法但这条调用语句必须放在构造方法的第一行而且不能写成一个循环调用比如无参构造调用有参构造、有参构造又调用无参构造这种死循环代码编译都不会让你过。2.3 后面几题封装、static与对象数组的组合拳到了第7到第11题知识点的密度就上来了。你会看到题目把封装、static、对象数组、toString这几个点串在一起考。这类综合题表面上代码量不大实际上每一步都在挖坑。封装这一步要求你把所有字段改成private对外只暴露getter和setter。注意setter不只是“把一个值塞进去”更准确地说是一个“检查后才赋值”的入口。比如题目要求年龄不能为负数setter里就应该做一次判断非法数据直接忽略或者置为0。static这个关键字很多人知道它是“静态的”但不知道它和对象的关系。static成员属于类本身就算一个对象都不创建也能访问。反过来static方法内部不能直接访问非static的成员变量因为static方法执行时可能根本没有对象存在你让它去访问“某个对象的字段”它连对象都找不到所以编译期就会报“无法从静态上下文引用非静态变量”。这个坑在函数题里出现得很多因为框架的main方法就是static的如果你在类里写了static方法又去访问普通字段很容易触发这个错误。对象数组是另一个经典坑点。Student[] arr new Student[3]这句话的本质是“创建一个能存放3个Student引用的数组容器”而不是“创建3个Student对象”。数组创建完毕后三个位置全是null如果你直接调用arr[0].getName()运行时会抛NullPointerException。这个异常信息只有短短一行对新手极不友好。解决办法就是每次使用前先arr[i] new Student(...)或者在声明的时候直接用花括号初始化。2.4 用“图纸和房子”理解类和对象聊点抽象的东西。类和对象这两个概念用“图纸和房子”来想最省力类就是图纸规定了房子有几个房间、门窗位置、屋顶坡度对象就是按图纸盖出来的具体房子。你手里有一张图纸想盖多少栋房子就盖多少栋每栋房子都是独立的住的人可以不同水电表读数也不同这就是“同一个类可以创建多个不同对象”的含义。Java在内存中也是这样工作的。new关键字负责按“图纸”造房子造好的房子放在堆内存里而代码中的引用变量比如Rectangle rect保存的只是房子的地址不是房子本身。理解了这一点后面学到引用传递、equals方法、深拷贝浅拷贝都是水到渠成的事。3. 核心代码怎么写从class声明到方法覆写3.1 一个可以直接抄的“标准类模板”刷了这么多函数题我发现绝大多数题目都能套用一个标准类模板。拿矩形类举例class Rectangle { private double width; private double height; public Rectangle() { this(1.0, 1.0); } public Rectangle(double width, double height) { this.width width; this.height height; } public double getWidth() { return width; } public void setWidth(double width) { this.width width; } public double getHeight() { return height; } public void setHeight(double height) { this.height height; } public double getArea() { return width * height; } }这个模板的核心思路是字段全部private构造方法提供重载无参构造复用有参构造对外提供getter/setter业务方法单独写。根据题目要求你只需要替换类名、字段名和方法逻辑即可。需要注意如果你只是在测试程序里读取字段有些getter可以不写但为了安全最好把题目框架里调用过的方法全部实现出来一个都不能少。3.2 方法重载同一名字不同参数表Java允许你在同一个类里定义多个同名方法只要参数列表不同这叫方法重载。构造方法重载是重载的一种特殊情况。这里必须强调一个常见误区返回值类型不能作为区分重载的依据。public int getValue()和public double getValue()绝对不能共存编译器会认为你重复定义了相同的方法。只有参数个数、顺序、类型不同才算重载。实际做题时方法重载最常见的用途是给使用者提供几种不同的初始化方式。比如学生类可以提供一个Student()无参构造再提供一个Student(String name, int age)带参构造前者调用后者并传入默认值。这样写出来的代码调用方用起来非常舒服这正是面向对象“对外友好”的体现。3.3 toString和equals建议主动覆写几乎每一套类和对象题里都会遇到输出对象信息的需求。很多人直接写一个show()方法在类里打印这本身没错但当测试框架用了System.out.println(obj)这种写法时你就必须知道println会自动调用对象的toString()方法。Object类自带的toString返回的是“类名十六进制哈希码”比如Rectangle6bc168e5这显然不是题目想要的输出格式。正确做法是在类中覆写toStringOverride public String toString() { return String.format(width:%.1f,height:%.1f, width, height); }覆写时注意两点方法签名必须是public String toString()前面的Override注解可以写可以省略但建议写上能帮编译器检查签名。另外toString方法里不要直接println而是返回拼接好的字符串把打印的职责交给调用方。equals方法同理。Object的equals比较的是引用地址不是内容。如果题目需要判断两个对象是否相同比如“判断两个点是否重合”就必须覆写equals。但注意有些题目并没有这个需求别画蛇添足写一个不必要的equals增加出错概率。看清楚main方法里有没有用equals或者去比较你自己写的对象再决定动不动手。3.4 对象数组完整的三步走写法对象数组是类与对象章节的压轴考点我建议严格按照三步来写少一步都会出问题// 第一步创建数组容器 Student[] students new Student[3]; // 第二步为每个数组元素创建对象 students[0] new Student(张三, 20); students[1] new Student(李四, 21); students[2] new Student(王五, 19); // 第三步遍历访问 for (Student s : students) { System.out.println(s.toString()); }第二步就是新手最容易漏掉的。在学基本类型数组时int[] arr new int[3]之后数组元素是0可以直接用但对象数组不一样里面的每个元素都是引用类型的默认值null。只有new出来对象并赋值给元素之后这个位置才真正有东西可访问。有一种更简洁的写法是用花括号初始化Student[] students { new Student(张三, 20), new Student(李四, 21) };这种写法在功能上完全等价而且代码更短。如果题目规定了数组长度和初始值直接套这种写法很方便。4. 提交代码前的自查清单与典型报错4.1 十连坑对照表编译错误速查我在辅导过程中整理了函数题里出现频率最高的十个错误做成一张速查表遇到报错直接对照报错现象根本原因解决办法找不到符号: class Rectangle类名拼写或大小写不一致对照框架里的new语句逐字检查类名找不到符号: 方法 getarea()方法名大小写/拼写错误Java方法名区分大小写getArea不等于getareaRectangle() 已经在类中定义构造方法误加了void构造方法不能有任何返回类型去掉void需要.而不是(this()写在了构造方法非第一行把this(...)移到构造方法第一句无法从静态上下文引用非静态变量static方法里直接访问了成员变量将字段改为static或把方法改为非staticNullPointerException对象数组元素未逐个new先初始化数组元素再访问对象公共类型Rectangle必须在自己文件中声明提交代码里包含了public class提交时去掉public或者删掉整个Main类缺少返回语句声明了非void返回类型但没写return给所有分支添加return语句类Rect是公共的应在Rect.java中声明与文件名不匹配的public类去掉类前面的public输出结果多了或少了内容打印了多余信息或漏了字段严格比对输出样例逐字符检查这些错误里面最让人无语的是最后一个输出格式问题。程序逻辑完全正确但多打了一个空格、把英文括号写成了中文括号、或者println和print没分清OJ都会判错。我每次提交前都比对输出样例连末尾有没有空格都注意别嫌麻烦这个习惯能帮你省下很多冤枉的提交次数。4.2 三找法快速锁定题目需要你写的类我总结了一个“三找法”专门用来对付函数题。做题前先花30秒做三件事第一找找类名。看裁判测试程序里都new了哪些类这些类名就是你必须定义的类。第二找找构造方法。看new的时候传了几个参数参数是什么类型比如new Rectangle(1.0, 2.0)说明至少需要一个两个double参数的构造方法。第三找找方法调用。看框架调用了什么方法比如System.out.println(rect.getArea())说明需要getArea方法if (p.equals(q))说明可能需要覆写equals的类。把这三项用注释写在代码头部等于给自己画了一张路线图。后面实现类时就不用猜只需要填空。这个方法同样适用于未来面试手写代码场景按接口倒推实现方向永远是对的。4.3 本地调试别在OJ答案框里直接写大段代码我强烈不建议直接在网站上那个小小的提交框里写代码。缩进混乱、无法实时调试、报错信息不直观新手体验极差。我的做法是把“裁判测试程序样例”复制到本地IDE如果你写的类是独立的就把它们放一个包或者一个文件里跑一遍。注意一个文件里最多只能有一个public类所以运行时需要把框架里的Main类前的public去掉或者干脆不用public修饰类。跑通之后把你写的类片段复制回OJ提交框。这时如果仍然报错多数情况下是提交框里混入了测试代码造成的——比如不小心把main方法也带上了或者把import语句放在类里面。提交前再扫一眼确保只提交题目要求的那一段。5. 刷完这11道题我总结的几条实操心得5.1 别按1到11的顺序死磕按难度跳着做不少同学做题有个强迫症必须按编号顺序一道做不出来就卡在那里后面的题全都不看。实际上这套题的难度并不是严格递增的中间时不时会插一个比较综合的题再跟一个很基础的题。我的建议是先快速扫一遍所有题从最简单的开始做把能拿的分先拿到再回头啃硬骨头。这样也不会因为一道题卡太久而心态崩掉而且更奇妙的是你做后面题目时经常会遇到“原来前面那题用这个知识点能解”的顿悟时刻。刷题是为了掌握知识不是为了一路绿灯。5.2 每道题做完多问自己三个问题一道题拿到满分只是开始。我会再问自己三遍这个类能不能去掉setter如果字段一旦创建就不该改setter是不是写多了构造方法能不能少写一个无参构造和带参构造是不是有重复代码如果测试数据多一组边界值我的代码会不会崩比如宽度为0甚至负数面积算出来是负数题目允不允许这些问题不会让OJ多给你分但会让你的代码从“能过测试”变成“像正常人写的代码”。如果你是把这套题当成期末考试的练习题那就更要这样做因为考试题往往就喜欢在边界值上做文章。5.3 类和对象不是“语法知识”是“设计习惯”最后聊点题外话。我见过很多同学把类和对象这一章当语法知识背天天背“类是抽象对象是具体”“封装是隐藏细节”这些概念但一写代码就露馅。其实类与对象的核心思想就一句话把数据和操作绑在一起并且通过接口对外暴露行为。你想想你每天用的Scanner、ArrayList哪一个不是别人设计好的类你new一个Scanner对象调它的nextInt()方法这本身就是在使用对象。从“使用别人写的类”到“自己写一个类被别人使用”这个身份转变才是这11道函数题真正要教会你的东西。如果你现在还在和sdut-Java面向对象-05这11道题死磕我的建议很简单先别急着写代码把每一题的裁判测试程序读到几乎能背下来再动手。我当年也是被构造方法重载折磨得不行后来发现根本不是自己不会写而是没看清调用方到底要什么。等你把题刷完回头看类、对象、封装这几个概念就像你用惯了的筷子一样自然考场上看到“定义一个类”的题闭着眼都能把框架搭出来。
返回列表