面试集合 java基础的集合 
Collection接口的常用方法
1 2 3 4 5 增加:add(E e) addAll(Collection<? extends E> c) 删除:clear() remove(Object o) 修改: 查看:iterator() size() 判断:contains(Object o)  equals(Object o) isEmpty() 
 
总结一下:首先是接口不能创建对象,利用实现类创建对象,
集合有一个特点:只能存放引用数据类型的数据,不能是基本数据类型
基本数据类型放入到集合里面会自动装箱。
特别问题 String、StringBuilder、StringBuffer 区别和联系 1、String 类是不可变类、即一旦一个 String 对象被创建后,包含在这个对象中的字符序列是不可改变的,制止这个对象销毁。
2、StringBuffer 类则代表一个字符序列可变的字符串,可以通过 append、insert、reverse、setCharAt、setLength 等方法改变其内容。一旦生成了最终的字符串,调用 toString 方法将其转变为 String
3、JDK1.5 新增了一个 StringBuilder 类和 StringBuffer 相似,构造方法和方法基本相同。不同的是 StrtingBuffer 是线程安全的,而 StringBuilder 是线程不安全的,所以性能略高,通常情况下,创建一个内容可变的字符串,应该优先考虑使用 StringBuilder。
 StringBuilder:JDK1.5 开始 效率高 线程不安全
 StringBuffer:JDK1.0 开始 效率低 线程安全
并发问题 问题1:购票系统的读写一致性问题 假如你设计一个购票系统,如何设计车票,让车票的读和写是一致的,比如一共有15个车票,买了5张,还剩10张,如何保证准确性 
数据库事务 + 悲观锁 通过数据库的事务和悲观锁(如 SELECT FOR UPDATE)来确保同一时间只有一个线程可以修改票数。
实现思路 :
在购票时,使用事务锁定票数记录,确保其他线程无法同时修改。 
读操作可以直接读取数据库中的剩余票数。 
 
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 public  class  TicketService  {    private  static  final  String  URL  =  "jdbc:mysql://localhost:3306/ticket_db" ;     private  static  final  String  USER  =  "root" ;     private  static  final  String  PASSWORD  =  "password" ;     public  boolean  purchaseTicket (int  ticketId, int  userId)  {         Connection  conn  =  null ;         try  {             conn = DriverManager.getConnection(URL, USER, PASSWORD);             conn.setAutoCommit(false );                                        String  selectSql  =  "SELECT remaining FROM tickets WHERE id = ? FOR UPDATE" ;             PreparedStatement  selectStmt  =  conn.prepareStatement(selectSql);             selectStmt.setInt(1 , ticketId);             ResultSet  rs  =  selectStmt.executeQuery();             if  (rs.next()) {                 int  remaining  =  rs.getInt("remaining" );                 if  (remaining > 0 ) {                                          String  updateSql  =  "UPDATE tickets SET remaining = remaining - 1 WHERE id = ?" ;                     PreparedStatement  updateStmt  =  conn.prepareStatement(updateSql);                     updateStmt.setInt(1 , ticketId);                     updateStmt.executeUpdate();                                          String  insertSql  =  "INSERT INTO purchases (user_id, ticket_id) VALUES (?, ?)" ;                     PreparedStatement  insertStmt  =  conn.prepareStatement(insertSql);                     insertStmt.setInt(1 , userId);                     insertStmt.setInt(2 , ticketId);                     insertStmt.executeUpdate();                     conn.commit();                      return  true ;                 }             }             conn.rollback();              return  false ;         } catch  (SQLException e) {             if  (conn != null ) {                 try  {                     conn.rollback();                 } catch  (SQLException ex) {                     ex.printStackTrace();                 }             }             e.printStackTrace();             return  false ;         } finally  {             if  (conn != null ) {                 try  {                     conn.setAutoCommit(true );                     conn.close();                 } catch  (SQLException e) {                     e.printStackTrace();                 }             }         }     }     public  int  getRemainingTickets (int  ticketId)  {         try  (Connection  conn  =  DriverManager.getConnection(URL, USER, PASSWORD);              PreparedStatement  stmt  =  conn.prepareStatement("SELECT remaining FROM tickets WHERE id = ?" )) {             stmt.setInt(1 , ticketId);             ResultSet  rs  =  stmt.executeQuery();             if  (rs.next()) {                 return  rs.getInt("remaining" );             }         } catch  (SQLException e) {             e.printStackTrace();         }         return  -1 ;      } } 
 
优点 :
简单直接,利用数据库的事务和锁机制保证一致性。 
适合中小型系统。 
 
缺点 :
乐观锁 通过版本号或时间戳来检测冲突,避免直接加锁。
实现思路 :
在票数表中增加一个 version 字段。 
每次更新票数时,检查版本号是否一致,如果一致则更新,否则重试。 
 
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 public  class  TicketService  {    private  static  final  String  URL  =  "jdbc:mysql://localhost:3306/ticket_db" ;     private  static  final  String  USER  =  "root" ;     private  static  final  String  PASSWORD  =  "password" ;     public  boolean  purchaseTicket (int  ticketId, int  userId)  {         Connection  conn  =  null ;         try  {             conn = DriverManager.getConnection(URL, USER, PASSWORD);             conn.setAutoCommit(false );                          String  selectSql  =  "SELECT remaining, version FROM tickets WHERE id = ?" ;             PreparedStatement  selectStmt  =  conn.prepareStatement(selectSql);             selectStmt.setInt(1 , ticketId);             ResultSet  rs  =  selectStmt.executeQuery();             if  (rs.next()) {                 int  remaining  =  rs.getInt("remaining" );                 int  version  =  rs.getInt("version" );                 if  (remaining > 0 ) {                                          String  updateSql  =  "UPDATE tickets SET remaining = remaining - 1, version = version + 1 WHERE id = ? AND version = ?" ;                     PreparedStatement  updateStmt  =  conn.prepareStatement(updateSql);                     updateStmt.setInt(1 , ticketId);                     updateStmt.setInt(2 , version);                     int  rowsUpdated  =  updateStmt.executeUpdate();                     if  (rowsUpdated > 0 ) {                                                  String  insertSql  =  "INSERT INTO purchases (user_id, ticket_id) VALUES (?, ?)" ;                         PreparedStatement  insertStmt  =  conn.prepareStatement(insertSql);                         insertStmt.setInt(1 , userId);                         insertStmt.setInt(2 , ticketId);                         insertStmt.executeUpdate();                         conn.commit();                         return  true ;                     }                 }             }             conn.rollback();             return  false ;         } catch  (SQLException e) {             if  (conn != null ) {                 try  {                     conn.rollback();                 } catch  (SQLException ex) {                     ex.printStackTrace();                 }             }             e.printStackTrace();             return  false ;         } finally  {             if  (conn != null ) {                 try  {                     conn.setAutoCommit(true );                     conn.close();                 } catch  (SQLException e) {                     e.printStackTrace();                 }             }         }     } } 
 
优点 :
缺点 :
分布式锁 + 缓存 在分布式系统中,使用分布式锁(如Redis的RedLock)来保证同一时间只有一个线程可以修改票数,同时使用缓存(如Redis)来加速读操作。
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 52 53 54 55 56 57 import  redis.clients.jedis.Jedis;public  class  TicketService  {    private  static  final  String  REDIS_HOST  =  "localhost" ;     private  static  final  int  REDIS_PORT  =  6379 ;     public  boolean  purchaseTicket (int  ticketId, int  userId)  {         Jedis  jedis  =  new  Jedis (REDIS_HOST, REDIS_PORT);         String  lockKey  =  "ticket_lock_"  + ticketId;         String  lockValue  =  String.valueOf(System.currentTimeMillis());         try  {                          String  result  =  jedis.set(lockKey, lockValue, "NX" , "PX" , 10000 );             if  ("OK" .equals(result)) {                                  boolean  success  =  doPurchaseTicket(ticketId, userId);                 if  (success) {                                          jedis.decr("ticket_remaining_"  + ticketId);                     return  true ;                 }             }             return  false ;         } finally  {                          if  (lockValue.equals(jedis.get(lockKey))) {                 jedis.del(lockKey);             }             jedis.close();         }     }     private  boolean  doPurchaseTicket (int  ticketId, int  userId)  {                  return  true ;     }     public  int  getRemainingTickets (int  ticketId)  {         try  (Jedis  jedis  =  new  Jedis (REDIS_HOST, REDIS_PORT)) {                          String  remaining  =  jedis.get("ticket_remaining_"  + ticketId);             if  (remaining != null ) {                 return  Integer.parseInt(remaining);             }                          int  remainingTickets  =  fetchRemainingTicketsFromDB(ticketId);             jedis.set("ticket_remaining_"  + ticketId, String.valueOf(remainingTickets));             return  remainingTickets;         }     }     private  int  fetchRemainingTicketsFromDB (int  ticketId)  {                  return  10 ;      } } 
 
总结 
悲观锁 :适合中小型系统,实现简单,但性能较差。 
乐观锁 :适合高并发场景,但需要处理重试逻辑。 
分布式锁 + 缓存 :适合分布式系统,性能较好,但实现复杂度较高。 
 
根据系统的规模和需求,可以选择合适的方案来保证车票的读和写操作的一致性。
问题2:如何看是否有命中,如何知道读取数据是最新的数据  通过查询结果判断 在执行 SELECT ... FOR UPDATE 后,可以通过检查查询结果集来判断是否命中。
缓存中的数据可能会过期或失效,因此需要确保读取的是最新的数据。以下是几种常见的方法:
缓存更新策略  
spring原理 问题1:讲一下spring ioc的实现原理 Spring 的 IOC 实现原理 1. 什么是 IOC(控制反转)? IOC(Inversion of Control)是一种设计原则,它将对象的创建、依赖注入和生命周期管理交给框架(如 Spring)来处理,而不是由程序员手动控制。IOC 的核心思想是 将控制权从应用程序代码转移到框架 。
传统方式 :程序员手动创建对象并管理依赖关系。 
IOC 方式 :Spring 容器负责创建对象并注入依赖。 
 
 
2. IOC 的核心组件 Spring 的 IOC 容器主要由以下几个核心组件实现:
BeanFactory :IOC 容器的基础接口,提供了最基本的依赖注入功能。 
ApplicationContext :BeanFactory 的子接口,提供了更多高级功能(如事件发布、国际化支持等)。 
BeanDefinition :用于描述一个 Bean 的元数据(如类名、作用域、依赖关系等)。 
BeanPostProcessor :用于在 Bean 初始化前后执行自定义逻辑。 
BeanFactoryPostProcessor :用于在 BeanFactory 初始化后修改 Bean 的定义。 
 
 
3. IOC 的实现原理 Spring 的 IOC 实现原理可以分为以下几个步骤:
3.1 加载配置文件或注解 Spring 容器会加载配置文件(如 applicationContext.xml)或扫描注解(如 @Component、@Service 等),获取 Bean 的定义信息。
XML 配置 :
1 2 3 <bean  id ="userService"  class ="com.example.UserService" >     <property  name ="userDao"  ref ="userDao" />  </bean > 
 
 
注解配置 :
1 2 3 4 5 @Service public  class  UserService  {    @Autowired      private  UserDao userDao; } 
 
 
 
3.2 解析 Bean 定义 Spring 容器会解析配置文件或注解,将每个 Bean 的定义信息封装为 BeanDefinition 对象,并存储在一个 BeanDefinitionRegistry 中。
3.3 实例化 Bean Spring 容器根据 BeanDefinition 中的信息,通过反射机制实例化 Bean。
单例 Bean :容器启动时就会创建并缓存单例 Bean。 
原型 Bean :每次请求时都会创建一个新的 Bean。 
 
3.4 依赖注入 Spring 容器会根据 Bean 的依赖关系,自动将依赖的 Bean 注入到目标 Bean 中。
Setter 注入 :
1 2 3 4 5 6 public  class  UserService  {    private  UserDao userDao;     public  void  setUserDao (UserDao userDao)  {         this .userDao = userDao;     } } 
 
 
构造器注入 :
1 2 3 4 5 6 public  class  UserService  {    private  UserDao userDao;     public  UserService (UserDao userDao)  {         this .userDao = userDao;     } } 
 
 
字段注入(通过注解) :
1 2 @Autowired private  UserDao userDao;
 
 
 
3.5 初始化 Bean 在 Bean 初始化前后,Spring 容器会调用 BeanPostProcessor 的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,执行自定义逻辑。
初始化方法 :可以通过 @PostConstruct 注解或 init-method 配置指定初始化方法。 
销毁方法 :可以通过 @PreDestroy 注解或 destroy-method 配置指定销毁方法。 
 
3.6 使用 Bean 应用程序可以通过 ApplicationContext 或 BeanFactory 获取 Bean 并使用。
1 2 3 ApplicationContext  context  =  new  ClassPathXmlApplicationContext ("applicationContext.xml" );UserService  userService  =  context.getBean(UserService.class);userService.doSomething(); 
 
 
4. IOC 的核心机制 4.1 反射机制 Spring 通过反射机制动态创建 Bean 实例并注入依赖。
4.2 工厂模式 Spring 使用工厂模式(BeanFactory)来管理 Bean 的创建和依赖注入。
4.3 依赖查找 vs 依赖注入 
依赖查找 :应用程序主动从容器中查找依赖(如 context.getBean())。 
依赖注入 :容器自动将依赖注入到目标 Bean 中。 
 
4.4 生命周期管理 Spring 容器负责管理 Bean 的整个生命周期,包括实例化、初始化、使用和销毁。