Android校招笔试客观题考点全解析:从Java基础到内存优化 📅 发布时间:2026/8/31 10:49:11 👁 浏览次数: 360公司2019校招笔试中Android开发工程师岗位的客观题我当年也参加过类似的笔试。这类笔试的客观题部分通常涵盖Java基础、Android核心组件、线程与进程管理、网络协议、数据结构与算法等多个知识域题目覆盖面广、考察点细致非常考验基本功是否扎实。很多人在准备校招笔试时会陷入一个误区光刷题、背答案却不理解知识点背后的运行机制和设计考量。结果就是碰到换个问法的题目就懵了。这篇文章会把这类笔试中最常出现的考点进行系统拆解结合真实场景说明每个知识点为什么重要、怎么考、怎么答希望帮助正在准备Android校招的同学少走弯路。1. 客观题考点全景笔试到底在筛选什么1.1 考察范围的四个维度360这类公司的Android开发笔试客观题看似随机其实考察范围非常稳定。我把近几年的题目做了归类基本逃不出四个维度。第一个维度是Java语言基础占比通常在30%以上。集合框架、并发编程、JVM内存模型、异常处理、泛型、反射这些是重灾区。很多Android开发同学觉得Java基础就是大学里学的那些实际上笔试考察的深度远超课本内容。比如HashMap在JDK 7和JDK 8中的实现差异、ConcurrentHashMap如何保证线程安全、volatile关键字的内存语义这些在笔试中出现频率极高。第二个维度是Android核心知识占比在40%左右。Activity的启动模式、生命周期在不同场景下的回调顺序、Fragment与Activity的通信方式、Service的两种启动方式及其区别、BroadcastReceiver的动态注册与静态注册差异、ContentProvider的权限管理、Handler的消息机制原理、View的绘制流程与事件分发机制这些是Android开发者的基本功也是笔试必考内容。第三个维度是计算机基础知识占比约20%。数据结构中的链表反转、二叉树遍历、栈和队列的应用场景算法中的排序和查找网络中的TCP三次握手四次挥手、HTTP与HTTPS的区别、TCP与UDP的区别操作系统中的进程与线程、死锁条件、虚拟内存等。这些知识虽然在日常Android开发中不会直接用到但它们是区分科班出身和半路出家的重要标准。第四个维度是逻辑推理和智力题占比约10%。这类题目主要考察思维灵活性和临场反应能力。常见的有排列组合问题、概率计算、逻辑推理偶尔会出现一些脑筋急转弯类型的题目。360的笔试中这类题目的比例不算高但一旦出现往往会让很多人卡住。1.2 客观题的命题逻辑和答题陷阱校招笔试客观题的命题逻辑有一个核心特征就是看似简单实则挖坑。出题人会在几个地方设置陷阱。第一种陷阱是概念混淆型。例如考察Activity的onSaveInstanceState方法的调用时机很多人记得在onStop之前调用却忽略了在onPause之后这个前置条件。实际上onSaveInstanceState在Android P之前是在onStop之前调用的但在Android P及以后它的调用时机调整到了onStop之后这是一个典型的版本差异陷阱。第二种陷阱是条件遗漏型。比如考察Handler的用法时出题人可能会设置一个在主线程中创建Handler的场景而实际上Handler在任何线程中都可以创建只是需要通过Looper.prepare()和Looper.loop()来维护消息队列。如果忽略了默认主线程有Looper这个隐含条件就很容易做错。第三种陷阱是答案绝对化。很多题目会设置以下说法正确的是以下说法错误的是这种题型选项中经常出现一定必须只能这类绝对化词汇这些选项大概率是错误的。而出现通常一般可能这类相对化词汇的描述往往是对的。这不是玄学而是由技术实现决定的任何技术方案都有取舍和边界条件。1.3 从2019年真题反推的备考重点以360公司2019年的Android开发工程师笔试为例从考后流出的真题回忆来看有几个知识点频繁出现。一是Activity的启动模式和任务栈管理。singleTop、singleTask、singleInstance三种启动模式的区别onNewIntent方法的回调时机以及Intent中FLAG_ACTIVITY_NEW_TASK、FLAG_ACTIVITY_CLEAR_TOP等标志位对任务栈的影响。这些知识点在约八成的Android笔试中都会出现。二是Handler消息机制。Handler、Looper、MessageQueue三者之间的关系Message的obtain方法和recycle方法如何优化内存Handler的postDelay并不是精准延时而是按消息的时间戳排序后依次执行。很多公司笔试都喜欢考Handler因为这是Android异步通信的核心也是面试中喜欢追问的知识点。三是JVM内存模型和GC机制。堆内存中新生代与老年代的比例关系对象何时从新生代晋升到老年代GC的触发条件强引用、软引用、弱引用、虚引用的区别和应用场景。Android开发中内存优化是重中之重所以笔试中关于JVM内存的题目几乎成了标配。四是网络编程相关知识点。HTTP请求的完整过程从DNS解析、TCP三次握手、发送HTTP请求、服务器响应、四次挥手断开连接这个流程是网络模块的大题必考内容。HTTPS的加密原理对称加密与非对称加密的应用场景CA证书的作用这些也是高频考点。2. Java基础考点拆解笔试失分的重灾区2.1 集合框架的底层原理与线程安全性Android开发中用得最多的集合类就是HashMap、ArrayList、LinkedList、HashSet但笔试不会直接问HashMap怎么用而是会问它的底层实现。比如HashMap在JDK 7中采用数组加链表的结构在JDK 8中则优化为数组加链表加红黑树。当链表长度超过阈值8且数组长度大于等于64时链表会转换为红黑树这样查找的时间复杂度从O(n)降为O(log n)。这个优化非常实用但要记住一个前提条件数组长度必须达到64。如果数组长度不够即使链表再长也不会树化而是会先扩容。关于加载因子HashMap的默认加载因子是0.75这个值是基于空间利用率和时间成本的折中。加载因子越小空间浪费越多但发生哈希冲突的概率越低查找效率越高。0.75是经过数学计算和大量测试得出的经验值笔试时直接记住即可。HashTable和HashMap的区别面试和笔试都爱考。HashTable的方法是线程安全的因为它的方法都加了synchronized锁但这也导致了性能较低。HashMap是线程不安全的但在单线程场景下性能更好。在并发场景下推荐使用ConcurrentHashMap它采用锁分段技术把整个Map分成多个段每段独立加锁这样多线程访问不同段的数据时可以并发执行大大提高了并发性能。JDK 8之后ConcurrentHashMap改用了CAS加synchronized的机制锁的粒度更细了。ArraList和LinkedList的区别也是经典考点。ArrayList基于动态数组实现随机访问快时间复杂度O(1)但插入和删除需要移动元素时间复杂度O(n)。LinkedList基于双向链表实现插入和删除快时间复杂度O(1)但随机访问需要遍历时间复杂度O(n)。这里有一个容易出错的点LinkedList的插入删除在已知节点的前提下才是O(1)如果只知道位置索引查找该节点仍需要O(n)的时间。2.2 并发编程基础与线程池并发编程是笔试客观题中技术含量较高的部分。synchronized和ReentrantLock的区别、volatile关键字的作用、ThreadLocal的使用场景这些几乎成了必考知识点。synchronized是JVM层面的关键字可以修饰方法、代码块和静态方法底层通过Monitor监视器实现锁的获取和释放由JVM自动管理。ReentrantLock是JDK层面的类需要手动加锁和解锁它提供了更灵活的功能比如可中断锁、公平锁、多个条件变量等。公平锁与非公平锁的对比ReentrantLock默认使用非公平锁因为非公平锁的吞吐量更大但可能出现线程饥饿现象。volatile关键字是并发编程中的另一个高频考点。volatile有两个语义保证内存可见性和禁止指令重排序。内存可见性是指当一个线程修改了volatile变量的值其他线程能立即看到这个修改这是通过内存屏障实现的。禁止指令重排序是指编译器和处理器不会对volatile变量相关的指令进行重排序这样能避免多线程环境下出现意外的执行顺序。但要注意volatile只能保证可见性和有序性不能保证原子性。比如volatile int count多线程执行count操作依然会出现线程安全问题因为count对应了读取、修改、写回三个操作volatile无法保证这三个操作的原子性。线程池的考察点集中在参数配置和执行流程上。ThreadPoolExecutor的核心参数有核心线程数、最大线程数、空闲线程存活时间、工作队列、线程工厂、拒绝策略。执行流程是当提交一个任务时如果当前线程数小于核心线程数则创建新线程执行任务如果当前线程数大于等于核心线程数且工作队列未满则将任务放入队列如果工作队列已满且当前线程数小于最大线程数则创建新线程执行任务如果线程数已到达最大值且队列已满则执行拒绝策略。四种拒绝策略分别是AbortPolicy抛出异常、CallerRunsPolicy让调用线程执行任务、DiscardPolicy直接丢弃任务、DiscardOldestPolicy丢弃队列中最旧的任务。Android中的线程池使用需要特别注意主线程与子线程的切换。由于Android规定在主线程中不能进行网络操作在子线程中不能更新UI所以实际开发中经常需要使用AsyncTask、HandlerThread、IntentService等工具。AsyncTask在低版本中存在线程池被占满的问题所以现在很多代码已经不再推荐使用AsyncTask而推荐协程或RxJava。2.3 JVM内存区域与垃圾回收Android是基于JVM的但Android使用的并不是标准的HotSpot JVM而是Dalvik虚拟机或ART虚拟机。笔试中JVM相关的题目依然会以标准JVM为背景因为Android开发者需要理解内存管理的底层机制。JVM内存区域分为线程共享区和线程私有区。线程共享区包括堆和方法区线程私有区包括虚拟机栈、本地方法栈和程序计数器。堆是对象分配内存的主要区域也是垃圾回收的主要区域。方法区存储类信息、常量、静态变量等数据在JDK 8中改为元空间。垃圾回收算法有标记清除、复制、标记整理三种。标记清除算法会产生内存碎片但实现简单复制算法不会产生内存碎片但需要额外空间标记整理算法将存活对象向一端移动然后清理边界外内存解决了碎片问题但需要移动对象。垃圾回收器方面笔试常考新生代和老年代使用的回收器Serial、Parallel、CMS、G1等。G1在JDK 9之后成为默认回收器它把堆分成多个Region可以并发标记能够预测停顿时间。引用类型的四种强度是Android内存优化中的理论基础。强引用只要有强引用存在垃圾回收器永远不会回收它即使发生OOM这导致了Android中常见的内存泄漏问题比如Activity被静态变量持有。软引用内存不足时回收常用于图片缓存但Android从API 9后推荐使用LruCache替代SoftReference因为LRU策略更可控。弱引用下次垃圾回收时回收常用于Handler的Context引用避免Activity泄漏。虚引用任何时候都可回收主要用于对象回收跟踪。3. Android核心考点拆解组件、消息与异步3.1 Activity生命周期与启动模式最经典的送分题Activity生命周期是Android笔试中出现频率最高的知识点。完整生命周期、可见生命周期、前台生命周期三个阶段的回调顺序必须烂熟于心。完整流程是onCreate到onDestroy可见流程是onStart到onStop前台流程是onResume到onPause。常考的场景是屏幕旋转。Activity旋转屏幕时会经历onPause、onStop、onDestroy、onCreate、onStart、onResume的完整流程因此旋转前后Activity会重建。要避免这种重建可以在AndroidManifest的activity标签中配置android:configChangesorientation|screenSize这样系统就会把配置变更交给onConfigurationChanged方法处理而不是销毁重建Activity。另一个高频场景是Activity被系统回收后的状态保存。onSaveInstanceState方法会在Activity被系统回收前调用适合保存一些临时状态。它的调用时机在onPause之后、onStop之前但也有上面提到的版本差异。从Android PAPI 28开始onSaveInstanceState的调用时机调整到了onStop之后。这一点是笔试陷阱题的重灾区。Activity启动模式在AndroidManifest中通过launchMode属性配置。standard模式每次启动都会创建新实例这是默认模式。singleTop模式如果栈顶已有该Activity实例不会创建新实例而会调用onNewIntent方法适合接收推送通知跳转的场景。singleTask模式会检查整个任务栈中是否存在该Activity实例如果存在就复用并清除其上方的所有Activity适合作为应用的主页面。singleInstance模式则是独享一个任务栈适合需要与外部应用共享的Activity比如来电界面。Intent的Flag也能影响Activity的启动方式。FLAG_ACTIVITY_NEW_TASK会为新Activity创建新的任务栈在非Activity上下文如Application中启动Activity时必须在Intent上添加这个Flag。FLAG_ACTIVITY_CLEAR_TOP会清除目标Activity之上的所有Activity它的行为与singleTask类似可结合使用。3.2 Handler消息机制原理与易错点Handler消息机制是Android异步任务的核心也是校招笔试中常考的内容。它的组成成员是Handler、Looper、MessageQueue、Message。主线程启动时ActivityThread会通过Looper.prepareMainLooper创建主线程的Looper并通过Looper.loop启动消息循环因此主线程中创建Handler不需要额外操作。Handler发送消息有两种方式。一种是使用sendMessage方法通过MessageQueue的enqueueMessage方法将Message按时间排序插入消息队列。这种方式的执行不会精准延时因为如果消息队列中前面的消息执行时间较长后续消息会被阻塞。另一种是使用post方法本质上是把Runnable包装成Message。postDelayed方法与sendMessageDelayed方法本质相同。使用Handler时最常见的错误之一是内存泄漏。如果Handler是非静态内部类它会持有外部Activity的隐式引用当Activity销毁后Handler仍有可能在消息队列中有待处理的消息导致Activity无法被垃圾回收。解决办法是使用静态内部类加WeakReference的方式持有Activity或者在onDestroy方法中调用removeCallbacksAndMessages(null)移除所有未处理的消息。笔试中关于Handler的经典题目包括子线程中能不能创建Handler在线程中直接创建Handler代码运行时会抛出RuntimeException因为系统不会自动为子线程创建Looper需要首先调用Looper.prepare()为当前线程初始化Looper然后创建Handler最后调用Looper.loop()启动消息循环。还可以考Message的复用机制使用Message.obtain()从消息池中获取Message而不是直接new。消息池是一个链表结构容量上限为50能有效避免频繁创建Message对象带来的内存抖动。3.3 Service与BroadcastReceiver、ContentProvider四大组件的联动考察Service的两种启动方式startService和bindService的区别是笔试必考题。startService启动的Service与启动它的组件没有绑定关系即使启动组件销毁Service依然会继续运行直到调用stopService或自身执行stopSelf。bindService启动的Service与绑定组件生命周期绑定组件销毁后Service也随之销毁如果没有其他组件绑定。通过bindService可以获取Service的Binder对象从而调用Service中定义的方法。Service的生命周期也分两种。startService方式下生命周期是onCreate到onStartCommand再到onDestroyonStartCommand可以被多次调用。bindService方式下生命周期是onCreate到onBind再到onUnbind最后到onDestroy。绑定Service时可以通过LocalBinder、Messenger、AIDL三种方式进行通信。LocalBinder适用于同一进程内的通信最简单Messenger基于Handler实现跨进程通信AIDL用于需要并发处理的跨进程通信场景。BroadcastReceiver的注册方式有两种。动态注册是在代码中通过registerReceiver注册需要在onDestroy中反注册否则会泄漏静态注册是在AndroidManifest中声明应用安装后系统会在应用未启动时也能接收广播。从Android 8.0API 26开始系统对静态注册的隐式广播进行了限制很多系统广播无法再通过静态注册方式接收。同时从Android 14API 34开始动态注册广播接收器时也必须指定RECEIVER_EXPORTED或RECEIVER_NOT_EXPORTED标志。ContentProvider是Android中跨应用共享数据的主要方式。访问ContentProvider需要通过ContentResolver的query、insert、update、delete方法使用Uri来标识数据。在IPC过程中ContentProvider的底层使用Binder机制实现跨进程调用。如果访问的数据量较大应该使用openAssetFile或openFile等方法来传递大文件避免通过Binder传递过大的数据导致TransactionTooLargeException。4. 内存与性能优化考点从原理到实战的结合题4.1 常见内存泄漏场景与规避方案内存泄漏是Android开发中非常常见的质量问题也是笔试中经常结合场景考察的知识点。其中一个最常见的场景是Handler造成的泄漏这个前面已经说过。另一个经典泄漏场景是静态Context引用。如果把Activity的Context赋值给静态变量Activity在被销毁后无法被垃圾回收。解决方法是尽量使用Application的Context或者在合适时机将静态变量置空。匿名内部类的使用也是内存泄漏的高发场景。比如创建一个匿名Runnable对象并交给Handler如果这个Runnable需要在Activity销毁后仍执行就可能造成泄漏。这种情况下需要把生命周期与外部组件解绑比如在onDestroy时把Runnable移除。还有单例模式造成的内存泄漏。例如使用单例持有Activity的Context引用单例的生命周期与应用进程一致所以被持有的Activity永远无法被回收。正确的做法是单例中只持有ApplicationContext。网络请求回调也可能造成泄漏如果Activity发起了网络请求在请求返回之前Activity被销毁而回调中还需要处理Activity相关操作就会泄漏。解决方法是使用弱引用引用Activity或在销毁时取消网络请求。这些泄漏场景在笔试题中出现时往往不会直接说这会导致泄漏而是用长长的描述覆盖代码场景需要自己定位问题。答题的核心思路是寻找生命周期长的对象是否持有生命周期短的对象引用。4.2 布局优化与绘制性能布局优化是Android性能优化的基本功。include标签用于布局复用merge标签用于减少布局层级ViewStub用于延迟加载不常用的布局。笔试中常考的一个知识点是为什么使用merge标签因为FrameLayout中如果只有一个子ViewRoot布局直接用merge标签可以让子View直接放到父容器中减少一层嵌套提高绘制效率。布局层级越深measure和layout阶段的时间花费就越多因此需要减少过度的嵌套。官方建议布局层级控制在5层以内一旦超过这个层级就应该检查是否有必要通过RelativeLayout或ConstraintLayout来压缩层级。笔试中可能会给出一段布局代码让你分析布局层级如何优化。View的绘制流程是measure、layout、draw三个阶段。measure阶段确定View的大小layout阶段确定View的位置draw阶段绘制View的内容。常考的题目是在onMeasure中调用setMeasuredDimension来设置测量宽高ViewGroup在onMeasure中要遍历所有子View并调用它们的measure方法measure时传递的MeasureSpec包括mode和size两部分mode有三种取值UNSPECIFIED表示父容器不对子View的大小做限制EXACTLY表示父容器确定子View的精确大小AT_MOST表示子View的大小不能超过父容器给定的最大值。自定义View是笔试中的高阶考点。常考的题目有自定义View的属性如何声明和获取在自定义View中处理padding需要写一个继承自View的类在构造方法中获取自定义属性。需要重写onMeasure处理wrap_content的情况。如果自定义ViewGroup想支持子View需要重写onLayout方法对子View进行布局。4.3 LruCache与图片加载框架的原理图片缓存是Android开发中非常高频的性能优化场景。LruCache是Android 3.1之后提供的缓存工具类全称是Least Recently Used Cache即最近最少使用算法。它的核心原理是用LinkedHashMap来存储数据通过accessOrder参数来控制访问顺序。当缓存满时会淘汰最久未使用的数据这就是LRU策略。LruCache在Android中的典型应用场景是图片缓存。同时需要注意LruCache的总容量通常根据应用可用内存来设置比如总容量的八分之一。图片加载到内存时用计算后的字节数而不是直接计算图片宽高乘以像素大小。在使用Bitmap时要注意复用recycle方法但在高版本中recycle方法不是必要的系统会自动回收Native层内存。复用inBitmap的方式在一定的API级别才能用可以复用Bitmap的内存区域避免重新分配。关于图片加载框架笔试有时会问Glide、Fresco、Picasso和ImageLoader的选型和底层实现。Glide默认使用RGB_565解码图片部分版本默认ARGB_8888内存占用只是其他框架用ARGB_8888的一半因此内存占用比Picasso要小。Glide的生命周期感知能力很强与Activity和Fragment的生命周期绑定能在页面销毁时自动取消加载任务位图。还可以通过DiskCacheStrategy控制磁盘缓存策略如缓存原图、缓存处理后的结果等。5. 网络与操作系统非专业但必须拿下的分数5.1 TCP与UDP、HTTP与HTTPS这些网络协议题怎么答TCP协议的三次握手和四次挥手是校招笔试几乎必考的题目。三次握手建立连接的过程是客户端发送SYN报文服务器回复SYNACK报文客户端再发送ACK报文。为了确保双方收发能力都正常。三次握手中每次丢失报文如何处理SYN Flood攻击如何防御是区分基础题和进阶题的节点。四次挥手断开连接的过程是主动方发送FIN报文被动方回复ACK被动方发送FIN报文主动方回复ACK。因为在TCP连接中可能存在半关闭状态所以被动方收到FIN后可以先回复ACK告诉主动方我已收到关闭请求等自己数据发送完毕后再发送FIN这就是中间有两次等待的原因。TIME_WAIT状态出现在主动关闭连接的一方等待2MSL时间确保最后的ACK到达MSL是报文段最大生存时间通常为2分钟。TCP与UDP的区别是基础题。TCP是面向连接的、可靠的、基于字节流的传输层协议UDP是无连接的、不可靠的、基于数据报的传输层协议。TCP有流量控制和拥塞控制机制UDP没有。TCP适合传输文件、邮件等需要可靠交付的场景UDP适合视频通话、实时游戏等对延迟敏感的场景。HTTP与HTTPS的区别同样是高频考点。HTTP使用明文传输HTTPS使用SSL/TLS加密传输。HTTPS的默认端口是443HTTP是80。HTTPS的加密过程是客户端请求HTTPS连接服务器返回数字证书客户端验证证书合法性并通过证书获取服务器的公钥客户端生成对称加密密钥并用服务器的公钥加密后发送给服务器服务器用私钥解密得到对称加密密钥之后双方通过对称加密密钥进行通信。题目的变种是为什么不用纯非对称加密因为非对称加密解密速度慢对称加密速度快但密钥分发不安全所以使用非对称加密来传输对称密钥。HTTP的请求方法也是常考点。GET和POST的区别是面试笔试必考题。GET请求的参数放在URL中POST放在请求体中GET方式容易因为URL长度限制而截断POST方式没有这个限制。实际上GET和POST的语义区别更为根本GET应该用于获取数据不应有副作用POST用于提交操作允许有副作用。HTTP状态码也是常考知识点2xx表示成功3xx表示重定向4xx表示客户端错误5xx表示服务器错误。常考的有200 OK表示请求成功301表示永久重定向302表示临时重定向304表示未修改缓存400是请求错误401未授权403禁止访问404找不到资源500服务器内部错误502网关错误503服务不可用。5.2 进程与线程、死锁与并发操作系统笔试要点操作系统部分的笔试题目主要有进程与线程的区别、死锁的四个必要条件、进程状态转换、虚拟内存等。进程是操作系统资源分配的基本单位线程是CPU调度的基本单位。同一进程的多个线程共享进程的地址空间和资源而进程之间拥有独立的地址空间和资源。进程间通信方式有管道、消息队列、共享内存、信号量、套接字线程间通信可以直接通过共享变量实现。死锁的四个必要条件是互斥条件、请求与保持条件、不可剥夺条件、循环等待条件。前三个条件中的任何一个如果被破坏死锁就不会发生。死锁的预防策略有破坏互斥条件很难做到、破坏请求与保持条件一次性申请所有资源、破坏不可剥夺条件适当剥夺资源、破坏循环等待条件按序分配资源。常考的一个细节是死锁的避免算法有银行家算法。进程状态转换是经典题目。进程有三种基本状态就绪、运行、阻塞。就绪状态到运行状态的转换是由调度程序完成的运行状态到阻塞状态通常是因为等待I/O事件或信号量阻塞状态到就绪状态是等待的事件完成。画进程状态转换图是这类题目的常见操作型考点。5.3 数据结构与算法选择题中的送分题与陷阱题数据结构部分的笔试题相对基础主要以选择题形式考察。要掌握各种数据结构的基本特点和适用场景数组随机访问快、链表增删快、栈先进后出、队列先进先出、二叉树有左右孩子、哈希表以空间换时间、图用于表示多对多关系。二叉树遍历是必考题。前序遍历的顺序是根左右中序遍历的顺序是左根右后序遍历的顺序是左右根。给出两种遍历序求第三种遍历序是常考的题目。如果给出前序和后序一般无法唯一确定一棵二叉树除非二叉树是满二叉树或每个节点的度要么为0要么为2。同时要清楚排序算法的时间复杂度冒泡排序平均O(n^2)、快速排序平均O(n log n)、堆排序O(n log n)、归并排序O(n log n)且空间复杂度O(n)。快速排序在最坏情况下的时间复杂度退化为O(n^2)这种情况发生在每次选择的基准元素都是最小或最大的时候所以快速排序有时候会加入随机化来避免最坏情况。二叉树中的经典题目包括完全二叉树的节点个数、二叉搜索树中查找第k小的元素、按层遍历二叉树、二叉树的最大深度与最小深度、两个节点的最近公共祖先。这些题目在笔试中往往以选择题形式考察时间复杂度与实现思路。哈希表的冲突解决方法有开放定址法和链地址法。开放定址法的探测方式有线性和二次探测与双重散列当冲突发生时按某种方式寻找下一个空位。链地址法是把哈希值相同的元素放在同一个链表中。HashMap在Java中采用链地址法当链表过长时转化为红黑树来优化查询效率这一点在第二章已经细说。LRU缓存算法在操作系统中是页面置换算法的一种。它是基于最近使用的数据在不久的将来还会被使用这个假设。实现思路是用哈希表加双向链表哈希表负责O(1)的查找双向链表负责记录访问顺序链表头部是最新访问的数据尾部是最久未访问的数据。每次访问时把数据移到链表头部缓存满时淘汰链表尾部的数据。复杂度分析get和put操作的时间复杂度都是O(1)。这种数据结构在Android的LruCache中被直接应用。6. 笔试中的实战策略与备考建议6.1 时间分配与答题顺序客观题部分的时间分配很关键。一场笔试通常在60到90分钟题量大约在50到80题之间平均每题只有1分钟左右的时间。我的建议是拿到试卷后先快速浏览一遍标记出自己确定的题和不确定的题。答题顺序上先做自己有把握的题把确定的分数牢牢抓在手里然后再回来啃难题。客观题中遇到完全没思路的题目不要死磕先在草稿纸上标记题号继续往下做。客观题不像编程题不会因为代码没写完而整题零分先做完所有会做的题再回头处理不会的题这样的策略能最大程度利用有限的答题时间。多选题是最容易丢分的题型。多选、少选、错选都不得分答题时需要非常谨慎。如果对某个选项没有十足把握我的经验是可以不选因为有把握的选项至少能拿部分分猜一个不确定的选项容易全军覆没。6.2 错题复盘的正确姿势备考过程中做完题不是结束复盘才是真正拉开差距的环节。刷题时不要只对答案而要去分析每个错误选项的考察意图。一道考察Activity启动模式的题目如果做错了不要只记住正确答案而要思考出题人为什么设置另外三个错误选项它们代表了哪些常见的理解偏差。我习惯用表格记录错题包含题干、我的错误选项、正确答案、错误原因、关联知识点五列。复习时重点看错误原因和关联知识点尤其是反复出现的高频错误点。在即将笔试的冲刺阶段可以只看错题本和知识图谱不需要再大量刷新题。把之前总结的知识点框架再过一遍比盲目刷题更有价值。6.3 笔试与面试的衔接准备笔试结束到面试通知的过程通常有几天到两周不等。这段时间不要干等着可以把笔试中暴露出来的薄弱知识点整理成面试自我介绍中的讲述素材。笔试考的内容面试中大概率会再次出现。比如笔试考了Handler消息机制面试中可能会让你手写一个Handler消息机制的简化版本或者追问Handler内存泄漏的原因和解决方案。笔试考了Activity启动模式面试中可能会给你一个具体场景问你该用哪种启动模式。其实很多公司笔试客观题都在筛选基本功而不需要真正掌握某个高深技术。如果真的把上面这些知识点吃透了即便笔试不通过面试环节也大概率能顶住。2019年360的这套笔试客观题说实话难度并没有超出常规校招范围关键在于知识体系是否成型。我建议读者可以把这份考点整理当作自检清单逐项核验自己的知识盲区然后针对性地补齐早日拿下理想offer。