2022年大厂Android面试题汇总(二)(含答案)
如今,Android开发岗的就业形势逐渐严峻,想要在众多面试者中脱颖而出,拿下大厂offer,就必须全面梳理知识点,扩充题库,并针对面试中可能遇到的关键知识,进行深入分析。本文是专为Android工程师准备的高薪面试真题汇总题库,涵盖多年大厂面经总结+核心考点深度解析,让你秒变offer收割机。由于篇幅原因分为上下两篇:上接:2022年大厂Android面试题汇总(一)(含答案)从下图可以看出ja
前言
如今,Android开发岗的就业形势逐渐严峻,想要在众多面试者中脱颖而出,拿下大厂offer,就必须全面梳理知识点,扩充题库,并针对面试中可能遇到的关键知识,进行深入分析。
本文是专为Android工程师准备的高薪面试真题汇总题库,涵盖多年大厂面经总结+核心考点深度解析,让你秒变offer收割机。
由于篇幅原因分为上下两篇:
上接:2022年大厂Android面试题汇总(一)(含答案)
Android面试真题
JVM的加载原理
1.编译机制
从下图可以看出java文件经过了一次编译后,java代码编译成java字节码(class文件)。然后再不同平台上使用不同的虚拟机(JVM)解释,解释成机器码,然后执行。由此可见,如果我们要在mac系统上运行,只需要装一个mac 的java虚拟机就可以了。这也就是java的一次编译,到处运行
2.类的加载机制
-
1.加载
加载简单来说分为三步:
第一步:获取二进制字节流也就是上面的class文件。
第二步:将静态的存储结构转换为方法区中的运行时数据结构。
第三步:生成一个对象放入java堆中,做为对方法区的引用。 -
2.验证
验证主要是检验如下的几项是否正确:
class文件的表示(魔数),class文件的版本号,class文件的每个部分是否正确(字段表、方法表等),验证常量池(常量类型、常量类型数据结构是否正确,utf-8是否标准),元数据验证(父类验证,继承验证,final验证),字节码(指令)验证,符号引用验证(是否能根据符号找到对应的字段、表、方法等)
如果一项不对,就会验证失败。 -
3.准备
准备阶段为类变量分配内存 和设置类变量初始化。这个过程中,只对static类变量进行内存分配,这个时候只是分配内存,没有进行复制,所有的类变量都是初始化值。如果是final的话,会直接对应到常量池中。会在准备阶段直接赋值。 -
4.解析
解析阶段是读符号引用进行解析。将符号引用解析为直接引用(指向目标的指针或者偏移量)。主要涉及到的解析有类,接口,字段,方法等。 -
5.初始化
初始化就是执行方法的过程, 对静态变量,静态代码块进行初始化,对类进行初始化。 -
6.使用
使用阶段就是使用这个class。 -
7.卸载
卸载阶段就是不在使用,将class给卸载。
3.类加载器
如图所示,每当我们类被加载的时候,都会去走类加载器。
我们自定义加载器有一个父类,就是AppClassLoader,而AppClassLoader也有一个父类加载器ExtClassLoader,ExtClassLoader同样也有一个父类加载器BootstarpClassLoader,BootstarpClassLoader就是最终的加载器了。
当加载类的时候,会启动类加载器,会通过自己定义的类加载器去找AppClassLoader,然后通过AppClassLoader找到ExtClassLoader,再通过ExtClassLoader找到最终BootstarpClassLoader。
可能看到这里,有人人就有疑问了,为什么我有自己的加载器,干嘛还要去找父类的加载器呢?这这种机制叫双亲委任机制,目的就是为了加载累的安全。也就是说我父类加载的不给子类去加载,这样保证最终都是BootstarpClassLoader去加载,可以保证一个类只会加载一次。而你判断两个对象时候一样的时候,最重要的一个条件就是是不是一个加载器去加载的
全局变量和局部变量的区别
1.全局变量也称为外部变量,它是在函数外部定义的变量。 它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。 只有在函数内经过说明的全局变量才能使用。全局变量的说明符为extern。 但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明
2.局部变量也称为内部变量。局部变量是在函数内作定义说明的。其作用域仅限于函数内, 离开该函数后再使用这种变量是非法的
3.在同一源文件中,允许全局变量和局部变量同名。在局部变量的作用域内,全局变量不起作用。
4.从作用域看,全局变量具有全局作用域。 全局变量只需在一个源文件中定义,就可以作用于所有的源文件。当然,其他不包含全局变量定义的源文件需要用extern 关键字再次声明这个全局变量。
- 静态局部变量具有局部作用域,它只被初始化一次,自从第一次被初始化直到程序运行结束都一直存在,它和全局变量的区别在于全局变量对所有的函数都是可见的,而静态局部变量只对定义自己的函数体始终可见。
- 局部变量也只有局部作用域,它是自动对象(auto),它在程序运行期间不是一直存在,而是只在函数执行期间存在,函数的一次调用执行结束后,变量被撤销,其所占用的内存也被收回。
- 静态全局变量也具有全局作用域,它与全局变量的区别在于如果程序包含多个文件的话,它作用于定义它的文件里,不能作用到其它文件里,即被static关键字修饰过的变量具有文件作用域。这样即使两个不同的源文件都定义了相同名字的静态全局变量,它们也是不同的变量。
5.从分配内存空间看:
全局变量,静态局部变量,静态全局变量都在静态存储区分配空间,而局部变量在栈里分配空间。
全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序,当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其它源文件中引起错误。
1)、静态变量会被放在程序的静态数据存储区(全局可见)中,这样可以在下一次调用的时候还可以保持原来的赋值。这一点是它与堆栈变量和堆变量的区别。
2)、变量用static告知编译器,自己仅仅在变量的作用范围内可见。这一点是它与全局变量的区别。
从以上分析可以看出,把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域,限制了它的使用范围。因此static 这个说明符在不同的地方所起的作用是不同的。应予以注意。
Tips:
A.若全局变量仅在单个C文件中访问,则可以将这个变量修改为静态全局变量,以降低模块间的耦合度;
B.若全局变量仅由单个函数访问,则可以将这个变量改为该函数的静态局部变量,以降低模块间的耦合度;
C.设计和使用访问动态全局变量、静态全局变量、静态局部变量的函数时,需要考虑重入问题,因为他们都放在静态数据存储区,全局可见;
D.如果我们需要一个可重入的函数,那么,我们一定要避免函数中使用static变量(这样的函数被称为:带“内部存储器”功能的的函数)
E.函数中必须要使用static变量情况:比如当某函数的返回值为指针类型时,则必须是static的局部变量的地址作为返回值,若为auto类型,则返回为错指针。
静态方法锁和非静态方法锁区别
1.一个锁的是类对象,一个锁的是实例对象。
2.若类对象被lock,则类对象的所有同步方法(static synchronized func)全被lock。
3.若实例对象被lock,则该实例对象的所有同步方法(synchronized func)全被lock。
4.关于同一个类的方法上的锁,来自于调用该方法的对象,如果调用该方法的对象是相同的,那么锁必然相同,否则就不相同。比如 new A().x() 和 new A().x(),对象不同,锁不同,如果A的单利的,就能互斥。
5.静态方法加锁,能和所有其他静态方法加锁的 进行互斥
线程本地ThreadLocal的作用
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。
ThreadLocal与synchronized同步机制的比较
在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。
ThreadLocal是线程局部变量,是一种多线程间并发访问变量的解决方案。和synchronized等加锁的方式不同,ThreadLocal完全不提供锁,而使用以空间换时间的方式,为每个线程提供变量的独立副本,以保证线程的安全。
单例模式几种写法
1.饿汉式
public class SingletonInstance {
//私有构造方法
private static SingletonInstance (){
}
//声明成员变量
private static SingletonInstance singletonInstance = new SingletonInstance();
//对外提供接口获取该实例
public static SingletonInstance getSingletonInstance(){
return singletonInstance ;
}
}
2.懒汉式
public class SingletonInstance {
//私有构造方法
private SingletonInstance (){
}
//声明成员变量
private static SingletonInstance singletonInstance ;
//对外提供接口获取该实例
public static SingletonInstance getSingletonInstance(){
if(singletonInstance == null){
singletonInstance = new SingletonInstance();
}
return singletonInstance ;
}
}
3.double check lock(dcl)
public class SingletonInstance {
//私有构造方法
private SingletonInstance (){
}
//声明成员变量
private static SingletonInstance singletonInstance ;
//对外提供接口获取该实例
public static SingletonInstance getSingletonInstance(){
if(singletonInstance == null){
synchronized (SingletonInstance.class){
//两次判断是否为null
if(singletonInstance==null){
singletonInstance = new SingletonInstance();
}
}
}
return singletonInstance ;
}
}
4.静态内部类
public class SingletonInstance {
//私有构造方法
private SingletonInstance (){
}
private static class Builder{
//声明成员变量
private static SingletonInstance singletonInstance = new SingletonInstance();
}
//对外提供接口获取该实例
public static SingletonInstance getSingletonInstance(){
return Builder.singletonInstance ;
}
}
死锁
原文链接
死锁,是指多个进程在运行过程中因争夺资源而造成的一种僵局,当进程处于这种僵持状态时,若无外力作用,它们都将无法再向前推进。 因此我们举个例子来描述,如果此时有一个线程A,按照先锁a再获得锁b的的顺序获得锁,而在此同时又有另外一个线程B,按照先锁b再锁a的顺序获得锁
1.死锁产生的原因
a. 竞争资源
系统中的资源可以分为两类:
可剥夺资源,是指某进程在获得这类资源后,该资源可以再被其他进程或系统剥夺,CPU和主存均属于可剥夺性资源;
另一类资源是不可剥夺资源,当系统把这类资源分配给某进程后,再不能强行收回,只能在进程用完后自行释放,如磁带机、打印机等。
产生死锁中的竞争资源之一指的是竞争不可剥夺资源(例如:系统中只有一台打印机,可供进程P1使用,假定P1已占用了打印机,若P2继续要求打印机打印将阻塞)
产生死锁中的竞争资源另外一种资源指的是竞争临时资源(临时资源包括硬件中断、信号、消息、缓冲区内的消息等),通常消息通信顺序进行不当,则会产生死锁
b. 进程间推进顺序非法
若P1保持了资源R1,P2保持了资源R2,系统处于不安全状态,因为这两个进程再向前推进,便可能发生死锁
例如,当P1运行到P1:Request(R2)时,将因R2已被P2占用而阻塞;当P2运行到P2:Request(R1)时,也将因R1已被P1占用而阻塞,于是发生进程死锁
2.产生死锁的必要条件
1.互斥条件:进程要求对所分配的资源进行排它性控制,即在一段时间内某资源仅为一进程所占用。
2.请求和保持条件:当进程因请求资源而阻塞时,对已获得的资源保持不放。
3.不剥夺条件:进程已获得的资源在未使用完之前,不能剥夺,只能在使用完时由自己释放。
4.环路等待条件:在发生死锁时,必然存在一个进程–资源的环形链。
3.解决死锁的基本方法
1.资源一次性分配:一次性分配所有资源,这样就不会再有请求了:(破坏请求条件)
只要有一个资源得不到分配,也不给这个进程分配其他的资源:(破坏请保持条件)
2.可剥夺资源:即当某进程获得了部分资源,但得不到其它资源,则释放已占有的资源(破坏不可剥夺条件)
资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)
——————————————————————————
待更新。。。
1.事件分发,dispatch和intercept和ontouchevent是如何执行的, onTouchEvent是如何执行的
2.场景1:addView方法加载A布局然后remove(A),然后addView(B布局)方法执行 场景2:addView方法加载A然后addView(B), requestView方法在场景1和2分别是如何执行的, 各自执行效率如何
3.布局A经过scale进行属性缩放, 缩放之后y轴对应的点坐标有没有变化
4.数组和链表区别
5.rxjava中map和flapmap区别
6.viewGroup和view的onDraw方法区别
7.recyclerview的缓存机制,item不可见时是否回执行onBind方法
8.requestLayout,measureLayout,invalidata区别
9.图片压缩加载
10.项目中的设计模式理解
11.进程间通讯
12.单例模式双重校验机制
13.内存优化
14.ANR
15.webview处理
16.hashmap怎么实现的,扩展大小机制
17.图片压缩,狂傲压缩和质量压缩
18.https的ssl
19.有哪些进程通信,binder机制底层
20.activity里面的setContentView做了什么
21.acitvity,window,surface三者区别
22.listview和recyclerview区别
23.recyclerview四级缓存,自定义缓存如何做
24.GC回收机制,是如何世道那个对象被回收的
25.算法,输入两个链表,让其合一起输出一个链表
作者:Wocus
链接:https://www.jianshu.com/p/c7b6c6851231
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
最后
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。
现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
【Android 详细知识点思维脑图(技能树)】
【BATJ安卓开发工程师面试题库】
【详细可整理扫描下方二维码免费领取】
更多推荐
所有评论(0)