android 面试确实现在越来越底层,越来越详细。其实我觉得还是掌握两个基本点:第一,是你自身的项目 第二,就是技术技能。
java基础:
现在面试稍微好点的公司都会问到算法:
这里主要是冒泡排序、选择排序以及二分法就是必须要手写出来的代码了
1 、冒泡排序
public static void bubbleSort(int[] arr) {
for(int x=0; x<arr.length-1; x++) {
for(int y=0; y<arr.length-1-x; y++) {
if(arr[y] > arr[y+1]) {
int temp = arr[y];
arr[y] = arr[y+1];
arr[y+1] = temp;
}
}
}
}
2.选择排序
把0索引的元素,和索引1以后的元素都进行比较,第一次完毕,最小值出现在了0索引。同理,其他的元素就可以排好。
public static void selectSort(int[] arr) {
for(int x=0; x<arr.length-1; x++) {
for(int y=x+1; y<arr.length; y++) {
if(arr[y] < arr[x]) {
int temp = arr[x];
arr[x] = arr[y];
arr[y] = temp;
}
}
}
}
3、二分法查找
针对数组有序的情况(千万不要先排序,在查找)
public static int binarySearch(int[] arr,int value) {
int min = 0;
int max = arr.length-1;
int mid = (min+max)/2;
while(arr[mid] != value) {
if(arr[mid] > value) {
max = mid - 1;
}else if(arr[mid] < value) {
min = mid + 1;
}
if(min > max) {
return -1;
}
mid = (min+max)/2;
}
return mid;
}
然后问的集合也比较多,应为集合是容器非常重要
集合:
Iterator – > collection —-> map
collect :list/set
list :arraylist/LinkedList
set :Hashset/Treeset
HashSet–>LinkedHashSet
map : HashMap/TreeMap
HashMap–>LinkedHashMap
List:有序的,元素可重复,有索引
ArrayList:底层是数组结构,查询快,增删慢,不同步
LinkedList:底层是链表结构,增删快,查询慢,不同步
Set:无序的,无索引,元素不可重复
HashSet:底层是哈希表,线程不同步,无序、高效
TreeSet:底层是二叉树,可对元素进行排序,默认是自然排序
Map:
HashTable:底层数据结构是哈希表,不可存入null键和null值。同步的
HashMap:底层数据结构是哈希表,允许使用null键和null值,不同步,效率高
多态
3.多态
是父类或接口定义的引用变量可以指向子类或具体实现类的实例对象
好处:提高了程序的扩展性
弊端:当父类引用指向子类对象时,虽提高了扩展性,但只能访问父类中具备的方法,不可访问子类中的方法;即访问的局限性。
前提:实现或继承关系;覆写父类方法。
抽象类和接口的区别以及应用场景:
1. 相同点
两者都是抽象类,都不能实例化。
interface实现类及abstrct class的子类都必须要实现已经声明的抽象方法。
不同点
interface需要实现,要用implements,而abstract class需要继承,要用extends。
一个类可以实现多个interface,但一个类只能继承一个abstract class。
interface强调特定功能的实现,而abstract class强调所属关系。
尽管interface实现类及abstrct class的子类都必须要实现相应的抽象方法,但实现的形式不同。interface中的每一个方法都是抽象方法,都只是声明的(declaration, 没有方法体),实现类必须要实现。而abstract class的子类可以有选择地实现:
这个选择有两点含义:
Abastract class中并非所有的方法都是抽象的,只有那些冠有abstract的方法才是抽象的,子类必须实现。那些没有abstract的方法,在Abstrct class中必须定义方法体。
abstract class的子类在继承它时,对非抽象方法既可以直接继承,也可以覆盖;而对抽象方法,可以选择实现,也可以通过再次声明其方法为抽象的方式,无需实现,留给其子类来实现,但此类必须也声明为抽象类。既是抽象类,当然也不能实例化。
abstract class是interface与Class的中介。
interface是完全抽象的,只能声明方法,而且只能声明pulic的方法,不能声明private及protected的方法,不能定义方法体,也不能声明实例变量。然而,interface却可以声明常量变量,并且在JDK中不难找出这种例子。但将常量变量放在interface中违背了其作为接口的作用而存在的宗旨,也混淆了interface与类的不同价值。如果的确需要,可以将其放在相应的abstract class或Class中。
abstract class在interface及Class中起到了承上启下的作用。一方面,abstract class是抽象的,可以声明抽象方法,以规范子类必须实现的功能;另一方面,它又可以定义缺省的方法体,供子类直接使用或覆盖。另外,它还可以定义自己的实例变量,以供子类通过继承来使用。interface的应用场合
类与类之前需要特定的接口进行协调,而不在乎其如何实现。
作为能够实现特定功能的标识存在,也可以是什么接口方法都没有的纯粹标识。
需要将一组类视为单一的类,而调用者只通过接口来与这组类发生联系。
需要实现特定的多项功能,而这些功能之间可能完全没有任何联系。abstract class的应用场合
一句话,在既需要统一的接口,又需要实例变量或缺省的方法的情况下,就可以使用它。最常见的有:
定义了一组接口,但又不想强迫每个实现类都必须实现所有的接口。可以用abstract class定义一组方法体,甚至可以是空方法体,然后由子类选择自己所感兴趣的方法来覆盖。
某些场合下,只靠纯粹的接口不能满足类与类之间的协调,还必需类中表示状态的变量来区别不同的关系。abstract的中介作用可以很好地满足这一点。
规范了一组相互协调的方法,其中一些方法是共同的,与状态无关的,可以共享的,无需子类分别实现;而另一些方法却需要各个子类根据自己特定的状态来实现特定的功能
多线程
三、多线程
–进程和线程:
1)进程是静态的,其实就是指开启的一个程序;而线程是动态的,是真正执行的单元,执行的过程。其实我们平时看到的进程,是线程在执行着,因为线程是作为进程的一个单元存在的。
2)同样作为基本的执行单元,线程是划分得比进程更小的执行单位。
3)每个进程都有一段专用的内存区域。与此相反,线程却共享内存单元(包括代码和数据),通过共享的内存单元来实现数据交换、实时通信与必要的同步操作。
1、创建线程的方式:
创建方式一:继承Thread
1:定义一个类继承Thread
2:覆盖Thread中的run方法(将线程运行的代码放入run方法中)。
3:直接创建Thread的子类对象
4:调用start方法(内部调用了线程的任务(run方法));作用:启动线程,调用run方法
方式二:实现Runnable
1:定义类实现Runnable接口
2:覆盖Runnable接口中的run方法,将线程的任务代码封装到run中
3:通过Thread类创建线程对象
4、并将Runnable接口的子类对象作为Thread类的构造函数参数进行传递
作为参数传递的原因是让线程对象明确要运行的run方法所属的对象。
区别:
继承方式:线程代码放在Thread子类的run方法中
实现方式:线程存放在接口的子类run方法中;避免了单继承的局限性,建议使用。
2、线程状态:
新建:start()
临时状态:具备cpu的执行资格,但是无执行权
运行状态:具备CPU的执行权,可执行
冻结状态:通过sleep或者wait使线程不具备执行资格,需要notify唤醒,并处于临时状态。
消亡状态:run方法结束或者中断了线程,使得线程死亡。
3、多线程安全问题:
多个线程共享同一数据,当某一线程执行多条语句时,其他线程也执行进来,导致数据在某一语句上被多次修改,执行到下一语句时,导致错误数据的产生。
因素:多个线程操作共享数据;多条语句操作同一数据
解决:
原理:某一时间只让某一线程执行完操作共享数据的所有语句。
办法:使用锁机制:synchronized或lock对象
4、线程的同步:
当两个或两个以上的线程需要共享资源,他们需要某种方法来确定资源在某一刻仅被一个线程占用,达到此目的的过程叫做同步(synchronization)。
同步代码块:synchronized(对象){},将需要同步的代码放在大括号中,括号中的对象即为锁。
同步函数:放于函数上,修饰符之后,返回类型之前。
5、wait和sleep的区别:(执行权和锁区分)
wait:可指定等待的时间,不指定须由notify或notifyAll唤醒。
线程会释放执行权,且释放锁。
sleep:必须制定睡眠的时间,时间到了自动处于临时(阻塞)状态。
即使睡眠了,仍持有锁,不会释放执行权。
最近好像JVM 的内存结构和内存分配的问题也是比较多的
JVM 的内存结构和内存分配
a) Java 内存模型
Java 虚拟机将其管辖的内存大致分三个逻辑部分:方法区(Method Area)、Java 栈和 Java 堆。
1、方法区是静态分配的,编译器将变量绑定在某个存储位置上,而且这些绑定不会在运行时改变。
常数池,源代码中的命名常量、String 常量和 static 变量保存在方法区。
2、Java Stack 是一个逻辑概念,特点是后进先出。一个栈的空间可能是连续的,也可能是不连续的。
最典型的 Stack 应用是方法的调用,Java 虚拟机每调用一次方法就创建一个方法帧(frame),退出该
方法则对应的 方法帧被弹出(pop)。栈中存储的数据也是运行时确定的。
3、Java 堆分配(heap allocation)意味着以随意的顺序,在运行时进行存储空间分配和收回的内存管理模型。
堆中存储的数据常常是大小、数量和生命期在编译时无法确定的。Java 对象的内存总是在 heap 中分配。
我们每天都在写代码,每天都在使用 JVM 的内存。
b) java 内存分配
1、基础数据类型直接在栈空间分配;
2、方法的形式参数,直接在栈空间分配,当方法调用完成后从栈空间回收;
3、引用数据类型,需要用 new 来创建,既在栈空间分配一个地址空间,又在堆空间分配对象的类变量;
4、方法的引用参数,在栈空间分配一个地址空间,并指向堆空间的对象区,当方法调用完后从栈空间回收;
5、局部变量 new 出来时,在栈空间和堆空间中分配空间,当局部变量生命周期结束后,栈空间立刻被回收,
堆空间区域等待 GC 回收;
6、方法调用时传入的实际参数,先在栈空间分配,在方法调用完成后从栈空间释放;
7、字符串常量在 DATA 区域分配 ,this 在堆空间分配;
8、数组既在栈空间分配数组名称, 又在堆空间分配数组实际的大小!
ava虚拟机的垃圾收集线程对软可及对象和其他一般Java对象进行了区别对待:
软可及对象的清理是由垃圾收集线程根据其特定算法按照内存需求决定的。
也就是说,垃圾收集线程会在虚拟机抛出OutOfMemoryError之前回收软可及对象,而且虚拟机会尽可能优先回收长时间闲置不用的软可及对象,
对那些刚刚构建的或刚刚使用过的“新”软可反对象会被虚拟机尽可能保留。
在回收这些对象之前,我们可以通过,Object anotherRef=(Object)aSoftRef.get(),重新获得对该实例的强引用。
回收之后,调用get()方法就只能得到null了。
1.2.3弱引用(WeakReference)
弱引用与软引用的区别:只具有弱引用的对象拥有更短暂的生命周期。
特点:
a.生命周期比软引用更短;
b.在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存;
c.不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象;
d.类似于软引用,弱引用也可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,
Java虚拟机就会把这个弱引用加入到与之关联的引用队列中
1.2.4虚引用(PhantomReference)
“虚引用”顾名思义,就是形同虚设,与其他几种引用都不同,虚引用并不会决定对象的生命周期。
如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。
特点:
a.形同虚设;
b.可用来跟踪对象被垃圾回收器回收的活动;
c.虚引用与软引用和弱引用的一个区别在于:
虚引用必须和引用队列 (ReferenceQueue)联合使用,
当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中;
1.3 ReferenceQueue与软引用结合使用
ReferenceQueue的作用:
引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中;
利用ReferenceQueue的特性,即用来清除失去了软引用对象的SoftReference;
为什么需要ReferenceQueue:
作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。
所以,当软可及对象(SoftReference袋中对象)被回收之后,虽然这个SoftReference对象的get()方法返回null;
但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。
这时候需要用到ReferenceQueue类;
如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法,如:
Object object = new Object(); // 占用系统内容较多的对象 (内容)
ReferenceQueue queue = new ReferenceQueue(); // 装SoftReference的队列
SoftReference sr=new SoftReference(object, queue);
那么当这个SoftReference所软引用的object被垃圾收集器回收的同时,sr所强引用的SoftReference对象被列入ReferenceQueue。
也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。
另外从ReferenceQueue这个名字也可以看出,它是一个队列;
当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。
在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。
如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。
利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。
于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。
以上大概就是面试android会问到的java部分。