Java 面试宝典

一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是面试题,也是你 Java 知识点的扫盲贴。

  • 回答目前生成全局唯一ID比较常用的方式有5种:UUIDUUID是一种长度为128位的标识符,通常以32个字符串的十六进制字符串表示,它的生成依赖于当前的时间戳、随机数、MAC地址等信息。它保证了不同时间、不同机器生成的ID不会重复。它的优点就是实现过程简单,本地生成,无中心化需求。但是缺点也很明显,UUID是随机生成的,它不具备递增性,在某些场景下可能不是很适合,而且生成的ID是以32位十六进制字符串表示的,会给存储带来一定的压力。数据库自增id数据库自增ID是利用数据库管理系统的内部机制为每条新增记录自动生成唯一且递增的ID。它由于是利用数据库自身的功能,所以实现比较简单,性能也高效,但是由于它依赖数据库,且是单表,所以它无法跨表、跨库,存在单点瓶颈,所以它无法实现全局唯一ID。基于数据库的号段模式通过在数据库中维护一个号段表,应用系统每次获取一个号段后,能够从该号段中连续生成多个ID,

    2025-01-22
    阅读(670)
  • 回答由于雪花算法高度依赖系统时钟,当时钟回拨发生时,基于时间戳的分布式ID生成器雪花算法就可能会出现重复ID或者全局递增性被破坏的问题,这就直接威胁到系统的一致性和稳定性。解决时钟回拨问题,有几种常见的方法:直接抛出异常。不管3*7=21,直接抛出异常,将问题交给调用方去处理。延迟等待。将当前阻塞,知道系统时间重新追回上之前的时间戳。切换机器。利用多节点部署特性,这台机器有问题,切换到其他正常的机器上。追赶时钟。你不是慢么?那我就加速追赶时间。详解为什么会有时钟回拨问题?从根本上来说,时钟回拨的根本原因在于服务器的物理时间不总是精确同步,完全可靠的,而雪花算法严重依赖时间戳这一生成ID的核心元素。常见的时钟回拨原因:NTP时间同步异常:NTP(网络时间协议)用于同步服务器时间。如果服务器与时间源同步时,检测到系统时间比当前标准时间快,则会将系统时间回调到正确的时间点。手动时间调整:运维人员

    2025-01-22
    阅读(287)
  • 回答雪花算法(SnowflakeAlgorithm)是分布式环境下非常经典的一种分布式唯一ID生成算法,由Twitter的工程师设计出来的。它的核心目标是高效生成全局唯一的、趋势递增的ID,而且生成速度快、并发性能好,完全可以满足大规模分布式系统的需求。雪花算法生成的ID是一个64位的长整型数字,里面的每一部分都编码了特定的信息,比如时间戳、机器ID和序列号,这让它既能保证全局唯一性,又不会出现重复。而且,ID是按照时间递增的,能在数据写入时优化索引性能。详解雪花算法生成的64为二进制Id,可以分为如下几个部分位数字段名说明1bit符号位恒为0,保证生成的ID是正数。41bits时间戳相对时间戳,记录从某个时间点到当前的毫秒数。41位可以支持69年的时间范围,(2^41/(1000*60*60*24*365)≈69)10bits机器ID标识当前生成ID的机器或节点。10位最多可以支持102

    2025-01-22
    阅读(242)
  • 回答Mybatis的运行涉及到Executor、StatementHandler、ParameterHandler、ResultSetHandler等组件,插件的工作原理就是在这些组件执行过程中,插入一些自定义的代码逻辑。MyBatis插件机制允许用户自定义拦截器(实现Interceptor接口),以在执行SQL的各个阶段进行额外的处理。说一下Mybatis的工作原理?实现机制Mybatis使用JDK动态代理,为目标对象生成代理对象。从而在目标对象方法调用时被拦截器拦截处理。publicclassPluginimplementsInvocationHandler{/***创建代理对象*/publicstaticObjectwrap(Objecttarget,Interceptorinterceptor){//获取拦截器关注的类和方法签名的映射Map<Class<?>,Se

    2024-12-22
    阅读(354)
  • 回答Executor执行器是Mybatis核心组件,它负责执行SQL语句并处理数据库的交互,主要包括SQL语句的生成、参数绑定、执行、缓存处理以及结果集的映射等。Mybatis内置了三种基本的Executor。SimpleExecutor:每执行一次update或select,就开启一个Statement对象,用完立刻关闭Statement对象。ReuseExecutor:执行update或select,以sql作为key查找Statement对象,存在就使用,不存在就创建,用完后,不关闭Statement对象,而是放置于Map<String,Statement>内,供下一次使用。BatchExecutor:支持批处理的执行器,支持批量更新操作,依赖JDBC的批处理。publicExecutornewExecutor(Transactiontransaction,Executo

    2024-12-22
    阅读(986)
  • 回答Mybatis支持两种分页:手动分页和分页插件。手动分页手动分页指在SQL语句中添加LIMIT和OFFSET(或类似的关键字)来实现的。publicinterfaceUserMapper{@Select("SELECTid,name,emailFROMt_userLIMIT#{limit}OFFSET#{offset}")List<User>selectUsersWithPagination(@Param("limit")intlimit,@Param("offset")intoffset);}分页插件MyBatis提供了多种分页插件,可以自动处理分页逻辑,常用的分页插件有PageHelper和MyBatis-Plus。PageHelperPageHelper定义@ConfigurationpublicclassM

    2024-12-22
    阅读(927)
  • 回答Mybatis中可以用resultMap中的<result>属性来映射以及SQL别名。CREATETABLEt_user(idINTNOTNULLPRIMARYKEY,nameVARCHAR(50),emailVARCHAR(50))ENGINE=InnoDB;publicclassUser{privateIntegeruserId;privateStringuserName;privateStringuserEmail;//gettersandsetters}第一种,SQL语句中定义字段名的别名。<selectid="selectUserById"parameterType="int"resultType="com.damingge.domain.User">selectidasuserId,name

    2024-12-22
    阅读(902)
  • 回答是的,MyBatis的Mapper接口不需要显式实现,因为MyBatis内部使用动态代理机制在运行时生成这些接口的实现。动态代理JDK的动态代理机制可以在运行时创建接口的代理实例。MyBatis基于动态代理为每个Mapper接口生成一个代理对象,从而无需手动实现接口。核心流程注册Mapper接口:在MyBatis配置文件或代码中注册Mapper接口。<mappernamespace="com.damingge.mapper.UserMapper"/>sqlSessionFactory.getConfiguration().addMapper(UserMapper.class);获取Mapper接口代理对象:通过SqlSession获取Mapper接口的代理对象。UserMapperuserMapper=sqlSession.getMapper(UserM

    2024-12-22
    阅读(997)
  • 回答Mybatis的Mapper支持四种参数传递方式。单个参数:当Mapper方法只有一个参数时,SQL语句中支持使用#{}占位符来引用该参数。publicinterfaceUserMapper{UserselectUserById(Integerid);}<selectid="selectUserById"parameterType="java.lang.Integer"resultMap="User">SELECTid,name,emailFROMt_userWHEREid=#{id}</select>**多个参数:**当Mapper方法有多个参数时,使用@Param注解为每个参数指定名称。MyBatis会将这些参数放入Map中,键为参数名称。publicinterfaceUserMapper{User

    2024-12-22
    阅读(333)
  • 回答支持。Mybatis通过association、collection标签来实现一对一和一对多的关联查询。一对一关联查询一对一关联查询支持两种方式:嵌套查询和结果集嵌套映射。核心是通过association标签支持。publicclassUser{privateIntegerid;privateStringname;privateAddressaddress;//gettersandsetters}publicclassAddress{privateIntegerid;privateStringcity;privateStringprovince;//gettersandsetters}嵌套查询(NestedQueries)<mappernamespace="com.damingge.mapper.UserMapper"><resultMapid=&

    2024-12-22
    阅读(727)
  • 回答支持,Mybatis支持两种批量操作。foreach标签foreach主要用在构建in条件中,它可以在SQL语句中遍历一个集合。foreach标签的属性主要有item,index,collection,open,separator,close。item:表示集合中每一个元素进行迭代时的别名,随便起的变量名。index:指定一个名字,用于表示在迭代过程中,每次迭代到的位置,不常用。open:表示该语句以什么开始,常用“(”。separator:表示在每次进行迭代之间以什么符号作为分隔符,常用“,”。close:表示以什么结束,常用“)”。collection:指定要遍历的集合或数组。publicinterfaceUserMapper{voidbatchInsertUser(@Param("userList")List<User>userList);void

    2024-12-22
    阅读(549)
  • 回答#{}和${}是MyBatis两种不同的占位符,#{}为预编译处理,而${}是字符串替换。#{}${}用途传递参数,防止SQL注入传递参数,不能防止SQL注入实现机制MyBatis将#{}中的内容替换为?,并在运行时通过PreparedStatement的set方法设置参数值。MyBatis将${}中的内容直接替换为参数值,即将参数直接拼接到SQL字符串中。安全性安全性高,可以有效防止SQL注入。存在安全隐患,直接拼接SQL字符串容易导致SQL攻击。使用场景#{}用在动态SQL参数赋值,防止SQL注入。@Select("SELECT*FROMt_userwhereid=#{id}")UserselectUserById(@Param("id")Longid);//#{}解析替换为?===>SELECT*FROMt_userwhereid=?

    2024-12-22
    阅读(543)
  • 回答支持。Mybatis内置了一些动态SQL的标签,允许开发者根据不同的条件构建不同的SQL,提高代码的灵活性和可维护性。如<if>、<choose>、<when>、<otherwise>、<trim>、<where>、<set>和<foreach>等。<if>元素:根据条件动态包含SQL片段。<mappernamespace="com.damingge.mapper.EmployeeMapper"><selectid="selectEmployeeByCondition"resultType="com.damingge.model.Employee">SELECT*FROMt_employee&lt

    2024-12-22
    阅读(996)
  • 回答Mybatis内置了强大了事务性查询缓存机制。默认情况下,Mybatis定义了两级缓存,并且提供Cache接口扩展,提高了框架的扩展性。一级缓存:也叫“本地缓存”,默认情况下开启(SqlSession级别的缓存)二级缓存:也叫“全局缓存”,基于namespace级别的缓存,需要手动开启和配置。实现原理一级缓存Mybatis一级缓存由PerpetualCache实现,采用Map结构存储数据。其作用域是SqlSession,各个SqlSession之间的缓存相互隔离。每次查询后,结果会被缓存到PerpetualCache中,后续相同查询直接取缓存数据。核心实现类和方法BaseExecutor:负责执行SQL,管理一级缓存。publicabstractclassBaseExecutorimplementsExecutor{//...其他代码省略@Overridepublic<E>

    2024-12-22
    阅读(937)
  • 回答支持。延迟加载的意思是在使用的时候才去加载,这是一种性能优化策略。延迟加载针对有延迟设置的关联对象推迟查询,而主查询是直接执行SQL语句。优点:先从单表查询,需要时再从关联表查询,减少不必要的数据加载,提升数据库性能。不足:延迟加载会增加数据库访问次数以及增加事务管理的复杂性。扩展延迟加载配置1、Mybatis默认没有开启延迟加载,需要在全局配置文件mybatis-config.xml中进行设置,核心配置项:lazyLoadingEnabled:全局开关,启用或禁用延迟加载。aggressiveLazyLoading:侵入式延迟加载开关,false表示按需加载。<configuration><!--其他配置--><settings><!--延迟加载开关--><settingname="lazyLoadingEnabled&

    2024-12-22
    阅读(868)
  • 回答Mybatis包含一些核心组件如Configuration、SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession、Mapper等。它的生命周期也就是这些组件的生命周期。如下:ConfigurationMyBatis加载配置文件(如mybatis-config.xml)和映射文件(如XXXMapper.xml),生成一个全局配置对象(Configuration),它是一个单例。SqlSessionFactoryMyBatis基于加载的配置文件创建SqlSessionFactory。它是用来创建SqlSession,类比于数据库连接池。SqlSessionFactory是一个单例,它的生命周期是应用作用域。SqlSession通过SqlSessionFactory创建SqlSession。SqlSession类比于JDBC的Connec

    2024-12-22
    阅读(473)
  • 回答Mybatis是一款ORM框架,实现了SQL到Java对象的映射转换,简化了持久层代码的开发工作。其核心流程分两步,一是创建会话工厂,二是会话运行。加载配置文件,MyBatis加载全局配置文件(如mybatis-config.xml)和映射文件(如XXXMapper.xml),配置数据库连接、映射器、插件等信息,生成一个全局配置对象(Configuration)。创建SqlSessionFactory,MyBatis基于加载的配置文件创建SqlSessionFactory(用于生成SqlSession)。获取SqlSession,通过SqlSessionFactory获取SqlSession。SqlSession是MyBatis执行SQL操作的核心。执行SQL,Mybatis内部定义了Executor接口来操作数据库,它根据SqlSession传递的参数动态地生成执行SQL,并支持查询

    2024-12-22
    阅读(647)
  • 回答Mybatis是一款优秀的持久层框架。它支持自定义SQL、存储过程以及高级映射。Mybatis简化了JDBC代码操作,它通过XML或注解配置将原始类型、接口和JavaPOJO(PlainOldJavaObjects)映射为数据库中的记录。扩展Mybatis优点半ORM框架,Mybatis内部封装了JDBC操作,研发人员仅需关注SQL本身。//1.加载配置文件Propertiespro=newProperties();pro.load(newFileReader("resource/jdbc.properties"));//2.获取配置文件中连接数据库的信息Stringurl=pro.getProperty("url");Stringuser=pro.getProperty("user");Stringpassword=pro.

    2024-12-22
    阅读(331)
  • 回答在Spring中,@Enable*注解的作用是开启某种特定功能,以便简化开发配置流程。简单点来说就是@Enable*是一个开关,通过它我们可以非常方便的开启某种功能,例如AOP、事务、异步处理等等。比如:@EnableScheduling:开启任务调度。@EnableAsync:开启异步方法支持。@EnableTransactionManagement:开启事务管理。@Enable*背后的核心是组合式注解和扩展机制,它主要是通过@Import加载配置类或ImportSelector、ImportBeanDefinitionRegistrar动态注册相关Bean。详解我们已EnableTransactionManagement为例来讲解下@Enable*相关的原理。关于@Import相关的内容详见面试题:知道Spring中@lmport注解的作用吗?@Target(ElementType

    2024-12-18
    阅读(723)
  • 回答@Import主要用于将指定的配置类注入到Spring容器中,它的作用类似于直接在@Configuration配置类中使用@Bean方法声明Bean,但它提供了一种更加灵活的方式。从简单的使用到高级的扩展,@Import能帮我们完成:导入配置类:把其他配置类加载进来,便于模块化管理。导入普通组件:直接将某个类(例如第三方库的类)注册为Bean。基于逻辑条件动态导入:通过实现ImportSelector或ImportBeanDefinitionRegistrar,根据需求动态注册Bean。详解@Import的三种用法注入普通类注入普通类,这种方式非常简单,如下:@Configuration@Import(SkService.class)publicclassSkServiceConfig{}publicclassSkService{publicvoiddoSomething(){Syst

    2024-12-18
    阅读(298)
  • 回答Aware接口是Spring提供的一组标志性接口,所有以*Aware结尾的接口都具有"感知"的能力。通过实现这些接口,Spring会在Bean的生命周期中,自动将对应的容器资源(如ApplicationContext、BeanFactory等)注入到Bean中,从而使Bean能够与Spring容器进行交互。Aware接口的核心逻辑是依赖于BeanPostProcessor的实现。当Bean实现某个Aware接口时,Spring的ApplicationContextAwareProcessor会在Bean初始化后,调用对应的方法注入相关的资源。详解源码解析我们直接看Aware接口定义:/***Markersuperinterfaceindicatingthatabeaniseligibletobe*notifiedbytheSpringcontainerofapart

    2024-12-18
    阅读(1054)
  • 回答在Spring中,当一个接口有多个实现类时,需要通过一些特定的方法来明确指定注入的实现类,避免Spring在自动装配时出现歧义。目前主流的方式有两种:使用@Qualifier注解:在注入时指定具体的Bean名称。参考面试题:Spring中的@Qualifier注解的作用?使用@Primary注解:将一个实现类标记为默认优先注入的Bean。参考面试题:SpringBean如何设置为默认Bean?

    2024-12-18
    阅读(325)
  • 回答Spring不推荐@Autowired字段注入而是构造器注入,主要是因为字段注入有如下几个缺点:容易违背了单一职责原则:使用基于字段注入的方式,添加依赖非常简单,要使用一个接口直接添加依赖即可,哪怕我们添加10个、20个也不觉得有什么问题,这就会导致过度使用,再加上对依赖的隐蔽性,会导致一个类承担过多的职责,很容易违背了单一职责原则。难以进行单元测试:由于与Spring容器强依赖,所以使得在不依赖Spring框架的情况下进行单元测试变得更加困难。不支持不可变性:字段注入不支持final字段,这意味着无法创建不可变的bean。关于Spring注入相关请参考面试题:Spring为什么建议使用构造器来注入?

    2024-12-18
    阅读(361)
  • 回答首先我们需要明确@Component标注在类上,@Bean标注在方法上,如果同一个类里面既有@Component又有@Bean,那么Spring会同时将:该类本身作为一个Bean@Bean方法返回的对象也作为一个独立的Bean注册到容器中例如:@ComponentpublicclassSkService{@BeanpublicStringskJava(){return"ThisisaskJava!";}}在这种情况下:Spring容器会注册一个名为skService的Bean,该Bean对应SkService类的实例。Spring容器也会注册一个名为skJava的Bean,对应方法skJava()的返回值但是,在实际开发过程中,并不推荐@Component和@Bean搭配使用,而是使用@Configuration。

    2024-12-18
    阅读(1005)
  • 回答@Qualifier的主要作用事解决Spring容器中多实例注入时的歧义问题。当Spring容器中有多个同类型的Bean可供注入的时候,默认情况下Spring会不知道应该注入哪个,这个时候我们就可以通过@Qualifier明确指定需要注入哪个具体的Bean。详解在大多数情况下,我们都是使用@Autowired来实现Spring的依赖注入的,在默认情况下,Spring会根据类型(byType)查找匹配的Bean,如果Spring容器中有多个同类型的Bean,Spring会抛出异常:NoUniqueBeanDefinitionException:Noqualifyingbeanoftype[...]available:expectedsinglematchingbeanbutfound2:bean1,bean2具体情况请参考面试题:SpringBean如何设置为默认Bean?的多候选Bea

    2024-12-18
    阅读(519)
  • 回答如果我们想将某个Bean设置为默认Bean,一般推荐使用@Primary。当应用中存在多个候选Bean的时候,Spring会优先选择标记了@Primary的那个作为注入的默认Bean。详解举个例子:@ComponentpublicclassSkServiceAimplementsSkService{//ImplementationofMyService}@Component@PrimarypublicclassSkServiceBimplementsSkService{//ImplementationofMyService}在这里,如果某个地方需要注入SkService,Spring会优先选择@Primary标注的SkServiceB。但是,如果我们使用了@Qualifier指定了具体的Bean名称,那么即使有@Primary,Spring也会注入指定的Bean。多候选Bean问题Sp

    2024-12-18
    阅读(389)
  • 回答可以的。在Spring中,null和空字符串的注入有明确的语法支持。我们可以通过配置或者注解的方式注入null和空字符串。比如在XML中<null/>用于注入null,而空字符串可以直接通过""或者留空来注入。详解XML方式在XML中,<null/>是专门用于注入null的配置指令。Spring容器在解析配置时会自动将<null/>转换为Java的null。<beanid="userService"class="com.skjava.UserService"><propertyname="orderService"><null/></property></bean>对于空字符串也很好实现:<beanid=&

    2024-12-18
    阅读(1038)
  • 回答Spring事务中支持数据库事务的隔离级别有5种,他们定义了一个事务与其他事务如何交互,主要用来解决脏读、不可重复度和幻读问题。ISOLATION_DEFAULT:默认隔离级别,依赖于底层数据库的默认设置。ISOLATION_READ_UNCOMMITTED:允许读取未提交的数据。允许另外一个事务可以看到这个事务未提交的数据。这种隔离级别会产生脏读,不可重复读和幻读。ISOLATION_READ_COMMITTED:读已提交。保证一个事务修改的数据提交后才能被另外一个事务读取,另外一个事务不能读取该事务未提交的数据。这种事务隔离级别可以避免脏读出现,但是可能会出现不可重复读和幻读。ISOLATION_REPEATABLE_READ:可重复读。可以防止脏读、不可重复读,但是可能出现幻读。它除了保证一个事务不能读取另一个事务未提交的数据外,还保证了不可重复读。ISOLATION_SERIA

    2024-12-18
    阅读(1030)
  • 回答在Spring中,目前最主流的管理事务的方式有两种,编程式事务和声明式事务。编程式事务编程式事务需要我们开发者手动编写代码来控制事务的开启、提交和回滚。我们可以利用TransactionTemplate或者PlatformTransactionManager来实现:TransactionTemplate是Spring提供的一种事务管理模板类,它封装了事务的开始、提交和回滚逻辑,我们只需要在模板中编写具体的业务代码就可以了。PlatformTransactionManager是Spring事务管理的核心接口,我们可以直接使用它来手动管理事务的生命周期。编程式事务的优点在于它控制的事务粒度非常精细,我们可以非常灵活地控制事务的范围、传播行为、回滚逻辑。缺点就是增加了代码的复杂度,同时事务的管理和业务代码也耦合了。所以,它比较适用于那些需要灵活控制事务边界的场景。声明式事务声明式事务是目前S

    2024-12-18
    阅读(976)
  • 回答在Spring中同时使用@Transactional和@Async会导致事务失效。这是因为@Async会开启一个独立的线程来执行方法,而事务管理依赖于当前线程的上下文。当事务开启后,事务的上下文信息是绑定在当前线程上的。如果@Async切换了线程,那么事务的上下文就无法传播到新的线程中,导致事务不会生效。详细情况请参考这几篇面试题:为什么不建议直接使用Spring的@AsyncSpring的事务在多线程下生效吗?为什么?@Async注解一定会异步执行吗?@Transactional注解的失效场景

    2024-12-18
    阅读(1132)