秋招斩获所有互联网大厂面经之Java

>> 背熟,唯手熟尔,大家都这么厉害,只能记得更准才行。

1
JVM
① JVM 是运行 Java 字节码的虚拟机。
② JVM 支持多系统,相同的字节码具有相同的结果,“一次编译,到处运行”。
 
 

-----------------------------------

2
JDK 和 JRE
① JRE 是 Java 运行时环境,包含 JVM, Java 类库, Java 命令。
② JDK 是 Java 开发套件,包含 JRE 一切,还有 javac, javadoc, jdb.
③ JRE 只能运行,不能创建新程序,而 JDK 可以。
 
 

-----------------------------------

3
Java 和 C++ 的区别
① 都是面向对象,支持封装、继承、多态
② Java 没有指针
③ Java 类单继承,C++ 多继承,Java 接口多继承
④ Java 自动内存管理,C++ 手动内存管理
⑤ C++ 中\0代表结束,Java 没有结束符
 
 

-----------------------------------

4
Java 重写与重载
① 重载:方法名相同,参数、返回值、访问修饰符不同
② 重写:子类重写父类方法,方法名、参数相同。
 
 

-----------------------------------

5
封装、继承、多态
① 封装:把一个对象的属性私有化,同时提供一些可供外界访问的方法。
② 继承:子类继承父类,可以增加新的属性和方法,方便代码复用。
③ 多态:程序中定义的引用变量的具体类型以及它的方法,在编程时不确定,在运行期间才确定。使用继承和接口实现。
 
 

-----------------------------------

6
String StringBuffer 和 StringBuilder 的区别是什么?String 为什么是不可变的?
① 可变性:String 不可变,另外两个可变,因为 String 类中使用 final 修饰了字符数组。
② 线程安全:String 不可变,线程安全。StringBuffer 增加了同步锁,线程安全,StringBuilder 不安全。
③ String 会生成一个新的对象;StringBuilder 比 StringBuffer 快 10% 到15%
 
 

-----------------------------------

7
装箱与拆箱
① 装箱:基本类型转换为引用类型,使用 valueOf 方法实现。
② 拆箱:引用类型转换为基本类型,使用 xxxValue 方法实现。
 
 

-----------------------------------

8
接口和抽象类的区别
① 接口中不能有实现,而抽象类中可以有已经实现的方法。
② 类可以实现多个接口,但只能实现一个抽象类。
③ 接口方法默认 public, 抽象方法除了 private 都可以。
④ 接口中变量只能 static / final, 抽象类不一定。
⑤ 从设计层面上来说,接口是对行为的规范,抽象是对类的抽象。
 
 

-----------------------------------

9
成员变量与局部变量的区别
① 语法:成员变量属于类,可以被修饰;局部变量方法内有效,只能被 final 修饰。
② 内存:成员变量在堆中,局部变量在栈中。
③ 生存周期:成员变量与对象共存亡,局部变量方法调用后自动消失。
④ 初值:成员变量会自动赋初值,局部变量不自动赋初值。
 
 

-----------------------------------

10
== 与 equals
① == 基本类型比较值,引用类型比较地址。
② equals 没有重写时和 == 一样,重写后一般用来比较内容是否相等。
 
 

-----------------------------------

11
hashCode 和 equals
① hashCode 的作用是获取哈希码,用来确定在哈希表中的位置。
② 规定:如果对象相等,则 hashCode 相等,equals 返回 true,因此 equals 重写也要重写 hashCode 。
 
 

-----------------------------------

12
线程的基本状态
① new 后是初始状态。
② 调用 start() 是就绪状态。
③ 获得 cpu 时间后是运行状态
④ 调用 wait() 后是等待状态。
⑤ 调用 wait(), sleep() 等待指定时间,是超时等待状态。
⑥ 调用同步方法没有得到锁,是阻塞状态。
⑦ 调用 run() 后是终止状态。
 
 

-----------------------------------

13
集合包括哪些?
顺序访问的 List、独一无二的 Set、用 key 搜索的 Map
 
 

-----------------------------------

14
ArrayList 与 LinkedList
① 线程安全:都不安全
② 数据结构:ArrayList 是数组,LinkedList 是双向链表。
③ 复杂度:ArrayList 修改 O(N), 访问 O(1);LinkedList 修改 O(1), 访问 O(N)
④ 空间占用:ArrayList 末尾预留空间,LinkedList 每一个节点要存放前驱后驱和数据。
 
 

-----------------------------------

15
HashMap 和 HashTable
① 线程安全:HashMap 不安全,HashTable 安全
② 效率:HashMap 比 HashTable 快
③ Null: HashMap 键值都可以为空,HashTable 都不支持空。
④ 容量:HashMap 初始 16,扩容变 2 倍;HashTable 初始 11,扩容变 2n + 1
⑤ 底层结构:HashMap 链表长度大于 8 变成红黑树,HashTable 没有这样的机制。
 
 

-----------------------------------

16
HashMap 底层实现
HashMap 底层实现:
① 数据结构:数组 + 链表,JDK1.8 以后:链表长度大于 8,转化为红黑树。
② 存放位置:hashCode 经过“扰动函数”得到 hash,(n - 1) & hash 为位置。这个扰动函数指的是:hashCode 与 右移 16 位后的 hashCode 异或
③ 冲突解决:开放地址法、拉链法、再哈希法:
 
冲突解决具体来说:
① 开放地址法:所谓的开放地址法就是一旦发生了冲突,就去寻找下一个空的散列地址,只要散列表足够大,空的散列地址总能找到
② 拉链法:每个哈希表节点都有一个next指针,多个哈希表节点可以用next指针构成一个链表,被分配到同一个索引上的多个节点可以用这个链表连接起来
③ 再哈希法:有多个不同的Hash函数,当发生冲突时,使用第二个,第三个,….,等哈希函数计算地址,直到无冲突。虽然不易发生聚集,但是增加了计算时间。
 
 
 

-----------------------------------

17
HashMap 长度为什么是 2 的幂次方?
① 存放位置是 hash 对数组长度取余数。
② % 符号效率低,而如果除数是 2 的幂次,则取余操作等价于“减去1后求与” (n - 1) & hash.
 
 

-----------------------------------

18
ConcurrentHashMap 和 HashTable
① 数据结构:1.8 之前,ConcurrentHashMap 采用分段数组+链表,HashTable 是数组 + 链表。1.8 以后ConcurrentHashMap 都变成了“数组、链表、红黑树”
② 实现线程安全的方式:(1.7) ConcurrentHashMap 对桶数组使用分段锁,多线程访问不同数据段的数据。(1.8) 使用 synchronized只锁定首节点; HashTable 使用 synchronized 的同一把锁,竞争激烈效率低。
 
 

-----------------------------------

19
synchronized 关键字
① 功能:synchronized 关键字解决多线程访问资源的同步问题,保证被它修饰的方法或代码块在任意时刻只能有一个线程执行。
② 三种使用方式:修饰实例方法(对对象实例加锁)、修饰静态方法(对当前类所有实例加锁)、修饰代码块(对指定对象加锁)
③ 底层原理:用 javap 可以看到指令,修饰代码块(monitor enter 指令开始位置,monitor exit 指令结束位置)、修饰方法(ACC_SYNCHRONIZED 标识)。
④ JDK 1.6 后的优化:偏向锁、自旋锁、适应性自旋锁、锁消除、锁粗化。
 
 

-----------------------------------

20
synchronized 和 ReentrantLock 的区别
① 两者都是可重入锁,自己可以再次获取自己的内部锁。
② synchronized 依赖于 JVM, 实现细节没有直接暴露;ReentrantLock 依赖于 API,需要 lock() / unlock() 配合 try / finally 完成。
③ ReentrantLock 比 synchronized 增加了高级功能:
等待可中断:正在等待的进程可以放弃等待。
公平锁:先等待的线程先获得锁,构造函数中传递 true。
选择性通知:结合 Condition 实例可以实现选择性通知。
 
 

-----------------------------------

21
volatile 关键字
① 保证变量的可见性:修饰一个变量,每次使用它都要去主存中读取。
② 防止指令重排:避免编译器优化代码的顺序。
 
 

-----------------------------------

22
JVM 内存区域
分类
JDK 1.8 以前:
① 线程共享的:堆、方法区
② 线程私有的:程序计数器、虚拟机栈、本地方法栈。
③ 直接内存(非运行时数据区)
JDK 1.8 后:
方法区替换成了元空间放在直接内存中
各个部分解释:
① :存放对象实例,细分为新生代和老年代,可以更好的分配与回收内存。
② 方法区:存放类、常量、静态变量、即时编译代码,又称永久代。永久代是 HotSpot 虚拟机对方法区的实现方式。
③ 直接内存:不是虚拟机运行时数据区的一部分,通过 NIO 类直接分配堆外内存,避免来回复制数据。
④ 程序计数器:依次读取指令,实现流程控制;多线程中记录当前线程执行的位置。
⑤ 虚拟机栈:方法执行的内存模型,每个方法执行的同时会创建一个栈帧,里面有局部变量表,表中存放了 8 种基本数据类型、引用类型、returnAddress 类型。
⑥ 本地方法栈:虚拟机栈执行 Java 方法,本地方法栈执行 Native 方法,同样创建一个栈帧。
 
 

-----------------------------------

23
对象的创建过程
① 类加载检查:遇到 new 后,首先定位类的符号引用,然后判断是否已经被加载过,如果没有加载过,需要执行类加载。
② 分配内存:给新生对象分配内存,分配方式有指针碰撞、空闲列表两种,线程安全方面有“CAS + 失败重试”和“TLAB 分配”两种。
③ 初始化零值:将除了对象头外的内存空间初始化为零值。
④ 设置对象头:如设置是哪个类的实例、哈希码、GC 分代年龄等。
⑤ 执行 init 方法:虚拟机产生了一个新的对象,再按照程序员的意愿进行初始化。
 
 

-----------------------------------

24
垃圾回收算法
① 标记 - 清除:首先标记出所有需要回收的对象,在标记完成后统一回收。存在效率和空间碎片问题。
② 复制:解决效率问题,内存分为两块,一块使用完后将存活的对象复制到另一块中,然后清理掉之前的,这样每次都是回收一半。
③ 标记 - 整理:标记后不直接回收,而是移动到一端,然后清理边界以外的内存。
④ 分代收集:根据年代特点选择合适的算法。新生代死亡多,选择复制算法;老年代死亡少,使用“标记 - 清除”或“标记 - 整理”算法。
 
 

-----------------------------------

25
常见的垃圾回收器
① Serial / Serial Old:单线程,新生代使用复制算法,老年代使用“标记 - 整理”算法。
② ParNew:多线程的 Serial.
③ Parallel Scanvenge / Parallel Old: 类似 ParNew, 关注吞吐量,利用 CPU 的效率更高。
④ CMS: 关注于最短停顿时间,使用“标记 - 清除”算法,有初始标记、并发标记、重新标记、并发清除四步。
⑤ G1: 面向多核、大内存的服务器,停顿时间短并且吞吐量大。
 
 

-----------------------------------

26
反射
① 概念:程序在运行时能获取自身的信息。
② 功能
判断对象的类、调用对象的方法、判断类的成员变量和方法、构造一个对象
③ 用途
各种框架、加载驱动、读取配置文件
④ 优缺点
优点:提高了灵活性和拓展性。
缺点:性能变慢、内部逻辑模糊。
⑤ 使用
a. forName 、类名.class 、对象.getClass()
b. getConstructor() 、newInstance()
c. getFields() 、getDeclaredFields() 、getMethod() 、 getDeclareMethod()
d. Method.invoke()
 
 

-----------------------------------

27
注解
① 概念:也叫元数据,对代码元素进行说明与注释,在编译、类加载、运行时读取,并执行相应的处理。
② JDK 内置的注解
Overried 重写,Deprecated 过时,SuppressWarnings 消除警告,FunctionalInterface 函数式接口
③ 定义注解
public @interface Auth {
  String name() default "xxx";
}
④ 获取注解的值
isAnnotationPresent(xxx.class) 判断是否存在某个注解
getAnnotation 获取注解对象
⑤ 常见注解
Spring boot 中:
@PostMapping
@Components等
安卓中:
@BindView
@NonNull
@MainThread
@RequiresPermission
⑥ 使用场景
生成文档、配置文件、框架处理
 
 

-----------------------------------

28
双亲委派模型
JAVA 的四种类加载器:
① 根类加载器 Bootstrap:C++ 写的,看不到源码。
② 扩展类加载器 Extension: 加载位置是 jre\lib\ext
③ 应用类加载器 Application: 加载位置: classpath
④ 自定义类加载器:继承 ClassLoader
双亲委派模型:
双亲委派是指每次收到类加载请求时,先将请求委派给父类加载器完成(所有加载请求最终会委派到顶层的Bootstrap ClassLoader加载器中),如果父类加载器无法完成这个加载(该加载器的搜索范围中没有找到对应的类),子类尝试自己加载, 如果都没加载到,则会抛出 ClassNotFoundException 异常.
好处
① 避免重复加载
② 安全性:如果不使用这种模式,那么核心类型就可能会替换,带来非常大的安全隐患。
 
 

-----------------------------------

29
线程安全
我主要从定义、5 种安全分级、3种实现方法来阐述。
一、线程安全的定义
多个线程操作一个对象,得到的结果都是正确的,就称为是线程安全的。
二、JAVA 线程安全分级
① 不可变:基本数据类型或被 final 修饰,如 String.
② 绝对线程安全:无论环境如何,都不需要额外的同步措施。
③ 相对线程安全:不需要额外的保障措施,一些特定的情况才需要使用额外的同步手段。
④ 线程兼容:本身不安全,使用同步手段才安全。
⑤ 线程对立:无论是否采取同步措施,都无法并发使用。
三、线程安全的实现方法
① 互斥同步:互斥(多线程并发同一时刻只有一个访问)、同步(实现互斥的手段,如临界区、互斥量、信号量)。常用的是 syncronized 关键字和 ReentrantLock 重入锁。
② 非阻塞同步:乐观的先操作,有冲突再解决。通过 CAS (Compare And Swap),但是可能存在 ABA 问题,原子引用类通过版本号保证 CAS 的正确性,但是还是互斥同步更有效。
③ 无同步:不涉及共享数据,天生就是线程安全的,如可重入代码,任何时候都能中断。还有线程本地存储,Thread Local Storage (TLS),每个线程访问的都是自己的变量。
 
 

-----------------------------------

30
AQS 底层原理
① 概念:全称 Abstract Queued Synchronizer, 中文是抽象队列同步器,是一个并发包的基础组件,用来实现各种锁、组件。
② 组成:包含 state 变量、加锁进程、等待队列。state 代表加锁的状态,0 没有锁,大于 0 已经加锁。加锁进程在加锁后被修改,等待队列存放处于等待状态的进程。
③ 重入:发现 state 大于 0,但是加锁进程就是自己,那么仍然可以加锁,state++。
④ 等待:state 大于 0,且加锁进程不是自己,就进入等待。
⑤ 释放:业务执行完后,释放锁,state--,减到 0 后设置加锁进程为空。
 
 

-----------------------------------

31
自旋锁
① 概念:没有获取到锁的进程一直循环等待,判断资源是否已经释放,称为自旋锁。
② 原理:如果持有锁的进程能在短时间内释放资源,那么等待锁的进程只需要等到资源被释放即可获取,这样就避免了用户进程和内核切换的消耗。为了避免等待时间过长,需要设置一个自旋时间,JDK 1.6 以后引入了自旋时间不固定的适应性自旋锁。
③ 优缺点:优点是在竞争不激烈、占用时间短时,性能得到提高;缺点是在竞争激烈、占用时间长时,自旋的消耗反而大于线程阻塞的消耗。
④ 公平性:自旋锁不公平,在此基础上提出了排队叫号的 TicketLock,TicketLock 需要进行缓存同步,因此又引入了 CLHLock 和 MCSLock。
 
 

-----------------------------------

32
synchronized 优化、锁优化、锁升级
4 种锁状态:
锁的 4 种状态:无锁、偏向锁、轻量级锁、重量级锁。
① 无锁:是一个普通对象,是否偏向锁 = 0,锁标志位 = 01。
② 偏向锁:线程发现无锁状态后,将是否偏向锁设置成 1,锁标志位还是 01,markword 线程 ID 改成当前线程 ID.
③ 轻量级锁:如果发现是偏向锁,线程ID 是当前线程,则进入;如果不是当前线程,则升级为轻量级锁。轻量级锁,用 CAS 将 markword 设置为指向自己的指针,成功者得到锁,失败者继续循环,因此又称为自旋锁。
④ 重量级锁:如果自旋次数太多会导致 CPU 消耗过大,反而不如阻塞,因此升级为重量级锁,将不拥有锁的线程都阻塞。
2 种额外的锁优化:
① 锁消除:将已经线程安全的代码的锁消除掉。
② 锁粗化:连续多次对同一个对象加锁,则优化为对整段进行加锁。
 
 

-----------------------------------

33
ConcurrentHashMap 了解吗?
① 线程安全:1.7 使用 Segment + HashEntry + ReentrantLock, 1.8 中使用 Node + CAS + Synchronized.
② 数据结构:1.7 是分段锁,1.8 是数组 + 链表 + 红黑树。
③ 锁的粒度:1.7 Segment 加锁,1.8 对 Node 加锁。
④ 时间复杂度:从 O(N) 变成 O(logN)
⑤ get 操作不需要加锁:是因为 Node.val 使用了 volatile 修饰。这个 volatile 指的不是数组那个,数组那个是为了保证扩容时的可见性。
 
 

-----------------------------------

34
GC 如何判断对象是否需要回收?
① 引用计数法:给对象增加一个引用计数器,引用的时候计数器加 1,失效的时候计数器减 1,计数器为 0 的对象是需要回收的。但是主流的 JVM 中没有使用这种方法,因为它难以解决循环引用的问题。
② 可达性分析法:在有向图中,以一系列的 GC Roots 对象为起点向下搜索,走过的路径称为引用链。当一个对象没有任何引用链相连时,则对象是不可达的,需要回收。
 
 

-----------------------------------

35
内部类和静态内部类的区别
① 内部类的成员变量和方法不能声明为静态的,而静态内部类可以。因为如果内部类可以静态声明的话,那么可能外部类还没有实例化,这时候访问就会出现冲突。
② 内部类实例化是 new A().new B(), 静态内部类可以直接new A.B()。
③ 内部类可以引用外部类的所有属性和方法,而静态内部类只能引用静态属性和方法。
 
 

-----------------------------------

36
Java中线程状态有哪些,引发线程状态变化的例子
状态
① new 后是初始状态。
② 调用 start() 是就绪状态。
③ 获得 cpu 时间后是运行状态
④ 调用 wait() 后是等待状态。
⑤ 调用 wait(), sleep() 等待指定时间,是超时等待。
⑥ 调用同步方法没有得到锁,是阻塞状态。
⑦ 调用 run() 后是终止状态。
引发线程状态变化的例子:
① 线程同步:获得锁进入就绪态,获得 CPU 进入运行态。没有获得锁则进入阻塞态。
② 线程通信 : wait() 进入阻塞态,notify() 进入就绪态,获得 CPU 时间进入运行态。
③ 线程自身
  • sleep() 进入就绪态,暂时让出 CPU 但不释放锁;
  • yield() 由运行态变就绪态,称为让步,正在运行的线程让就绪态的线程先执行一下,自己变成就绪态等待再次执行;
  • 其他线程对象.join():先执行一下其他线程,然后自己再接着执行。
 
 

-----------------------------------

37
thread.yield()作用?使线程进入哪个状态?
yield() 由运行态变就绪态,称为让步,正在运行的进程让就绪态的进程先执行一下,自己变成就绪态等待再次执行
 
 

-----------------------------------

38
LinkedHashMap
① 数据结构:继承 HashMap,数组 + 链表 + 红黑树的基础上增加一条双向链表。
② 方法:继承 put() / remove(), 重写 get()。
③ 节点:增加 before / after,用于维护双向链表。插入节点时,将节点追加到双向链表尾部,实现插入有序和访问有序。
④ 实现 LRU:继承 LinkedHashMap,重写 removeEldestEntry() 方法,改为超出一定长度返回 true。
 
 

-----------------------------------

39
介绍几种线程安全的List
① ArrayList 线程不安全,可以通过 SynchronizedList 将所有 List 接口的实现类转换成线程安全的 List。
② Vector 是线程安全的。因为所有方法都被同步了。
③ CopyOnWriteArrayList: 复制再写入,添加元素的时候先把原 List 复制一份,然后再添加新元素。写锁读不锁。
 
 

-----------------------------------

40
JVM 1.7和1.8的改动
① 接口可以添加一个非抽象的方法,使用 default 修饰即可。
② Lambda 表达式,类似于 javascript es6 的箭头函数,不同的是这里用的是 单线箭头 ->
③ 增加函数式接口:接口里只能有一个抽象方法。
④ 支持多重注解。
⑤ 增加 Base64 编码
⑥ javascript 引擎 Nashorn,可以相互调用。
⑦ 编译器优化:将方法的参数名加入到了字节码。
⑧ 一些对象的数据结构、加锁发生了变化,如 HashMap 的红黑树,ConcurrentHashMap 的加锁机制。
⑨ 内存布局:移出永生代,方法区替换成了元空间放在直接内存中。
 
 

-----------------------------------

41
类加载的过程,详细说说
一共七个步骤
① 加载:获取类的二进制字节流,将其代表的静态存储结构转化为方法区的运行时数据结构,然后在堆中生成一个 java.lang.Class 对象,作为方法区数据的访问入口。
② 验证:保证 Class 文件符合 JVM 规范,分为四部分:文件格式、元数据、字节码、符号引用。
③ 准备:为成员变量分配内存并初始化零值。
④ 解析:将常量池内的符号引用替换成直接引用。
⑤ 初始化:按照程序员的意愿去初始化,如构造方法、父类初始化等。
⑥ 使用:按程序定义的行为执行。
⑦ 卸载:GC 完成。
 
 

-----------------------------------

42
wait和sleep的区别
① wait 是 Object 类的方法,sleep 是 Thread 类的方法。
② wait 会释放锁,sleep 不会释放锁。
③ wait 依赖 synchronized 关键字,sleep 不依赖。
④ wait 需要被唤醒,sleep 不需要被唤醒。
 
 

-----------------------------------

43
JAVA 基本数据类型
整数类型

整数类型变量用来表示整数的数据类型。整数类型又分为字节型(byte)、短整型(short)、整型(int)、长整型(long)这四种。

浮点类型

浮点类型用来表示小数的数据类型。浮点类型分为float(单精度型)和double(双精度类型)这两种。float(单精度型)一般在精度要求不高的时候使用。double(双精度类型)一般在数值精度高的时候使用。

字符类型

字符类型的关键字是“char”。

布尔类型

布尔类型是表示逻辑值的基本数据类型。boolean常量有“真”和“假”这两个状态。一般用true表示“真”、false表示“假”。它的定义方法是在变量名前加上类型关键字boolean。
 
 

-----------------------------------

44
给40亿个不重复的unsigned int,没有排序。然后给定一个数,如何判断这个数是否在40亿个里面?
① 分析:传统方案占用内存空间 2^32 = 4GB 内存,而如果使用 Bitmap 的话,一个 Byte 有 8 位,占用空间为 4GB / 8 = 512 MB。
② 思路:用 512 MB 的内存,建立一个 Bitmap 数组,扫描 40 亿个数,如果某个数字出现,就将 Bitmap 数组的对应位置赋 1,扫描完毕就建立了一个索引,对于给定的数,检查 Bitmap 的位置,就知道了是否出现过。同样也可以实现排序,再次遍历 bitmap,如果为 1 就输出,相当于计数排序。
 
 

-----------------------------------

45
序列化
① 概念:将 Java 对象转换为字节序列,可以保存在硬盘上或者通过网路传输。
② 实现:实现 Serializable 接口的对象是可序列化的。
③ 步骤:1、创建一个 ObjectOutputStream 输出流。2、调用 writeObject 方法输出可序列化对象。反序列化则是 ObjectInputStream 和 readObject()
 
 

-----------------------------------

46
volatile的语义 怎么实现的
volatile 的语义
① 保证变量的可见性:修饰一个变量,每次使用它都要去主存中读取。
② 防止指令重排:避免编译器优化代码的顺序。
volatile 如何实现的
加入 volatile 关键字时,汇编代码中会多出一个 lock 前缀指令,相当于一个内存屏障。内存屏障有三大功能:
① 保证指令重拍时,后面的指令不会排到屏障的前面,前面的指令不会排到屏障的后面。
② 对缓存的修改立刻写入主存。
③ 如果是写操作,则 CPU 中对应的缓存无效。
 
 

-----------------------------------

47
强软弱虚
① 强引用:JVM 不会回收具有强引用的对象,即使内存不足,宁愿抛出 OOM 也不会回收这种对象。是生活必需品。如 Object obj = new Object();
② 软引用:在内存不足的时候才会回收。用 SoftReference 表示。
③ 弱引用:垃圾回收器只要发现了只具有弱引用的对象,无论内存是否够用,都会回收。用 WeakReference 表示。
④ 虚引用:虚引用不会对生存时间造成影响,也无法通过虚引用获取真实引用,唯一的用途就是在对象被回收时收到系统通知,使用 PhantomReference 表示。
 
 

-----------------------------------

48
线程池用过没,有什么优点
① 概念:线程池在启动时创建大量空闲的线程,程序将任务传递给线程池,线程池指派一条线程来执行这个任务,执行完毕后线程不销毁,而是重新回到线程池中。
② 四种线程池:可缓存的 CacheThreadPool、固定数量的 FixedThreadPool、数量为 1 的 SingleThreadPool 、支持定时和周期任务的 ScheduledThreadPool。
③ 优点:线程重用,降低了资源消耗;不用创建线程直接运行任务,提高了响应速度;提高了线程的可管理性。
 
 

-----------------------------------

49
jvm和dvm有什么区别
① JVM 基于栈,DVM 基于寄存器,编译时间更短。
② JVM 是每一个文件生成一个 class,DVM 是一个 dex 文件包含了所有的类
 
 

-----------------------------------

50
final关键字
① final 修饰一个类,表示这个类不能被继承
② final 修饰一个方法,表示这个方法不能被重写
③ final 修饰一个变量,表示这个变量不能被修改,但是这个变量的内容是可变的。
 
 

-----------------------------------

51
抽象类能不能继承普通类
抽象类可以继承普通类。抽象类和普通类的区别只是抽象类不能被实例化、允许抽象方法,必须由其他类继承它来完成工作,除此之外没有其他特殊的地方。
 
 

-----------------------------------

52
wait不在同步块里面执行会怎么样
会抛出异常:IllegalMonitorStateExpection
wait / notify 存在竞争,如果不在同步块中执行,wait 被调用的时候,可能 wait 的条件已经不能满足了,notify 就毫无效果。
 
 

-----------------------------------

53
设计模式 责任链讲一下
责任链模式,为请求创建了一个接受者的链。如果一个接受者不能处理请求,则传递给下一个接受者。
 
 

-----------------------------------

54
MVP
 
 
 

-----------------------------------

55
线程通信
① 使用 volatile 关键字,是共享内存的思想
② wait / notify
③ ReentrantLock 结合 Contition 实例
④ CountDownLatch、 LockSupport 类
 
 

-----------------------------------

56
HashMap 扩容
概述
当 HashMap 的大小超过了负载因子 loadFactor 定义的 75% 容量,会创建一个两倍大小的新数组,并进行重哈希,更新后位置可能在原下标的位置,也可能在“原下标+原容量”的位置。
 
 

-----------------------------------

57
HashMap 多线程扩容会出现什么问题?
结论
1.7 的时候,会出现死循环,1.8 不会。
原因
① 1.7 的时候,扩容后链表的节点在新的桶中使用头插法插入,新的桶会倒置原链表,多个线程同时扩容就可能会产生一个环形链表,就会导致死循环。
② 1.8 的时候,采用的是尾插法,所以不会导致死循环。
 
 

-----------------------------------

58
CAS 
① 概念:从某一内存上取值V,和预期值A进行比较,如果内存值V和预期值A的结果相等,那么我们就把新值B更新到内存,如果不相等,那么就重复上述操作直到成功为止。
② 缺点:
  • ABA 问题,可以通过版本号机制解决
  • 长时间自旋非常消耗时间
 
 

-----------------------------------

59
Java 实现多线程的几种方式
① 继承Thread类

② 实现Runnable接口

③ 实现Callable接口

④ 使用线程池
 
 

-----------------------------------

60
生产者消费者模型
生产者生产数据到缓冲区中,消费者从缓冲区中取数据。

如果缓冲区已经满了,则生产者线程阻塞;

如果缓冲区为空,那么消费者线程阻塞。
 
 

-----------------------------------

61
BIO / NIO / AIO
① BIO: 用户进程在发起一个IO操作以后,必须等待IO操作的完成
② NIO: 用户进程发起一个IO操作以后可以返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪
③ AIO: 用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会收到IO操作完成的通知。
 
 

-----------------------------------

62
计算 Bitmap 的大小
getRowBytes() 乘以 getHeight()
不使用 getByteCount 是因为它要求的版本号较高
 
 

-----------------------------------

63
原子性、可见性、有序性
① 原子性:同一时刻只能有一个线程操作,如 CAS、synchronized 和 ReentrantLock
② 可见性:一个线程对主存的修改能及时被其他线程看到,如 volatile 关键字
③ 有序性:指令没有被编译器重排,可以通过 volatile, synchronized, lock 保证有序性
 
 

-----------------------------------

64
内部类访问的变量为什么要用final修饰?
如果不使用 final 修饰的话,外部类的方法执行完毕后,这个变量就 GC 了,而内部类的某个方法还没有执行完,这时候它引用的变量已经找不到了。
而如果定义为 final,这个变量所指向的内存区域就不会变。
一句话来说,就是为了解决局部变量的生命周期与局部内部类的对象的生命周期的不一致性问题
 
 

-----------------------------------

65
Okhttp的拦截器链的设计模式
责任链模式,适合重构很长的 if-else 结构,或者 switch-case 结构
 
 

-----------------------------------

66
Python 和 Java 的区别
语言特性的区别
① Java是一种静态类型语言,Python是一种动态类型语言,Java中的所有变量需要先声明(类型)才能使用,Python中的变量不需要声明类型
② Java编译以后才能运行,Python直接就可以运行
③ 兼容性:Java 比较稳定,Python 2/3 大部分不兼容
 
语法上的区别
① JAVA 里的块用大括号对包括,Python 以冒号 + 四个空格缩进表示。
② JAVA 的类型要声明,Python 的类型不需要。
③ JAVA 每行语句以分号结束,Python 可以不写分号。
 
 

-----------------------------------

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MichaelToLearn

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值