安卓面试题整理

知乎上一篇文章

1)JAVA基础

1.java基本数据类型有哪些,int, long占几个字节

    四类八种基本数据类型:

数值型整型(byte-1,short-2,int-4,long-8) 浮点型(float-4, double-8)
字符型char
布尔型boolean

2.== 和 equals有什么区别

     ==判断基本数据类型是判断值是否相等,判断引用类型是判断地址是否指向同一个对象

     equals在Object类里面和==一样,重写后可以判断内容是否相同

3.hashcode 和 equals作用

equal()相等的两个对象他们的hashCode()肯定相等,也就是用equal()对比是绝对可靠的。

      hashCode()相等的两个对象他们的equal()不一定相等,也就是hashCode()不是绝对可靠的。

 所有对于需要大量并且快速的对比的话如果都用equal()去做显然效率太低,所以解决方式是,
每当需要对比的时候,首先用hashCode()去对比,如果hashCode()不一样,
则表示这两个对象肯定不相等(也就是不必再用equal()去再对比了),
如果hashCode()相同,此时再对比他们的equal(),如果equal()也相同,
则表示这两个对象是真的相同了,这样既能大大提高了效率也保证了对比的绝对正确性!

4.new String创建了几个对象

一个是字符串字面量"xyz"所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,
另一个是通过new String(String)创建并初始化的、内容与"xyz"相同的实例
String   s   =   new   String( "xyz "); 

首先在string字符串常量池内找,找到?不创建string对象,否则创建,   这样就一个string对象
 
遇到new运算符号了,在堆内存上创建string对象,并将其返回给其引用s,又一个对象 

6.java的拆装箱

    为了满足Java面向对象的思想,针对基本数据类型设计了相对于的包装类。基本数据类型转换成包装类是装箱,反之是拆箱。

    Java5.0之后引入了自动装箱拆箱功能。

7.compareable 和 compartor的区别

 *  (01) "Comparable"
 *   它是一个排序接口,只包含一个函数compareTo()。
 *   一个类实现了Comparable接口,就意味着“该类本身支持排序”,它可以直接通过Arrays.sort() 或 Collections.sort()进行排序。
 *   (02) "Comparator"
 *   它是一个比较器接口,包括两个函数:compare() 和 equals()。
 *   一个类实现了Comparator接口,那么它就是一个“比较器”。其它的类,可以根据该比较器去排序。
 *
 *   综上所述:Comparable是内部比较器,而Comparator是外部比较器。
 *   一个类本身实现了Comparable比较器,就意味着它本身支持排序;若它本身没实现Comparable,也可以通过外部比较器Comparator进行排序。

2)数据结构和算法

常见的数据结构就是:数组,栈,队列,集合,映射,链表,堆,二分搜索树,红黑树,AVL平衡树等一些数据结构。

我们要做的就是了解它们的实现原理和各自的优缺点。

数据结构部分面试遇到最多的就是:

1.ArrayList和LinkedList的区别,优缺点

List集合:
	ArrayList 基于动态数组,值可为null 线程不安全 每次扩容0.5倍空间

	LinkedList 双向链表 增删速度快,查询速度慢 线程不安全

	vector 线程安全,效率比ArrayList低 每次扩容2倍空间

	ArrayList是实现了基于动态数组的数据结构,而LinkedList是基于链表的数据结构

    Queue 队列是一种特殊的线性表,它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。


2.hashmap实现,扩容是怎么做的,怎么处理hash冲突,hashcode算法等
3.链表需要知道。LinkedHashMap一般再问LRU的时候会问到
4.二分搜索树的特性和原理。前中后序遍历写出其中一种,当问到二分搜索树的缺点的时候,你需要提出基于二分搜索树的红黑树,说出他的特性。
5.堆的实现,最大堆,最小堆,优先队列原理。

HashMap的本质 = 1个存储Entry类对象的数组 + 多个单链表  https://blog.csdn.net/vking_wang/article/details/14166593

	HashMap   线程不安全 可以使用Collection类静态方法Map map = Collections.synchronizedMap(new HashMap());
	                        获取线程安全hashMap                                               
	          key可为空(只能有一个null key)值可为null                   
	          初始容量16,扩容<<1  2n
	          计算hash值方法不同(扰动函数)

	HashTable 线程安全
	          key value不能为空(nullPointerException) key.hashCode();
	          初始容量11,扩容 2n+1

	LinkedHashMap HashMap子类,记录了插入顺序,具有HashMap全部特性,遍历速度比HashMap慢

	TreeHashMap   保存的顺序按照键排序,默认是升序排序,可以指定排序比较器。键值都不能为空

	ConcurrentHashMap  数据结构  多个segment -> HashEntry

 

算法

算法其实就是我们平时常见的一些排序:选择排序,插入排序,冒泡排序,希尔排序,归并排序,快速排序。以及和数据结构相关联的解决部分问题的一些计算方法。

算法面试遇到的一些题:

1.手写快速排序,插入排序,冒泡排序
2.翻转一个数字
3.翻转一个链表
4.O(n)复杂度找出数组中和是9的两个数的索引
5.写出二分搜索树前中后序遍历中的其中一个
6.实现一个队列,并能记录队列中最大的数。

二叉树最大深度:
		//计算二叉树的最大深度  BFS实现
    public static int maxDepth(TreeNode root){
        if(root == null){
            return 0;
        }

        List<TreeNode> list = new ArrayList<>();
        int count = 0;
        list.add(root);
        while (list.size()>0){
            int size = list.size();
            for(int i=size; i>0; i--){
                TreeNode node = list.remove(0);
                if(node.left != null){
                    list.add(node.left);
                }

                if(node.right != null){
                    list.add(node.right);
                }
            }
            count ++;
        }
        return count;
    }

    //二叉树最大深度,递归实现
    public static int maxDepth1(TreeNode root){
        if(root == null){
            return 0;
        }
       if(root.left == null && root.right == null){
            return 1;
       }else {
            int left = maxDepth1(root.left);
            int right = maxDepth1(root.right);
            return 1+Math.max(left,right);
        }
    }
public static void quickSort(int[] arr, int L, int R) {
        int i = L;
        int j = R;

        int pivot = arr[(L + R) / 2];

        while (i <= j) {

            while (pivot > arr[i])
                i++;
            
            while (pivot < arr[j])
                j--;

            if (i <= j) {
                int temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
                i++;
                j--;
            }
        }
        if (L < j)
            quickSort(arr, L, j);

        if (i < R)
            quickSort(arr, i, R);
    }

3)JVM虚拟机

JVM虚拟机我们需要知道他们内部组成:堆,虚拟机栈,本地方法栈,方法区,计数器。每一块都存放什么东西,以及垃圾回收的时候主要回收哪些块的东西。GC-ROOT链是从哪些地方开始的,垃圾回收集算法(很少遇到问的)。

类加载ClassLoader已经双亲委派机制,类加载的过程,类加载的信息对应在JVM的哪些块中。

(1)JVM的内存结构大概分为:

	堆(heap):线程共享,所有的对象实例以及数组都要在堆上分配。回收器主要管理的对象。
	方法区(MEATHOD AREA):线程共享,存储类信息、常量、静态变量、即时编译器编译后的代码。
	虚拟机栈(JVM Stack):线程私有、存储局部变量表、操作栈、动态链接、方法出口,对象指针。
	本地方法栈(NATIVE METHOD STACK):线程私有。为虚拟机使用到的Native 方法服务。如Java使用c或者c++编写的接口服务时,代码在此区运行。
	程序计数器(PC Register):线程私有。指向下一条要执行的指令

	(2)JVM垃圾回收算法
		标记-清除算法 (效率低,产生内存碎片)
		标记-整理算法 (效率低)
		复制算法		 (效率高,浪费内存空间)

		现代虚拟机采用 分代回收算法
			java堆分为新生代和老年代。
			将内存分为一块较大的Eden空间和两块较小的Survivor空间,每次使用Eden和其中一块Survivor 。当回收时,
			将Eden和Survivor中还存活着的对象一次性地复制到另外一块Survivor空间上,
			最后清理掉Eden和刚才用过的Survivor空间。当Survivor空间不够用时,需要依赖其他内存(这里指老年代)进行分配担保
 

4)线程安全

当多个线程访问一个对象的时候,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获取正确的结果,我们就认为这个对象时线程安全的。

线程安全就是一些多线程下载,同步,锁,死锁,线程池。volatile关键字的特性,变量的原子性。以及java.util.concurrent包下的类,也需要了解一下。

一般会问的是手写单例,以及双重锁式单例的优点。还有就是让你自己实现一个多线程下载,看你怎么设计。

java线程安全问题
   (1).*非线程安全存在于'实例变量'中,如果是方法内部的'局部变量'则不存在非线程安全
  (2)."锁重入" 的概念是:自己可以再次获取自己的内部锁 (在一个synchronized方法内可以调用本地其他的synchronized方法); 当存在父子类继承关系时,子类完全可以通过‘可重入锁’调用父类的同步方法的。
   (3).线程共享变量和私有变量  静态成员变量在同一个对象或者不同对象都是共享变量,非静态成员变量在同一个对象间共享,在不同对象间不共享
    (4). synchronized关键字加到static方法上是给class类加锁,而synchronized关键字加到非static方法上是给对象加锁。 对象锁只对同一个对象有用,而class锁可以对类的所有对象实例起作用。

    	三大性质:单线程下操作可以认为都是原子操作,多线程则不一定。
    (1) 原子性:java中原子操作包括除long和double之外的基本类型的赋值操作
    (2) 可见性:当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值,java提供volatile,synchronized和lock关键字来保证可见性。
    (3) 有序性:程序执行的顺序按照代码的先后顺序执行。(为了效率java允许编译器和处理器对指令进行重排序,对单线程不会有影响,但是多线程下会有影响)java提供volatile,synchronized和lock关键字来保证有序性。
Thread线程相关:
		https://www.jianshu.com/p/fa6667dfb4ca
 (1) sleep 和 wait区别
	   由于CPU分配的每个线程的时间片极为短暂(一般为几十毫秒),所以,CPU通过不停地切换线程执行,这样就给程序员一种错觉,以为多个线程是在同时执行。sleep就是正在执行的线程主动让出CPU,CPU去执行其他线程,在sleep指定的时间过后,CPU才会回到这个线程上继续往下执行,如果当前线程进入了同步锁,sleep方法并不会释放锁,即使当前线程使用sleep方法让出了CPU,但其他被同步锁挡住了的线程也无法得到执行。

   (2) sleep方法只让出了CPU,并不会释放同步资源锁
	     wait方法则让当前线程退让出同步资源锁,以便其他等待该资源线程可以得到执行。只有调用了notify()方法,之前调用wait()的线程才会解除wait状态,可以去参与竞争同步资源锁,进而得到执行。notify()会唤醒wait状态的线程。
   
   (3)sleep可以在任何地方执行,wait只能在同步代码块中执行

   (4)join让指定的线程先执行完再执行其他的线程,而且会阻塞当前线程。thread.join();

   (5) yield让指定线程让出当前cpu资源,从运行状态转为就绪状态,等待CPU调度。

   (6) volatile 保证可见性,禁止指令重排。

   	ReentrantReadWriteLock(读写锁)。读写所允许同一时刻被多个读线程访问,但是在写线程访问时,所有的读线程和其他的写线程都会被阻塞
    ReentrantLock 可重入、可中断、可限时、公平锁
停止线程
		(1)非阻塞线程,设置标志位,while循环时判断是否可以继续进行
		(2)阻塞线程(线程中sleep),interrupt(),方法打断线程执行。   
				异常情况:线程休眠时,使用interrupt()方法来抛出InterruptedException异常,在catch中break出循环
		 在正常状态下调用interrupt方法使用isInterrupted()作为标志位来退出,在阻塞状态下通过捕获异常来退出。因此使用interrupt()来退出线程的最好的方式应该是两种情况都要考虑:
		(3)使用stop强行退出,会出现异常情况,不建议使用。(有可能数据不一致)
线程池
	优点   (1)重用线程池中的线程,避免频繁创建和销毁所带来的性能损耗
		 (2)有效控制线程的最大并发数量,避免线程过大抢占资源带来的系统阻塞
		 (3)可以对线程进行一定的管理
    线程池运行规则:
    	(1)如果线程池中的线程数未达到核心线程数,则会立马启用一个核心线程去执行。
    	(2)如果线程池中的线程数已经达到核心线程数,且workQueue未满,则将新线程放入workQueue中等待执行
    	(3)如果线程池中的线程数已经达到核心线程数但未超过非核心线程数,且workQueue已满,则开启一个非核心线程来执行任务。
    	(4)excute一个线程之后,如果线程池中的线程数已经超过非核心线程数,则拒绝执行该任务

    分类:(1) FixedThreadPool  (任务列队长度Integer.MAX_VALUE)
    	  FixedThreadPool是一个核心线程数量固定的线程池,当所有的核心线程都在执行任务的时候,新的任务只能进入线程队列中进行等待,直到有线程被空闲出来
    	 
    	 (2) SingleThreadExecutor  
    	 	SingleThreadExecutor的核心线程数只有1,任务列队长度 Integer.MAX_VALUE
    	 
    	 (3) CacheThreadPool
    	 	没有核心线程,每当我们添加一个新任务进来的时候,如果线程池中有空闲的线程,则由该空闲的线程执行新任务,如果没有空闲线程,则创建新线程来执行任务
    	 	超时时间60秒  (适合执行大量耗时较少的操作,SynchronousQueue任务队列其实相当于一个空集合,这将导致任何任务都会被立即执行)
    	
    	(4)ScheduleThreadPool
    		定时定期执行任务功能的线程池,核心线程固定(构造参数传入),非核心线程无限制,非核心线程闲置会被马上回收

    	如果调用了shutDown方法,则线程池处于shutDown状态,此时不能接受新的任务,它会等待所有任务执行完毕。
    	如果调用shutDownNow方法,线程池处于stop状态,此时不能接受新的任务,并且尝试去终止正在执行的任务。
    	当线程池处于shutDown或者stop状态,并且所有工作线程已经销毁,任务列队已被清空。此时线程池处于Terminaled状态。
AsyncTask:内部封装了线程池和handler的框架

    		onPreExecute() 主线程 初始化准备工作

    		doInBackgroud() 子线程,耗时操作

    		onProgressUpdate() 主线程,用于更新进度

    		onPostExecute() 主线程,任务执行完毕

 

5)编程思想

封装,继承,多态,抽象,反射,注解,设计模式,设计模式的原则。

面试中一般会问下:

1.抽象和接口有什么不一样
2.工作中常用的设计模式,一些源码中的设计模式
3.具体给你一个设计模式让你说说你对他的了解,比如观察者,工厂。

浅谈常用设计模式 
以上这些东西主要考察你的代码设计能力。

6)网络协议

1.互联网的实现主要分为几层,http、ftp、tcp、ip分别位于哪一层。
2.http和https的区别
3.为什么tcp要经过三次握手,四次挥手
4.socket了解过吗
一般http和https问的比较多,对称加密和非对称加密也会问。tcp和socket偶尔遇见问的。
 

三 Android相关

Android部分我就不分几大块了。直接列举,但是列举到的每一项都是面试经常会问到并且会延伸问的,所以需要深入的去研究。

1.四大组件有哪些,说出你对他们在Android系统中的作用和理解。

2.Activity生命周期,A启动B两个页面生命周期怎么运行的,为什么会 这样,生命周期为什么这么设计,你有了解过吗。

3.四种启动模式,内部堆栈是怎么回事,你工作中怎么使用的。

4.Activity的启动过程,这个我强烈建议每个Android开发人员都要清楚的知道,并且跟一下源码,几个核心类的作用。你会对Android有一个更好的认识。

5.事件分发流程,怎么处理滑动冲突。举例:长按ListView的一个Item它变灰了。这个时候在滑动。item恢复原来的样子,这个时候他们内部的事件传递是什么样子。有很多种问法,所以你一定要搞清楚。

6.自定义View,View的绘制流程。onMeasure,onLayout,onDraw都是什么作用。

ViewGroup是怎么分发绘制的。onDraw里面怎么去做绘制,Canvas,Path,Paint你都需要了解。

并且配合ValueAnimtor或者Scroller去实现动画。有时候面试的会突发奇想问你ViewGroup是树形结构,我想知道树的深度,你怎么计算,突然就变成了一个数据结构和算法的题。

自定义view生命周期
	->构造方法
	->onFinishInflate(从xml加载完成后调用)
	->onAttachedToWindow   (将view绑定到actvity所在的window,随后开始进行所有view的绘制) 在宿主activity的onResume之后调用。
	->onMeasure
	->onLayout
	->onDraw
	->onWindowFocusChanged   (判断view是否获得焦点,可以判断view进出前后台)
	->onDetachedFromWindow   (activity销毁后,view会从activity抽离,此时view销毁)

7.Bitmap和Drawable

8.Animation和Animator

9.LinearLayout、RelativeLayout、FrameLayout三种常用布局的特性,他在布局的时候是怎么计算的。效率如何。CoordinatorLayout配合AppbarLayout的使用,以及自定义Behavior。ConstraintLayout的使用。用来减少层级。

10.Handler消息机制,推荐看一下Looper的源码

11.进程间通信,Binder机制

12.AsyncTask源码看一下。

13.图片的压缩处理,三级缓存,Lru算法

14.分辨率和屏幕密度,以及计算一个图片大小。mdpi,hdpi的关系和比例。

15.优化,内存优化,布局优化,启动优化,性能优化。内存泄露,内存溢出。怎么优化,用了什么工具,具体怎么做的。

LeakCanary内存泄漏检查工具

16.listView和RecycleView对比,以及缓存策略。

17.JNI(很少问)

18.MVC,MVP,MVVM

19.开源框架Okhttp,Glide,EventBus,Rxjava等,以及JetPack下的开源库,要会用,还说说出一些东西,推荐 Retrofit,Okhttp,Glide,EventBus这些看一下源码。

20.RecyclerView四大块,能实现什么效果,大致怎么实现的,心里要有数

21.DecorView,Window,WindowManager,PhoneWindow关系,以及个子的职责。
加分项:Kotlin,Gradle,Flutter,组件化,插件化,热修复。

热修复原理
    我们知道Java虚拟机 —— JVM 是加载类的class文件的,而Android虚拟机——Dalvik/ART VM 是加载类的dex文件,
    而他们加载类的时候都需要ClassLoader,ClassLoader有一个子类BaseDexClassLoader,而BaseDexClassLoader下有一个
    数组——DexPathList,是用来存放dex文件,当BaseDexClassLoader通过调用findClass方法时,实际上就是遍历数组,
    找到相应的dex文件,找到,则直接将它return。而热修复的解决方法就是将新的dex添加到该集合中,并且是在旧的dex的前面,
    所以就会优先被取出来并且return返回。
webView相关
	(1) webView生命周期:
			onResume     -> webView活跃状态时回调,可以正常执行网页的响应

			onPause      -> webView被切换到后台使用,页面失去焦点,变成不可见状态。通知内核暂停所有动作,DOM解析,js执行...

			pauseTimers()   ->应用程序切换到后台时调用,针对全应用。暂停所有webView的layout,javaScriptTimer,降低CPU功耗。

			resumeTimers()   -> 回复pauseTimers时的动作

			destroy()        ->关闭activity时回调,webView destroy时仍绑定在activity上,需要先移除,再销毁webView.

	(2) webViewClient帮助webView处理各种通知请求时间
			shouldOverrideUrlLoading作用: 打开网页时不调用系统浏览器,而是在本webView中显示,在网页上所有加载都经过这个方法
			除了你主动用webview加载的url,其他的加载都会回调shouldOverrideLoading。后面还有一句注释:post请求不会回调shouldOverrideLoading
			返回true和false的区别:返回ture 网页会被加载到webView的历史记录中,加载后按返回键会回到这个网页。返回false不会加载到webView历史记录中,返回也不会回到加载的网页

		webChromeClient 辅助webView处理javaScript的对话框,网站标题,进度等。
			onProgressChanged() 获取进度
			onReceivedTitle() 获取web页中的标题
UTF-8中文三个字节  英文一个字节

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值