1. JavaSE基础加强课程概览
这个系列课程是面向已经掌握Java基础语法的开发者设计的进阶训练营,重点解决实际开发中那些教科书里不会讲但工作中一定会遇到的痛点问题。我在带Java新人团队时发现,很多开发者虽然能写基础代码,但遇到泛型擦除、注解处理器、反射动态代理这些"熟悉的陌生人"时就会手足无措——这正是本课程要攻克的技术高地。
以泛型为例,表面上看就是个类型参数化的语法糖,但当你需要:
- 在运行时获取泛型实际类型(比如实现通用DAO时)
- 处理泛型数组创建时的类型安全检查
- 设计支持泛型边界的工具类
这些场景下如果只懂List<String>这种基础用法是远远不够的。课程会通过MyBatis源码解析+手写迷你ORM框架的实战,让你真正掌握类型擦除背后的实现原理和应对方案。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 核心知识点深度解析
2.1 泛型系统进阶实战
类型擦除不是简单的删除类型信息,JVM在编译阶段会进行两个关键操作:
- 类型替换(用限定类型或Object替换类型参数)
- 桥接方法生成(保持多态性)
java复制// 源码
public class Box<T extends Number> {
private T value;
public void set(T val) { this.value = val; }
}
// 编译后(通过javap -p查看)
public class Box {
private Number value;
public void set(Number val) { ... }
// 编译器生成的桥接方法
public synthetic void set(Object val) {
set((Number)val);
}
}
开发实战技巧:
- 获取泛型实际类型需要借助
ParameterizedType接口:
java复制Type type = field.getGenericType();
if (type instanceof ParameterizedType) {
Type actualType = ((ParameterizedType)type).getActualTypeArguments()[0];
}
- 创建泛型数组的正确姿势:
java复制// 错误示范:直接创建泛型数组会触发ClassCastException
T[] arr = new T[10];
// 正确做法:使用Array.newInstance结合类型标记
public <T> T[] createArray(Class<T> clazz, int size) {
return (T[]) Array.newInstance(clazz, size);
}
2.2 注解处理器开发指南
注解处理器的核心价值在于编译期代码生成和验证。以手写Lombok的@Getter为例:
- 定义注解:
java复制@Target(ElementType.FIELD)
@Retention(RetentionPolicy.SOURCE)
public @interface MyGetter {}
- 实现处理器:
java复制@SupportedAnnotationTypes("com.example.MyGetter")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
public class MyGetterProcessor extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnv) {
for (Element element : roundEnv.getElementsAnnotatedWith(MyGetter.class)) {
// 生成getter方法代码
String fieldName = element.getSimpleName().toString();
String methodName = "get" + fieldName.substring(0,1).toUpperCase()
+ fieldName.substring(1);
// 使用JavaPoet等工具生成方法...
}
return true;
}
}
避坑指南:
- 处理器必须打包在独立的Jar中,配置
META-INF/services/javax.annotation.processing.Processor文件 - 使用
FilerAPI创建新源文件时要注意处理重复生成问题 - IntelliJ IDEA中需要开启"Enable annotation processing"选项
2.3 反射性能优化全攻略
直接上基准测试数据(JMH测试,纳秒/操作):
| 操作类型 | 直接调用 | 反射调用 | 反射+setAccessible | MethodHandle |
|---|---|---|---|---|
| 普通方法调用 | 15 | 1200 | 800 | 50 |
| 字段访问 | 12 | 950 | 600 | 40 |
| 构造函数调用 | 20 | 1500 | 1000 | 70 |
优化方案对比:
- 缓存
Method/Field对象(基础优化) - 使用
setAccessible(true)跳过访问检查(有安全风险) MethodHandle(需要JVM 7+)- 运行时生成字节码(ASM/CGLib方案)
动态代理的典型应用场景:
java复制public class DebugProxy implements InvocationHandler {
private Object target;
public static Object newInstance(Object obj) {
return Proxy.newProxyInstance(
obj.getClass().getClassLoader(),
obj.getClass().getInterfaces(),
new DebugProxy(obj));
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
3. 类加载机制深度剖析
3.1 双亲委派模型突破实践
Tomcat打破双亲委派的经典实现:
java复制class WebappClassLoader extends URLClassLoader {
protected Class<?> loadClass(String name, boolean resolve) {
// 1. 检查本地已加载类
Class<?> clazz = findLoadedClass(name);
if (clazz != null) return clazz;
// 2. 优先自己加载WEB-INF/classes和WEB-INF/lib下的类
try {
clazz = findClass(name);
if (clazz != null) return clazz;
} catch (ClassNotFoundException e) {
// ignore
}
// 3. 最后委派给父加载器
return super.loadClass(name, resolve);
}
}
热加载实现关键点:
- 自定义ClassLoader每次创建新实例
- 需要控制旧类实例的引用释放
- 使用
-XX:+TraceClassUnloading监控类卸载情况
3.2 字节码增强实战
ASM修改方法体的典型流程:
java复制ClassReader cr = new ClassReader(bytes);
ClassWriter cw = new ClassWriter(cr, ClassWriter.COMPUTE_MAXS);
ClassVisitor cv = new ClassVisitor(Opcodes.ASM7, cw) {
@Override
public MethodVisitor visitMethod(int access, String name, String desc,
String signature, String[] exceptions) {
MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
if ("targetMethod".equals(name)) {
return new MethodVisitor(Opcodes.ASM7, mv) {
@Override
public void visitCode() {
// 在方法开始插入代码
visitFieldInsn(Opcodes.GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;");
visitLdcInsn("Method entered!");
visitMethodInsn(Opcodes.INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false);
super.visitCode();
}
};
}
return mv;
}
};
cr.accept(cv, ClassReader.EXPAND_FRAMES);
byte[] newBytes = cw.toByteArray();
4. 并发工具高阶用法
4.1 AQS实现原理拆解
以手写简单锁为例展示AQS核心逻辑:
java复制class MyLock {
private final Sync sync = new Sync();
private static class Sync extends AbstractQueuedSynchronizer {
protected boolean tryAcquire(int arg) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int arg) {
if (getState() == 0) throw new IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
}
public void lock() { sync.acquire(1); }
public void unlock() { sync.release(1); }
}
4.2 CompletableFuture组合技巧
异步任务编排的几种典型模式:
java复制// 1. 并行执行后合并结果
CompletableFuture<String> futureA = CompletableFuture.supplyAsync(() -> queryA());
CompletableFuture<String> futureB = CompletableFuture.supplyAsync(() -> queryB());
futureA.thenCombine(futureB, (a, b) -> a + b)
.thenAccept(System.out::println);
// 2. 异常处理流水线
CompletableFuture.supplyAsync(() -> parseJson(input))
.exceptionally(ex -> {
log.error("Parse failed", ex);
return defaultValue;
})
.thenApplyAsync(data -> process(data))
.handle((result, ex) -> {
if (ex != null) return fallbackResult;
return result;
});
// 3. 超时控制方案
CompletableFuture.supplyAsync(() -> longTimeTask())
.completeOnTimeout(defaultValue, 2, TimeUnit.SECONDS)
.thenAccept(result -> { ... });
5. 实战:手写迷你Spring IOC容器
核心实现步骤:
- 组件扫描与类加载
java复制public void scan(String basePackage) throws Exception {
String path = basePackage.replace('.', '/');
Enumeration<URL> resources = classLoader.getResources(path);
while (resources.hasMoreElements()) {
URL url = resources.nextElement();
if (url.getProtocol().equals("file")) {
File dir = new File(url.toURI());
for (File file : dir.listFiles()) {
if (file.getName().endsWith(".class")) {
String className = basePackage + "." +
file.getName().replace(".class", "");
Class<?> clazz = classLoader.loadClass(className);
if (clazz.isAnnotationPresent(Component.class)) {
registerBean(clazz);
}
}
}
}
}
}
- 依赖注入处理
java复制private void injectDependencies(Object instance, Class<?> clazz) throws Exception {
for (Field field : clazz.getDeclaredFields()) {
if (field.isAnnotationPresent(Autowired.class)) {
Object dependency = getBean(field.getType());
field.setAccessible(true);
field.set(instance, dependency);
}
}
}
- 循环依赖检测方案
java复制private final ThreadLocal<Set<String>> dependencyStack = ThreadLocal.withInitial(HashSet::new);
public Object getBean(Class<?> type) throws Exception {
String beanName = type.getSimpleName();
if (dependencyStack.get().contains(beanName)) {
throw new IllegalStateException("Circular dependency detected: " +
String.join(" -> ", dependencyStack.get()) + " -> " + beanName);
}
dependencyStack.get().add(beanName);
try {
// ...正常创建bean逻辑
} finally {
dependencyStack.get().remove(beanName);
}
}
