Java基础面试整理

文章详细介绍了Java的类加载过程,包括编译、加载、链接和初始化阶段,以及双亲委派机制。接着讨论了Spring框架中的设计模式,如单例、工厂、代理模式,和Spring的IoC容器、AOP、事务管理。此外,还提到了数据库事务的隔离级别和Spring的事务处理策略。
摘要由CSDN通过智能技术生成

java加载过程

编译,加载,链接,初始化

编译-将.java文件编译成.class文件

加载-把.class文件加载到jvm中,创建对应的class对象和类方法存储到方法区,为了防止出现多个字节码相同的文件,一般会使用双亲委派机制加载,自己不会去加载引用类,会交给父加载器去文成,依次往上

链接-可以分为验证,准备,解析

验证-验证是否符合java规范和jvm规范

准备-为类的静态变量分配内存空间并初始化,如果是被static final修饰的常量,则直接初始化为目标常量

解析-其实符号引用转换为直接引用的过程,实际文件再编译时并不知道引用的实际位置,则用com.Wanger来标识,用符号标识则是符号引用,通过符号引用解析获取引用类的实际地址,就是直接引用,这个过程就是解析初始化-再准备阶段已经将类变量赋值了默认初始值,初始化阶段就是将变量赋值成代码实际期望值,也就是执行类构造器方法的过程

类加载器JDK提供了3种类加载器-BootstrapClassLoader,ExtClassLoader,AppClassLoader BootstrapClassLoader-顶层的类加载器,也称root类加载器,负责加载JRE/lib/rt.jar中的class文件,加载目录可以通过-Xbootclasspath修改

ExtClassLoader-扩展类加载器,负责加载JRE/lib/ext文件夹中的class文件,优先级次于BootstrapClassLoader

AppClassLoader-系统类加载器,负责加载我们应用的class文件和classpath环境变量中指定目录下的class文件,优先级次于ExtClassLoader

双亲委派机制描述:当前类加载器再加载一个class对象的时候,首先不会自己去加载class对象,是会委托自己的父加载器去加载,父加载器未加载到当前class对象的时,会递归的委托自己的父加载器去加载,直到父加载器不存在或者父加载器都加载不到时,才会自己加载。描述中的父加载器不是java中的继承关系,是指职责上的关系

优点:可以明确分工每种类加载器的职责,保证class加载的唯一性,当一个class文件被父加载器加载后,不会再重复加载

Spring中常用的设计者模式单例模式-懒汉模式,饿汉模式

懒汉模式-第一次调用的时候实例化自己

public class Singleton{ private Singleton(){} private static Singleton intance = null; //存在线程安全问题,适用于单线程环境,不推荐使用 public static Singleton getIntance(){ if(null== intance) { intance = new Singleton(); } return intance; } //使用关键字修饰-保证线程安全,但是效率会比较慢,不推荐使用 public static synchronized Singleton getIntance(){ if(null==intance) { intance = new Singleton(); } return intance; } //双重检查模式-保证线程安全效率也不影响,推荐使用 public static Singleton getIntance(){ if(null==intance) { synchronized(Singleton.class) { if(null==intance){ intance = new Singleton(); } } } return intance; } //静态内部类-加载一个类的时候,其内部的类不会被加载,当且有且其中的某个静态变量被调用才会加载 //兼顾了懒汉的使用被加载有的优点,也兼具了饿汉模式的安全性,但是加载后属于永久带对象 private static class SingletonStaticIntance(){ private static final Singleton INTANCE = new Singleton(); } public static Singleton getIntance(){ return SingletonStaticIntance.INTANCE; }} 饿汉模式-类加载的时候就会被创建,存在被加载后不使用,产生内存浪费的情况public static Singleton(){ private static final Singleton intance = new Singleton(); private Singleton{} public static Singleton getIntance(){ return intance; }}

工厂模式-主要通过BeanFactory和ApplicationContext来生产对象,

代理模式-最常见的Aop(反向代理)就是代理实现的,Spring主要使用JDK的代理和Cglib代理。JDK代理只能对实现了接口的类进行代理,cglib代理可以对普通类代理。JDK的代理是通过发射的方式来实现动态代理,cglib则是通过为目标类生成一个字类实现代理,所以cglib代理的类,不能使用private和final修饰,无法生成子类代理对象。aop这是判断是否实现了接口来判断使用那种代理模式,如果目标对象实现接口则会使用JDK代理,没有则会强制使用cglib库,spring会自动在JDK和cglib中转换

模板方法模式-主要是一些对数据库操作用的类使用,比如JdbcTemplate,JpaTemplate,都是查询数据库的建立连接,执行查询,关闭连接这几个过程,非常适用模板方法模式

IOC容器和AOP面向切面编程

IOC容器-负责创建对象,管理对象(通过依赖注入(DI))装配对象,配置对象,并且管理这些对象的生命周期,Spring提供了BeanFactory和ApplicantContext两种IOC容器BeanFactory和ApplicationContext

区别-BeanFactory在加载配置文件是不会创建对象的,获取对象时创建。ApplicationContext是BeanFactory的子接口,功能更强大,会在配置文件加载时创建对象BeanFactory和FactoryBean的区别-BeanFactory是bean工厂,是ApplicationContext的父类,是IOC容器的核心,负责生产和管理Bean对象。FactoryBean是Bean对象,可以通过实现FactoryBean接口实例化Bean逻辑,通过代理Bean对象,对方法前后做操作。

AOP面向切面编程-

AOP是基于动态代理的方式实现的,如果实现了接口就会使用JDK代理,没有实现接口会强制使用Cglib代理,Spring会自动切换代理模式。AOP本身应用体现在事务,日志,异常处理方面,同时解耦。Spring提供了

JoinPoint连接点:类里面哪些方法被增强

PointCut切点:实际被真正增强的方法

Advice通知:被增强方法中新增的逻辑部分,通知分为5种,前置@Before,后置@AfterReturning,环绕@Around,异常@AfterThrowing,最终@After

Aspect切面:将通知应用到切点- SpringBean的生命周期实例化-创建一个bean对象填充属性-为属性赋值初始化-通过实现不同的Aware接口拿到Spring容器资源,设置到具体的bean对象中。

bean实现BeanNameAware接口,Spring将bean的ID传递给setBean-Name()方法。bean实现BeanFactoryAware接口,Spring调用setBeanFactory()方法,将BeanFactory容器是传入bean实现ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传进来如果实现了BeanPostProcessor接口,则会回调该接口的postProcessBeforeInitialzation和postProcessAfterInitialzation方法对bean对象进行扩展如果配置了init-method方法,则会执行init-method配置的方法完成初始化 销毁-容器关闭后,如果Bean实现了DisposableBean接口,会调用该接口的destory方法,如果配置了destory-method方法,则会执行配置额destory-method- 循环依赖原因解决方法-三级缓存- Spring事务事务的7种传播性(rollbackFor=Exception.class,propagation=Propagation.REQUIRED)REQUIRED(默认事务):如果当前没有事务的话,新建一个事务。如果当前存在事务,这加入到当前事务中REQUIRED_NEW:新建事务,如果当前存在事务,把当前事务挂起,新建事务执行完再执行当前事务,但是新的事务提交失败或者时回滚都不影响主事务,主事务的提交和回滚都会影响新事务NESTED:如果当前没有事务,则新建事务,如果存在事务,则创建一个当前事务的子事务(嵌套事务),子事务不能单独提交,只能和父事务一起提交SUPPORTS:支持当前事务,如果当前没有事务,以非事务方法执行NOT_SUPPORTS:以非事务方式执行,如果存在当前事务就把当前事务挂起NEVER:以非事务方式执行,如果当前存在事务就抛异常MANDATORY:使用当前事务,如果当前没有事务,就抛异常4种数据隔离级别脏读:一个事务读取到另一个事务中修改未提交的数据不可重复读:一个事务读取了某些数据后,该数据被其他的事务修改,此时第一个事务读取到的事务就是错误的(强调修改),即同一个事务中前后两次读取到的数据不一致幻读:一个事务读取了某些数据后,未提交再次读取的时,数据相比上一次读取多了或者是少了,类似幻觉(强调增删)丢失修改:一个事务读取一个数据时,另一个事务也读取了该数据。在第一个事务中修改了这个数据,第一个事务也修改了这个数据。第一个事务内的修改结果就会被丢失,和就是丢失修改。4种数据库隔离级别读未提交:保证了数据读取过程中不会读取到非法数据,但是不能避免脏读读已提交:保证了一个事务不会读到另一个并行事务也中已修改但是未提交的数据。避免了脏读,但是不能避免不可重复读,比如oracle默认的数据隔离级别可重复读:保证了一个事务不会修改已经由另一个事务读取但是未提交的事务。可以避免脏读和不可重复读的情况,无法解决幻读的问题,但是消耗了更多的性能。mysql的默认隔离等级序列化:最严格的等级,即事务串行处理,最安全也是性能消耗最大事务使用可以使用注解式事务和模板化事务注解式事务时使用spring框架的@transactional注解,其实现时代理,生成当前方法的子类,所以使用这个注解的方法不能用私有关键字或者时final修改,而且方法中尽量不要try-catch异常,防止注释失效模板化事务-是更细粒度的事务控制,业务实现时需要入侵到代码逻辑中,主要由两种,可以自己创建事务管理器,自己控制事务传播性,但是需要手动提交和回滚事务,也可以直接创建模板化事务对象,对象会自动的提交和回滚事务。这两个事务方式再使用的时候,我理解还是需要根据业务需求去选择,最贴合业务的才是最好的- MVCC机制其原来主要是依赖记录中的3个隐式字段,undo日志,readView来实现的隐式字段:DB_TRX_ID:记录创建这条数据记录或者是最后一次修改记录的事务IDDB_ROLL_PTR:回滚指针,指向这条记录的上一个版本(存储了rollback segment里面)DB_ROW_ID:隐含的自增ID,如果数据表没有主键,InnoDB会自动的以DB_ROW_ID产生一个聚簇索引undo日志insert undo log:事务在insert新纪录是产生的undo log,只需要事务回滚时需要,并且在事务提交后被理解丢弃update undo log:事务做update或者是delete操作时产生的undo log,不仅在事务需要的时候进行回滚,在快读的时候也是需要的,只有在快照读或者是事务回滚不涉及该日志时,才会被purge线程统一清除purge线程:更新和删除的时候都只是设置老记录的delete_bit属性,不是真正的清楚掉过期的记录,但是为了节省磁盘空间,purge线程会定时清理掉delete_bit属性为true数据,readView读视图事务进行快照读操作产生的一个数据库系统当前的一个快照,记录并维护当前活跃事务ID(DB_TRX_ID,事务ID是由小到大依次递增),并以此比较当前事务可以看到那个版本的数据,但是在读已提交和可重复读的隔离等级中,readView中的数据是不同的,如果是读已提交readView中的数据是可以看到别的事务已经提交的数据,即每次快照读都会产生一个新的快照。在可重复读中,快照产生后,前后事务读取的是同一个快照。示例:目前是事务ID是1,但还是存在其他4个事务 事务2 事务3 事务4 事务5 事务开始 事务开始 事务开始 事务开始 .... ..... ..... 修改其事务提交 进行中 ...... 进行中 ...... 快照读 ..... 事务5在事务3快照读之间提交了事务,事务3能否读到事务5提交的内容?readview在事务5提交后,事务3快照读前生产了一个事务数组trx_list,记录正在活动中的事务ID,同时也给两个属性up_limit_id(trx_list集合中最小的事务Id)和low_limit_id(快照读时刻系统尚未分配的下一个事务Id,也就是当前最大事务Id+1),根据事务3的Id判断读取数据的版本,由于事务3ID是否小于up_limit_id,事务3ID是否大于low_limit_id,并且事务5ID又不在目前的活跃事务Id中,所以事务3可以到事务5提交的数据,而且事务提交的数据目前是全局最新的版本- Spring分布式事务(7种)2PC-两阶段提交(准备阶段-提交阶段)1.准备阶段-事务协调者首先向参与者,询问是否可以提交事务,然后等待所有参与者的答复。各参与者执行本地事务操作,将undo和redo信息记入事务日志中,但是不提交。所有的参与者执行成功后,给协调者反馈同意,否则中止,表示事务不可以执行。2.提交阶段-如果协调者收到了参与者的准备YES后,通知所有的参与者提交事务,并释放再整个事务期间内占用的资源,参与者完成事务提交后,向协调者发送ack消息,协调者节点收到所有参与者节点反馈的ack消息后,完成事务。3.回滚-如果任意一个参与者节点在第一阶段返回的消息为中止,或者协调者在第一阶段的询问超时之前无法获取所有参与者节点的相应消息时,那么整个事务将会被回滚。4.缺点-性能问题:事务执行过程中,所有的参与节点都是事务柱塞的,当参与者占用公共资源时,其他三方节点访问公共资源就不得不阻塞,对性能影响较大,不适合高并发和高性能的业务场景。可靠性问题:2PC很依赖协调者,一旦协调者宕机,所有的参与者都会处于锁定事务的状态中,无法继续完成事务。数据一致性问题:当前协调者发送commit请求后,网络异常导致一部分参与者接收到了commit操作请求并执行了commit,但是一部分未收到commit请求,导致事务无法执行,整个分布式系统的数据出现不一致的现象。而2PC无法解决的问题:协调者在发出commit请求后宕机,而唯一一个接到commit的参与也宕机,通过选举协议产生的协调者,对这条事务的状态时无法确定的3PC-三阶段提交(准备阶段-预提交阶段-提交阶段)1.三阶段提交协议,是二阶段提交协议的升级版,有两个改动点。在协调者和参与者之间引入了超时机制,在第一阶段和第二阶段中插入了一个准备阶段,保证了在最后提交阶段之前各参与节点的状态是一致的。2.协调者向所有参与者发送包含事务内容的是否可以提交的请求,并进入等待状态。参与者收到请求后,如果认为执行事务操作,反馈yes并进入预备状态,否则返回no。如果所有的参与者均返回yes,协调者向所有的参与者发送预提交请求,并进入准备阶段,参与者接到请求后,会执行本地事务操作,将undo和redo信息记录到事务日志中,参与者执行完后向协调者发送ack相应,同时等待最终指令 。如果有任何一个参与者发送了no或者超时,协调者就会发送回滚请求中止事务。如果接收到所有的参与者的yes回应后,协调者向所有的所有的参与者发送提交请求,接收到所有参与者的ack相应后,结束事务。优缺点:相比与2PC,3PC降低了阻塞范围,并且在等待超时之后,协调者或参与者会中断事务,但是在三阶段后,如果协调者宕机,参与者接收不到提交或者是回滚请求后,会超时后继续提交事务。无法保证数据一致性TCC-应用层两阶段提交对代码的侵入性强,核心思想是:针对每个操作,都要首先对应的确认和补偿操作,也就是业务逻辑的每个分支都要实现try,confirm,cancel三个操作。第一阶段由业务代码调用try接口进行资源预留,否则调用每个参与者的cannel接口回滚事务,并且由于confirm和cancel有可能会重试,所有还要支持幂等。主要流程-第一阶段try业务系统调用try对预留资源加锁,比如下单,在try阶段不是真正的减少库存,只是把下单的那部分锁住。根据第一阶段的结果,判断执行confirm(执行业务,释放锁)或者是cancel(回滚,释放锁)操作。TCC涉及最初是认为一但try成功,第二阶段不存在失败可能,如果confirm和cancel失败,有TCC框架进行重试补偿。注意事项-允许空回滚:try超时或者是丢包,导致第二阶段回滚,触发cannel操还,但是参与者未收到try,却收到了cannel请求,但是执行cannel操作时为发现对应的事务id或者时主键,需要返回回滚成功。防悬挂控制:悬挂指得是二阶段的的cancel比一阶段的try先执行,原因是try由于网络拥堵而超时,导致事务管理器先生成回滚,触发了cannel接口,但是在网络恢复后,参与者又收到try的请求,但是由于回滚操作这个时候已经执行,所以不应该再接收try操作,因此在cannel空回滚执行前应该先记录该事务主键或者是业务主键,标识事务已经回滚,try接口执行前检查事务是否已经回滚成功。由于tcc框架的重试,所以在try-confirm-cannel三者执行前都应该进行幂等校验优缺点-性能提升,具体是根据业务逻辑实现,控制锁的细粒度更小,不会锁定整个资源。数据最终是保持一致性的,可以解决单点单点故障问题,由于是业务发起并控制整个业务,所以可以引入集群。因为整体流程按具体业务实现,侵入代码较深,开发成本提高Saga/Mq事务消息/本地消息表/最大努力通知2PC/3PC:依赖于数据库,能够很好的提供强一致性和强事务性,但延迟比较高,比较适合传统的单体应用,在同一个方法中存在跨库操作的情况,不适合高并发和高性能要求的场景。TCC:适用于执行时间确定且较短,实时性要求高,对数据一致性要求高,比如互联网金融企业最核心的三个服务:交易、支付、账务。本地消息表/MQ 事务:适用于事务中参与方支持操作幂等,对一致性要求不高,业务上能容忍数据不一致到一个人工检查周期,事务涉及的参与方、参与环节较少,业务上有对账/校验系统兜底。Saga 事务:由于 Saga 事务不能保证隔离性,需要在业务层控制并发,适合于业务场景事务并发操作同一资源较少的情况。Saga 由于缺少预提交动作,导致补偿动作的实现比较麻烦,例如业务是发送短信,补偿动作则得再发送一次短信说明撤销,用户体验比较差。所以,Saga 事务较适用于补偿动作容易处理的场景- 分布式缓存- 分布式锁- #和$区别#{}:是占位符,可以有效的防止sql注入,比较安全${}:是拼接符,可以传输对象,参数字符串,无法防止sql注入,不安全 - @Autowired和@Resource区别@Autowired是spring中的注解,@Resource是JDK中的注解@Autowired会使用type注解,如果存在多个类型相同的会根据配置的@Qualifier中的name查询@Resource则是会优先使用name查询,名字无法匹配到才会使用type查询。@Autowired是spring框架注解,与spring耦合高,一旦离开spring框架注解不再起作用,@Resource可以和其他框架一起使用- 值传递和引用传递值传递-方法接收的是实参值得拷贝,生成新得内存地址引用传递-方法接收得是实参所引用得对象的推地址,不会创建副本,也不创建新的内存地址- Java接口和抽象类的区别- - 线程的生命周期- - SpringBean的生命周期- - Spring的常用注解@Controller:控制层@Service:业务层@Compoent:泛指各种组件或者是工具类@Repository:数据访问层@Autowired:bean注入,主要是根据bean类型注入,查到多个可以根据@Qualifier中的名称匹配,Spring框架注解,放在别的框架中无法起作用@Rresource:默认先根据名称匹配,名称无法匹配时,根据类型匹配- SpringBoot核心注解原理- - MySQL的数据结构和索引- - Spring涉及的设计模式- - redis的哨兵模式Redis的哨兵模式是redis2.8版本新增的机制,机制主要是在为了实现在redis集群时,当主节点宕机或者无法正常工作时,及时推选出新的主节点来防止redis集群的不可用。提供主从复制,master节点提供读写,但是从节点只读,主节点会自动同步数据到从节点,保持数据一直,数据备份的同时可以进行读写分离,提高服务器性能工作流程-是哨兵会每秒ping各个节点,来确定各个节点的状态,一旦有哨兵发现有redis主节点有存在问题,会首先将该节点比较为主观下线,然后和其他哨兵投票,判断是否将改节点标记为客观下线,一定投票数多于哨兵集群的一半+1(这个数据可以进行配置,但是最好是集群数量的一半+1),就会正式确定问题节点为客观下线。然后将自己观察的节点标记为候选节点,并投票自己观察的节点,然后问询哨兵投保,每个哨兵只能投票一次,一旦候选节点获取的投票数超过哨兵集群的一半+1,就会作为新的主节点,哨兵框架也会将新的主节点地址同步给服务。同步会进行主从数据备份,来防住数据的丢失和不一致。注意:Redis哨兵实际实现了数据的高可用,可以读写分离,但是有会产生冗余备份和集群扩容问题比较麻烦,而且有点redis节点宕机超过一半后,无法产生新主节点,会导致整个集群的不可用。在Redis3.0就推出了分区模式-主要是集群起码有三主三从6个节点组成(可以一主对多从),数据存储也采用了16843哈希槽的方式来,提高读写性能。主节点在读写数据时,从节点会进行备份。主要模式三个主节点相互通讯,来确定主节点的状态,一旦某个主节点出现问题,会推选他的从节点替换主节点。哈希槽是根据每一个key的哈希值分配一个哈希槽,每个节点负责不同哈希槽的数据读取,数据读取是只要根据key的哈希值找到对应的redis然后具体的哈希槽。解决了哨兵模式产生冗余备份问题,提高读写数据,多从节点配置也提高了集群的稳定性,但是如果主节点和对应的从节点都宕机时就无法提供服务了。- 线程池的创建方式线程池的创建一共分为两类:通过实现ThreadPoolExecutor创建的线程池,通过Executors创建线程池。其中Executors中有6种,ThreadPoolExector一种。Executors.newFixedThreadPool:创建一个固定大小的线程池,可控制并发的线程数,超出的线程会在队列中等待 。Executors.newCachedThreadPool:创建一个可缓存的线程池,若线程数超过处理所需,缓存一段时间会回收,如果线程数量不够,则创建新的线程Executors.newSingleThreadPool:创建单个线程数的线程数,它可以保证先进先出的执行顺序Executors.newScheduledThreadPool:创建一个可以执行延迟任务的线程池Executors.newSingleThreadScheduleExecutorThreadPool:创建一个单线程的可以执行延时任务的线程池Executors.newWorkStealingPool:创建一个抢占式执行的线程池(任务顺序不确定)ThreadPoolExecutor:最原始的创建线程池的方式,需要自己设置对应的参数,7个参数包括,核心线程数,最大线程数,线程存活时间,时间单位,阻塞队列,拒绝策略,线程工厂线程池的工作流程:线程提交后,首先会检查核心线程是否都在工作,没有执行当前任务,有的则判断阻塞队列是否已满,未满则将当前任务加入阻塞队列,已满判断线程是否到达最大线程池数,未满创建一个非核心线程执行任务,已满则根据拒绝策略进行处理。4种拒绝策略丢弃当前任务,抛出exexutorException异常,默认拒绝策略丢弃当前任务,不处理不抛出异常不丢弃当前任务,并由当前调用者线程处理当前最早的任务丢弃,尝试执行当前任务ThreadPoolTaskExecutor是spring封装的创建线程池的方法,实际也是使用ThreadPoolExecutor创建的线程池,但是对参数进行重新定义.有核心线程数,最大线程数,非核心线程存活时间,非核心线程存活时间单位,阻塞队列容量,拒绝策略.会根据阻塞队列的容量判断,使用链表无界阻塞队列(阻塞队列数量大于0),还是同步移交阻塞队列(阻塞队列数量小于等于0).使用时new一个TheardPoolTaskExecutor对象,设置对应的参数,然后初始化返回就行- Nacos相关- kafka相关

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值