最近在准备面试,结合之前的工作经验和近期在网上收集的一些面试资料,准备将Android开发岗位的知识点做一个系统的梳理,整理成一个系列:Android应用开发岗 面试汇总。本系列将分为以下几个大模块:
Java基础篇、Java进阶篇、常见设计模式
Android基础篇、Android进阶篇、性能优化
网络相关、数据结构与算法
常用开源库、Kotlin、Jetpack
注1:以上文章将陆续更新,直到我找到满意的工作为止,有跳转链接的表示已发表的文章。
注2:该系列属于个人的总结和网上东拼西凑的结果,每个知识点的内容并不一定完整,有不正确的地方欢迎批评指正。
注3:部分摘抄较多的段落或有注明出处。如有侵权,请联系本人进行删除。
线程概念: 指进程中的一个按顺序执行的流程,一个进程中可以运行多个线程。线程总是属于某个进程,进程中的多个线程共享进程的内存
Thread thread = new Thread() {
@Override
public void run() { //重写Thread的run方法
System.out.println("Thread started!");
}
};
thread.start();
}
static void runnable() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Thread with Runnable started!");
}
};
Thread thread = new Thread(runnable);
thread.start();
}
static void threadFactory() {
final AtomicInteger count = new AtomicInteger(0); //原子类型
ThreadFactory factory = new ThreadFactory() {
@Override
public Thread newThread(Runnable runnable) {
return new Thread(runnable, "Thread-" + count.incrementAndGet());//++count
}
};
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " started!");
}
};
Thread thread = factory.newThread(runnable);
thread.start();
Thread thread2 = factory.newThread(runnable); //复用runnable
thread2.start();
}
static void executor() {
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("Thread with executor started!");
}
};
Executor executor = Executors.newCachedThreadPool();
executor.execute(runnable);
executor.execute(runnable);//复用
}
static void callable() {
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
Thread.sleep(5000);//等待5秒模,拟耗时操作
return "Done!";
}
};
ExecutorService executorService = Executors.newCachedThreadPool();
Future<String> future = executorService.submit(callable);
while (true) { //该循环的逻辑可以理解成加载网络转菊花的情况
//xx1() 处理其他事情
//xx2()
if (future.isDone()) { //判断任务是否执行完
try {
//卡主线程,阻塞式的方法,需要等5秒后(sleep的时长)取到值。用来获取任务执行完的时机
String result = future.get();
System.out.println("result:" + result);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
break;
}
}
}
几种不同类型的线程池
用到的类
Executor: 是一个接口,里面只有一个execute方法
Executors: 是一个工具类,用来创建不同类型的线程池:ThreadPoolExecutor,返回ExecutorService对象,ExecutorService为Executor的子接口
ExecutorService: 为Executor的子接口。该类中有shutdown()和shutdownNow(),用来结束executor,即关闭线程池
- shutdown() 保守的结束,指如果线程池中有正在执行的任务或正在排队的任务,则等他们执行完后再结束。不允许有新的任务加进来或者排队
- shutdownNow() 积极性的结束,指马上结束线程池中的所有任务,用到的是Thread的interrupt()方法
ThreadPoolExecutor构造方法的参数说明:
corePoolSize: 线程数的默认值(也称核心线程数),即当创建该线程池时,就自动创建默认数个线程,当线程被回收时,保留默认数个线程
maximumPoolSize: 线程数的最大值,当线程增加到最大数个后,线程就不能再增加
keepAliveTime: 线程等待被回收的时间,当线程执行完处于闲置状态时,线程不会被立即回收,需要等待设定的时候后再进行回收,便于复用(避免频繁创建线程损耗性能)
unit: 时间单位
workQueue: 线程池所使用的缓冲队列,达到核心线程数之后,新的线程放入该队列中
threadFactory: 线程池用于创建线程
handler: 线程池对拒绝任务的处理策略
分析下线程池处理的程序是CPU密集型,还是IO密集型
CPU密集型:核心线程数 = CPU核数 + 1
IO密集型:核心线程数 = CPU核数 * 2
CPU核数 = Runtime.getRuntime().availableProcessors()
如何确定是CPU密集型还是IO密集型?
具体程序是CPU密集型,还是IO密集型请参考链接
线程之间有两种通信的方式:消息传递和共享内存
Java内存模型中规定所有变量都存储在主内存(虚拟机内存的一部分)中,主要对应Java的堆内存。这里提到的变量实际上是指共享变量,存在线程间竞争的变量,如:实例变量、静态变量和构成数组对象的元素,而局部变量和方法参数因为是线程私有的,所以不存在线程间共享和竞争关系,所以也就不在前面提到的变量范围内。
每个线程有着自己独有的工作内存,工作内存中保存了被该线程使用到的变量,这些变量来自主内存变量的副本拷贝。线程对变量的所有读写操作都必须在工作内存中进行,不能直接读写主内存中的变量。而不同线程间的工作内存也是独立的,一个线程无法访问其他线程的工作内存中的变量。即子线程内的局部变量不能被其他线程共享。
原理:
线程工作时,把需要的变量从主内存中拷贝到自己的工作内存,线程运行结束之后再将自己工作内存中的变量写回到主内存中,而多个线程间对变量的交互只能通过主内存来间接实现。具体的线程、工作内存、主内存的交互关系图如下:

如何解决以上线程同步问题呢?Java中有如下几种方式:
即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是temp = a + 1;a = temp,是可分割的,所以他不是一个原子操作。
非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。另,大致可以认为基础数据类型的访问和读写是具备原子性的。
保证资源的同步性,即被volatile 修饰的变量,如int aaa = 0,会以最高的积极性去维护同步。当在其他线程中aaa被修改后,会立刻同步到主内存中。当其他线程读取aaa时,会先去主内存中读取,同步到子线程中,以防被其他线程修改。即保证了每次读写变量都从主内存中读。
该关键字作用在代码块、方法(一般方法、静态方法)上。由于可以使用不同的类型来作为锁,因此分成了类锁和对象锁。给代码块、方法加一个同步的monitor,被监视的地方具有同步性。被同一个monitor修饰的方法、代码块具有互斥性。
public Object obj = new Object();
// 静态同步函数,使用本类字节码做类锁(即Demo.class)
public static synchronized void method1() {
}
public void method2() {
// 同步代码块,使用字节码做类锁
synchronized (Demo.class) {
}
}
// 同步函数,使用本类对象实例即this做对象锁
public synchronized void method3() {
}
// 同步代码块,使用本类对象实例即this做对象锁
public void method4() {
synchronized (this) {
}
}
public void method5() {
//同步代码块,使用共享数据obj实例做对象锁。
synchronized (obj) {
}
}
}
synchronized括号里的参数,可以看成是一个monitor。
对代码块进行加锁。ReentrantLock允许同一个线程多次调用lock接口获取锁,每调用一次计数便加一。因此在释放锁的时候必须调用相应多次数unlock才能释放锁:
mLock.lock();
System.out.println("ThreadOne: start");
try {
for (int m = 0; m < Integer.MAX_VALUE; m++) {
num++;
}
System.out.println("ThreadOne: over");
} finally { //需要在finally中进行解锁,避免被锁的代码块出现异常而无法释放锁的情况
mLock.unlock();
}
同时,可以使用ReentrantReadWriteLock分别获取读锁和写锁,当只进行读操作时,只用读锁即可。保证了多个线程同时调用被读锁锁住的资源,不会出现同步性问题。
ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建了ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题
作用:
ThreadLocal是除了加锁这种同步方式之外的一种保证一种规避多线程访问出现线程不安全的方法,当我们在创建一个变量后,如果每个线程对其进行访问的时候访问的都是线程自己的变量这样就不会存在线程不安全问题。即实现了线程隔离,线程直接是不能共享内存的,即该对象不管在哪个线程中读取数据,读到的都是该线程中自己的数据,不跟其他线程同步。
结束线程的方式:

功能和wait()、notify()方法一样,当某个线程threadA执行时,需要等待另一个线程threadB先执行完,则可以在线程A中调用threadB.join(),达到等待的效果。
该方法作用是把自己的线程时间线让出一下下,让给跟自己同优先级的其他线程。作用等价于wait(),更短时间的wait,自动wait然后恢复。
数据库相关的业务常用(高并发控制),安卓中不常用。
多重锁容易导致死锁
synchronized (obj1) {
work();
synchronized (obj2) {
work();
}
}
略
参考
io是输入输出流,它的作用就是对外部进行数据交互使用的,内部和外部分别表示的是内存以及内存以外的,外部包括手机文件,电脑文件和网络, 服务器等都称为外部 ,外部统称为文件和网络
详情略
参考链接
Java集合里使用接口来定义功能,是一套完善的继承体系。Iterator是所有集合的总接口,其他所有接口都继承于它,该接口定义了集合的遍历操作,Collection接口继承于Iterator,是集合的次级接口(Map独立存在),定义了集合的一些通用操作。


List:有序、可重复;索引查询速度快;插入、删除伴随数据移动,速度慢;
Set:无序,不可重复;
Map:键值对,键唯一,值多个;
Arraylist:
优点:ArrayList是实现了基于动态数组的数据结构,因地址连续,一旦数据存储好了,查询操作效率会比较高(在内存里是连着放的)。
缺点:因为地址连续,ArrayList要移动数据,所以插入和删除操作效率比较低。
LinkedList:
优点:LinkedList基于链表的数据结构,地址是任意的,其在开辟内存空间的时候不需要等一个连续的地址,对新增和删除操作add和remove,LinedList比较占优势。LikedList 适用于要头尾操作或插入指定位置的场景。
缺点:因为LinkedList要移动指针,所以查询操作性能比较低。
适用场景分析:
当需要对数据进行对应访问的情况下选用ArrayList,当要对数据进行多次增加删除修改时采用LinkedList。
ArrayList: ArrayList 的初始大小是0,然后,当add第一个元素的时候大小则变成10。并且,在后续扩容的时候会变成当前容量的1.5倍大小。
LinkedList: LinkedList 是一个双向链表,没有初始化大小,也没有扩容的机制,就是一直在前面或者后面新增就好。
Java中HashMap是利用“拉链法”处理HashCode的碰撞问题。在调用HashMap的put方法或get方法时,都会首先调用hashcode方法,去查找相关的key,当有冲突时,再调用equals方法。hashMap基于hasing原理,我们通过put和get方法存取对象。当我们将键值对传递给put方法时,他调用键对象的hashCode()方法来计算hashCode,然后找到bucket(哈希桶)位置来存储对象。当获取对象时,通过键对象的equals()方法找到正确的键值对,然后返回值对象。HashMap使用链表来解决碰撞问题,当碰撞发生了,对象将会存储在链表的下一个节点中。hashMap在每个链表节点存储键值对对象。当两个不同的键却有相同的hashCode时,他们会存储在同一个bucket位置的链表中。键对象的equals()来找到键值对。
Classloader负责将Class加载到JVM中,并且确定由那个ClassLoader来加载(父优先的等级加载机制)。还有一个任务就是将Class字节码重新解释为JVM统一要求的格式
当一个类加载器收到一个类加载的请求,它首先会将该请求委派给父类加载器去加载,每一个层次的类加载器都是如此,因此所有的类加载请求最终都应该被传入到顶层的启动类加载器(Bootstrap ClassLoader)中,只有当父类加载器反馈无法完成这个列的加载请求时(它的搜索范围内不存在这个类),子类加载器才尝试加载。其层次结构示意图如下:


从上图可知,JVM主要包括四个部分:
1.类加载器(ClassLoader):在JVM启动时或者在类运行将需要的class加载到JVM中。(下图表示了从java源文件到JVM的整个过程,可配合理解。

3.内存区(也叫运行时数据区):是在JVM运行的时候操作所分配的内存区。运行时内存区主要可以划分为5个区域,如图:

方法区(MethodArea):用于存储类结构信息的地方,包括常量池、静态常量、构造函数等。虽然JVM规范把方法区描述为堆的一个辑部分, 但它却有个别名non-heap(非堆),所以大家不要搞混淆了。方法区还包含一个运行时常量池。
java堆(Heap):存储java实例或者对象的地方。这块是GC的主要区域。从存储的内容我们可以很容易知道,方法和堆是被所有java线程共享的。
java栈(Stack):java栈总是和线程关联在一起,每当创一个线程时,JVM就会为这个线程创建一个对应的java栈在这个java栈中,其中又会包含多个栈帧,每运行一个方法就建一个栈帧,用于存储局部变量表、操作栈、方法返回等。每一个方法从调用直至执行完成的过程,就对应一栈帧在java栈中入栈到出栈的过程。所以java栈是现成有的。
程序计数器(PCRegister):用于保存当前线程执行的内存地址。由于JVM程序是多线程执行的(线程轮流切换),所以为了保证程切换回来后,还能恢复到原先状态,就需要一个独立计数器,记录之前中断的地方,可见程序计数器也是线程私有的。
本地方法栈(Native MethodStack):和java栈的作用差不多,只不过是为JVM使用到native方法服务的。
Java中使用可达性算法对对象进行是否可回收的标记算法。通过一系列称为GCRoots的对象作为起始点,从这些节点从上向下搜索,所走过的路径称为引用链,当一个对象没有任何引用链与GCRoots连接时就说明此对象不可用,也就是对象不可达。
我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/
我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www
我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我
什么是ruby的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht
这篇文章是继上一篇文章“Observability:从零开始创建Java微服务并监控它(一)”的续篇。在上一篇文章中,我们讲述了如何创建一个Javaweb应用,并使用Filebeat来收集应用所生成的日志。在今天的文章中,我来详述如何收集应用的指标,使用APM来监控应用并监督web服务的在线情况。源码可以在地址 https://github.com/liu-xiao-guo/java_observability 进行下载。摄入指标指标被视为可以随时更改的时间点值。当前请求的数量可以改变任何毫秒。你可能有1000个请求的峰值,然后一切都回到一个请求。这也意味着这些指标可能不准确,你还想提取最小/
HashMap中为什么引入红黑树,而不是AVL树呢1.概述开始学习这个知识点之前我们需要知道,在JDK1.8以及之前,针对HashMap有什么不同。JDK1.7的时候,HashMap的底层实现是数组+链表JDK1.8的时候,HashMap的底层实现是数组+链表+红黑树我们要思考一个问题,为什么要从链表转为红黑树呢。首先先让我们了解下链表有什么不好???2.链表上述的截图其实就是链表的结构,我们来看下链表的增删改查的时间复杂度增:因为链表不是线性结构,所以每次添加的时候,只需要移动一个节点,所以可以理解为复杂度是N(1)删:算法时间复杂度跟增保持一致查:既然是非线性结构,所以查询某一个节点的时候
遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg
我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.
Java的Collections.unmodifiableList和Collections.unmodifiableMap在Ruby标准API中是否有等价物? 最佳答案 使用freeze应用程序接口(interface):Preventsfurthermodificationstoobj.ARuntimeErrorwillberaisedifmodificationisattempted.Thereisnowaytounfreezeafrozenobject.SeealsoObject#frozen?.Thismethodretur
在Java中,可以像这样从一个字符串创建一个IO流:Readerr=newStringReader("mytext");我希望能够在Ruby中做同样的事情,这样我就可以获取一个字符串并将其视为一个IO流。 最佳答案 r=StringIO.new("mytext")和here'sthedocumentation. 关于java-Java的StringReader的Ruby等价物是什么?,我们在StackOverflow上找到一个类似的问题: https://st