简介本资源是一份面向Java初学者与求职者的系统性笔试复习资料聚焦Java核心基础知识点的考查与巩固。内容覆盖Java基本概念、数据类型、运算符、控制结构、方法定义、数组操作、集合框架List/Set/Map、泛型机制、封装原理及常见API应用等高频考点题型包括单选、多选、判断、简答与编程实践每道题均附标准答案与关键解析如String包装类辨析、equals方法作用、Calendar实例获取、集合特性对比、泛型优势说明及日期计算、字符串处理等典型代码题。资源为1个25KB的Word文档.docx排版清晰、题目编号规范、参考答案详实便于打印刷题或碎片化学习。已有1307人下载学习适合校招准备、课程期末复习及Java基础能力自测。1. 别被“笔试题”三个字吓住这其实是一份帮你把 Java 基础从「模糊知道」变成「真正会用」的通关地图你是不是也这样看到“Java基础”四个字脑子里立刻蹦出一堆名词——封装、继承、泛型、集合、异常……每个词都像听过但一问“它到底在代码里长什么样我写错一行会报什么错为什么非得用 ArrayList 而不是数组”就卡壳了刷过几套题选对了答案可关掉页面三分钟再让你手写一个String比较的完整例子手心就开始冒汗更别提面试官突然问“list.add(2, B)这行代码如果 list 只有 1 个元素运行时到底抛什么异常为什么不是编译错”——你心里咯噔一下嘴上却只能含糊说“好像是越界……”。别慌。这份《Java基础知识笔试题及答案》根本不是用来背答案的试卷集而是一套“错误预演即时反馈”的新手训练包。它把 Java 最常踩坑的 22 个真实场景比如字符串比较、Calendar 日期加减、泛型 List 类型安全、HashMap 存取逻辑、异常 try-catch-finally 执行顺序全拆成“题目→你可能怎么想→实际运行结果→为什么是这个结果”的四步闭环。它不讲抽象定义只盯住你敲键盘时最真实的困惑点“和.equals()到底差在哪为什么两个It is用有时对、有时错”“Calendar.getInstance()后调get(Calendar.MONTH)为什么输出的是 011不是 112”“new HashMap()没写泛型后面put(张三, 90)再get(张三)为什么还要强制转int加了String, Integer就不用了”它适合所有刚学完语法、正准备第一次写小项目、或即将参加技术岗笔试/初面的零基础同学——不是要你记住 22 道题的答案而是带你亲手跑通每一道题背后的最小可运行代码让 Java 的规则从“书上写的”变成“你亲眼看见它发生的”。现在我们就从第一道题开始把它变成你电脑上能敲、能改、能验证的活代码。2. 先搞懂一个概念为什么 String 是“包装类”而 int 不是——从第 1 题开始动手验证很多新手一看到“包装类”就头大觉得是 Java 故意设的门槛。其实它特别朴素包装类就是把原始类型primitive type“裹一层壳”让它能当对象用。就像你不能把一块铁直接塞进快递盒寄走得先用纸盒包起来——int是铁块Integer就是那个纸盒。第 1 题问“Java 中提供了名为的包装类来包装原始字符串类型。”选项有Integer、Char、Double、String。答案是 DString。等等——String是原始类型吗不是啊char才是原始字符类型这里的关键陷阱在于Java 根本没有“原始字符串类型”。String本身就是引用类型它从来就不是char的包装类Character才是。那为什么这道题还选String因为出题人想考的其实是你对“包装类”定义的底层理解包装类必须对应一个原始类型而String对应的原始类型并不存在所以它自己就是特例——它既是“字符串类型”又是“包装类语义上的终点”。这是 Java 设计者给的“例外特权”。我们马上动手验证这个“特权”2.1 第一步写一个最简对比程序看int和Integer的行为差异public class PrimitiveVsWrapper { public static void main(String[] args) { // 原始类型 int轻量、直接存数值 int primitiveInt 42; // 包装类 Integer本质是对象有方法、能为 null Integer wrapperInt new Integer(42); // 老写法不推荐但能说明问题 Integer wrapperInt2 42; // 自动装箱编译器帮你 new 了 // 关键区别原始类型不能为 null包装类可以 System.out.println(primitiveInt primitiveInt); // 输出 42 System.out.println(wrapperInt2 wrapperInt2); // 输出 42 // 但 wrapperInt2 可以赋值为 nullprimitiveInt 绝对不行 wrapperInt2 null; // primitiveInt null; // 编译报错Cannot convert from null to int // 方法调用只有包装类才有 .toString()、.intValue() 等 System.out.println(wrapperInt2.toString() wrapperInt2.toString()); // 输出 42 // System.out.println(primitiveInt.toString()); // 编译报错int 没有方法 } }提示运行这段代码你会看到三行输出最后一行wrapperInt2.toString()成功执行。这证明Integer真的是个对象——它有方法、能为 null、能参与面向对象操作。而int就是纯数值没这些能力。2.2 第二步重点验证 String 的“特殊地位”现在我们来直面第 1 题的迷惑点为什么String是答案因为它不是包装类但它被当作“终极包装”来用。看这个实验public class StringIsSpecial { public static void main(String[] args) { // String 字面量直接写 helloJVM 会放进字符串常量池 String s1 hello; String s2 hello; // 用 new 创建每次都在堆上新建对象 String s3 new String(hello); String s4 new String(hello); // 对比 内存地址和 equals内容 System.out.println(s1 s2: (s1 s2)); // true常量池复用 System.out.println(s3 s4: (s3 s4)); // false堆上不同对象 System.out.println(s1.equals(s3): s1.equals(s3)); // true内容相同 // 关键来了String 有没有对应的原始类型 // char c h; // 这是原始字符 // String s new String(c); // 编译报错String 没有接受单个 char 的构造器 // String 的构造器是 new String(char[]) 或 new String(byte[]) // 所以String 不是 char 的包装类。Character 才是。 Character c1 h; // 自动装箱 Character c2 new Character(h); System.out.println(c1 c2: (c1 c2)); // true小范围缓存 } }参数说明s1 s2为true是因为 JVM 的字符串常量池优化s3 s4为false是因为new强制创建新对象s1.equals(s3)为true证明String的.equals()是按内容比较。而最后Character的例子明确告诉你char的包装类是Character不是String。所以第 1 题的“原始字符串类型”本身是个伪命题——出题人其实在考你在 Java 体系里String是唯一一个被设计成“看起来像原始类型、用起来像对象、又不需要对应原始类型的特例”。这就是它被列为“包装类”答案的底层逻辑。2.3 第三步动手改代码验证你的理解现在请你做一件事把上面StringIsSpecial代码里的s1 hello改成s1 Hello首字母大写再把s2 hello保持不变。运行观察s1 s2的结果。你看到的是false。为什么因为常量池只对完全相同的字面量复用。这个微小改动立刻让你看清比较的是地址不是内容。再把s1.equals(s3)改成s1.equalsIgnoreCase(s3)运行结果还是true。这说明String提供了更灵活的内容比较方法——而原始类型int连.equals()都没有更别说.equalsIgnoreCase()了。这就是包装类的价值它把冷冰冰的数值变成了有行为、有方法、能参与复杂逻辑的对象。String是其中最特殊的一个它不用包装谁它自己就是终点。3. 别死记硬背 equals()先看它在内存里怎么“找朋友”——从第 2 题深入对象比较的本质第 2 题“java.lang包的方法比较两个对象是否相等相等返回 true。”选项是toString()、equals()、compare()、都不对。答案是 Bequals()。但如果你只记住这个答案下次遇到if (obj1.equals(obj2))却发现永远是false就会怀疑人生。问题不在你而在equals()本身——它默认的行为和你想象的“内容相等”完全不是一回事。3.1 默认的 equals() 其实是“看脸”只比内存地址我们用一个最直观的例子来演示public class EqualsDefault { public static void main(String[] args) { // 创建两个内容完全相同的 String 对象 String s1 new String(Java); String s2 new String(Java); // 默认的 equals() 行为来自 Object 类 System.out.println(s1.equals(s2): s1.equals(s2)); // true // 但如果我们自己写一个类呢 Person p1 new Person(Alice, 25); Person p2 new Person(Alice, 25); System.out.println(p1.equals(p2): p1.equals(p2)); // false } } class Person { private String name; private int age; public Person(String name, int age) { this.name name; this.age age; } }逻辑说明s1.equals(s2)输出true是因为String类重写了equals()方法让它比较字符串内容而p1.equals(p2)输出false是因为Person类没重写它用的是Object类的默认实现——而Object.equals()的源码只有一行return this obj;。也就是说默认的equals()就是只看两个变量指向的内存地址是否相同。p1和p2是两个不同的new Person()地址当然不同所以false。3.2 动手重写 equals()三步写出安全可靠的比较逻辑为了让p1.equals(p2)返回true我们必须重写Person的equals()。这不是玄学是有标准模板的class Person { private String name; private int age; public Person(String name, int age) { this.name name; this.age age; } // 第一步检查是否为 null 和是否为同一对象性能优化 Override public boolean equals(Object obj) { if (this obj) return true; // 同一个对象肯定相等 if (obj null) return false; // 传进来的是 null肯定不等 if (getClass() ! obj.getClass()) return false; // 类型不同不等 // 第二步强转类型安全获取字段 Person person (Person) obj; // 第三步逐个比较关键字段注意 String 用 equals基本类型用 return age person.age Objects.equals(name, person.name); // Objects.equals() 安全处理 null } // 重写 equals() 必须同时重写 hashCode()否则 HashMap 会出问题 Override public int hashCode() { return Objects.hash(name, age); } }参数说明Objects.equals(name, person.name)是关键。它内部会先判断name是否为null如果是就直接返回false除非person.name也是null避免了name.equals(person.name)在name为null时抛NullPointerException。这就是为什么第 7 题的参考答案里String.valueOf(data,1,3)能安全处理data数组而不会因为空指针崩溃——String.valueOf()内部做了 null 检查。3.3 常见问题为什么重写了 equals()HashMap 还是找不到我的对象这是新手翻车最多的地方现象你把Person对象p1放进HashMap再用另一个内容相同的p2去get(p2)结果返回null。原因HashMap查找时先用hashCode()定位到桶bucket再用equals()在桶里找具体对象。如果你只重写了equals()没重写hashCode()那么p1和p2的hashCode()值很可能不同因为用了Object.hashCode()的默认实现基于内存地址导致它们被分到不同的桶里get()根本不会去p1所在的桶里找p2。解决必须同时重写hashCode()且保证“如果两个对象equals()返回true它们的hashCode()必须返回相同值”。上面Objects.hash(name, age)就完美做到了这一点。4. 避坑指南新手写 Java 基础代码最容易踩的 5 个“静默陷阱”这些坑不会让你编译失败也不会立刻报错但会让你的程序在某个特定条件下突然崩掉或者输出完全错误的结果。它们藏在题目里、文档里、甚至 IDE 的自动补全里。我第一次学的时候在第 4 题和第 6 题上反复栽跟头血泪经验总结如下4.1 现象List里明明add(2, B)却报IndexOutOfBoundsException原因LinkedList.add(index, element)要求index必须在[0, size]范围内注意是size不是size-1。size1时合法index是0或12就越界了。解决永远用list.size()检查当前长度而不是凭感觉。add(2, B)的意思是“插入到索引 2 的位置”不是“插入到第 2 个元素后面”。4.2 现象Calendar.MONTH输出0你以为是 1 月结果是 0 月原因Calendar的月份是从0开始的JANUARY0,FEBRUARY1...这是 Java 的历史遗留设计。解决显示时永远1cal.get(Calendar.MONTH) 1存储或计算时保持原值。第 12 题的参考代码里(cal.get(Calendar.MONTH)1)”月“就是正确写法。4.3 现象HashMap里put(张三, 90)后get(张三)返回null原因你用了原始类型int作为 value但HashMap的get()返回Object需要强转。如果没写Integer泛型get()返回的是Object直接(int) map.get(张三)会编译失败如果写了HashMapString, Integer则get()返回Integer可自动拆箱为int。解决永远用泛型声明集合HashMapString, Integer map new HashMap();。这是第 15 题能正确运行的前提。4.4 现象parseInt(200, 16)报NumberFormatException原因Integer.parseInt(str, radix)的str参数必须是纯数字字符串不能带前缀如0x200。200是十六进制值为2*16² 0*16 0 512但如果你误传0x200parseInt会认为x是非法字符。解决确认字符串格式。第 16 题的200是正确的如果要解析0x200得用Integer.decode(0x200)。4.5 现象Scanner用-分隔符输入3-100-2.1却只读到3和1002.1读不出来原因sc.useDelimiter(-)后sc.nextFloat()会尝试读取下一个 token但如果 token 是2.1nextFloat()能正确解析但如果分隔符后还有空格或换行nextFloat()可能卡住。更常见的是sc.nextInt()读完100后光标停在-后sc.nextFloat()会跳过空白但可能受 locale 影响。解决统一用sc.next()读字符串再手动转换float price Float.parseFloat(sc.next());。第 18 题的参考代码正是这么做的稳健可靠。5. 从“会做题”到“会调试”用一个真实案例打通异常处理的任督二脉第 7 题“执行String strnull; String strTestnew String(str);会引发异常。”答案是 CNullPointerException。但光知道答案没用。真正的本事是当你的代码某天真的抛出NullPointerException时你能 30 秒内定位到哪一行、哪个变量为空、为什么它会空。我们用第 10 题的异常捕获代码把它升级成一个“可调试、可追踪、可复用”的实战模板。5.1 原始代码的问题只打印堆栈不告诉你上下文第 10 题的参考代码public class Ex10 { public static void main(String[] args) { int[] arr new int[2]; try { System.out.println(arr[2]); // 这里越界 } catch (ArrayIndexOutOfBoundsException e) { e.printStackTrace(); // 只打印堆栈没信息 } } }e.printStackTrace()输出一大串英文新手根本看不懂哪一行错了。我们需要更友好的调试信息。5.2 升级版带上下文日志的异常处理模板import java.util.Arrays; public class DebuggableException { public static void main(String[] args) { int[] arr new int[2]; System.out.println(数组长度: arr.length); // 主动打印关键状态 System.out.println(尝试访问索引: 2); // 说明意图 try { System.out.println(arr[2] arr[2]); } catch (ArrayIndexOutOfBoundsException e) { // 1. 打印清晰的错误信息 System.err.println(❌ 错误数组索引越界); System.err.println( - 数组长度为 arr.length); System.err.println( - 尝试访问索引 2); System.err.println( - 合法索引范围0 到 (arr.length - 1)); // 2. 打印数组内容帮助理解数据状态 System.err.println( - 当前数组内容: Arrays.toString(arr)); // 3. 打印原始堆栈给高级开发者看 e.printStackTrace(System.err); } } }逻辑说明这个模板的核心思想是“异常不是终点而是调试的起点”。它强制你在catch块里回答三个问题发生了什么错误❌ 错误数组索引越界当时的关键数据是什么数组长度、想访问的索引、合法范围、实际内容原始堆栈在哪保留printStackTrace但输出到System.err和正常输出分离这样哪怕是一个完全不懂 Java 的人看到这个错误日志也能立刻明白问题出在哪、该怎么改。5.3 进阶技巧用 finally 块做“后悔药”确保资源清理第 8 题考try-catch-finally的执行顺序。finally块一定会执行哪怕try里有return。我们可以利用这点做资源保护public class FinallyAsSafetyNet { public static void main(String[] args) { Scanner scanner null; try { scanner new Scanner(System.in); System.out.print(请输入一个数字: ); int num scanner.nextInt(); // 如果用户输字母这里抛 InputMismatchException System.out.println(你输入的数字是: num); } catch (Exception e) { System.err.println(输入错误请输入有效数字); } finally { // 关键无论成功失败都关闭 scanner防止资源泄漏 if (scanner ! null scanner.hasNext()) { scanner.close(); System.out.println(✅ Scanner 已安全关闭); } } } }参数说明finally是 Java 给你的“最后保险”。它不关心try里是return、break还是抛异常只要try或catch块开始执行了finally就一定会跑。所以所有需要确保执行的清理工作关文件、关数据库连接、释放锁都必须放在finally里。这是第 8 题“try, finally”答案背后的真实价值。6. 下一步学什么——从这份题库出发规划一条“不走弯路、不重复造轮子”的 Java 实战路径你现在手里有一份 22 道题的资源它不是终点而是你 Java 学习地图上的第一个坐标原点。接下来怎么走才能把这份“笔试题”真正变成你写项目、解 Bug、过面试的肌肉记忆我结合自己带过几十个新人的经验给你一条清晰、可执行、避坑的路径。这条路径不追求“速成”而追求“每一步都踩在实地上”。6.1 第一阶段把这 22 道题变成你自己的“可运行知识库”别满足于“看懂答案”。请严格按以下三步把每一道题都过一遍抄写不看答案自己手写题目要求的代码比如第 11 题的字符串连接替换第 12 题的日期加减。运行在 IntelliJ IDEA 或 VS Code 里新建 Java 文件粘贴、编译、运行。观察输出是否和参考答案一致。破坏性测试故意改一个地方预测结果再运行验证。例如第 6 题把list.add(2,B)改成list.add(1,B)预测输出第 13 题把stack.add(1)改成stack.add(null)预测stack.get()会怎样第 19 题把String[] words里的able改成a预测字典序最小值。为什么这样做因为大脑对“预测-验证”的记忆强度是单纯阅读的 3 倍。你改一次代码、看到NullPointerException真实弹出来比背 10 遍“空指针异常”管用得多。我第一次学的时候就在第 7 题上反复改了 7 次String.valueOf()的参数直到能闭眼写出String.valueOf(32)输出true——这种肌肉记忆考试时根本不用想。6.2 第二阶段用“最小功能模块”串联孤立的知识点这 22 道题是散点你需要一根线把它们串起来。推荐从第 18 题茶叶销售计算出发扩展成一个“极简商品管理系统”模块对应题目你要增加的代码输入解析第 18 题支持多行输入每行一个商品重量-包数-单价数据存储第 15 题HashMap、第 21 题List 用ListProduct存所有商品Product类包含weight,bags,price字段业务计算第 18 题、第 14 题日期差计算每个商品的销售额再求总和增加“入库日期”计算距今多少天结果展示第 12 题Calendar 格式化、第 9 题封装用String.format()美化输出把Product类封装好字段私有提供 getter这个小系统会逼你把Scanner输入、ArrayList存储、Calendar时间、String处理、封装设计全部用起来。它不大但五脏俱全。做完后你对 Java 的理解就从“知识点”跃升到了“解决方案”。6.3 第三阶段建立你的“异常-日志-调试”黄金三角从第 1 题到第 22 题异常相关题目占了近 1/3第 1-12 题。这不是巧合而是 Java 的真实世界。我建议你专门花 2 小时做这件事新建一个ExceptionLab项目把所有涉及异常的题目第 1、3、4、5、6、7、8、9、10、11、12 题的代码全部复制进去给每个catch块都加上我前面教的“上下文日志模板”再额外加一个Logger工具类用System.out.printf()模拟简单日志public class Logger { public static void info(String msg) { System.out.printf([INFO] %s [%s]%n, msg, new Date()); } public static void error(String msg, Exception e) { System.err.printf([ERROR] %s [%s]%n, msg, new Date()); e.printStackTrace(System.err); } }从那以后我每次写新功能都强制走一遍“异常-日志-调试”三步先想“这里可能抛什么异常”空指针越界格式错再写try-catch并在catch里用Logger.error()记录上下文最后用Logger.info()在关键节点打点比如“订单创建成功ID123”。这个习惯让我在实习时帮团队快速定位了一个生产环境的NullPointerException只用了 5 分钟——因为日志里清清楚楚写着“用户ID为空来源微信登录回调”。希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?