android面试题记录

[TOC]

线程安全和不安全?

简单来说,存在成员变量(全局变量)的线程是不安全的。 使用局部变量的线程是安全的。

ANR产生的原因和解决步骤

ANR的全称是application not responding,意思就是程序未响应。出现ANR的三种情况:

a.主线程对输入事件5秒内没有处理完毕

b.主线程在执行BroadcastReceiver的onReceive()函数时10秒内没有处理完毕

c.主线程在Service的各个生命周期函数时20秒内没有处理完毕。

那么导致ANR的根本原因是什么呢?简单的总结有以下两点:

1.主线程执行了耗时操作,比如数据库操作或网络编程

2.其他进程(就是其他程序)占用CPU导致本进程得不到CPU时间片,比如其他进程的频繁读写操作可能会导致这个问题。

解决方案:

1.避免在主线程执行耗时操作,所有耗时操作应新开一个子线程完成,然后再在主线程更新UI。

2.BroadcastReceiver要执行耗时操作时应启动一个service,将耗时操作交给service来完成。

3.避免在Intent Receiver里启动一个Activity,因为它会创建一个新的画面,并从当前用户正在运行的程序上抢夺焦点。如果你的应用程序在响应Intent广 播时需要向用户展示什么,你应该使用Notification Manager来实现。

hashtable和hashmap的区别,并简述Hashmap的实现原理?

hashTable是线程安全的,hashMap是线程不安全的

HashTable不允许null值的存在,HashMap允许null值的存在

HashTable是直接使用对象的哈希值,HashMap是使用处理过的哈希值

hashMap是数组+链表的数据结构。

实现原理:

HashTable和HashMap的区别详解

Hashtable源码剖析

array,arrayList, List ,三者有何区别?

array是数组,大小固定,不可增删。

arrayList是集合,大小不固定,可以增删。

list是接口,定义list集合框架的通用方法。

abstract和interface的区别?

都不能被实例化。

interface需要实现,要用implements,而abstract class需要继承,要用extend。

一个类可以实现多个interface,但一个类只能继承一个abstract class。

interface强调特定功能的实现,而abstract class强调所属关系。

abstract的成员修饰符可以自定义,可以有方法体,抽象方法需要用abstract修饰。

interface的成员修饰符只能是public,不能定义方法体和声明实例变量,可以声明常量变量。interface内接口默认是 public abstract 类型

线程中sleep()和wait()有和却别,各有什么含义

sleep()是Thread类的方法,必须传一个long类型的时间,在到指定时间前让出cpu给其他线程,但不释放锁,指定的时间到了又会自动恢复运行状态。

wait()是Object类的方法,可以不传参,当在线程中调用wait()方法时,线程会放弃对象锁进入等待,直到此对象调用notify()、notifyAll()方法或到指定时间,本线程才进入准备阶段,进而获取对象锁进入运行状态。

请描述安卓四大组建之间的关系,并说下安卓MVC的设计模式。

Activity: 处理与UI相关的事件,呈现界面给用户并响应用户的请求

Service: 后台服务,一般用于耗时操作,在后台和长时间运行

BoadcastReceiver: 接收广播事件并对事伯点击进行处理,如当收到短信时系统会发现短信到来的广播,能够处理该广播的BoadcastReceiver就会根据自己需要进进处理

ContentProvider: 存储、处理数据并提供给外界一致的处理接口

MVC全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范。用户与视图交互,视图接爱并反馈用户的动作,同时把用户的请求传给控制器,由控制器调用模型进行加工处理,模型把处理后的数据返回给控制器,由控制器调用视图格式化和渲染返回的数据。android中的activity是一个controller,对应的layout.xml则是视图,具体的功能则是model。

彻底明白Context

[干货]让你彻底搞懂Context到底是什么,如果没弄明白,还怎么做Android开发?

什么情况下Activity走了onCreat(),而不走onStart()?

在onCreate()方法中直接finish或者出现为捕获的异常时

安卓子线程是否能更新UI,如果能请说明具体细节。

Android UI操作并不是线程安全的,并且这些操作必须在UI线程执行

如果在其它线程访问UI线程,Android提供了以下的方式:


Activity.runOnUiThread(Runnable)

View.post(Runnable)

View.postDelayed(Runnable, long)

Handler

知识点:

Android子线程真的不能更新UI么

深入理解Message, MessageQueue, Handler和Looper

Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系

Java的基本类型有哪些?

byte,char, short, int, long, float, double, boolean。

所有类的父类是什么

Object

多态是什么?

多态是指一个名字多种实现。多态使得一个实体通过一个通用的方式来实现不同的操作。具体的操作是由实际的实现来决定的。

多态在Java里有三种表现方式:方法重载通过继承实现方法重写通过Java接口进行方法重写。

继承

继承使得一个对象可以获取另一个对象的属性。使用继承可以让已经测试完备的功能得以复用,并且可以一次修改,所有继承的地方都同时生效

对象封装的原则是什么?

封装是将数据及操作数据的代码绑定到一个独立的单元。这样保障了数据的安全,防止外部代码的错误使用。对象允许程序和数据进行封装,以减少潜在的干涉。对封装的另一个理解是作为数据及代码的保护层,防止保护层外代码的随意访问。

静态方法与静态成员变量可以被继承吗,为什么?

静态方法与静态成员变量可以被继承,但是不能被重写。它对子类隐藏,因此静态方法也不能实现多态。

为什么Java里的匿名内部类只能访问final修饰的外部变量?

因为匿名内部类最终用会编译成一个单独的类,而被该类使用的变量会以构造函数参数的形式传递给该类,例如:Integer paramInteger,如果变量 不定义成final的,paramInteger在匿名内部类被可以被修改,进而造成和外部的paramInteger不一致的问题,为了避免这种不一致的情况,因为Java 规定匿名内部类只能访问final修饰的外部变量。

Java的编码方式?

为什么需要编码?


计算机存储信息的最小单元是一个字节即8bit,所以能表示的范围是0~255,这个范围无法保存所有的字符,所以需要一个新的数据结构char来表示这些字符,从char到byte需要编码。

常见的编码方式有以下几种:


1. ASCII:总共有 128 个,用一个字节的低 7 位表示,031 是控制字符如换行回车删除等;32126 是打印字符,可以通过键盘输入并且能够显示出来。

1. GBK:码范围是 8140~FEFE(去掉 XX7F)总共有 23940 个码位,它能表示 21003 个汉字,它的编码是和 GB2312 兼容的,也就是说用 GB2312 编码的汉字可以用 GBK 来解码,并且不会有乱码。

3.UTF-16:UTF-16 具体定义了 Unicode 字符在计算机中存取方法。UTF-16 用两个字节来表示 Unicode 转化格式,这个是定长的表示方法,不论什么字符都可以用两个字节表示,两个字节是 16 个 bit,所以叫 UTF-16。UTF-16 表示字符非常方便,每两个字节表示一个字符,这个在字符串操作时就大大简化了操作,这也是 Java 以 UTF-16 作为内存的字符存储格式的一个很重要的原因。

4. UTF-8:统一采用两个字节表示一个字符,虽然在表示上非常简单方便,但是也有其缺点,有很大一部分字符用一个字节就可以表示的现在要两个字节表示,存储空间放大了一倍,在现在的网络带宽还非常有限的今天,这样会增大网络传输的流量,而且也没必要。而 UTF-8 采用了一种变长技术,每个编码区域有不同的字码长度。不同类型的字符可以是由 1~6 个字节组成。

Java中需要编码的地方一般都在字符到字节的转换上,这个一般包括磁盘IO和网络IO。


Reader 类是 Java 的 I/O 中读字符的父类,而 InputStream 类是读字节的父类,InputStreamReader 类就是关联字节到字符的桥梁,它负责在 I/O 过程中处理读取字节到字符的转换,而具体字节到字符的解码实现它由 StreamDecoder 去实现,在 StreamDecoder 解码过程中必须由用户指定 Charset 编码格式。

静态代理与动态代理区别是什么,分别用在什么样的场景里?

静态代理与动态代理的区别在于代理类生成的时间不同,如果需要对多个类进行代理,并且代理的功能都是一样的,用静态代理重复编写代理类就非常的麻烦,可以用动态代理动态的生成代理类。

描述一个类的加载过程?

Person person = new Person()

  1. 查找Person.class,并加载到内存中。

  2. 执行类里的静态代码块。

  3. 在堆内存里开辟内存空间,并分配内存地址。

  4. 在堆内存里建立对象的属性,并进行默认的初始化。

  5. 对属性进行显示初始化。

  6. 对对象进行构造代码块初始化。

  7. 调用对象的构造函数进行初始化。

  8. 将对象的地址赋值给person变量。

Java对象的生命周期是什么?

  1. 加载:将类的信息加载到JVM的方法区,然后在堆区中实例化一个java.lang.Class对象,作为方法去中这个类的信息入口。

  2. 连接:验证:验证类是否合法。准备:为静态变量分配内存并设置JVM默认值,非静态变量不会分配内存。解析:将常量池里的符号引用转换为直接引用。

  3. 初始化:初始化类的静态赋值语句和静态代码块,主动引用会被触发类的初始化,被动引用不会触发类的初始化。

  4. 使用:执行类的初始化,主动引用会被触发类的初始化,被动引用不会触发类的初始化。

  5. 卸载:卸载过程就是清楚堆里类的信息,以下情况会被卸载:① 类的所有实例都已经被回收。② 类的ClassLoader被回收。③ 类的CLass对象没有被任何地方引用,无法在任何地方通过 反射访问该类。

类的加载机制?


类的加载就是虚拟机通过一个类的全限定名来获取描述此类的二进制字节流,而完成这个加载动作的就是类加载器。

类和类加载器息息相关,判定两个类是否相等,只有在这两个类被同一个类加载器加载的情况下才有意义,否则即便是两个类来自同一个Class文件,被不同类加载器加载,它们也是不相等的。

类加载器可以分为三类:

  1. 启动类加载器(Bootstrap ClassLoader):负责加载\lib目录下或者被-Xbootclasspath参数所指定的路径的,并且是被虚拟机所识别的库到内存中。

  2. 扩展类加载器(Extension ClassLoader):负责加载\lib\ext目录下或者被java.ext.dirs系统变量所指定的路径的所有类库到内存中。

  3. 应用类加载器(Application ClassLoader):负责加载用户类路径上的指定类库,如果应用程序中没有实现自己的类加载器,一般就是这个类加载器去加载应用程序中的类库。

线程为什么阻塞,为和要使用多线程?

  • 使用多线程更多的是为了提高CPU的并发,可以让CPU同事处理多个事情

  • 一个耗时任务在主线程长时间占用CPU,将会造成主线程堵塞。

  • 多线程场景的使用场景:


1.  为了不让耗时操作阻塞主线程,开启新线程执行耗时操作。

2. 某种任务虽然耗时但是不消耗CPU,例如:磁盘IO,可以开启新线程来做,可以显著的提高效率。

3. 优先级比较低的任务,但是需要经常去做,例如:GC,可以开启新线程来做。

线程的生命周期

  • NEW:创建状态,线程创建之后,但是还未启动。

  • RUNNABLE:运行状态,处于运行状态的线程,但有可能处于等待状态,例如等待CPU、IO等。

  • WAITING:等待状态,一般是调用了wait()、join()、LockSupport.spark()等方法。

  • TIMED_WAITING:超时等待状态,也就是带时间的等待状态。一般是调用了wait(time)、join(time)、LockSupport.sparkNanos()、LockSupport.sparkUnit()等方法。

  • BLOCKED:阻塞状态,等待锁的释放,例如调用了synchronized增加了锁。

  • TERMINATED:终止状态,一般是线程完成任务后退出或者异常终止。

线程进入RUNNABLE运行态一般分为五种情况:

  • 线程调用sleep(time)后查出了休眠时间

  • 线程调用的阻塞IO已经返回,阻塞方法执行完毕

  • 线程成功的获取了资源锁

  • 线程正在等待某个通知,成功的获得了其他线程发出的通知

  • 线程处于挂起状态,然后调用了resume()恢复方法,解除了挂起。

线程进入BLOCKED阻塞态一般也分为五种情况:

  • 线程调用sleep()方法主动放弃占有的资源

  • 线程调用了阻塞式IO的方法,在该方法返回前,该线程被阻塞。

  • 线程视图获得一个资源锁,但是该资源锁正被其他线程锁持有。

  • 线程正在等待某个通知

  • 线程调度器调用suspend()方法将该线程挂起

线程状态相关的一些方法

  • sleep()方法让当前正在执行的线程在指定时间内暂停执行,正在执行的线程可以通过Thread.currentThread()方法获取。

  • yield()方法放弃线程持有的CPU资源,将其让给其他任务去占用CPU执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU时间片。

  • wait()方法是当前执行代码的线程进行等待,将当前线程放入预执行队列,并在wait()所在的代码处停止执行,知道接到通知或者被中断为止。该方法可以使得调用该方法的线程释放共享资源的锁, 然后从运行状态退出,进入等待队列,直到再次被唤醒。该方法只能在同步代码块里调用,否则会抛出IllegalMonitorStateException异常。

wait(long millis)方法等待某一段时间内是否有线程对锁进行唤醒,如果超过了这个时间则自动唤醒。

  • notify()方法用来通知那些可能等待该对象的对象锁的其他线程,该方法可以随机唤醒等待队列中等同一共享资源的一个线程,并使该线程退出等待队列,进入可运行状态。

  • notifyAll()方法可以是所有正在等待队列中等待同一共享资源的全部线程从等待状态退出,进入可运行状态,一般会是优先级高的线程先执行,但是根据虚拟机的实现不同,也有可能是随机执行。

  • join()方法可以让调用它的线程正常执行完成后,再去执行该线程后面的代码,它具有让线程排队的作用。

线程池了解吗,有几种线程池,应用场景是什么?


线程池用来管理线程,避免频繁的CPU时间片切换造成的CPU资源消耗。

Executors类提供了一系列工厂方法用来创建线程池。这些线程是适用于不同的场景。

  • newCachedThreadPool():无界可自动回收线程池,查看线程池中有没有以前建立的线程,如果有则复用,如果没有则建立一个新的线程加入池中,池中的线程超过60s不活动则自动终止。适用于生命 周期比较短的异步任务。

  • newFixedThreadPool(int nThreads):固定大小线程池,与newCachedThreadPool()类似,但是池中持有固定数目的线程,不能随时创建线程,如果创建新线程时,超过了固定 线程数,则放在队列里等待,直到池中的某个线程被移除时,才加入池中。适用于很稳定、很正规的并发线程,多用于服务器。

  • newScheduledThreadPool(int corePoolSize):周期任务线程池,该线程池的线程可以按照delay依次执行线程,也可以周期执行。

  • newSingleThreadExecutor():单例线程池,任意时间内池中只有一个线程。

堆和栈的区别

堆和栈在数据结构中是两种不同的数据结构。 两者都是数据项按序排列的数据结构。

  • 栈:是一种具有后进先出的数据结构, 是系统自动分配空间的。由系统自动分配,速度较快。但程序员是无法控制的。

  • 堆:常用来实现优先队列,堆的存取是随意的, 是由程序员自己分配内存,需要自己释放。是由new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便。

堆和栈的区别

数组的特点,优点,缺点

特点:

  • 在内存中,数组是一块连续的区域

  • 数组需要预留空间,在使用前要先申请占内存的大小,可能会浪费内存空间,

  • 插入数据和删除数据效率低,插入数据时,这个位置后面的数据在内存中都要向后移。删除数据时,这个数据后面的数据都要往前移动。

  • 随机读取效率很高。因为数组是连续的,知道每一个数据的内存地址,可以直接找到给地址的数据。

  • 并且不利于扩展,数组定义的空间不够时要重新定义数组。

优点:

  • 随机访问性强

  • 查找速度快

缺点:

  • 插入和删除效率低

  • 可能浪费内存

  • 内存空间要求高,必须有足够的连续内存空间。

  • 数组大小固定,不能动态拓展

链表的特点,优点,缺点

特点:

  • 在内存中可以存在任何地方,不要求连续。

  • 每一个数据都保存了下一个数据的内存地址,通过这个地址找到下一个数据。

  • 增加数据和删除数据很容易。

  • 查找数据时效率低,因为不具有随机访问性,所以访问某个位置的数据都要从第一个数据开始访问,然后根据第一个数据保存的下一个数据的地址找到第二个数据,以此类推。

  • 不指定大小,扩展方便。链表大小不用定义,数据随意增删。

优点:

  • 插入删除速度快

  • 内存利用率高,不会浪费内存

  • 大小没有固定,拓展很灵活。

缺点:

  • 不能随机查找,必须从第一个开始遍历,查找效率低

TCP与UDP区别总结:

  1. TCP是面向连接的(发送数据前需要先建立连接,3次握手4次拜拜),UDP是无连接的(即发送数据之前不需要建立连接)。

  2. TCP 提供的可靠的服务(通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达),UDP 不能保证可靠交付数据,只是尽最大努力交付。

  3. TCP 为保证可靠传输通过校验和、重传控制、序号标识、滑动窗口、确认应答实现。丢包时的重发控制可以对次序乱掉的分包进行顺序控制。

  4. UDP具有较好的实时性,工作效率比TCP高,适用于对告诉传输和实时性有较高的通信或广播通信。

  5. 每一条TCP连接只能是点对点;UDP支持一对一,一对多,多对一,多对多的交互通信。

  6. TCP对系统资源要求较多,UDP对系统资源要求较少。

  7. TCP一旦发生丢包,TCP会将后续的包缓存起来,等前面的包重传并接收到在继续发送,延时会越来越大。并且由于TCP内置的系统协议栈中,极难对其进行改进。

  8. UDP可以采用自定义重传机制,对实时性要求较为严格的情况下,能够吧丢包产生的延迟降到最低尽量减少网络问题造成的影响。

你可能感兴趣的:(android面试题记录)