前言
轻松掌握程序员面试技巧,功夫在字面之间!
当今的科技行业中,程序员是一个备受关注和追捧的职业。无论你是正在寻找第一份编程工作,还是想要提升自己在行业中的地位,面试都是你必须经历的一道门槛。面对激烈的竞争,如何在面试中脱颖而出成为了每个程序员所关注的话题。
程序员面试并非只是简单的回答问题,它更像是一场技术较量和能力展示的舞台。在面试中,除了展示你的技术知识和编码能力,还需要体现你的逻辑思维、解决问题的能力以及团队合作的素质。因此,在走向面试前的准备工作尤为重要。
面试前的准备
在这里要给大家的建议就是:准备面试的小伙伴,一定要根据自身情况制定好复习计划! 并且,你最好还要时不时自测一下,对着一些面试常见的问题进行自问。这样查漏补缺,找到自己的问题所在。之后再着实从高频的面试题考试入手,因为高频的面试题被问答的概率远远大于其他面试题,以点入面,把这一个面试题涉及到的所有面试题(面试点)深入详细的搞懂。
常见面试题
一、Java异常机制中,异常Exception与错误Error区别
这道题想考察什么?
在开发时需要时候需要自定义异常时,应该选择定义Excption还是Error?编写的代码触发Excption或者Error分别代表什么?
考察的知识点
Java异常机制
考生应该如何回答
在Java中存在一个Throwable
可抛出类,Throwable
有两个重要的子类,一个是Error,另一个则是Exception。
Error
是程序不能处理的错误,表示程序中较严重问题。例如,Java虚拟机运行错误(Virtual MachineError),当 JVM 不再有继续执行操作所需的内存资源时,将出现 OutOfMemoryError等等。这些错误发生时,JVM一般会选择线程终止。这些错误是不可查的,它们在程序的控制和处理能力之外,而且绝大多数是程序运行时不允许出现的状况。
Exception
是程序可以处理的异常。而Exception又分为运行时异常(RuntimeException)与非运行时异常。
- 运行异常
运行时异常,又称不受检查异常 。所谓的不受检查,指的是Java编译检查时不会告诉我们有这个异常,需要在运行时候才会暴露出来,比如下标越界,空指针异常等。 - 非运行时异常
RuntimeException之外的异常我们统称为非运行时异常,比如IOException、SQLException,是必须进行处理的异常(检查异常),如果不处理(throw到上层或者try-catch),程序就不能编译通过。
二、谈谈ArrayList和LinkedList的区别?
- ArrayList是基于数组的数据结构,LinkedList是基于链表的数据结构。
- ArrayList适用于查询操作,LinkedList适用于插入和删除操作。
三、请简述 LinkedHashMap 的工作原理和使用方式?
查看LinkedHashMap源码发现是继承HashMap实现Map接口。也就是HashMap的方法LinkedMap都有。
LinkHashMap与HashMap的主要区别是:LinkedHashMap是有序的,hashmap是无序的。LinkedHashMap通过维护一个双向链表实现有序,也正是因为要维护这个链表,内存上有更大的开销。
补充下有序和无序:我们说的无序是插入顺序和输出顺序不一致。
补充一下链表结构和顺序结构:线性结构分为顺序结构,和链表结构。
- 顺序结构:在内存中是一块完整有序内存。所以我们在查询的时候时候直接索引index,便可找到要查询的数据,速度非常快,缺点是插入删除慢。有点类似班级排队时(一列纵队),每个人都知道自己在第几个位置。老师只要说第三个位置,那这个同学立马知道老师要找的是自己。这时候要插入一个同学到第二个位置,所以之前第二个位置开始往后的每个同学的位置都要+1。所以比较慢。
- 链表结构:通过结点头记录该结点的上一个结点和下一个下一个结点(就是传统的双链表,单链表就是只记录下一个结点,循环链表就是最后一个结点的下一个结点指向第一个结点)。正是因为这种关系,所以链表结构不需要一块完整的内存,而且插入删除相对快,但是查询相对慢。但是因为要维护结点头,所以内存开销相对大一点。有点类似于班级排队时,每个人虽然不知道自己的位置,但是知道自己前面是谁和后面是谁。当要插入一个同学b时到c前面时,只要c 同学记住自己之前是a,现在换成b.b记住自己前面是a,后面是c。所以想对来说插入很快。删除类似。但是当老师按位置查询时,就要先从第一个开始计数,知道找到老师要找的数字。所以查询慢。
四、如何实现多线程中的同步?
多线程同步和异步不是一回事。几种情况,
- 就是大家说的synchronized 他可以保证原子性,保证多个线程在操作同一方法时只有一个线程可以持有锁,并且操作该方法,
- 就是手动调用读写锁,
- 手动操作线程的wait和notify
- volatile我记得是没有原子性的,他可以保证内存可见性,在多线程的情况下保证每个线程的数据都是最新的
五、谈谈你对 Activity.runOnUiThread 的理解?
一般是用来将一个runnable绑定到主线程,在runOnUiThread源码里面会判断当前runnable是否是主线程,如果是直接run,如果不是,通过一个默认的空构造函数handler将runnable post 到looper里面,创建构造函数handler,会默认绑定一个主线程的looper对象
六、子线程能否更新UI?为什么?
子线程是不能直接更新UI的
注意这句话,是不能直接更新,不是不能更新(极端情况下可更新)绘制过程要保持同步(否则页面不流畅),而我们的主线程负责绘制ui,极端情况就是,在Activity的onResume(含)之前的生命周期中子线程都可以进行更新ui,也就是onCreate,onStart和onResume,此时主线程的绘制还没开始。
七、 谈谈 Handler 机制和原理?
首先在UI线程我们创建了一个Handler实例对象,无论是匿名内部类还是自定义类生成的Handler实例对象,我们都需要对handleMessage方法进行重写,在handleMessage方法中我们可以通过参数msg来写接受消息过后UIi线程的逻辑处理,接着我们创建子线程,在子线程中需要更新UI的时候,新建一个Message对象,并且将消息的数据记录在这个消息对象Message的内部,比如arg1,arg2,obj等,然后通过前面的Handler实例对象调用sendMessge方法把这个Message实例对象发送出去,之后这个消息会被存放于MessageQueue中等待被处理,此时MessageQueue的管家Looper正在不停的把MessageQueue存在的消息取出来,通过回调dispatchMessage方法将消息传递给Handler的handleMessage方法,最终前面提到的消息会被Looper 从MessageQueue中取出来传递给handleMessage方法。
八、简述下Handler机制的总体原理
- Looper 准备和开启轮循: Looper#prepare()初始化线程独有的 Looper 以及 MessageQueue Looper#loop()开启死循环读取 MessageQueue 中下一个满足执行时间的 Message 尚无 Message 的话,调用
Native 侧的 pollOnce()进入无限等待存在 Message,但执行时间 when 尚未满足的话,调用
pollOnce()时传入剩余时长参数进入有限等待 - Message 发送、入队和出队: Native 侧如果处于无限等待的话:任意线程向 Handler 发送 Message 或 Runnable 后,Message 将按照 when 条件的先后,被插入 Handler 持有的 Looper 实例所对应的MessageQueue 中适当的位置。 MessageQueue 发现有合适的 Message 插入后将调用 Native 侧的wake()唤醒无限等待的线程。这将促使 MessageQueue 的读取继续进入下一次循环,此刻 Queue 中已有满足条件的Message 则出队返回给 Looper Native 侧如果处于有限等待的话:在等待指定时长后 epoll_wait将返回。线程继续读取 MessageQueue,此刻因为时长条件将满足将其出队 Looper 处理Message 的实现:
- Looper 得到 Message 后回调 Message 的 callback 属性即 Runnable,或依据 target 属性即 Handler,去执行 Handler 的回调。存在 mCallback属性的话回调 Handler$Callback 反之,回调 handleMessage()
以上这些面试题是我梳理了各大厂的春秋招面经分享,总结了上千道面试真题,包含三十二个模块,分别是:「Java 基础、集合、多线程、虚拟机、反射、泛型、并发编程、Android四大组件、异步任务和消息机制、UI绘制、性能调优、SDN、第三方框架、设计模式、Kotlin、计算机网络、系统启动流程、Binder、Handler、AMS、Dart、Flutter、算法和数据结构、NDK、H.264、H.265.音频编解码、FFmpeg、OpenMax、OpenCV、OpenGL ES」 ,如下图所示: