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();
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐