一、Java基础部分
面向对象的概念
- 面向对象是一种编程思想,
JDK JRE JVM 的区别与联系
- JDK 是Java开发工具,由JRE 和 Java工具组成,包含了JRE 和 JVM
- JRE 是Java运行环境,由jvm和lib类库组成,包含了jvm
- jvm是Java虚拟机,
== 和 equals 的区别
- == 对基本数据类型比较的是变量值,对引用类型比较的是堆中内存对象的地址
- 在object类中 equals也是使用==进行比较的,但在String类中进行了重写,重写后的equals方法是用来比较两个字符串的内容是否相等
final 的作用
- 一个Java修饰符,用来修饰类,表示该类不能被继承,修饰方法,表示方法不能被重写,但是可以重载,用来修饰变量,如果是基本数据类型的变量,那么他的值在初始化后就不能修改,如果是引用类型的变量,那么对其初始化后便不能让他指向另一个对象,但是引用的值是可以改变的
String StringBuffer StringBuilder 的区别
- String是final修饰的,不可变,每次操作都会产生新的String对象
- 而StringBuffer和StringBuilder都是在原对象上操作的
- StringBuffer方法都是synchronized修饰的,是线程安全的而StringBuilder是线程不安全的
- 性能上 StringBuilder > StringBuffer>String
- 使用场景:经常需要改变字符串内容使用String Builder和String Buffer,优先使用String Builder,当在多线程场景需要使用共享变量的时候使用String Buffer
重载和重写的区别、
- 重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理
- 重写就是子类对父类方法的重写改造,外部样子都不能改变,但是内部的逻辑可以随意编写
接口和抽象类
- 抽象类中可以写普通的方法,而接口中只能写抽象方法
- 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是public static final类型
- 抽象类只能继承一个 但是接口可以实现多个
List 和 Set 的区别
- list是有序的,按对象进入的顺序保存,可重复,允许有多个null元素,可以使用迭代器取出所有元素,再逐个遍历,还可以使用get获取指定下标的元素
- set是无序的,不可以重复,最多允许有一个null元素,只能使用迭代器取得所有元素再逐个遍历
hashCode 和 equals 的联系
ArrayList 和 LinkedList 的区别
- ArrayList是基于动态数组,而Linked List是基于链表的,ArrayList是连续内存存储的,LinkedList可以存储在分散的内存中,ArrayList适合查询,使用下标访问,LinkedList适合做数据插入及删除操作,不适合查询,查询的话需要逐一遍历,在使用尾插法并指定初始容量的时候,ArrayList的插入性能甚至会超过LinkedList。
HashMap 和 HashTable 的区别
- HashMap方法没有用synchronization修饰的,是线程非安全的,而HashTable是线程安全的
- HashMap允许key和value为null,而HashTable不允许
- 目前HashTable由于性能问题基本已经被弃用了,可以使用concurrentHashMap代替
HashMap 原理简述,jdk7 和 jdk8的区别
如何实现一个IOC容器
什么是字节码 好处是什么
- Java源程序经过编译器编译后变成字节码,字节码由虚拟机解释执行,Java执行流程是,编译器讲Java源代码编译成jvm可执行的Java字节码,然后由jvm将要执行的字节码发给解释器,由解释器将其转为特定机器可执行的二进制机器码,然后机器上执行程序
- Java通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时还保留了可移植的特点,而且字节码并不专对一种特定的机器,这也是Java的一处编译 处处运行的特点
Java 类加载器有哪些
双亲委派模型
Java中的异常体系
GC如何判断对象可以被回收
线程的生命周期及状态
- 线程通常有五种状态,创建,就绪,运行,阻塞和死亡状态
- 新建:新建了一个线程对象
- 就绪:调用线程的Start()方法后,这时候线程处于等待cpu分配资源阶段,谁先抢到cpu资源,谁开始执行
- 运行:就绪状态的线程被调度并获得CPU资源时,就进入运行状态
- 阻塞:在运行状态的时候,可能因为某些原因导致运行状态的线程变成了阻塞状态,比如sleep()、wait()之后线程就处于了阻塞状态,这个时候可以使用notify或notify All方法将线程唤醒,唤醒的线程不会立刻执行run方法,他们要再次等待cpu调度进入运行状态
- 销毁:线程执行完了或者因为异常退出了run方法,那么线程就要被销毁,释放资源,该线程结束生命周期
sleep() wait() join() yield() 的区别
对线程安全的理解
- 线程安全就是内存安全,堆是共享内存,可以被所有线程访问,当多个线程同时访问一个对象时,如果不用进行额外的同步控制或其他的协调操作,调用这个对象都可以获得正确的结果,这个对象就是线程安全的
Thread Runable的区别
- Thread 和 Runnable的实质是继承关系,没有可比性。无论使用Runnable还是Thread,都会new Thread然后执行run方法,用法上,如果有复杂的线程操作需求,那就选择继承Thread,如果只是简单的执行一个任务,那就实现Runnable
说说你对守护线程的理解
ThreadLocal 的原理和使用场景
- 每一个Thread对象均含有一个ThreadLocalMap类型的成员变量thread locals
ThreadLocal 内存泄漏的原因 ,如何避免
并发 并行 串行的区别
- 串行在时间上不可能发生重叠,前一个任务没搞定,下一个任务就只能等着
- 并行在时间上是重叠的,两个任务在同一时刻互不干扰的同时运行
- 并发允许两个任务彼此干扰。统一时间点,只有一个任务运行,交替执行
并发的三大特性
- 原子性:原子性是指在一个操作中cpu不可以在中途暂停然后在调度,即不被中断操作,要不全部执行完成,要不都不执行。就好比转账,从账户A往账户B转1000元,那么必然包括两个操作:从账户A减去1000元,往账户B加上1000元。两个操作必须全部完成
- 可见性:当多个线程访问同一个变量始,一个线程修改了这个变量的值,其他线程能够立刻看到修改的值
- 有序性:虚拟机在进行代码编译时,对于哪些改变顺序之后不会对最终结果造成影响的代码,虚拟机不一定会按照我们写的代码顺序来执行,有可能将他们重新排序。实际上对于有些代码进行重排序后,虽然对变量的值没有造成影响,但有可能会出现线程安全问题。
为什么要用线程池?
- 降低资源消耗;提高线程利用率,降低创建和销毁线程的消耗
- 提高响应速度;任务来了,直接有线程可用可执行,而不是先创建线程,在执行
- 提高线程的可管理性;线程是稀缺资源,使用线程池可以统一分配调优监控
简述线程池的处理流程
- 线程池执行任务先看核心线程是不是满了 未满创建核心线程执行,已满再检查任务队列是否已满,未满的话将任务放到队列中,已满再检查最大线程数是否达到,未达到就创建临时线程执行,已达到就根据拒绝策略处理任务
线程池中阻塞队列的作用?为什么是先添加队列而不是先创建最大线程
一般的队列只能保证作为一个有限长度的缓冲区,如果超出了缓冲长度,就无法保留当前的任务了,阻塞队列通过阻塞可以保留住当前想要继续入队的任务。
阻塞队列可以保证任务队列中没有任务时阻塞获取任务的线程,使得线程进入wait状态,释放cpu资源。
阻塞队列自带阻塞和唤醒的功能,不需要额外处理,无任务执行时,线程池利用阻塞队列的take方法挂起,从而维持核心线程的存活、不至于一直占用cpu资源
在创建新线程的时候,是要获取全局锁的,这个时候其它的就得阻塞,影响了整体效率。
线程池中线程复用原理
- 线程池将线程和任务进行解耦,线程是线程,任务是任务,摆脱了之前通过Thread创建线程时的一个线程必须对应一个任务的限制
- 在线程池中,同一个线程可以从阻塞队列中不断获取新任务来执行,其核心原理在于线程池对Thread进行了封装,并不是每次执行任务都会调用Thread.start()来创建新线程,而是让每个线程去执行一个循环任务,在这个循环任务中不停检查是否有任务需要被执行,如果有就直接执行,也就是调用任务中的run方法,将run方法当成一个普通的方法执行,通过这种方式只使用固定的线程就将所有任务的run方法串联起来。
三、Spring框架
如何实现一个IOC容器
- 配置文件中指定需要扫描的包路径
- 定义一些注解
- 从配置文件中获取需要扫描的包路径,获取到当前路径下的文件信息及文件夹信息,我们将当前路径下所有以.class结尾的文件添加到一个set集合中进行存储
- 遍历这个set集合,获取在类上有指定注解的类,并将其交给IOC容器,定义一个安全的Map对象用来存储这些对象
- 遍历这个IOC容器,获取到每一个类的实例,判断里面是否有依赖其他类的实例,然后进行递归注入
Spring是什么?
- Spring是一个轻量级开源的框架,它是一个容器框架,用来装JavaBean,也可以说是一个中间层框架,可以起一个连接的左右,比如可以把SpringMVC和MyBatis联和在一起使用,可以让我们的企业开发更快更简洁,可以通过控制反转(ioc)的技术达到松耦合的目的,还提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统服务进行内聚性的开发
谈谈你对AOP的理解
- AOP:将程序中的交叉业务逻辑(比如安全、日志、事务等)封装成一个切面,然后注入到目标对象(具体业务逻辑)中去。AOP可以对某个对象或某些对象的功能进行增强,比如对象中的方法进行增强,可以在执行某个方法之前额外的做一些事情,在某个方法执行之后额外的做一些事情
谈谈你对IOC的理解
- IOC就是一个容器的概念加控制反转和依赖注入
- IOC容器实际上就是一个map,里面存放的是各种对象(在xml里配置的bean节点,@repository、@service、@controller、@component),在项目启动时会读取配置文件里面的bean节点,然后根据全限定类名使用反射创建对象放到map里,扫描到打上上述注解的类也是通过反射创建对象放到map里,这个时候map里就有各种对象了,接下来我们在代码里需要用到里面的对象时,在通过DI注入,如Autowired,resource等注解
- 控制反转就是将全部对象的控制权交给IOC容器管理,没引入IOC容器之前,比如对象A依赖对象B,对象A运行到需要对象B的时候,自己必须主动去创建对象B或者使用已经创建过的对象B,无论是创建还是使用对象B,控制权都在自己手上,但是在引入IOC容器之后,对象A和对象B之间失去了直接联系,当对象A运行到需要对象B的时候,由IOC容器主动创建一个对象B注入到对象A需要的地方,通过引入IOC容器前后对比,对象A获得依赖对象B的过程,由主动行为变成了被动行为,控制权颠倒过来了,我认为这就是控制反转
- 依赖注入就是控制反转之后,获得依赖对象的过程由自身管理变为了由IOC容器主动注入,依赖注入就是实现IOC的方法,就是在IOC容器运行期间,动态的将某种依赖关系注入到对象之中
BeanFactory和ApplicationContext有什么区别?
- ApplicationContext是BeanFactory的子接口,ApplicationContext功能更完善,
- BeanFactory采用的是延迟加载形式来注入bean的,只有在使用某个bean的时候调用getBean方法,才对该Bean进行加载实例化。这样我们不容易发现一些Spring的配置问题,如果Bean的某个属性没有注入,Bean Factory加载后,直至第一次调用getBean方法才会抛出异常
- 而ApplicationContext它是在容器启动时,一次性创建了所有的bean。这样在容器启动时我们就能发现Spring中存在的配置错误,这样有利于检查所依赖属性是否注入
- 相对于基本的BeanFactory,Application Context唯一的不足就是占用内存空间,当应用程序配置的Bean较多时,程序启动较慢
- 总结:BeanFactory的优点是,应用启动占用资源很少,对资源要求较高的应用,比较有优势,而ApplicationContext的优点是,所有Bean在启动时就加载好了,系统的运行速度快,能尽早的发现系统中的配置问题
Spring Bean的生命周期
- Spring 对 Bean进行实例化
- Spring将值和bean的引用注入到实例中
- Bean实现哪些接口就调用对应的方法
- bean准备就绪后就驻留在应用的上下文中,一直到容器销毁
- 调用bean实现的销毁接口,自定义的销毁方法销毁
- 总的来说就是就是加载到实例化到初始化最后到销毁的一个过程
Spring支持的几种bean的作用域
- singleton:默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
- prototype:为每一个bean请求提供一个实例。
- request:为每一个网络请求创建一个实例,在请求完成以后,bean会失效并被垃圾回收器回收。
- session:与request范围类似,确保每个session中有一个bean的实例,在session过期后,bean会随之失效。
- global-session:全局作用域,global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。
Spring框架中的单例Bean是线程安全的么?
Spring框架中都用到了哪些设计模式?
Spring事务的实现方式和原理以及隔离级别?
spring事务传播机制
spring事务什么时候会失效?
什么是bean的自动装配,有哪些方式?
- 开启自动装配,只需要在xml配置文件中定义“autowire”属性
- 自动装配有4种方式:byName – 根据bean的属性名称进行自动装配
- byType – 根据bean的类型进行自动装配
- constructor – 类似byTpye,不过是应用于构造器的参数,如果一个bean与构造器参数的类型相同,就自动进行装配,否则导致异常
- autodetect – 如果有默认的构造器,则通过constructor方式进行自动装配,否则使用byType方式进行自动装配
- @Autowired注解自动装配bean,可以在字段、setter方法、构造函数上使用。
四、springMVC和SpringBoot
SpringBoot、SpringMVC和 Spring有什么区别
- Spring是一个IOC容器,用来管理Bean,使用依赖注入实现控制反转,可以很方便的整合各种框架
- SpringMVC是Spring对web框架的一个解决方案,提供了一个总的前端控制器servlet,用来接收请求,然后定义了一套路由策略及适配执行handle,将handle结果使用视图解析技术生成视图展现给前端
- springboot是spring提供的一个快速开发工具包,让程序员能更方便、更快速的开发spring+springmvc应用,简化了配置,整合了一系列的解决方案,很多框架都可以开箱即用
SpringMVC的工作流程
- 用户发送请求到前端控制器DispatcherServlet
- 然后DispatcherServlet收到请求调用HandlerMapping处理器映射器
- 处理映射器根据xml配置和注解找到具体的处理器,生成处理器及处理器拦截器一并返回给DispatcherServlet。
- DispatcherServlet调用HandlerAdapter处理器适配器
- Handler Adapter经过适配调用具体的处理器(controller,也叫后端控制器)
- controller执行完成后返回ModelAndView
- HandlerAdapter将ModelAndView返回给DispatcherServlet
- DispatcherServlet将ModelAndView传给ViewReslover视图解析器
- ViewReslover解析后返回具体的View
- DispatcherServlet根据View进行渲染视图
- DisPatcherServlet将结果响应给用户
SpringMVC的主要组件
SpringBoot自动配置原理
- SpringBoot启动类上有个SpringBootApplication注解,这是个组合注解,里面有个@EnableAutoConfiguration注解,这个注解的意思是开启自动装配,同时他也是一个组合注解,他下面有一个@import和@AutoConfigurationPackage注解,@import注解是自动装配的核心,他将AutoConfigurationImportSelector这个类导到Spring容器中,这个类下有一个getCandidateConfigurations方法,在这个方法里调用了SpringFactoriesLoader类中的loadFactoryNames方法,这个方法会获取META-INF下面的spring.factories文件里的所有组件,最后SpringBoot启动时会执行selectImports方法找到所有的自动装配类并导入到Spring容器中
- 虽然SpringBoot启动时会加载全部的自动配置,但不是都会生效,最终会按照条件装配规则(@Conditional)实现按需加载
如何理解SpringBoot中的starter
- starter就是定义一个starter的jar包,写一个@Configuration配置类、将这些bean定义在里面,然后在starter包的META-INF/spring.factories中写入该配置类,springboot会按照约定来加载该配置类,开发人员只需要将相应的starter包依赖进应用,进行相应的属性配置(使用默认配置时,不需要配置),就可以直接进行代码开发,使用对应的功能了
- 比如使用Spring+SpringMVC的时候,如果需要引入mybatis框架,需要自己到xml文件里面去配置需要的bean,而使用SpringBoot的话只需要引入mybatis的Starter包就可以使用了
什么是嵌入式服务器?为什么要使用嵌入式服务器?
- SpringBoot内置的tomcat就是一个嵌入式的服务器,这样可以节省下载安装tomcat,应用也不需要打war包后再放到webapp目录下去运行了,只需要一个安装了java的虚拟机,就可以直接在上面部署项目了
五、Mybatis
mybatis的优缺点
mybatis与hibernate的区别
#{}和${}的区别是什么
- #是预编译处理,是占位符,$是字符串替换,是拼接符
- mybatis在处理#时,会将sql里的#{}替换为?号,而处理$时,就会直接把${}替换为变量的值
- 变量替换后,#{}对应的变量会自动加上单引号,而${}对应的变量不会加上单引号
- 使用#可以有效的防止sql注入,提高系统安全性
简述mybatis的插件运行原理,如何编写一个插件
六、mysql
索引的基本原理
索引用来快速地寻找那些具有特定值的记录。如果没有索引,一般来说执行查询时遍历整张表。
索引的原理:就是把无序的数据变成有序的查询
步骤:
先把创建了索引的列的内容进行排序
对排序结果生成倒排表
在倒排表内容上拼上数据地址链
在查询的时候,先拿到倒排表内容,再取出数据地址链,从而拿到具体数据
- Post title:java面试-22版
- Post author:周瑜
- Create time:2022-02-21 10:43:06
- Post link:https://xinblog.github.io/2022/02/21/面试题-22年版-md/
- Copyright Notice:All articles in this blog are licensed under BY-NC-SA unless stating additionally.