Skip to content

Latest commit

 

History

History
204 lines (161 loc) · 8.65 KB

面试精要.md

File metadata and controls

204 lines (161 loc) · 8.65 KB

JAVA基础

JAVA多线程并发

JVM

安卓基础

网络相关

设计模式

阅读源码

简要解答

JAVA基础

== 和 equals 区别

地址比较用 == ,内容比较用 equals
注意的是 Object 的 equals 方法是 == 实现的,也就是说 equals 的具体实现依赖于类的创造者

final到底修饰了什么

final 修饰符作用与地址,类似于 == 1.final 修饰8大基本类型,则该引用不能重新赋值 2.final 修饰类类型,则该引用的地址是死的,即不能再引用其他的类,但是该类本身是可以修改的,比如修改类的成员属性是OK的

hashmap 原理

hashmap 的主干是 Node 数组,Node 是 实现了 Map.Entry 的内部类,其包含了 hash,key,value,next 四个成员,Node 正式实现单链表的关键

假设 hashmap 保有一个长度为 2^n(必须是2的幂) 的 Node 数组,put 的时候首先计算 key 的 hash 值,在根据 hash 值的后 n 位得出存贮的下标,如果没有冲突就完事,有冲突则采取链表法解决冲突,即 Node 数组每一位可以不存,存一个 Node 类,存一个 Node 链表。

那长度为什么要是2^n呢?因为这样保证了 hash 碰撞率低。代码中有这样一句 hash & (length - 1) ,化成二进制即 XXXX & 1111 (n=4,长度为16),保证了后 n 位都是 1,方便 & 操作,这样 n 位数可以表示 2^n 个数,与数组长度吻合

hashset 原理

hashset 虽然是 Collection 的子类,但它的实现确实借助 HashMap: public HashSet() { map = new HashMap<>(); } 利用 HashMap 的 key 不可重复的特性,HashSet 把值放入 HashMap 的 key 中,而 value 则放入 private static final Object PRESENT = new Object();

内部类和静态内部类,有什么特性和用处?

1.非静态内部类可以访问外部类所有属性和方法,包括 private 修饰的(包含外部类引用),而静态内部类不可以访问除static外的外部类任何属性或方法(不包含外部类引用)

2.非静态内部类隐式保有外部类的对象(OutClass.new InnerClass 可见创建非静态内部类一定要先创建外部类),而静态内部类与外部类没有关联

3,非静态内部类中不能有任何 static 修饰的东西,而静态内部类可以拥有一切属性和方法

4.另外,匿名内部类内部用到外部类属性需加 final

5.内部类 + 接口 可以实现多继承,比如:

class Father{
    public String powful = "市长";
}
class Mother{
    public String wealthy = "一百万";
}
class Son{
    class From_Father extends Father{
        
    }
    class From_Mother extends Mother{
        
    }
    void showFamily(){
        Log.d("lpy","I'm powerful " + new From_Father().powful);
        Log.d("lpy","I'm rich " + new From_Mother().wealthy);
    }
}

安卓基础

线程和进程(结合安卓实例说明ActivityA和ActivityB是否在一个线程中?

进程:资源分配的最小单位

线程:程序执行的最小单位

1.多进程的程序健壮,一个进程死了,不影响其他进程,而一个线程死了,程序就挂了

2.进程切换花销巨大,线程则更轻便

对于安卓,默认所有组件都在主线程中运行,但是可以通过修改 manifest 中 等四大组件标签的 android:process 属性来使其运行在指定的线程中,不仅如此, 标签也支持此属性

系统内存不足时,可能会强行关闭某些进程,其资源也会随之销毁,优先级如下:

  1. 前台进程 用户当前操作所必须的进程,即满足一下任一条件即视为前台进程:
  • 进程正在托管用户与之交互的 Activity

LruCache如何实现?为什么用LinkedHashmap?

LruCache 内部使用 LinkedHashMap 存取数据,使用时只需要新建一个类 extends LruCache 即可,构造函数需要传入缓存的最大值

每次 put 时,会调用 getSize 函数计算 put 的 item 的实际大小,LruCache 会根据 item 大小进行判断是否添加到缓存中抑或是直接丢掉,每次 put 会调用 evict 函数弹出最近最少未使用的元素,也就实现了 LruCache 的核心思想。

被弹出的元素会回调 onItemEvicted 函数

如果想改变缓存大小,使用 setSizeMultiplier 函数传入一个 float 型的 multiplier,最终调用了 Math.round(initialMaxSize * multiplier) 和 evict 函数实现了改变缓存大小

那为什么要使用 LinkedHashMap 呢?

因为 LinkedHashMap 可以极其方便的实现 最近最少未使用 的算法

玄机就在 LinkedHashMap 的构造函数里:

public LinkedHashMap(int initialCapacity,
                         float loadFactor,
                         boolean accessOrder) {
        super(initialCapacity, loadFactor);
        this.accessOrder = accessOrder;
    }

accessOrder 为 true 则是 access-order,访问顺序,你每次 put 或者 get 都让该元素重新排列在链表末端,也就是说链表头部的元素就是 最近最少未使用的元素啦

accessOrder 为 false 则是 insertion-order,插入顺序,也就是你插入时的顺序就是最终的顺序

JNIEnv 和 JavaVM?

JNIEnv 是线程相关的,表示该线程的 java 运行环境,也就是说,jni 想调用 java 的东西,就需要这个 JNIEnv 来帮忙,JNIEnv 可以有很多个 JavaVM 是 Java虚拟机在 JNI 层的代表, JNI 全局仅仅有一个

dp,px,dentify关系,如何适配不同分辨率

dpi : dots per inch,在安卓中,表示对角线上有多少个像素

density : 屏幕像素密度, dp * density = px , density = dpi / 160

推荐今日头条的适配方案,动态修改 denstity 的值,举例说明:

设计图 1920 px * 1080 px ,宽度经计算为 300dp ,我们手机的宽度为 Xpx 以及对应的 Ydp,那么我们想 X = 300dp * dendity,那么 density = 手机实际物理像素 / 设计图宽度的dp值,这样就完成了适配

但是,这个方案会影响第三方库中的 UI 设计,导致其控件尺寸不可避免的放大或缩小