一、基础概念
1. JDK、JRE、JVM 的区别与联系?
- JVM(Java虚拟机):运行Java字节码的虚拟计算机,是Java跨平台的核心。
- JRE(Java运行环境):包含JVM和核心类库,用于运行Java程序,不包含开发工具。
- JDK(Java开发工具包):包含JRE + 开发工具(如javac编译器、jar打包工具等)。
- 联系:
JDK ⊇ JRE ⊇ JVM,开发时用JDK,运行时只需JRE。
2. Java是编译型还是解释型语言?
Java是编译与解释并存的语言:
- 先由
javac将.java源文件编译为.class字节码文件(编译)。
- 再由JVM对字节码进行解释执行或通过JIT即时编译器编译为本地机器码执行(解释+编译)。
3. 面向对象三大特性(封装、继承、多态)的理解?
- 封装:将数据和操作封装在类中,隐藏内部实现细节,只暴露必要的接口。通过访问修饰符(
private, protected, public)实现。
- 继承:子类可继承父类的属性和方法,实现代码复用。Java是单继承,但可通过接口实现多继承效果。
- 多态:同一行为的不同表现形式,分为编译时多态(方法重载)和运行时多态(方法重写)。运行时多态依赖继承、重写、父类引用指向子类对象。
4. 抽象类与接口的区别(JDK8+)?
| 对比项 |
抽象类 |
接口 |
| 实例化 |
不能 |
不能 |
| 构造方法 |
有 |
无 |
| 方法实现 |
可以有具体方法 |
JDK8起支持default和static方法 |
| 成员变量 |
无限制 |
只能是public static final常量 |
| 继承关系 |
单继承 |
可多实现(多继承) |
| 设计理念 |
is-a 关系,模板式设计 |
has-a / can-do 关系,行为契约 |
5. == 和 equals() 的区别?
==:基础类型比较值,引用类型比较内存地址。
equals():Object类中默认用==比较地址,但String、Integer等重写了equals(),转而比较内容。
- 重点:重写
equals()必须重写hashCode(),否则在HashSet、HashMap中会出现逻辑错误。
6. final 关键字的作用?
- 修饰类:类不能被继承(如String类)。
- 修饰方法:方法不能被重写。
- 修饰变量:变量成为常量,基础类型值不可变,引用类型地址不可变但内容可变。
7. String、StringBuilder、StringBuffer 的区别?
- String:不可变类,每次修改都生成新对象,线程安全(因不可变)。
- StringBuffer:可变类,线程安全(方法用
synchronized修饰),效率较低。
- StringBuilder:可变类,线程不安全,单线程下性能最优。
二、异常处理
8. Java 异常体系是怎样的?
Throwable
├── Error(严重问题,不建议处理,如OutOfMemoryError)
└── Exception
├── RuntimeException(运行时异常/非受检异常,如NullPointerException)
└── 非RuntimeException(编译时异常/受检异常,如IOException)
9. finally 块一定会执行吗?final、finally、finalize 的区别?
finally:通常都会执行,除非在try/catch中调用了System.exit(0)或JVM崩溃。
- final:关键字,修饰类、方法、变量。
- finally:异常处理中保证代码执行的块。
- finalize:Object类的方法,对象被GC回收前调用,JDK9已废弃,不推荐使用。
三、集合框架
10. ArrayList 和 LinkedList 的区别?
| 对比项 |
ArrayList |
LinkedList |
| 底层结构 |
动态数组 |
双向链表 |
| 随机访问 |
快 O(1) |
慢 O(n) |
| 增删(非尾部) |
慢(需移动元素) O(n) |
快(只需修改指针) O(1) |
| 内存占用 |
相对小(只存数据) |
相对大(存数据+前后指针) |
| 适用场景 |
频繁读取、尾部插入 |
频繁头尾插入删除 |
11. HashMap 的底层原理(JDK8)?
- 数据结构:数组 + 链表 + 红黑树。
- 存储过程:
- 通过
hashCode()计算哈希值,再通过(n-1) & hash确定桶下标。
- 该桶为空,直接存入。
- 不为空,判断
key是否相等(equals),相等则覆盖。
- 不相等则以链表形式插入。
- 链表长度≥8且数组长度≥64时,链表转为红黑树,提升搜索效率。
- 扩容机制:默认容量16,负载因子0.75,超过阈值时扩容为原来的2倍。
- 为什么线程不安全:并发put可能导致数据丢失(覆盖)、链表成环(JDK7头插法)、size计数不准。
12. HashMap 与 Hashtable、ConcurrentHashMap 的区别?
| 对比项 |
HashMap |
Hashtable |
ConcurrentHashMap |
| 线程安全 |
否 |
是(synchronized方法) |
是(分段锁/CAS+synchronized) |
| 效率 |
高 |
低(全表锁) |
高(细粒度锁) |
| null键值 |
允许一个null键 |
不允许 |
不允许 |
| JDK版本 |
1.2 |
1.0(已不推荐) |
1.5 |
13. List、Set、Map 三者的区别?
- List:有序(存入顺序),可重复,按索引访问。
- Set:无序(部分如LinkedHashSet有序),不可重复,底层多为Map实现。
- Map:键值对存储,key不可重复,value可重复。
四、多线程与并发
14. 创建线程有哪几种方式?
- 继承
Thread类,重写run()方法。
- 实现
Runnable接口,作为参数传入Thread。
- 实现
Callable接口 + FutureTask(有返回值,可抛异常)。
- 通过线程池(
Executors框架)创建(推荐)。
15. synchronized 关键字的用法与锁升级?
- 修饰对象:
- 修饰实例方法:锁是当前对象实例
this。
- 修饰静态方法:锁是当前类的
Class对象。
- 修饰代码块:锁是括号里指定的对象。
- 锁升级(JDK6后优化):偏向锁 → 轻量级锁(CAS自旋) → 重量级锁(操作系统互斥量),随竞争程度逐渐膨胀。
16. volatile 关键字的作用?
- 保证可见性:一个线程修改了变量,其他线程立即可见(强制读写主内存,禁止线程工作内存缓存)。
- 禁止指令重排序:通过内存屏障防止JVM对指令的乱序执行优化。
- 不保证原子性:如
i++这种复合操作不具备原子性。
17. wait() 和 sleep() 的区别?
| 对比项 |
wait() |
sleep() |
| 所属类 |
Object类 |
Thread类 |
| 锁释放 |
调用后释放锁 |
调用后不释放锁 |
| 唤醒条件 |
需notify()/notifyAll()唤醒,或设置超时 |
时间到自动唤醒 |
| 使用位置 |
必须在synchronized块中 |
任意位置 |
18. 线程池的核心参数与拒绝策略?
- 七大核心参数(
ThreadPoolExecutor):
corePoolSize 核心线程数
maximumPoolSize 最大线程数
keepAliveTime 空闲存活时间
unit 时间单位
workQueue 工作队列(有界/无界)
threadFactory 线程工厂
handler 拒绝策略
- 四种拒绝策略:
AbortPolicy:抛异常(默认)
CallerRunsPolicy:由调用线程执行任务
DiscardOldestPolicy:丢弃队列最早任务
DiscardPolicy:静默丢弃新任务
五、JVM 基础
19. JVM 内存模型(运行时数据区)包含哪些部分?
- 程序计数器:线程私有,记录当前线程执行字节码的行号。
- 虚拟机栈:线程私有,由栈帧组成,每个方法执行时创建栈帧,存放局部变量表等。
- 本地方法栈:为Native方法服务。
- 堆:所有线程共享,存放对象实例,GC的主要区域。
- 方法区(元空间):存储类信息、常量、静态变量、即时编译后的代码。JDK8后用本地内存的元空间替代永久代。
20. 常见垃圾回收算法?
- 标记-清除:标记存活对象,清除未标记对象。产生内存碎片。
- 标记-复制:将内存分为两块,存活对象复制到另一块,整体清除当前块。无碎片但浪费空间(新生代使用)。
- 标记-整理:标记后,存活对象向一端移动,清理边界外内存。无碎片,代价较高(老年代使用)。
- 分代收集:新生代用复制算法,老年代用标记-清除/整理算法。
六、进阶特性(JDK8+)
21. Lambda 表达式与函数式接口?
- 函数式接口:只有一个抽象方法的接口,可用
@FunctionalInterface注解标识。
- Lambda表达式:用于简化匿名内部类的写法,本质是函数式接口的实现。
22. Stream 流的常用操作?
| 操作类型 |
常用API |
| 中间操作(惰性求值) |
filter, map, sorted, distinct, limit |
| 终端操作(触发计算) |
collect, forEach, reduce, count, anyMatch |
23. Optional 类的作用?
解决NullPointerException问题,作为可能为null的值的容器,强制要求使用者显式处理null情况。常用方法:ofNullable(), orElse(), ifPresent()。
七、编码实践
24. Comparable 和 Comparator 的区别?
- Comparable:内部比较器,类自身实现
compareTo方法,定义自然排序规则。
- Comparator:外部比较器,不修改类本身,可定义多个排序规则。使用时作为参数传入
sort方法。
25. 深拷贝与浅拷贝的区别?
- 浅拷贝:复制对象时,基础类型复制值,引用类型复制引用地址(指向同一对象)。
- 深拷贝:复制对象时,引用类型也会创建新对象并复制内容,两者完全独立。
所有评论(0)