☰
网易2018Android实习生笔试题解析:高频考点与备战指南
2026/10/27 4:29:37 网站建设 项目流程

1. 网易2018实习生招聘笔试题整体拆解:一份Android实习生的“体检报告”

每年三四月份,各大厂的实习生招聘就陆续启动了。2018年那会儿,网易的实习生笔试题目在圈子里流传得挺广,原因很简单:题量适中、覆盖面广,而且非常看重基础功底。我当年也刷过这套题,后来带团队面试实习生时,偶尔还会翻出来参考一下。说句实在话,虽然是2018年的题,但放到今天来看,核心知识点基本没有过时——Android开发的基础栈就那么大,变来变去都是那几板斧。

先给没参加过这类笔试的朋友交代一下背景。网易的实习生笔试通常采用的是牛客网或赛码网平台,Android开发实习生的试卷一般分为三块:单选题、多选题和编程题。单选题大概20道左右,覆盖Java基础、Android基础、计算机网络和数据结构;多选题会有5到10道,经常在一些易混淆的API和概念上“挖坑”;编程题一般是2道,一道偏算法,一道偏工程实现。

从考察目标来看,这套题本质上不是要你写出多惊艳的代码,而是要在有限时间里证明三件事:第一,你的Java功底扎实不扎实;第二,你对Android的四大组件、消息机制、进程通信这些核心概念有没有真正理解,而不是只会调用API;第三,你遇到问题时的分析思路是否清晰。说白了,这就是一份针对实习生的“体检报告”,检查你有没有成为一个合格Android工程师的基本潜质。

我见过不少同学刷题时只关注“这道题选A还是选B”,然后把答案死记硬背下来。这种备考方式在实习生笔试里特别吃亏,因为大厂的笔试题变数很大,同一个考点换个问法,死记答案的人就懵了。所以我写这篇解析的思路是:把题目背后真正要考的知识点拎出来,讲清楚原理,再说说这类知识点在实际开发中会怎么用。这样你以后不管遇到哪个厂的题,都能从容应对。

2. 高频考点第一梯队:Java基础与并发,Android岗笔试的地基

2.1 面向对象与集合框架:别在送分题上丢分

Java基础在Android开发实习生的笔试里通常占30%到40%的题量,重要性不用多说。网易这套题里,面向对象部分考得比较典型,我记得有几道题涉及继承中的构造方法调用顺序、接口和抽象类的区别、重载和重写的差异。这些题在概念上并不难,但出题人喜欢在细节上做文章。

举个典型例子,关于构造方法调用顺序那道题:父类有静态代码块、非静态代码块、构造方法,子类同样有这三者,问执行顺序是什么。正确答案是先执行父类静态代码块,再执行子类静态代码块,然后执行父类非静态代码块和父类构造方法,最后执行子类非静态代码块和子类构造方法。这里面的关键点是静态代码块只在类加载时执行一次,而且父类先于子类加载。很多同学会记混“非静态代码块和构造方法的先后顺序”,我在这里给个记忆技巧:非静态代码块和成员变量初始化都在构造方法之前,它们按照代码中出现的顺序执行。你可以理解为,构造方法是“组装成品”,非静态代码块和成员变量初始化是“准备零件”,组装前肯定要先准备好零件。

集合框架在笔试中的出镜率也相当高,尤其是HashMap和ArrayList这两个最常用的。2018年的题目里,HashMap还没有变成现在JDK 8那种“数组+链表+红黑树”的结构,但考的点其实差不多:HashMap的底层数据结构、扩容机制、put方法的流程、hash碰撞的解决办法。这里我说一个面试官特别喜欢的追问点:为什么HashMap的默认负载因子是0.75?这个数值是时间和空间成本的一个折中——负载因子太高,比如1.0,虽然节省了内存空间,但hash碰撞的概率增大,链表变长,查询效率下降;负载因子太低,比如0.5,虽然查询快了,但频繁扩容浪费内存。所以0.75这个值,本质上是在“空间”和“时间”之间找平衡。

ArrayList和LinkedList的区别也是老生常谈,但我要提醒一点:ArrayList的扩容机制在笔试里经常被细化考察。ArrayList默认容量是10,每次扩容为原来的1.5倍,也就是oldCapacity + (oldCapacity >> 1)。这个位运算表达式看起来很巧妙,实际上就是“原容量加原容量的一半”。为什么不是2倍?因为1.5倍的扩容策略在空间利用率和扩容次数之间取得了更好的平衡,如果翻倍,扩容次数少但浪费空间;如果扩得太少,频繁System.arraycopy的性能损耗又大了。

2.2 并发与线程:从底层开始理解,笔试才有底气

Java并发相关的考点,在Android笔试里通常占两道到四道题。网易在这一块考得不深,但很讲究概念的准确性。我印象比较深的是一道关于volatile关键字的题:问volatile是否能保证原子性,以及它和synchronized的区别。正确答案是volatile保证可见性和有序性,但不保证原子性。比如i++这种操作,即使变量被volatile修饰,在多线程环境下依然可能出问题,因为i++在底层是“读取-修改-写入”三步,volatile管不了这个复合操作的原子性。

关于线程池,笔试常考的是ThreadPoolExecutor的几个核心参数——corePoolSize(核心线程数)、maximumPoolSize(最大线程数)、keepAliveTime(存活时间)、workQueue(任务队列)、RejectedExecutionHandler(拒绝策略)。网易有一道多选题就是让选出正确的线程池执行流程:当提交一个新任务时,如果当前运行的线程数小于核心线程数,就创建新线程来执行;如果大于等于核心线程数,就把任务放入队列;如果队列满了且线程数小于最大线程数,就创建非核心线程执行;如果线程数已经达到最大且队列也满了,就执行拒绝策略。这个流程在Android开发中极其常用,因为很多场景要自己封装线程池来管理异步任务。

我在实际项目里踩过一个坑,跟线程池的队列选择有关。有次我负责的一个下载模块,用的是无界队列LinkedBlockingQueue,当时想的是任务多的时候排队等着就行,结果遇到高峰期,任务积压了几千个,内存占用飙升,App直接卡顿。后来改成了有界队列ArrayBlockingQueue,配合CallerRunsPolicy拒绝策略——线程池满了就让提交任务的线程自己执行,虽然偶尔会阻塞UI线程,但至少不会内存爆掉。这个经验说明,笔试里考线程池参数,不是让你背定义,而是要理解不同参数组合在真实场景下的表现差异。

2.3 内存模型与引用类型:JVM考点在Android面试中的变体

JVM内存区域划分也是笔试的常客,网易喜欢考的是“哪些区域是线程共享的、哪些是线程私有的”。堆和方法区是线程共享的,虚拟机栈、本地方法栈、程序计数器是线程私有的。在Android场景里,这个知识点会演变成“OOM(OutOfMemoryError)通常发生在哪个区域”——绝大多数情况下是堆内存不够了,但如果你用C++通过NDK写代码,native层的内存溢出就是另一回事了。

强引用、软引用、弱引用、虚引用这四种引用类型,笔试里经常以选择题形式出现。Android开发里SoftReference(软引用)和WeakReference(弱引用)用得比较多,比如图片缓存框架在早期版本会用SoftReference来实现内存敏感的高速缓存,但因为软引用在系统内存不足时容易被回收,后来大家逐渐改成用LruCache加DiskLruCache配合使用。不过笔试里考引用类型,核心还是要记住:强引用只要活着就不会被回收,软引用在内存不足时会被回收,弱引用在下一次GC时就会被回收,虚引用随时可能被回收且无法通过它获取对象实例。

3. 高频考点第二梯队:Android核心机制,理解本质比记住答案重要

3.1 四大组件:Activity和Service是笔试重灾区

四大组件是Android笔试的绝对主角,Activity和Service更是重灾区中的重灾区。网易这套题里,Activity的考察集中在生命周期、启动模式和任务栈三个方向。

生命周期不必多说,但要注意一个容易忽略的细节:在API 28(Android 9.0)之前,onPause之后才能执行onStop,期间不允许做过重的操作,因为系统会等onPause执行完才让新的Activity可见。这一块笔试很喜欢用场景题来考察,比如“Activity A启动Activity B,两个Activity的生命周期回调顺序是什么”。完整顺序是:A.onPause -> B.onCreate -> B.onStart -> B.onResume -> A.onStop。如果B是透明主题或者Dialog样式,A只会走到onPause,不会走onStop,这在开发中是个很实用的经验。

启动模式这部分,standard、singleTop、singleTask、singleInstance是必背的。网易有一道关于Intent Flag的题目,考的是FLAG_ACTIVITY_NEW_TASK和FLAG_ACTIVITY_CLEAR_TOP组合使用的效果。这个组合和singleTask模式的行为基本一致:如果任务栈中已经存在目标Activity实例,就把这个实例之上的所有Activity出栈,然后复用这个实例,同时会回调它的onNewIntent而不是onCreate。我在实际开发中用过这个Flag组合来做App的主界面跳转,比如从推送通知栏点击跳转到首页时,为了避免堆积一堆重复页面,就会用这个方案。

Service在笔试里的考法比较常规,大概率会问startService和bindService的区别,以及它们的生命周期。这里有个细节值得注意:startService启动的Service,如果调用方直接退出,Service还是在后台运行,需要调用stopService或者Service内部调stopSelf才能停止;而bindService启动的Service,生命周期和调用方绑定,调用方销毁时系统会解绑,但如果这个Service之前也用过startService,那它还需要通过stopService才能停止。混合使用startService和bindService的场景也是笔试多选题的热门,大家把两种方式的独立行为理解透了,这种题就不会错。

ContentProvider和BroadcastReceiver在笔试里占的比重相对小一些,但ContentProvider在2018年的题目里有一道涉及进程间数据共享的题,问的是ContentProvider底层用的是什么进程通信机制——答案是Binder。Binder是Android系统最核心的IPC机制之一,笔试会问你为什么Android不直接用Linux的管道、消息队列或者共享内存,而是自己搞了一个Binder。这个问题我在下文专门讲IPC时展开。

3.2 Handler消息机制:面试官最爱的连环追问题

Handler消息机制是Android开发面试中出镜率最高的考点,没有之一。网易的笔试里,Handler、Looper、MessageQueue三者的关系属于“标配题”。考察方式一般是让你描述一个完整的消息流转过程:Handler发送消息 -> 消息进入MessageQueue -> Looper通过loop()方法不断从队列中取出消息 -> 回调Handler的handleMessage方法处理消息。

这里面的关键点在于,Looper是整个机制的“发动机”,而且每个线程只能有一个Looper实例。主线程的Looper在App启动时就已经通过Looper.prepareMainLooper()创建好了,所以我们在主线程里可以直接new Handler使用。但如果在子线程里创建Handler,就必须先调用Looper.prepare()创建当前线程的Looper,再调用Looper.loop()让它转起来,否则会直接抛RuntimeException:“Can't create handler inside thread that has not called Looper.prepare()”。

还有一个高频考点是Handler的post方法和sendMessage方法有什么区别。本质上它们没有区别,post(Runnable)最后还是通过getPostMessage把Runnable包装成了一个Message,然后走sendMessageDelayed。所以post和sendMessage殊途同归,最终都是往MessageQueue里插入一条消息。

死锁问题在Handler机制里也会考:在主线程中调用Looper.loop()会不会ANR(Application Not Responding)?答案是会,但这不是死锁,而是主线程被阻塞,无法处理用户的输入事件。Looper.loop()本身是一个死循环,它不断从消息队列里取消息并处理,手机系统正是靠这个死循环保证UI线程随时待命。这个点一定要理解透,因为面试官会顺着这个问题往下问:既然主线程是死循环,那为什么不会卡死?答案在于,没有消息处理时,主线程会阻塞在MessageQueue的next()方法中的nativePollOnce里,这时候线程是休眠状态,不占用CPU,一旦有新消息进来,系统会通过epoll机制唤醒它。

3.3 IPC与Binder:理解Android进程通信的设计哲学

Android笔试里,进程间通信相关的内容一般在选择题中出现一到两道。网易2018年那套题里关于Binder的描述题挺有意思:四个选项里有三个是常见的套话,比如“Binder性能高是因为只拷贝一次数据”“Binder基于C/S架构”“Binder使用mmap映射内核空间和用户空间”——这些都是对的。有一个错误选项是“Binder是Android基于Linux消息队列实现的”,这就是在考你有没有真正搞清楚Binder的底层实现。

Binder为什么只拷贝一次数据?传统IPC管道、消息队列需要在内核空间和目标进程用户空间之间拷贝两次数据,而Binder借助mmap把内核缓冲区和目标进程的用户空间做了地址映射,数据只需要从发送进程的用户空间拷贝到内核缓冲区,目标进程通过映射直接访问这块区域,所以只需要一次拷贝。这个设计在性能上比传统的SharedPreferences那种文件方式要好得多,而且足够安全——内核可以为每个进程分配UID,在传输过程中自动校验身份。

作为Android开发实习生,你不需要写出Binder驱动代码,但至少要能说出AIDL的基本使用流程:定义接口文件,实现Stub类,在Service的onBind方法中返回这个Binder对象,客户端通过bindService拿到IBinder再调用asInterface或asProxy方法。笔试如果考到AIDL支持的参数类型,记住基本数据类型、String、CharSequence、List、Map、Parcelable都可以,但不能直接传普通的Java对象。

3.4 自定义View与事件分发:考验空间想象力的硬骨头

自定义View这块在2018年的实习生笔试里出现率没有前面那些考点高,但一旦出现,难度都不低,而且网易比较喜欢考事件分发机制和View绘制流程的结合题。

事件分发机制的核心是三个方法:dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent。笔试会考它们的调用顺序:事件先传给最外层的ViewGroup的dispatchTouchEvent,然后它的onInterceptTouchEvent决定是自己处理还是继续往下传递。这里有个非常关键也容易混淆的细节:子View可以通过requestDisallowInterceptTouchEvent(true)禁止父View拦截事件,但父View可以在ACTION_DOWN事件时无视这个请求,照常拦截。为什么这样设计?因为父View需要确保自己在滑动等场景下能拿到事件的控制权,如果子View把事件永久“锁死”在自己手里,父View的滑动功能就废了。

View的绘制流程也是笔试重点:onMeasure测量尺寸 -> onLayout确定位置 -> onDraw绘制内容。其中MeasureSpec的三种模式——AT_MOST、EXACTLY、UNSPECIFIED——在笔试里经常配合wrap_content和match_parent来考。简单来说,match_parent对应EXACTLY,规格大小就是父容器剩余空间;wrap_content对应AT_MOST,视图最大不能超过父容器剩余空间;ListView、ScrollView这种可滚动容器里面的item在测量时可能会收到UNSPECIFIED,意思是不限定大小。我在实际开发中自定义View时经常处理wrap_content相关的问题,很多初学者自定义View时会忘记处理wrap_content的情况,导致设置无效——这背后的原因就是在onMeasure里直接把宽高写死了,没有根据MeasureSpec的不同模式做分支处理。

3.5 序列化与数据存储:Parcelable和Serializable的对比考点

关于序列化,网易笔试考了一道Parcelable和Serializable的区别。这两个都能实现对象的序列化和反序列化,但Parcelable是Android专有的,性能更高,用于进程间通信和Intent传对象时使用;Serializable是Java原生的,使用方便但利用反射机制,序列化和反序列化时性能较差,而且会创建大量临时对象,容易引发GC。

这里有一个容易踩坑的点:Intent传递自定义对象时,如果对象实现了Serializable接口,可以直接putExtra传进去,但如果这个对象里面嵌套了其他对象,那所有嵌套对象也必须实现Serializable,否则会抛NotSerializableException。Parcelable也有类似的约束,嵌套对象要实现Parcelable或者用Bundle包装。另外,Parcelable的writeToParcel方法中写变量的顺序必须和CREATOR中读出来的顺序保持一致,否则运行时会得到错误数据,这个Bug排查起来非常隐蔽,因为编译期完全不会报错。

Android本地存储方面,笔试常考的是SharedPreferences和SQLite。SharedPreferences底层是XML文件,适合存储轻量级的键值对数据,但不适合存大量数据,而且跨进程读取时会有并发问题——即使是官方后来推出的SharedPreferences的多进程模式,也只是做了文件锁,性能堪忧,所以实际开发中如果多进程场景需要共享数据,我一般会直接用ContentProvider或者数据库。SQLite在笔试里考得不多,一般问一下SQLiteOpenHelper的onCreate和onUpgrade回调时机就差不多了,但有个细节值得提起:数据库升级时如果只增加了一张表,只要在onUpgrade里执行CREATE TABLE语句就行,但如果改动的是已有表的结构,比如加字段,就要用ALTER TABLE或者重建表加数据迁移,这一环没做好,线上用户升级App后很容易出现“no such column”崩溃。

4. 性能优化与专项考点:笔试里的“隐性加分项”

4.1 内存泄漏与OOM:必考的三板斧+排查思路

Android笔试题里,性能优化相关的内容一般作为“区分度”题目出现,网易也差不多。内存泄漏这块,考得最多的是Handler造成的内存泄漏和静态变量持有Activity引用。

Handler为什么会导致内存泄漏?原因是MessageQueue中等待处理的Message持有Handler的引用,Handler如果是非静态内部类,就会持有外部Activity的引用。如果消息一直没被处理,比如延迟了很长时间,或者Activity已经销毁但消息还没有被取出,Activity就无法被GC回收。解决方法是使用静态内部类加WeakReference,同时在Activity的onDestroy方法中调用mHandler.removeCallbacksAndMessages(null)清掉所有未处理的消息。

这个内存泄漏场景在实际开发中非常常见,我被坑过不止一次。有个项目在做启动页倒计时时,用了Handler.postDelayed发了一个延迟消息,结果有个用户在倒计时还没结束就退出了App,后面反复横跳几次,内存就蹭蹭往上涨。后来排查发现就是这个问题——Activity销毁后,Handler里还挂着一个延迟消息没清理。修复方案也很简单,onDestroy里移除所有回调和消息即可,代码就一行,但如果不了解背后的原理,根本想不到问题出在这里。

OOM的考察点主要集中在“哪些操作容易导致OOM”。常见的答案是:加载大图片、内存缓存没有限制、频繁创建对象。图片加载这关对Android开发来说几乎无法避免,Bitmap在内存中的占用计算方式是:宽度 x 高度 x 每像素字节数。ARGB_8888模式下一个像素占4字节,一张1920x1080的图片在内存中就要占用约8MB。笔试如果给你一张图片的尺寸和颜色格式,让你算内存占用,你要会这个公式。优化思路一般是压缩采样率(inSampleSize)、使用RGB_565模式(一个像素占2字节)、配合LruCache做内存缓存。

4.2 ANR与进程优先级:为什么主线程不能做耗时操作

ANR(Application Not Responding)相关知识点在笔试中出现的频率也不低,网易喜欢考的是ANR的触发条件。Android系统对主线程有时间上的硬性约束:BroadcastReceiver的onReceive在10秒内没执行完会ANR,Service的onCreate等生命周期在20秒内没处理完会ANR,输入事件(屏幕点击、滑动)在5秒内没有被处理完,也就是主线程阻塞超过5秒,会产生InputDispatching Timeout类型的ANR。

备考时不要只记时间,要理解底层逻辑:主线程的Looper负责处理所有UI事件,如果主线程被耗时操作阻塞,用户点击屏幕后系统发出的输入事件就一直排在消息队列里得不到处理,超过一定时间,系统就判定应用无响应。所以“不要在主线程做耗时操作”这个开发铁律,本质上就是保证消息队列畅通。

笔试还有一道关于进程优先级的题:前台进程 > 可见进程 > 服务进程 > 后台进程 > 空进程。这个优先级决定了一个App进程被系统杀掉的先后顺序,优先级越低的进程越容易被LMK(Low Memory Killer)回收。比如手机内存不足时,系统会优先杀掉空进程和后台进程来释放内存。应用开发中常见的优化做法是用前台Service或者WorkManager来提升后台任务的存活率,但这块涉及的内容比较敏感——不是技术上的敏感,而是国内不同手机厂商的杀后台策略差异很大,这里就不展开说了,笔试也不会深挖到厂商定制系统层面。

4.3 网络与数据解析:HttpURLConnection与图片加载的笔试常规题

网络相关的题目在Android开发实习生笔试里不算特别多,但也不缺席。网易2018年的题里就有一道关于HTTP状态码的选择题,问201、301、302、401、404这些状态码的含义。常见的坑是301是永久重定向,302是临时重定向,401是未授权,需要认证,403才是禁止访问(服务器理解了请求但拒绝执行),404是资源不存在。这道题本质上考的不是Android知识,而是计算机基础,所以想进大厂,HTTP协议还是要认真过一遍。

Retrofit、OkHttp、Glide这些网络和图片加载库,笔试主考的是缓存策略和线程调度机制。比如Glide的三级缓存:活动资源(内存缓存中正在使用的资源)、内存缓存(LRU算法管理)、磁盘缓存。图片加载流程大致是:检查活动资源 -> 检查内存缓存 -> 检查磁盘缓存 -> 网络下载。这个流程和后面要说的LruCache原理是相通的。

OkHttp的拦截器机制也是特色考点:它会按照添加顺序把多个拦截器组成一个责任链,从上到下依次执行。应用拦截器和网络拦截器的区别在于,应用拦截器能看到的是请求的原始状态,而网络拦截器能看到经过重试、重定向之后的最终请求,所以在日志打印、修改header这些场景要选对拦截器类型。笔试不会考得非常细,但至少要知道OkHttp的拦截器是可以拆解整个网络请求流程的。

5. 算法与编程题:如何在笔试中拿下两道送分(保命)题

5.1 常见算法题类型:排序、链表、二叉树、动态规划

网易实习生笔试的编程题,正常情况下是一道简单偏中等、一道中等偏难的组合。简单那道通常可以用基本数据结构和算法解决,难的那道会涉及动态规划、贪心或者二分查找。2018年的Android开发实习生的编程题,听说是有字符串处理的题目和一道动态规划的题目。

针对Android岗位的笔试编程题,刷题范围其实很明确。排序算法要掌握快排和归并排序的思路,以及它们的平均复杂度和最坏复杂度:快排平均O(nlogn),最坏O(n²),归并排序稳定在O(nlogn)但需要额外空间。链表类的题目要会反转链表、判断是否有环、找倒数第K个节点,这些题在牛客和LeetCode上都是热题。二叉树要掌握前中后序遍历和层序遍历,其中层序遍历比较常考,用队列实现。

动态规划是很多人的噩梦,但实习生笔试里的DP题一般不会太变态,最多考个背包问题或者简单的线性DP。以背包问题为例,你需要能写出二维dp数组的版本,然后能解释清楚状态转移方程的含义——dp[i][j]表示前i个物品在容量为j的背包中能获得的最大价值,转移方程是dp[i][j] = max(dp[i-1][j], dp[i-1][j-w[i]] + v[i])。这里有个面试官爱追问的点:为什么要倒序遍历j?因为要保证每个物品只被选一次,倒序遍历时可以避免在同一个物品上重复选择。

5.2 编程题的实战策略:先暴力、后优化、再兜底

笔试编程题的实战策略很重要。很多同学一上来就死磕最优解,结果卡了半小时没写出来,其他题也没时间做。我的建议是:先快速用暴力解法把题目过了,确保有分,如果时间还有富余,再优化复杂度。毕竟笔试系统只以最后提交的代码能不能AC(Accepted)为准,不看你过程多优雅。

如果是LeetCode上做练习,建议按标签来刷,比如“数组操作”刷半个月、“链表”刷一周、“二叉树”刷一周、“动态规划”刷两周。Java提交代码时有个细节要留意:数组和List之间的转换要熟练,因为笔试现场经常需要快速构造辅助的数据结构,如果API都记不熟,写的时候会非常痛苦。

编程题还有一个经常被忽略的点:输入输出格式。牛客上的笔试编程题,第一行通常是一个整数n,表示数据个数,接下来一行是n个整数,你要能用BufferedReader和BufferedWriter做高效的输入输出。用Scanner虽然写起来简单,但在数据量大的场景下性能会差不少。而且笔试环境一般不给调试提示,交上去超时了也不会告诉你到底超在哪里,所以平时练习时就要养成用高效IO的习惯。我在牛客上刷题时一开始用Scanner,后来数据量大的一道题就超时了,换成BufferedReader之后秒过,这个教训还挺深刻的。

5.3 一道典型的动态规划笔试题:从题目文本到AC代码

我之前刷过一道类似网易风格的编程题,题目描述大致是这样:给定一个长度为n的数组,每次操作可以把任意一个元素加1,问最少需要多少次操作,能让数组中所有元素相等。

这道题看似简单,实际上有个很直接的数学结论:把所有元素变成中位数需要的总变动次数最少。因为中位数到所有数字的距离和是最小的,小于平均值。所以解法就是:排序,取中位数,然后遍历累加每个元素与中位数的差值的绝对值。时间复杂度O(nlogn),主要来自排序。

代码实现大概长这样:

import java.util.Arrays; import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc = new Scanner(System.in); int n = sc.nextInt(); int[] arr = new int[n]; for (int i = 0; i < n; i++) { arr[i] = sc.nextInt(); } Arrays.sort(arr); int median = arr[n / 2]; long ans = 0; for (int num : arr) { ans += Math.abs(num - median); } System.out.println(ans); sc.close(); } }

注意这里ans要用long类型,因为最坏情况下数组长度很大、数值差异很大时,int会溢出。这个细节在笔试中非常容易被忽略,需要特别留意。

如果再延伸一下,这道题可以变成:“每次操作可以让一个元素加1或减1,问最少操作几次让所有元素相等”。答案还是一样的,取中位数。

6. 高频考点第三梯队:Android Studio与工程化能力,笔试之外的隐性考察

6.1 从工具考察看网易对实习生的工程预期

虽然很多笔试题目都是选择题形式,但网易这种级别的公司其实很看重候选人的工程化能力。2018年的Android题目里,偶尔会夹杂一两道和Android Studio、Gradle、依赖管理相关的题目,问的是apk打包流程、混淆配置这些。

先说Gradle这块,笔试可能会问Android工程的构建流程:Java源码 -> javac编译成.class文件 -> 结合第三方库和资源文件 -> 通过D8/R8编译成dex文件 -> 打包成apk -> 签名 -> 对齐。Android Gradle Plugin更新到8.0以后,构建工具链变化比较大,但核心流程还是这些。作为实习生,你不需要精通Gradle插件开发,但至少要能看懂build.gradle里的常用配置。

关于R8/混淆,笔试不会考太深,通常是问混淆配置的作用。常见的混淆配置包括keep规则:不混淆某个类的完整类名(-keep class com.example.bean.** { *; })、不混淆某个方法(-keepclassmembers)、去掉日志输出(-assumenosideeffects搭配Log)等。这部分的实操性很强,如果实际没做过,理解起来会有点抽象。我的建议是,自己找个项目在debug和release两种模式下分别打一次包,对比一下apk的字节大小和反编译后的代码内容,体感会强很多。

6.2 版本适配与兼容性:大厂格外看重的隐性能力

大厂的实习生笔试不会明着考版本适配,但在一些开放性的选择题中,偶尔会以“Android 8.0通知渠道”“Android 6.0运行时权限”这样的方式出现。你要知道Android 6.0开始引入了动态权限机制,危险权限需要在运行时申请,不能只在Manifest里声明;Android 8.0开始引入了通知渠道(NotificationChannel),创建通知时必须指定一个channel,否则通知不显示。

这些版本变化背后其实暗示了一件事:优秀工程师要有兼容性意识。不同版本的Android系统,行为和API差异非常大,开发时如果只在自己手机上调试,上线后很容易翻车。我记得有次用了Android 9.0的API来获取设备唯一标识,结果在Android 8.0的测试机上直接崩了,后来加了Build.VERSION.SDK_INT判断才解决。这就是典型的兼容性意识不足。

6.3 第三方框架的使用:会调API不算真会,理解原理才加分

实习生笔试一般不直接考Retrofit和Glide的源码细节,但面试环节有极大可能问到“你用过哪些第三方库,它们是怎么实现的”。笔试阶段相关的选择题一般围绕注解、反射等基础概念来出。

建议你在准备笔试的时候,把常用的第三方框架的底层原理过一遍,这里挑几个高频的:Retrofit的核心是“动态代理+注解解析”,OkHttp的核心是“Dispatcher+拦截器链”,Glide的核心是“三级缓存+生命周期感知”,EventBus的核心是“注解+反射”。能把这些原理说清楚,笔试和面试的通过率都会大幅提升。虽然2018年的题目没有直接列明这些框架的源码,但我猜很多同学遇到Retrofit相关题时就是把注解处理器、动态代理这些词往选项里怼,基础扎实的人一眼就能选出正确答案。

7. 常见问题与备考建议:刷题之外的避坑经验

7.1 笔试中最容易翻车的三个坑

第一个坑是Java基础不牢就冲Android题。很多初学者Android用得很爽,但一考到HashMap扩容、线程池拒绝策略、类加载顺序就露馅。笔试中Java相关的题往往占比不低,如果你Java基础薄弱,刷再多的Android题也补不回来。建议先把《Java编程思想》中集合与并发两章精读一遍,再做几套往年的真题摸一遍底。

第二个坑是会写代码不会选答案。笔试中的选择题,尤其是多选题,喜欢在一些边界条件上做文章。比如“以下哪些方法会阻塞主线程”,很多人只记得Thread.sleep阻塞,忘了Looper.loop()本身其实也是阻塞的。这类细节只有通过刷题才能暴露出来,所以做真题错题集是非常必要的。

第三个坑是时间分配不合理。网易这种笔试,题型多、时间紧,我的经验是:先做会做的,不会的题标记一下,最后再回头思考。编程题如果一时没有思路,最多留20分钟,写个暴力解法保证过测试点,比死磕最优解要实际得多。毕竟笔试的加分是看分数线的,拿到基础分比追求满分稳妥。

7.2 真题之外:如何系统化准备Android实习笔试

我的建议是分三步走。第一步,夯实基础:Java集合、JVM内存、并发编程、网络协议、数据库基础,这些属于通用基础,任何方向的实习生都会被考察。第二步,Android专项:四大组件生命周期、Handler消息机制、Binder、自定义View、事件分发、序列化、存储方式、性能优化、ANR与OOM,这些是Android岗的必考范围。第三步,刷题和总结:每周至少刷10道LeetCode算法题,同时整理一份错题集,把每道错题背后的知识点写清楚,考前集中翻阅。

还有一点值得提的是,网易笔试之后通常紧跟一轮专业面试。面试官可能会顺着你笔试的答案深挖,比如你选了“Binder性能高是因为只拷贝一次数据”,面试官就可能会问“一次拷贝具体是怎么实现的”。所以备考笔试不能只看题目本身,要带着“再往下深挖一层”的心态去准备。

7.3 笔试之后的复盘:从错题中找到知识盲区

笔试结束不等于学习结束,但我发现很多同学考完就丢,这是最可惜的。笔试是一个极好的知识自测机会,你应该在考后把每一道拿不准的题目都翻一遍书:为什么是这样?我当时为什么选错?正确的结论还有哪些延伸?我每次带实习生,都会建议他们把笔试当成一次“学习地图”,因为考试范围往往就是业内公认的工程师必备知识清单,把每道题背后的知识网络展开,学习效率和效果都比漫无目的地刷帖子好得多。

另外,很多笔试平台(牛客等)在活动结束后会放出题目解析,但2018年的题在网上还能找到原题截图和一些考生的回忆版解析,只是不够系统。你可以搜一下“网易2018实习生招聘笔试题Android”这个关键词,再搭配“牛客网 Android 笔试题”一起看,能搜到不少别人整理的题解。我在写这篇博文时也参考了当年几位网友分享的回忆版题目,在这里一并感谢他们的分享。我把公共考点整理成了一份参考表,帮助大家快速对照自查。

考察方向核心知识点高频题型准备优先级
Java基础集合、并发、JVM内存、序列化选择/多选高
Android基础四大组件、Handler、Binder选择/多选高
自定义View测量布局绘制、事件分发选择/多选中
性能优化内存泄漏、ANR、OOM选择/多选中
网络与数据HTTP状态码、图片加载缓存选择/多选中
算法编程排序、链表、二叉树、DP编程题高
工具链Gradle、混淆、版本适配选择/问答低

这套表是我根据自己的经验整理的优先级排序,供大家参考。总体思路很清晰:Java基础和Android核心机制是第一优先级,算法题是第二优先级,工具链和工程化能力是“加分项”。备考时不要本末倒置,把大量时间花在研究Gradle插件上,而忽略了Handler和四大组件这些真正的核心考点。

8. 写在最后:一份“过来人”的备战心得

我自己备考时走过不少弯路,最深刻的体会是:Android开发实习生的笔试,本质上考的是“你有没有在真实开发中咂摸过这些底层机制的味道”。死记硬背的答案,在选择题里也许能对,但到了面试环节一问细节就崩。所以准备笔试最好的方式,不是刷几套题,而是把每一个考点放到真实项目里去理解。比如你写一个网络请求工具,顺手想想OkHttp的拦截器链是怎么设计的;你做一个头像上传功能,顺手想想Bitmap采样压缩和内存占用的关系;你的App偶尔卡顿,顺手用Profile工具定位一下是不是主线程被耗时操作阻塞了——这些都是把知识从“做题”变成“内化”的过程。

回到“网易2018实习生招聘笔试题-Android开发实习生”这个标题,虽然年份已经过去这么久,但题目背后的考点体系,依然是Android岗位笔试的核心框架。我在写这篇博文时,也把当年纠结过的一些细节重新梳理了一遍,有一种温故而知新的感觉。如果你正在准备Android实习生的笔试,不妨按照我整理的这些方向逐个过一遍,再搭配真题练手,相信你会比当年的我准备得更加扎实。祝顺利。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询