前言
马上就要到金九银十面试季了,需要找工作的小伙伴们要行动起来开始准备复习了。特此将我今年收录的一些腾讯、阿里巴巴、字节跳动等等一线互联网巨头的面试真题体系的归类、整理了一下,拿来出分享给大家,希望能给有需要的小伙伴一些力所能及的帮助。
除了面试题外,还给大家总结了以下的一些面试技巧:
- 准备面试知识:对待面试需有前瞻性。了解公司、职位和行业相关的技术要求,并研究常见的面试问题和算法/数据结构问题。也要复习基本的编程语言、数据库、网络等方面的知识。
- 编写简洁而规范的代码:在面试过程中,重视展示你的编码能力。写出易读、可维护和高效的代码。遵守最佳实践,使用适当的命名规范并注意缩进和注释。
- 理解问题并提出合适的解决方案:仔细阅读面试题目,确保理解问题的核心需求。在思考解决方案时,考虑多种可能性,并权衡不同的优缺点。如果有疑问,可以向面试官提出澄清问题。
- 打牢基础知识:掌握计算机科学的基础概念,如数据结构、算法、操作系统、数据库等。这些知识将帮助你更好地理解问题,提供有效的解决方案。
- 强调沟通能力:除了技术能力,沟通能力也是程序员职业发展中的重要一环。在面试中,清晰地表达你的思路、解决方案和观点。与面试官建立良好的沟通,与团队合作的能力也很重要。
- 自信与谦逊并存:展示自信而谦虚的态度。肯定你的技能和经验,但同时也愿意接受他人的意见和反馈。以积极的姿态对待复杂或未知的问题,展示解决问题的能力。
- 进行实践和模拟面试:通过参加模拟面试和刷题训练平台,提高解决问题的速度和效率。多做编程练习,培养对各种算法和数据结构的熟悉程度。
- 学会分析和优化代码:在面试过程中,注重对代码的分析和优化。思考时间和空间复杂度,并提出改进方案。优化代码的能力将体现你对性能和效率的关注。
- 关注最新技术趋势:了解当前的技术趋势和前沿领域。对新兴技术有所了解,并能就其应用场景进行讨论和展示。
- 胜任常规面试问题:准备回答关于你的技能、职业经历和项目经验的问题。为了展示你的个人价值观和职业目标,思考并准备好针对这些问题的清晰、有说服力的回答。
好了,面试技巧就分享到这,接下来的才是重点!!
面试题展示
一、Java 中深拷贝与浅拷贝的区别?
1、浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,此为浅拷贝。
2、深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。
二、谈谈Error和Exception的区别?
1、Exception是java程序运行中可预料的异常情况,咱们可以获取到这种异常,并且对这种异常进行业务外的处理。
2、Error是java程序运行中不可预料的异常情况,这种异常发生以后,会直接导致JVM不可处理或者不可恢复的情况。所以这
种异常不可能抓取到,比如OutOfMemoryError、NoClassDefFoundError等。
三、什么是反射机制?反射机制的应用场景有哪些?
Java 反射机制是在运行状态中,对于任意一个类,都能够知道这个类中的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。 应用场景:1. 逆向代码,例如反编译2. 与注解相结合的框架,如 Retrofit3. 单纯的反射机制应用框架,例如 EventBus(事件总线)4. 动态生成类框架 例如Gson
四、谈一谈startService和bindService的区别,生命周期以及使用场景?
-
生命周期上的区别
执行startService时,Service会经历onCreate- >onStartCommand。当执行stopService时,直接调用onDestroy方法。调用者如果没有stopService,Service 会一直在后台运行,下次调用者再起来仍然可以stopService。执行bindService时,Service会经历onCreate- >onBind。这个时候调用者和Service绑定在一起。调用者调用unbindService方法或者调用者Context不存在了(如Activity被finish了),Service就会调用onUnbind- >onDestroy。这里所谓的绑定在一起就是说两者共存亡了。多次调用startService,该Service只能被创建一次,即该Service的onCreate方法只会被调用一次。但是每次调用startService,onStartCommand方法都会被调用。Service的onStart方法在API 5时被废弃,替代它的是onStartCommand方法。第一次执行bindService时,onCreate和onBind方法会被调用,但是多次执行bindService时,onCreate和onBind 方法并不会被多次调用,即并不会多次创建服务和绑定服务。 -
调用者如何获取绑定后的Service的方法
onBind回调方法将返回给客户端一个IBinder接口实例,IBinder允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。我们需要IBinder对象返回具体的Service对象才能操作,所以说具体的Service对象必须首先实现Binder对象。 -
既使用startService又使用bindService的情况
如果一个Service又被启动又被绑定,则该Service会一直在后台运行。首先不管如何调用,onCreate始终只会调用一次。对应startService调用多少次,Service的onStart 方法便会调用多少次。Service的终止,需要unbindService和stopService同时调用才行。不管startService与bindService的调用顺序,如果先调用unbindService,此时服务不会自动终止,再调用stopService之后,服务才会终止;如果先调用stopService,此时服务也不会终止,而再调用unbindService或者之前调用bindService的Context不存在了(如Activity被finish的时候)之后,服务才会自动停止。
那么,什么情况下既使用startService,又使用bindService呢?
如果你只是想要启动一个后台服务长期进行某项任务,那么使用startService便可以了。如果你还想要与正在运行的Service取得联系,那么有两种方法:一种是使用broadcast,另一种是使用bindService。前者的缺点是如果交流较为频繁,容易造成性能上的问题,而后者则没有这些问题。因此,这种情况就需要startService和bindService一起使用了。
另外,如果你的服务只是公开一个远程接口,供连接上的客户端(Android的Service是C/S架构)远程调用执行方法,这个时候你可以不让服务一开始就运行,而只是bindService,这样在第一次bindService的时候才会创建服务的实例运行它,这会节约很多系统资源,特别是如果你的服务是远程服务,那么效果会越明显(当然在Servcie创建的是偶会花去一定时间,这点需要注意)。 -
本地服务与远程服务
本地服务依附在主进程上,在一定程度上节约了资源。本地服务因为是在同一进程,因此不需要IPC,也不需要AIDL。相应
bindService会方便很多。缺点是主进程被kill后,服务变会终止。远程服务是独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被kill的是偶,该服务依然在运行。缺点是该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。对于startService来说,不管是本地服务还是远程服务,我们需要做的工作都一样简单。
五、谈谈你对 Activity.runOnUiThread 的理解?
一般是用来将一个runnable绑定到主线程,在runOnUiThread源码里面会判断当前runnable是否是主线程,如果是直接run,如果不是,通过一个默认的空构造函数handler将runnable post 到looper里面,创建构造函数handler,会默认绑定一个主线程的looper对象
六、子线程能否更新UI?为什么?
子线程是不能直接更新UI的
注意这句话,是不能直接更新,不是不能更新(极端情况下可更新)绘制过程要保持同步(否则页面不流畅),而我们的主线程负责绘制ui,极端情况就是,在Activity的onResume(含)之前的生命周期中子线程都可以进行更新ui,也就是onCreate,onStart和onResume,此时主线程的绘制还没开始。
七、你了解 Android 系统启动流程吗?
当按电源键触发开机,首先会从ROM中预定义的地方加载引导程序BootLoader 到 RAM中,并执行BootLoader程序启动Linux Kernel,然后启动用户级别的第一个进程: init 进程。init 进程会解析init.rc脚本做一些初始化工作,包括挂载文件系统创建工作目录以及启动系统服务进程等,其中系统服务进程包括 Zygote、service manager、media 等。在 Zygote中会进一步去启动 system_ server进程,然后在 system_server 进程中会启动AMS、WMS、PMS等服务,等这些服务启动之后,AMS中就会打开Launcher应用的home Activity,最终就看到了手机的“桌面”。
八、Binder有什么优势
性能方面
- 共享内存 0次数据拷贝
- Binder 1次数据拷贝
- Socket/管道/消息队列 2次数据拷贝
稳定性方面
- Binder:基于C/S架构,客户端 (Client) 有什么需求就丢给服务端 (Server) 去完成,架构清晰、职责明确又相互独立,自然稳定性更好
- 共享内存:虽然无需拷贝,但是控制复杂,难以使用
- 从稳定性的角度讲,Binder机制是优于内存共享的。
安全性方面
- 传统的IPC没有任何安全措施,安全依赖上层协议来确保。
- 传统的IPC方法无法获得对方可靠的进程用户ID/进程UI (UID/PID) ,从而无法鉴别对方身份。
- 传统的IPC只能由用户在数据包中填入UID/PID,容易被恶意程序利用。
- 传统的IPC访问接入点是开放的,无法阻止恶意程序通过猜测接收方地址获得连接。
- Binder既支持实名Binder,又支持匿名Binder,安全性高。
九、Binder机制是如何跨进程的
- Binder驱动
- 在内核空间创建一块接收缓存区,
- 实现地址映射:将内核缓存区、接收进程用户空间映射到同一接收缓存区
- 发送进程通过系统调用 (copy_from_user) 将数据发送到内核缓存区。由于内核缓存区和
接收进程用户空间存在映射关系,故相当于也发送了接收进程的用户空间,实现了跨进程通信
十、简述下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()
由于文章篇幅有限,不能将我整理的所有面试题全部展示出来,不过也没关系,我已经将所有的面试题整理好了
最后
面试是一个挑战和机遇并存的过程,不论结果如何,都当做一次宝贵的经验。
整理不易,想要进阶或者是更多面试题,查看我的个人简介,说不定就有你需要的