源本科技 | 码上会

企业社招真题 Java 反射与泛型

2026/04/05
1
0

Java 反射机制及性能影响

Java 反射是允许程序在运行时获取类的完整信息(构造、方法、属性),并动态调用对象方法、操作属性的机制,核心依靠Class对象实现。它让代码更灵活,是 Spring、MyBatis 等框架的底层核心。但反射有明显性能影响:调用时会跳过编译期检查,JVM 无法做优化;需要解析元数据、校验权限,比直接调用慢很多;频繁反射还会增加堆内存占用。日常开发应少用反射,高频调用场景可缓存反射对象,降低性能损耗。

反射调用私有方法的实现

反射可以打破封装,直接调用类的私有方法,步骤固定且简单。首先通过对象.getClass()类名.class获取Class对象;然后调用getDeclaredMethod(方法名, 参数类型)获取私有方法对象,这个方法能获取所有权限的方法;接着必须调用method.setAccessible(true)关闭访问检查,否则会报权限异常;最后用method.invoke(目标对象, 参数)完成调用。该方式常用于框架源码、工具类中,但会破坏封装性,业务代码不建议随意使用。

动态代理机制及应用场景

动态代理是运行时动态生成代理类,无需手动编写代理代码的技术,是 AOP 的核心实现。Java 主要有两种实现:JDK 动态代理基于接口,CGLIB 基于继承类。它的原理是通过反射 / 字节码生成,在目标方法执行前后加入增强逻辑(日志、事务、权限)。优势是通用、解耦,一个代理类能处理多个目标对象。应用场景极广:Spring AOP、事务管理、日志监控、RPC 框架调用、权限拦截,是框架开发必不可少的技术。

静态代理与动态代理的区别

静态代理和动态代理都是代理模式,核心区别在代理类生成时机。静态代理在编译期手动编写代理类,每个目标类都要写对应代理,代码冗余、扩展性差。动态代理在运行时自动生成代理类,不用手动编码,一个代理类可服务多个目标类。静态代理性能略高,但维护麻烦;动态代理更灵活,适合批量增强方法。静态代理适合固定业务,动态代理是框架首选,实现无侵入式功能增强。

Java 注解的工作原理及应用

注解是 Java 的元数据,相当于给代码加标签,本身不执行逻辑,由程序解析使用。工作原理:JDK 内置注解用于编译校验,自定义注解通过反射(运行时)或APT(编译时)解析。注解保留策略分源码、编译、运行时三种。应用场景非常广泛:Spring 的@Autowired依赖注入、MyBatis 的@Select映射 SQL、JUnit 的@Test标记测试方法,还能自定义注解实现权限校验、日志记录,让配置替代硬编码。

元注解的作用及示例

元注解是专门修饰注解的注解,用来定义注解的使用规则,Java 提供 4 种标准元注解。@Target指定注解能用在类、方法、属性等位置;@Retention指定注解保留到源码、编译还是运行时;@Documented让注解生成到 Javadoc 文档;@Inherited允许子类继承父类的注解。比如自定义注解时,用@Target(ElementType.METHOD)限定注解只能修饰方法,@Retention(RetentionPolicy.RUNTIME保证运行时可通过反射获取,是定义注解的必备基础。

注解处理器的原理及应用

注解处理器(APT)是编译期处理注解的工具,在 Java 编译阶段扫描、解析注解,自动生成代码,不影响运行时性能。原理是继承AbstractProcessor,重写处理方法,通过 javac 编译器扫描注解,生成 Java 文件或字节码。它不用反射,性能极高。应用场景:Lombok 用它生成 get/set 方法、MyBatis Generator 生成实体类、ButterKnife 生成视图绑定代码,能减少重复代码,提高开发效率,是 Java 框架常用的编译期增强技术。

泛型的类型擦除机制及其影响

Java 泛型是伪泛型,核心是类型擦除:编译时会把泛型类型替换为原始类型(无边界擦为 Object,有边界擦为边界类型),字节码中无泛型信息。这种机制是为了兼容旧版 Java。影响很明显:运行时无法获取泛型类型,不能用instanceof判断泛型;不能创建泛型数组;泛型方法不能重载为参数仅泛型不同;静态方法不能使用类的泛型。虽然使用方便,但会限制部分泛型能力,开发时需要注意这些坑。

泛型通配符与边界的使用

泛型通配符?用来表示未知类型,配合边界限制泛型范围,解决泛型不可变问题。? extends T上界通配符,只能读取数据,不能写入,适合获取数据场景;? super T下界通配符,只能写入数据,不能读取,适合添加数据场景。遵循 PECS 原则:生产者用 extends,消费者用 super。通配符让泛型更灵活,能适配不同子类型 / 父类型,避免强制类型转换,是集合泛型操作的常用语法。

泛型和数组的兼容性问题

Java 数组是协变的,而泛型擦除后是不变的,两者天生不兼容,不能直接创建泛型数组。比如new List<String>[]会编译报错,因为数组会在运行时检查类型,而泛型被擦除,无法识别具体类型,会导致堆污染、类型异常。如果需要存储泛型对象,只能用List<List<String>>替代数组。强行创建泛型数组只能用强制类型转换 +@SuppressWarnings 注解,但存在安全风险。日常开发中,泛型场景优先用集合,放弃数组。