Java基础笔试核心知识点详解

Java基础是技术笔试的首要考察内容,涵盖了集合框架、反射机制、字符串操作、异常处理、设计模式等核心领域。本文将深入剖析这些知识点,帮助读者系统掌握Java基础的核心内容。

1. 集合框架深度解析

1.1 HashMap与ConcurrentHashMap的实现原理

HashMap实现原理

HashMap基于哈希表实现,采用数组+链表+红黑树的复合结构:

  • 存储结构:使用Node数组存储键值对,每个Node包含key、value、hash、next四个字段
  • 哈希计算:通过key的hashCode()计算哈希值,然后通过(n-1) & hash确定数组索引
  • 冲突处理:采用链地址法解决哈希冲突,当链表长度≥8且数组长度≥64时转为红黑树
  • 扩容机制:当元素数量超过阈值(容量×负载因子0.75)时触发扩容,容量变为原来的2倍
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// HashMap核心源码分析
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
boolean evict) {
Node<K,V>[] tab; Node<K,V> p; int n, i;
if ((tab = table) == null || (n = tab.length) == 0)
n = (tab = resize()).length;
if ((p = tab[i = (n - 1) & hash]) == null)
tab[i] = newNode(hash, key, value, null);
else {
// 处理哈希冲突
Node<K,V> e; K k;
if (p.hash == hash &&
((k = p.key) == key || (key != null && key.equals(k))))
e = p;
else if (p instanceof TreeNode)
e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
else {
for (int binCount = 0; ; ++binCount) {
if ((e = p.next) == null) {
p.next = newNode(hash, key, value, null);
if (binCount >= TREEIFY_THRESHOLD - 1)
treeifyBin(tab, hash);
break;
}
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
break;
p = e;
}
}
}
}

ConcurrentHashMap线程安全机制

ConcurrentHashMap在JDK1.8中采用CAS+synchronized保证线程安全:

  • 存储结构:与HashMap类似,但Node的val和next字段使用volatile修饰保证可见性
  • 并发控制
    • 插入时使用CAS操作保证原子性
    • 当发生哈希冲突时,使用synchronized锁住链表头节点
    • 扩容时支持多线程协助扩容(ForwardingNode机制)
  • size计算:使用baseCount和CounterCell数组来统计元素数量,避免全局锁
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// ConcurrentHashMap的put方法核心逻辑
final V putVal(K key, V value, boolean onlyIfAbsent) {
if (key == null || value == null) throw new NullPointerException();
int hash = spread(key.hashCode());
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
// 使用CAS操作插入新节点
if (casTabAt(tab, i, null,
new Node<K,V>(hash, key, value, null)))
break;
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
V oldVal = null;
synchronized (f) { // 锁住链表头节点
if (tabAt(tab, i) == f) {
// 处理链表或红黑树插入
}
}
}
}
addCount(1L, binCount);
return null;
}

1.2 Vector与ArrayList的线程安全比较

特性 Vector ArrayList
线程安全 是,所有方法使用synchronized修饰 否,非线程安全
扩容策略 扩容为原来的2倍 扩容为原来的1.5倍
性能 较低,方法级同步开销大 较高,无同步开销
初始容量 10 10
遍历方式 支持Enumeration和Iterator 仅支持Iterator
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Vector的线程安全实现
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}

// ArrayList的非线程安全实现
public boolean add(E e) {
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}

1.3 LinkedList的实现机制与适用场景

实现机制

LinkedList基于双向链表实现:

  • 节点结构:每个节点包含prev、next、item三个字段
  • 头尾指针:维护first和last指针,支持快速头尾操作
  • 队列特性:实现了Deque接口,支持队列和栈的操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// LinkedList节点定义
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;

Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}

// 头插法
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}

适用场景

  • 频繁插入删除:在中间位置插入删除元素时性能优于ArrayList
  • 队列/栈实现:作为队列或栈使用时效率很高
  • 内存敏感:不需要连续的内存空间

2. 反射机制详解

2.1 反射基本原理

反射机制允许程序在运行时获取类的信息并操作类或对象的属性、方法等:

  • Class对象:每个类在JVM中都有对应的Class对象,包含了类的完整结构信息
  • 获取方式
    • Class.forName("全限定类名")
    • 类名.class
    • 对象.getClass()

2.2 反射的应用场景

获取类信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 获取Class对象
Class<?> clazz = Class.forName("java.util.ArrayList");

// 获取类名
System.out.println("类名:" + clazz.getName());
System.out.println("简单类名:" + clazz.getSimpleName());

// 获取包信息
Package pkg = clazz.getPackage();
System.out.println("包名:" + pkg.getName());

// 获取父类
Class<?> superClass = clazz.getSuperclass();
System.out.println("父类:" + superClass.getName());

// 获取接口
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> iface : interfaces) {
System.out.println("接口:" + iface.getName());
}

创建对象

1
2
3
4
5
6
7
// 使用无参构造创建对象
Class<?> clazz = Class.forName("java.util.ArrayList");
Object obj = clazz.newInstance(); // 已过时

// 使用指定构造方法
Constructor<?> constructor = clazz.getConstructor(int.class);
Object obj = constructor.newInstance(10);

调用方法

1
2
3
4
5
6
7
8
9
10
11
// 获取方法
Class<?> clazz = Class.forName("java.util.ArrayList");
Object list = clazz.newInstance();

// 获取add方法
Method addMethod = clazz.getMethod("add", Object.class);
addMethod.invoke(list, "Hello");

// 获取size方法
Method sizeMethod = clazz.getMethod("size");
int size = (int) sizeMethod.invoke(list);

访问字段

1
2
3
4
5
6
7
8
// 获取私有字段
Class<?> clazz = Student.class;
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 设置可访问私有字段

Student student = new Student();
nameField.set(student, "张三");
String name = (String) nameField.get(student);

2.3 反射的性能优化

  • 缓存Class对象:避免重复获取Class对象
  • setAccessible(true):关闭安全检查提高性能
  • MethodHandle:JDK7引入的轻量级反射机制

3. 字符串操作深度分析

3.1 String、StringBuilder、StringBuffer区别

特性 String StringBuilder StringBuffer
可变性 不可变 可变 可变
线程安全 是(不可变天然线程安全)
性能 低(频繁修改时) 中等
使用场景 字符串常量 单线程字符串操作 多线程字符串操作

String不可变性原理

1
2
3
4
5
6
7
8
9
public final class String {
private final char value[]; // 使用final修饰,不可修改
private final int hash; // 缓存hash值

public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
}

StringBuilder实现原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// StringBuilder继承自AbstractStringBuilder
abstract class AbstractStringBuilder {
char[] value; // 非final,可扩容
int count;

public AbstractStringBuilder append(String str) {
if (str == null) str = "null";
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
}

3.2 字符串常量池机制

JVM为了提升性能和减少内存消耗,维护了一块特殊的内存区域——字符串常量池:

1
2
3
4
5
6
7
8
9
10
11
// 字符串常量池示例
String s1 = "hello"; // 创建在常量池
String s2 = "hello"; // 复用常量池中的对象
String s3 = new String("hello"); // 创建在堆内存

System.out.println(s1 == s2); // true,指向同一个常量池对象
System.out.println(s1 == s3); // false,一个在常量池,一个在堆内存

// intern()方法
String s4 = s3.intern(); // 将字符串放入常量池并返回引用
System.out.println(s1 == s4); // true

4. 异常处理体系

4.1 Java异常体系结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Throwable
├── Error(系统级错误,程序无法处理)
│ ├── OutOfMemoryError
│ ├── StackOverflowError
│ └── VirtualMachineError
└── Exception(程序可处理的异常)
├── RuntimeException(运行时异常,非受检异常)
│ ├── NullPointerException
│ ├── ArrayIndexOutOfBoundsException
│ ├── ClassCastException
│ └── IllegalArgumentException
└── 非RuntimeException(受检异常)
├── IOException
├── SQLException
└── ClassNotFoundException

4.2 自定义异常实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 自定义受检异常
public class BusinessException extends Exception {
private String errorCode;

public BusinessException(String message, String errorCode) {
super(message);
this.errorCode = errorCode;
}

public String getErrorCode() {
return errorCode;
}
}

// 自定义运行时异常
public class SystemException extends RuntimeException {
public SystemException(String message) {
super(message);
}

public SystemException(String message, Throwable cause) {
super(message, cause);
}
}

4.3 异常处理最佳实践

try-with-resources语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 传统方式
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("file.txt"));
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}

// try-with-resources(推荐)
try (BufferedReader br = new BufferedReader(new FileReader("file.txt"))) {
return br.readLine();
} catch (IOException e) {
e.printStackTrace();
}

异常捕获顺序

1
2
3
4
5
6
7
8
9
10
11
12
try {
// 可能抛出多种异常的代码
} catch (FileNotFoundException e) {
// 子类异常在前
log.error("文件未找到", e);
} catch (IOException e) {
// 父类异常在后
log.error("IO异常", e);
} catch (Exception e) {
// 通用异常处理在最后
log.error("未知异常", e);
}

5. 设计模式精讲

5.1 单例模式详解

饿汉式单例

1
2
3
4
5
6
7
8
9
10
// 线程安全,类加载时就初始化
public class HungrySingleton {
private static final HungrySingleton INSTANCE = new HungrySingleton();

private HungrySingleton() {}

public static HungrySingleton getInstance() {
return INSTANCE;
}
}

懒汉式单例(双重检查锁)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 线程安全,延迟加载
public class LazySingleton {
private volatile static LazySingleton INSTANCE;

private LazySingleton() {}

public static LazySingleton getInstance() {
if (INSTANCE == null) {
synchronized (LazySingleton.class) {
if (INSTANCE == null) {
INSTANCE = new LazySingleton();
}
}
}
return INSTANCE;
}
}

静态内部类单例(推荐)

1
2
3
4
5
6
7
8
9
10
11
12
// 线程安全,延迟加载,无锁机制
public class StaticInnerSingleton {
private StaticInnerSingleton() {}

private static class SingletonHolder {
private static final StaticInnerSingleton INSTANCE = new StaticInnerSingleton();
}

public static StaticInnerSingleton getInstance() {
return SingletonHolder.INSTANCE;
}
}

枚举单例(最推荐)

1
2
3
4
5
6
7
8
// 线程安全,防止反序列化破坏单例
public enum EnumSingleton {
INSTANCE;

public void doSomething() {
// 单例方法
}
}

5.2 工厂模式

简单工厂模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 产品接口
public interface Car {
void run();
}

// 具体产品
public class Benz implements Car {
@Override
public void run() {
System.out.println("奔驰在跑...");
}
}

public class Bmw implements Car {
@Override
public void run() {
System.out.println("宝马在跑...");
}
}

// 简单工厂
public class SimpleCarFactory {
public static Car createCar(String type) {
switch (type) {
case "benz":
return new Benz();
case "bmw":
return new Bmw();
default:
throw new IllegalArgumentException("未知车型");
}
}
}

工厂方法模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 工厂接口
public interface CarFactory {
Car createCar();
}

// 具体工厂
public class BenzFactory implements CarFactory {
@Override
public Car createCar() {
return new Benz();
}
}

public class BmwFactory implements CarFactory {
@Override
public Car createCar() {
return new Bmw();
}
}

5.3 观察者模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 主题接口
public interface Subject {
void registerObserver(Observer o);
void removeObserver(Observer o);
void notifyObservers();
}

// 观察者接口
public interface Observer {
void update(float temperature, float humidity, float pressure);
}

// 具体主题
public class WeatherData implements Subject {
private List<Observer> observers;
private float temperature;
private float humidity;
private float pressure;

public WeatherData() {
observers = new ArrayList<>();
}

@Override
public void registerObserver(Observer o) {
observers.add(o);
}

@Override
public void removeObserver(Observer o) {
observers.remove(o);
}

@Override
public void notifyObservers() {
for (Observer observer : observers) {
observer.update(temperature, humidity, pressure);
}
}

public void measurementsChanged() {
notifyObservers();
}

public void setMeasurements(float temperature, float humidity, float pressure) {
this.temperature = temperature;
this.humidity = humidity;
this.pressure = pressure;
measurementsChanged();
}
}

6. 高频面试题总结

6.1 集合框架相关

  1. HashMap的put过程是怎样的?

    • 计算key的hash值
    • 通过(n-1)&hash计算索引位置
    • 如果位置为空,直接插入
    • 如果位置不为空,判断key是否相同,相同则覆盖
    • 不同则判断是否为树节点,是则插入红黑树
    • 否则插入链表,链表长度≥8时转为红黑树
  2. HashMap为什么线程不安全?

    • 多线程put可能导致数据丢失
    • 扩容时可能导致链表成环(JDK1.7)
    • 非原子操作导致的数据不一致
  3. ConcurrentHashMap如何实现线程安全?

    • 使用CAS操作保证原子性
    • synchronized只锁定链表头节点
    • 使用volatile保证可见性
    • 扩容时支持多线程协助

6.2 反射机制相关

  1. 反射的优缺点?

    • 优点:动态性、灵活性、框架设计基础
    • 缺点:性能开销、安全限制、代码复杂
  2. 如何防止反射破坏单例?

    • 在构造方法中检查实例是否已存在
    • 使用枚举实现单例
    • 使用SecurityManager检查权限

6.3 字符串相关

  1. String为什么设计成不可变?

    • 线程安全
    • 支持字符串常量池
    • 作为HashMap的key安全
    • 避免被恶意修改
  2. StringBuilder和StringBuffer的区别?

    • StringBuilder非线程安全,性能高
    • StringBuffer线程安全,方法使用synchronized

6.4 异常处理相关

  1. finally块一定会执行吗?

    • 正常情况下会执行
    • System.exit()时不会执行
    • JVM崩溃时不会执行
    • 线程被中断时可能不执行
  2. try-with-resources原理?

    • 编译器自动生成close方法调用
    • 要求资源实现AutoCloseable接口
    • 异常抑制机制

6.5 设计模式相关

  1. 单例模式的实现方式?

    • 饿汉式、懒汉式、双重检查锁、静态内部类、枚举
  2. 工厂模式的作用?

    • 解耦对象的创建和使用
    • 统一管理对象的创建
    • 符合开闭原则

参考资料

  1. 《Java编程思想》
  2. 《Effective Java》
  3. 《Java并发编程实战》
  4. JDK 1.8源码
  5. 《设计模式:可复用面向对象软件的基础》

本文档会持续更新,如有疑问或建议,欢迎留言讨论!