Java程序员面试实录:从基础到架构的完整面试指南
·
Java程序员面试实录:从基础到架构的完整面试指南
引言
在互联网大厂的招聘过程中,技术面试往往是决定成败的关键环节。本文通过一个真实的面试实录,展现Java程序员谢飞机从基础面试到架构面试的完整过程,帮助读者了解大厂面试的技术要点和准备策略。
一、基础面试环节
1.1 JVM内存模型
面试官: 请介绍一下JVM的内存模型。
谢飞机: JVM内存模型主要包含以下几个区域:
public class JVMMemoryModel {
// 方法区(元空间)
public static final String CONSTANT = "constant";
// 虚拟机栈
public void stackExample() {
int localVar = 10; // 栈帧中的局部变量
System.out.println(localVar);
}
// 堆
private Object heapObject = new Object();
// 程序计数器
public void counterExample() {
for (int i = 0; i < 10; i++) {
System.out.println(i); // PC记录执行位置
}
}
}
技术要点:
- 方法区:存储类信息、常量、静态变量等
- 虚拟机栈:存储局部变量、操作数栈等
- 堆:存储对象实例和数组
- 程序计数器:记录当前执行的字节码行号
1.2 垃圾回收机制
面试官: 请说明垃圾回收的算法和机制。
谢飞机: 垃圾回收主要包括以下几种算法:
public class GarbageCollection {
public static void main(String[] args) {
// 引用计数法(简单但存在循环引用问题)
ReferenceCountingDemo demo1 = new ReferenceCountingDemo();
ReferenceCountingDemo demo2 = new ReferenceCountingDemo();
// 循环引用
demo1.setRef(demo2);
demo2.setRef(demo1);
// 可达性分析算法(主流JVM使用)
Object obj = new Object();
obj = null; // obj变为不可达
// 垃圾回收触发时机
System.gc(); // 建议JVM进行垃圾回收
}
}
class ReferenceCountingDemo {
private ReferenceCountingDemo ref;
public void setRef(ReferenceCountingDemo ref) {
this.ref = ref;
}
}
常见垃圾回收器:
- Serial GC:单线程回收,适用于客户端模式
- Parallel GC:多线程回收,吞吐量优先
- CMS GC:并发标记清除,低延迟
- G1 GC:分代收集,平衡吞吐量和延迟
- ZGC/Shenandoah:超低延迟回收器
1.3 线程安全
面试官: 如何保证线程安全?
谢飞机: 保证线程安全有几种主要方式:
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadSafety {
// 1. synchronized关键字
private int counter = 0;
public synchronized void incrementSync() {
counter++;
}
// 2. ReentrantLock
private final ReentrantLock lock = new ReentrantLock();
public void incrementWithLock() {
lock.lock();
try {
counter++;
} finally {
lock.unlock();
}
}
// 3. 原子类
private AtomicInteger atomicCounter = new AtomicInteger(0);
public void incrementAtomic() {
atomicCounter.incrementAndGet();
}
// 4. 不可变对象
private final ImmutableData immutableData = new ImmutableData("data");
static class ImmutableData {
private final String data;
public ImmutableData(String data) {
this.data = data;
}
public String getData() {
return data;
}
}
}
二、Spring生态面试
2.1 Spring自动配置原理
面试官: 请解释Spring Boot的自动配置原理。
谢飞机: Spring Boot的自动配置主要通过以下机制实现:
// @EnableAutoImport注解触发自动配置
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
}
// 自动配置导入选择器
class AutoConfigurationImportSelector {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
// 1. 获取所有候选的自动配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
// 2. 过滤掉已经加载的配置
configurations.removeIf(entry -> isExcluded(entry, excludeAutoConfigurations));
// 3. 排序
configurations.sort(AnnotationAwareOrderComparator.INSTANCE);
return configurations.toArray(new String[0]);
}
}
// 示例自动配置类
@Configuration
@ConditionalOnClass(RedisTemplate.class)
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(RedisTemplate.class)
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
return template;
}
}
2.2 事务传播机制
面试官: 请说明Spring事务的传播行为。
谢飞机: Spring事务传播机制包括以下几种:
import org.springframework.transaction.annotation.Transactional;
@Service
public class TransactionService {
// REQUIRED:如果当前没有事务,就新建一个事务;如果已经存在事务,就加入该事务
@Transactional(propagation = Propagation.REQUIRED)
public void requiredMethod() {
// 业务逻辑
}
// REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void requiresNewMethod() {
// 业务逻辑
}
// SUPPORTS:如果当前存在事务,就加入该事务;如果当前没有事务,就以非事务方式执行
@Transactional(propagation = Propagation.SUPPORTS)
public void supportsMethod() {
// 业务逻辑
}
// NOT_SUPPORTED:以非事务方式执行,如果当前存在事务,就把当前事务挂起
@Transactional(propagation = Propagation.NOT_SUPPORTED)
public void notSupportedMethod() {
// 业务逻辑
}
// NEVER:以非事务方式执行,如果当前存在事务,则抛出异常
@Transactional(propagation = Propagation.NEVER)
public void neverMethod() {
// 业务逻辑
}
// MANDATORY:必须在一个事务中执行,否则抛出异常
@Transactional(propagation = Propagation.MANDATORY)
public void mandatoryMethod() {
// 业务逻辑
}
// NESTED:如果当前存在事务,则在嵌套事务内执行;如果当前没有事务,则新建事务
@Transactional(propagation = Propagation.NESTED)
public void nestedMethod() {
// 业务逻辑
}
}
2.3 Spring Cloud组件
面试官: 请介绍Spring Cloud的核心组件。
谢飞机: Spring Cloud包含以下核心组件:
// Eureka服务注册与发现
@SpringBootApplication
@EnableEurekaClient
public class ServiceApplication {
public static void main(String[] args) {
SpringApplication.run(ServiceApplication.class, args);
}
}
// Feign声明式服务调用
@FeignClient(name = "user-service")
public interface UserServiceClient {
@GetMapping("/users/{id}")
User getUserById(@PathVariable("id") Long id);
}
// Hystrix熔断器
@Service
public class UserService {
@HystrixCommand(fallbackMethod = "getUserFallback")
public User getUser(Long id) {
// 调用远程服务
return remoteUserService.getUser(id);
}
public User getUserFallback(Long id) {
// 降级逻辑
return new User(id, "default", "fallback");
}
}
// Zuul网关
@SpringBootApplication
@EnableZuulProxy
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
return builder.routes()
.route("user-route", r -> r.path("/api/users/**")
.uri("http://user-service"))
.route("order-route", r -> r.path("/api/orders/**")
.uri("http://order-service"))
.build();
}
}
三、中间件面试
3.1 Redis持久化
面试官: 请说明Redis的持久化机制。
谢飞机: Redis主要有两种持久化机制:
// RDB持久化配置示例
# redis.conf配置
save 900 1 # 900秒内至少有1个key改变时触发保存
save 300 10 # 300秒内至少有10个key改变时触发保存
save 60 10000 # 60秒内至少有10000个key改变时触发保存
// AOF持久化配置示例
appendonly yes # 启用AOF持久化
appendfsync everysec # 每秒同步一次
auto-aof-rewrite-percentage 100 # AOF文件增长100%时重写
auto-aof-rewrite-min-size 64mb # AOF文件最小64mb时重写
// Redis配置类
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
// 设置key的序列化方式
template.setKeySerializer(new StringRedisSerializer());
template.setHashKeySerializer(new StringRedisSerializer());
// 设置value的序列化方式
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
3.2 分布式锁
面试官: 如何实现分布式锁?
谢飞机: 分布式锁有多种实现方式:
import redis.clients.jedis.Jedis;
import redis.clients.jedis.params.SetParams;
public class DistributedLock {
private Jedis jedis;
private String lockKey;
private String requestId;
private int expireTime = 30;
// 基于Redis的分布式锁
public boolean tryLock() {
SetParams params = SetParams.setParams().nx().ex(expireTime);
String result = jedis.set(lockKey, requestId, params);
return "OK".equals(result);
}
public void unlock() {
if (requestId.equals(jedis.get(lockKey))) {
jedis.del(lockKey);
}
}
// 基于ZooKeeper的分布式锁
public boolean tryLockWithZK() {
try {
// 尝试创建临时节点
zk.create(lockKey, requestId.getBytes(), ZooDefs.Ids.OPEN_ACL_UNSAFE,
CreateMode.EPHEMERAL);
return true;
} catch (Exception e) {
return false;
}
}
}
// Redisson分布式锁实现
public class RedissonLockExample {
public void executeWithLock() {
RLock lock = redissonClient.getLock("myLock");
try {
// 尝试获取锁,最多等待10秒,锁自动释放时间30秒
boolean locked = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (locked) {
// 执行业务逻辑
doBusiness();
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
if (lock.isHeldByCurrentThread()) {
lock.unlock();
}
}
}
}
3.3 消息队列
面试官: 请比较几种常见的消息队列。
谢飞机: 主流消息队列的比较:
// RabbitMQ配置
@Configuration
public class RabbitMQConfig {
@Bean
public Queue queue() {
return new Queue("hello.queue", true);
}
@Bean
public TopicExchange exchange() {
return new TopicExchange("hello.exchange");
}
@Bean
public Binding binding() {
return BindingBuilder.bind(queue())
.to(exchange())
.with("hello.routing.key");
}
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate template = new RabbitTemplate(connectionFactory);
template.setMessageConverter(new Jackson2JsonMessageConverter());
return template;
}
}
// Kafka配置
@Configuration
public class KafkaConfig {
@Bean
public ProducerFactory<String, String> producerFactory() {
Map<String, Object> configProps = new HashMap<>();
configProps.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
configProps.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
configProps.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
return new DefaultKafkaProducerFactory<>(configProps);
}
@Bean
public ConsumerFactory<String, String> consumerFactory() {
Map<String, Object> props = new HashMap<>();
props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
props.put(ConsumerConfig.GROUP_ID_CONFIG, "group-id");
props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
return new DefaultKafkaConsumerFactory<>(props);
}
}
// RocketMQ配置
@Configuration
public class RocketMQConfig {
@Bean
public DefaultMQProducer producer() {
DefaultMQProducer producer = new DefaultMQProducer("please_rename_unique_group_name");
producer.setNamesrvAddr("localhost:9876");
return producer;
}
}
四、数据库面试
4.1 索引优化
面试官: 如何优化数据库索引?
谢飞机: 索引优化需要考虑多个方面:
// 索引优化示例
public class IndexOptimization {
// 复合索引设计
@Query("SELECT u FROM User u WHERE u.name = :name AND u.age = :age")
List<User> findByNameAndAge(@Param("name") String name, @Param("age") Integer age);
// 覆盖索引
@Query("SELECT u.id, u.name, u.email FROM User u WHERE u.status = :status")
List<Object[]> findUserBasicInfoByStatus(@Param("status") String status);
// 分页查询优化
@Query(value = "SELECT * FROM users WHERE create_time > ?1",
countQuery = "SELECT COUNT(*) FROM users WHERE create_time > ?1")
Page<User> findByCreateTimeAfter(Date createTime, Pageable pageable);
// 慢查询分析
@Query(value = "SELECT u.* FROM users u FORCE INDEX (idx_create_time)
WHERE u.create_time BETWEEN ?1 AND ?2", nativeQuery = true)
List<User> findByCreateTimeRangeOptimized(Date startTime, Date endTime);
}
// SQL优化示例
public class SQLOptimization {
// 避免SELECT *
public List<UserDTO> findUserDTOs() {
return entityManager.createQuery(
"SELECT new com.example.dto.UserDTO(u.id, u.name, u.email) FROM User u",
UserDTO.class).getResultList();更多推荐



所有评论(0)