前言
本文乃找工作、招人必备之良品。后期不断完善中……
对于面试仍然充满畏惧?多次面试后信心被搓,被恶心的面试官打击?这里有一份宝典合集:包括有《1599页Android面试宝典》+《Android Framework 开发揭秘》+《Android项目合集》等等,一切你需要的面试资料,我们都有合集大礼包等着你,需要的朋友可以添加下面VX回复JJ即可领取,100%免费!!!
这是我既前两次做Android面试题合集之后重新整理和完善的一份新的面试题。
写在前面,之前面试题带入的都是咱们求职者的视角,这次我联络的是部分招聘人员,当然具体名字和公司不方便透露,总之,该部分面试是从招聘角度出发,当然作为求职者我觉得只会收获更加丰富。
如何招聘人,搜集了一些知识点。如何做好应聘准备,也收集了一些主要知识点,供你参考。
- Android基础知识:基本涵盖Android所有知识体系,四大组件,Fragment,WebView,事件分发,View绘制…
- Java基础知识&高阶知识点:基础部分不谈了,高阶部分:泛型,反射,Java虚拟机…
- 算法与数据结构:链表,堆,栈,树…
- Android常用框架:异步,网络,图片加载,内存优化,依赖注入,数据库等框架
- Android前沿技术:Android组件化,热更新,插件化,消息推送,AOP面向切面编程,Flutter(谷歌的移动UI框架)…
- 源码分析:Android源码分析,启动一个app的源码分析,常用框架源码分析,Java源码分析,集合源码分析…
- 网络基础:五层网络模型,三次握手&四次挥手,请求头&响应头,Socket&WebSocket…
21.ArrayMap和HashMap的区别
ArrayMap相比传统的HashMap速度更慢,因为其查找方法是二分法,并且当删除或添加数据时,会对空间重新调整,可以说ArrayMap是牺牲了时间来换空间,ArrayMap与HashMap的区别主要在:
存储方式不同:HashMap内部有一个HashMapEntry<K,V>[ ]对象,而ArrayMap是一个<key,value>映射的数据结构,内部使用两个数组进行数据存储,一个数组记录key的hash值,另一个数组记录value值。
添加数据时扩容的处理不一样:HashMap进行了new操作,重新创建对象,开销很大,而ArrayMap用的是copy数据,效率相对高很多。
ArrayMap提供了数组收缩的功能,在clear或remove之后,会重新收缩数组,释放空间。
ArrayMap采用的是二分法查找。
22.HashMap和HashTable的区别
HashMap是基于哈希表实现的,每一个元素是一个key—value对,其内部通过单链表解决冲突的问题HashMap是非线程安全的,只适用于单线程的环境下。多线程的环境下可以采用concurrent并发包下的concurrentHashMap,HsahMap实现了serializable接口,支持序列化,实现了cloneable接口,能被克隆。HashMap内部维持了一个存储数据的Entry数组,HashMap采用链表解决冲突,HashMap中的key和value都允许为null,key为null的键值对永远都放在以table[0]为节点的链表中。
HashTable同样是基于哈希表实现的,同样每个元素是一个key-value对,其内部也是通过单链表解决冲突问题,容量不足时,同样会自动增大,HashTble是线程安全的,能用在多线程的环境下,HashTable实现了serializable接口,它支持序列化,实现了cloneable接口,能被克隆。
HashMap和HashTable之间的区别有以下几点
继承的父类不同,hashTable继承自Dictionary类,而HashMap继承自AbstractMap类,但二者都实现了Map接口。
线程安全性不同,HashTable中的方法是synchronized的,而HashMap中的方法在缺省的情况下是非ynchronized的,在多线程的环境下,可以直接使用HsahTable,不需要为他的方法实现同步,但使用HashMap时就必须自己增加同步处理。
key和value是否允许为null值:关于其中的key和value都是对象,并且不能包含重复的key,但可以包含重复的value,hashtable中,key和value都不允许出现null值,但在hashmap中,null可以作为键,这样的键只有一个,可以有多个键对应的值为null.
23.HashMap和HashSet的区别
HashMap:其实现了Map接口,HashMap存储键值对,使用put( )方法将元素放入到Map中,HashMap使用键对象来计算hashcode值,HashMap比较快,因为是使用唯一的键来获取对象。
HashSet:实现了Set接口,hashSet仅仅存储对象,使用add()方法将元素放入到set中,hashset使用成员对象来计算hashcode值,对于两个对象来说,hashcode可能相同,所以equal方法用来判断对象的相等性,如果两个对象不同的话,那么返回false,hashSet较hashMap来说较慢。
24.ArrayList和LinkedList的区别
ArrayList和LinkedList,前者是Array(动态数组)的数据结构,后者是Link(链表)的数据结构,此外他们两个都是对List接口的实现
当随机访问List时(get和set操作),ArrayList和LinkedList的效率更高,因为LinkedList是线性的数据存储方式,所以需要移动指针从前往后查找。
当对数据进行增删的操作时(add和remove),LinkedList比ArrayList的效率更高,因为ArrayList是数组,所以在其中进行增删操作时,会对操作点之后的所有数据的下标索引造成影响,需要进行数据的移动
从利用效率来看,ArrayList自由性较低,因为需要手动的设置固定大小的容量,但是他的使用比较方便,只需要创建,然后添加数据,通过调用下标进行使用,而LinkedList自由性交给,能够动态的随数据量的变化而变化,但是它不便于使用。
25.数组和链表的区别
数组:是将元素在内存中连续的存储的,因为数据是连续存储的,内存地址连续,所以在查找数据的时候效率比较高,但在存储之前,需要申请一块连续的内存空间,并且在编译的时候就必须确定好他的空间大小。在运行的时候空间的大小是无法随着需要进行增加和减少的,当数据比较大时,有可能出现越界的情况,当数据比较小时,有可能浪费内存空间,在改变数据个数时,增加、插入、删除数据效率比较低。
链表:是动态申请内存空间的,不需要像数组需要提前申请好内存的大小,链表只需在使用的时候申请就可以了,根据需要动态的申请或删除内存空间,对于数据增加和删除以及插入比数组灵活,链表中数据在内存中可以在任意的位置,通过应用来关联数据。
26.Java中多线程实现的三种方式
Java中多线程实现的方式主要有三种:继承Thread类、实现Runnable接口、使用ExecutorService、Callable、Future实现有返回结果的多线程,其中前两种方式线程执行完没有返回值,只有最后一种是带返回值的。
继承Thread类实现多线程:继承Thread类本质上也是实现Tunnable接口的一个实例,他代表一个线程的实例,并且启动线程的唯一方法是通过Thread类的start()方法,start()方法是一个native方法,他将启动一个新线程,并执行run( )方法。
实现Runnable接口方式实现多线程:实例化一个Thread对象,并传入实现的Runnable接口,当传入一个Runnable target参数给Thread后,Thraed的run()方法就会调用target.run( );
使用ExecutorService、Callable、Future实现有返回结果的多线程:可返回值的任务必须实现Callable接口,类似的无返回值的任务必须实现Runnable接口,执行Callable任务后,可以获取一个Future的对象,在该对象上调用get就可以获取到Callable任务返回的Object了,在结合线程池接口ExecutorService就可以实现有返回结果的多线程。
27.Java中创建线程的三种方式
Java中使用Thread类代表线程,所有的线程对象都必须时Thread类或其子类的实例,Java中可以用三种方式来创建线程
继承Java中的Thread类创建线程:定义Thread类的子类,并重写其run( )方法,run( )方法也称为线程执行体,创建该子类的实例,调用线程的start()方法启动线程。
实现Runnable接口创建线程:定义Runnable接口的实现类,重写run()方法,run方法是线程的执行体,创建Runnable实现类的实例,并用这个实例作为Thread的target来创建Thread对象,这个Thread对象才是真正的线程对象,调用线程对象的Start方法启动线程。
使用Callable和Future创建线程:Callable接口提供了一个call( )方法,作为线程的执行体,call( )方法可以有返回值,call( )方法可以声明抛出异常,其创建线程并启动的步骤,创建Callable接口的实现类,并实现call( )方法,创建该实现类的实例,使用FutureTask类来包装Callable对象,该FuutureTask对象封装了callable对象的call( )方法的返回值,使用FutureTask对象作为Thread对象的target创建并启动线程,调用FutureTask对象的get( )方法来获得子线程执行结束后的返回值。
28.线程和进程的区别
线程是进程的子集,一个进程可以有很多线程,每条线程并行执行不同的任务,不同的进程使用不同的内存空间,而所有的线程共享一片相同的内存空间,注意勿与栈内存混淆,每个线程都拥有单独的栈内存用来存储本地数据。
29.Java中的线程的run( )方法和start()方法的区别
start()方法被用来启动新创建的线程,而且start( )内部调用了run ( )方法,这和直接调用run( )方法的效果不同,当调用run( )方法时,只会是在原来的线程中调用,没有新的线程启动,只有start( )方法才会启动新线程。
30.如何控制某个方法允许并发访问线程的个数
在Java中常使用Semaphore(信号量)来进行并发编程,Semaphore控制的是线程并发的数量,实例化一个Semaphore对象,如Semaphore semaphore = newSemaphore(5,true) ,其创建了对象semaphore,并初始化了5个信号量,即最多允许5个线程并发访问,在执行的任务中,调用semaphore的acquire()方法请求一个信号量,这时信号量个数就减1,(一旦没有可使用的信号量,再次请求就会阻塞),来执行任务,执行完任务,调用semaphore的release()方法释放一个信号量此时信号量的个数就会加1 。
31.Java中wait和sleep方法的不同
Java程序中wait和sleep都会造成某种形式的暂停,sleep()方法属于Thread类中,而wait( )方法属于Object类中,sleep( )方法是让程序暂停执行指定的时间,释放CPU资源,但不会释放锁,线程的监控状态依然保持着,当指定的时间到了又会自动恢复运行状态,而当调用wait( )方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后线程才进入对象锁定池准备,获取对象锁进入运行状态。
32.对Java中wait/notify关键字的理解
wait()、notify()、notifyAll( )都不属于Thread类,而是属于Object基础类,也就是每个对象都有wait( )、notify()、notifyAll( )的功能,因为每个对象都有锁,锁是每个对象的基础。
wait():会把持有该对象线程的对象控制权交出去,然后处于等待状态。
notify():会通知某个正在等待这个对象的控制权的线程可以运行。
notifyAll( ):会通知所有等待这个对象的控制权的线程继续运行,如果有多个正在等待该对象控制权时,具体唤醒哪个线程,就由操作系统进行调度。
33.什么是线程阻塞?线程该如何关闭?
阻塞式方法是指程序会一直等待该方法完成执行,而在此期间不做其他的事情,例如ServerSocket的accept( )方法就是一直等待客户端连接,这里的阻塞是指调用结果返回之前,当前线程会被挂起,直到得到结果之后才返回你,此外还有异步和非阻塞式方法在任务完成前就返回。
线程关闭的方法有如下两种:
一种是调用线程的stop( )方法;
另一种是自己自行设计一个停止线程的标记;
34.如何保证线程的安全
使用Synchronized关键字:
调用Object类的wait很notify;
通过ThreadLocal机制实现;
35.实现线程同步的方式
Java允许线程并发控制,当多个线程同时操作一个可共享的资源变量时(如数据的增删改查),将会导致数据的不准确,相互之间产生冲突,因此在加入同步锁以避免在该线程没有完成操作之前,被其他线程调用,从而保证该变量的唯一性和准确性,同步的方法有以下几种:
Synchronized关键字修饰的方法:由于Java的每个对象都有一个内置锁,当用此关键字修饰方法时,内置锁会保护整个方法;
Synchronized关键字修饰语句块:被Synchronized关键字修饰的语句块会自动加上内置锁,从而实现同步;
使用特殊域变量(volatile)实现线程同步:当一个共享变量被volatile修饰时,他会保证修改的值立即被更新到主存中,volatile的特殊性在于,内存可见性,就是一个线程对于volatile变量的修改,对于其他线程来说是可见的,即线程每次获取volatile变量的值都是最新的。
36.Java中Synchronized关键字的用法,以及对象锁、方法锁、类锁的理解
Java的内置锁:每个Java对象都可以用作一个实现同步的锁,这些锁称为内置锁,线程进入同步代码块或方法时,会自动获得该锁,在退出同步代码块或方法时会释放该锁,获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法。
Java的内置锁是一个互斥锁,这即意味着最多只有一个线程获得该锁,当线程A尝试去获得线程B持有的内置锁时,线程A必须等待或阻塞,直到线程B释放该锁,如果B线程不释放该锁,那么A线程将一直等待下去。
Java的对象锁与类锁,对象锁是用于实例方法的,或者一个对象实例上的,类锁是用于类的静态方法或者一个类的class对象上的,类的对象实例可以有很多个,但每个类只有一个class对象,所以不同对象的实例的对象锁是互不干扰,但是每个类只有一个类锁。
Synchronized的用法:Synchronized修饰方法和修饰代码块。
37.Java中锁与同步的相关知识
锁提供了两种主要的特性:互斥和可见性
互斥即一次只允许一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调访问协议,这样,一次就只有一个线程能够使用该共享的数据;可见性在必须确保锁释放之前对共享对象做出的更改对于随后获得该锁的另一个线程是可见的。
在Java中,为了确保多线程读写数据时的一致性,可以采用两种方式
同步:如使用synchronized关键字,或者使用锁对象;
使用volatile关键字:使变量的值发生改变时尽快通知其他线程;
Volatile关键字详解
编译器为了加快程序的运行的速度,对一些变量的写操作会先在寄存器或者CPU缓存上进行,最后写入内存中,而在这个过程中,变量的新值对于其他线程是不可见的,当对使用volatile标记的变量进行修改时,会将其它缓存中存储的修改前的变量清除,然后重新读取。
38.Synchronized和volatile关键字的区别
Volatile在本质上是告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主存中读取;Synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程会被阻塞。
Volatile仅能使用在变量级别,synchronized则可以使用在变量、方法和类级别。
Volatile仅能修改变量的可见性,不能保证原子性,而synchronized则可以保证变量的修改可见性和原子性。
Volatile不会造成线程的阻塞,synchronized可能会造成线程的阻塞。
Volatile标记的变量不会被编译器优化,synchronized标记的变量可以被编译器优化。
39.Java的原子性、可见性、有序性的理解
原子性:原子是世界上最小的物质单位,具有不可分割性,比如a=0,这个操作是不可分割的,那么我们就会说这个操作是原子操作,再如a++,这个操作实际上是a=a+1,是可以分割的,所以他不是一个原子操作,非原子操作都会存在线程安全的问题,需要使用synchronized同步技术来使其变成一个原子操作,一个操作是原子操作,那么我么称它具有原子性。
可见性:是指线程之间的可见性,一个线程修改的状态对于另一个线程是可见的,比如用volatile修饰的变量就具有可见性,volatile修饰的变量不允许线程内部缓存和重排序,即会直接修改内存,所以对其他线程是可见的,但volatile只能让其被修饰的内容具有可见性,并不能保证它具有原子性,因为volatile仅能使用在变量级别,并不能对方法进行修饰,
有序性:即线程执行的顺序按代码的先后顺序执行,在Java内存模型中,允许编译器和处理器对指令进行重排序,但重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性,在Java里面可以通过volatile关键字来保证一定的“有序性”,另外还可以通过synchronized和Lock来保证有序性。
40.ReentrantLock、Synchronized、Volatile关键字
Synchronized:即互斥锁,即操作互斥,并发线程,串行得到锁,串行执行代码,就像一个房间一把钥匙,一个人进去后,下一个人必须等到第一个人出来得到钥匙才能进去;
ReetrantLock:可重入锁,和同步锁功能类似,不过需要显性的创建和销毁,其特点在于①ReentrantLock有try Lock方法,如果锁被其他线程持有,返回false,可以避免形成死锁,②创建时可自定义是可抢占的,③ReentrantReadWriteLock,用于读多写少,且不需要互斥的场景大大提高性能;
Volatile:只保证同意变量在多线程中的可见,他会强制将对缓存的修改操作立即写入主存,如果是写操作,会导致其他CPU对应的缓存无效;
最后
按照国际惯例,给大家分享一套十分好用的Android进阶资料:《全网最全Android开发笔记》。
整个笔记一共8大模块、729个知识点,3382页,66万字,可以说覆盖了当下Android开发最前沿的技术点,和阿里、腾讯、字节等等大厂面试看重的技术。
好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码回复JJ免费领取哈~
因为所包含的内容足够多,所以,这份笔记不仅仅可以用来当学习资料,还可以当工具书用。
如果你需要了解某个知识点,不管是Shift+F 搜索,还是按目录进行检索,都能用最快的速度找到你要的内容。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照整个知识体系编排的。
(一)架构师必备Java基础
1、深入理解Java泛型
2、注解深入浅出
3、并发编程
4、数据传输与序列化
5、Java虚拟机原理
6、高效IO
……
(二)设计思想解读开源框架
1、热修复设计
2、插件化框架设计
3、组件化框架设计
4、图片加载框架
5、网络访问框架设计
6、RXJava响应式编程框架设计
……
(三)360°全方位性能优化
1、设计思想与代码质量优化
2、程序性能优化
- 启动速度与执行效率优化
- 布局检测与优化
- 内存优化
- 耗电优化
- 网络传输与数据储存优化
- APK大小优化
3、开发效率优化
- 分布式版本控制系统Git
- 自动化构建系统Gradle
……
(四)Android框架体系架构
1、高级UI晋升
2、Android内核组件
3、大型项目必备IPC
4、数据持久与序列化
5、Framework内核解析
……
(五)NDK模块开发
1、NDK开发之C/C++入门
2、JNI模块开发
3、Linux编程
4、底层图片处理
5、音视频开发
6、机器学习
……
(六)Flutter学习进阶
1、Flutter跨平台开发概述
2、Windows中Flutter开发环境搭建
3、编写你的第一个Flutter APP
4、Flutter Dart语言系统入门
……
(七)微信小程序开发
1、小程序概述及入门
2、小程序UI开发
3、API操作
4、购物商场项目实战
……
(八)kotlin从入门到精通
1、准备开始
2、基础
3、类和对象
4、函数和lambda表达式
5、其他
……
好啦,这份资料就给大家介绍到这了,有需要详细文档的小伙伴,可以微信扫下方二维码回复JJ免费领取哈~