每日面试题
JDK、JRE、JVM的区别
- JDK,是Java开发工具包,用于开发Java程序,提供了编译和运行Java程序的各种工具和资源、类库等
- JRE,Java程序的运行环境,用于解释执行Java的字节码文件,想要运行Java程序必须要安装JRE
- JVM,Java虚拟机,Java程序跨平台的核心,负责解析和执行字节码文件
- JDK包含JRE,JRE包含JVM
重写和重载的区别
- 重载,发生在同一个类中,方法名相同,参数列表不一致,与返回值无关
- 重写,发生在父子类中,方法名和参数列表必须一致,返回值和抛出的异常要小于等于父类,访问修饰符要大于等于父类,父类中的
private
方法不能被子类重写
Java中的==和equals的区别
- ==
- 基本数据类型,比较值是否相等
- 引用数据类型,比较的地址是否相等
- equals
- 未重写前,比较的是地址值
- 重写后,按照重写的逻辑进行比较
String、StringBuffer、StringBuilder的区别
-
String
代表字符串,是一个final修饰的不可变类,一旦创建就不能修改。 -
StringBuilder
是内容可变的字符串容器,可以进行字符串动态增删,它可以提高字符串操作的效率。如果是多线程开发,会有线程安全问题。 -
StringBuffer
是内容可变的字符串容器,可以进行字符串动态增删,可以提高字符串操作的效率。它是线程安全的类,实现线程安全的方式是所有方法都加上synchronized,效率低,不推荐使用。
什么是单例模式,有实现几种?
程序运行中,同一个类的的实例只有一个,就是单例模式。
- 懒汉式
- 饿汉式
- 饿汉式 + 锁
- 饿汉式 + 双重锁
- 静态内部类实现单例
- 枚举实现单例
- 静态Map工厂实现单例
接口和抽象类的区别?
- 抽象类,需要被类继承。接口需要被类实现
- 接口中的变量只能是公共的静态常量,而抽象类中的变量则可以是普通变量
- 接口可以继承接口,可以多继承,而抽象类只能单继承
List和Map、Set的区别?
- List和Set都是单列集合,Map是双列集合
- List存储的元素是有序的,并且允许重复
- Set存储的元素是无序的,并且不允许重复,HashSet依靠对象的hashCode和equals方法来确定元素位置,而TreeSet依靠元素实现Comparable接口或Compartor比较器确定位置
- Map存储的元素是无序的,键是唯一的,不允许重复,而值是允许重复的
创建线程的方式有?
- 继承Thread类,重写run方法
- 实现Runnable接口,重写run方法。这种方式没有返回值,不允许抛出编译时异常
- 实现Callable接口,重写call方法。这种方式有返回值,允许抛出编译时异常
- 使用线程池创建线程
Ruanable和Callable的区别?
- Runnable接口的run方法没有返回值,Callable接口的call方法有返回值,并且支持泛型
- Runnable接口的run方法不能抛出编译时异常,必须捕获处理。而Callable接口的call方法允许抛出编译时异常
如何启动一个线程?调用start()和run()有什么区别?
- 直接调用run()方法,只是在当前线程调用了对象的普通方法,并不是开启线程,run()方法运行在调用方的线程中
- 调用start()方法,则是让jvm开启一个线程,然后在新开启的线程中调用run()方法,run()方法运行在新线程中
线程有哪几种状态?状态之间是怎么流转的?
-
NEW:新建
-
RUNABLE:可运行(就绪)
-
TERMINATED:结束
-
BLOCK:阻塞
-
WATING:无限等待
-
TIME_WATING:定时等待
-
线程Thread类对象被new后,就是
新建状态
-
线程对象调用start()后就流转为
可运行状态
,等待CPU调度 -
线程的run()方法执行完毕,线程就会流转为
结束状态
,然后线程被销毁 -
线程执行到同步代码块时
- 如果没有抢到锁,则进入
阻塞状态
- 如果抢到锁,则可以继续执行
- 如果没有抢到锁,则进入
-
在阻塞状态时,如果重新抢到锁,则回归到
可运行状态
-
可运行状态时,如果调用了wait()方法,则进入
无限等待状态
,如果调用了sleep(timeount)或wait(timeout)则进入定时等待状态
-
当另外一个线程调用了notify()或notifyAll(),并且重新抢到锁后,则回归到
可运行状态
-
如果是
定时等待状态
,重新抢到了锁,则自动回归到可运行状态
wait()和sleep()的区别?
-
来自不同的类
- wait()来自Object,sleep()来自Thread
-
关于锁的释放
- wait()在等待过程中会释放锁
- sleep()在等待过程中不会释放锁
-
关于使用范围
- wait()必须在同步代码块中调用
- sleep()可以在任何地方调用
-
是否需要捕获异常
- wait()不需要捕获异常
- sleep()需要捕获异常
常用线程池种类
- newCacheThreadPool,创建一个可进行缓存,可重用的线程池
- newFixThreadTool,创建一个固定线程的,可重用的线程池
- newSingleThreadExecutor,创建一个单工作线程的Executor,它使用无界队列,该线程池最多执行一个线程
- newSingleThreadScheduledExcutor,创建一个单工作线程的Executor,它可以延时执行任务或定期执行任务
- newScheduledThreadPool,创建一个线程池,它可延时执行任务或定期执行任务
- newWorkStealingPool,创建一个并行级别的线程池,并行级别决定了同一时刻最多有多少个线程在执行,如果不传并行级别参数,那么默认为当前系统的CPU核心数 * 2
线程池创建时的参数作用?以及执行流程
- corePoolSize:核心线程数
- maxPoolSize:最大线程数
- keepAliveTime:空闲线程的空闲时间
- unit:空闲时间的时间单位
- workQueue:工作队列,保存任务的阻塞队列
- threadFactory:线程工厂
- handler:饱和策略(也叫拒绝策略)
ArrayList和LinkedList的区别
- ArrayList和LinkedList都实现了List接口,都用于存储元素,并提供对元素增删查改的方法。
- ArrayList底层使用数组实现,它的查找速度很快,并且提供索引进行访问元素的方法,当在尾部插入或删除元素时,耗时时间是一致的。但如果在中间添加或删除元素时,ArrayList需要移动元素,就会比较耗时。
- LinkedList的底层使用链表实现,它的增删元素只需要改变前后元素的前指针和后指针,所以效率比较高,而查找时需要移动指针,相对效率会比较低。
- ArrayList的空间浪费体现在它需要在尾部预留一定容量空间。而LinkedList的空间浪费体现在每个元素都要有一个前指针和一个后指针。
数据库的四大特性
- 原子性:多条指令要么同时成功,要么同时失败
- 一致性:事务前和事务后,数据是一致的
- 隔离性:事务提交前,不会影响其他事务
- 持久性:事务提交后,就会永久存入磁盘
事务的隔离级别
- 读未提交
- 读已提交
- 可重复读
- 可串行化
MyBatis的#{}
和${}
有什么区别?
-
#{}
,会将内容替换为?号,使用预编译,使用PreparedStatement来执行SQL,可以防止SQL注入 -
${}
,是字符串拼接,会有SQL注入的风险
MyBatis的resultType和resultMap的区别?
- 如果数据库表的字段名和实体类的成员变量名一致,就可以使用resultType,MyBatis会自动映射查询结果到实体类中
- 如果不一致,那么则要使用resultMap,单独配置2者的映射关系
MyBatis常用动态SQL标签有哪些?有什么作用?
- if标签,用于动态SQL的多条件动态拼接,根据参数拼接上对应的条件
- where标签,用于where条件,当有条件时拼接上where关键字,没有条件时则不拼接,就可以不需要使用 where 1 = 1 来保证SQL语法正确
- foreach标签,用于遍历传入的集合或数组,将遍历的每一项拼接为一个字符串,例如在 IN 和 NOT IN 中使用
- sql和include标签,sql标签可以把SQL的公共片段抽取,例如查询的字段列信息。include标签用于在SQL中引入sql标签的SQL片段,复用SQL片段,减少重复代码
- set标签,用于update语句中,能够智能去掉最后一个条件的逗号
Get请求和Post请求的区别
- Get请求不安全,它的请求参数在请求行中,会显示在浏览器的地址栏,用户是可见的,而Post的请求参数在请求体中,不会显示在地址栏中,用户不可见,相对安全
- Get请求传输数据量小,由于不同的浏览器,限制URL的长度不同,因为限制大小也不同,但一般在18kb以内。而Post请求默认不限制大小,所以可以传输更多的数据
- Get请求限制数据必须为ASCII字符,而POST请求则支持整个ISO10646字符集
- Get请求没有请求体,而Post请求有请求体,所以Get请求的效率更高,form表单默认使用Get请求
- 总结:传输非敏感数据,数据量小,使用Get请求。传输敏感数据,数据量大,使用Post请求
Servlet生命周期
- Servlet生命周期,就是Web服务器创建Servlet对象到销毁的过程
- 生命周期方法有:init()初始化方法,service()处理请求方法,destroy销毁方法
- init()初始化方法,创建Servlet对象时调用,只会调用一次
- service()处理请求方法,每次请求该Servlet资源都会调用一次
- destroy()销毁方法,Web服务器关闭或重启时调用,只会调用一次
- Servlet对象创建的时机,在第一次访问该Servlet资源时创建,它使用单例模式,所以只会创建一次,节省内存
- 通过配置load-on-startup参数,就可以让Web服务器启动时,就自动创建该Servlet对象,提升用户的访问速度
请求转发和重定向的区别
- 请求转发只有1次请求,而重定向会有2次请求
- 请求转发不会改变浏览器地址栏地址,而重定向会改变地址栏地址
- 请求转发是服务器内部跳转,而重定向是浏览器跳转
- 请求转发只能跳转当前项目内的资源,重定向可以跳转任何资源,包括外部资源
- 请求转发可以共享Request请求域内的数据,而重定向不可以
什么是HTTP协议,有什么特点和优缺点?
- HTTP协议,就是HyperText Transfer Protocol 超文本传输协议,规定了浏览器和服务器之间数据传输的规则
- 特点:
- 基于TCP协议,面向连接,安全
- 基于请求-响应模型,一次请求只有一次响应
- 请求数据包括,请求行、请求头、请求体
- 响应数据报错,响应行、响应头、响应体
- HTTP是无状态的协议,对于事务处理没有记忆能力,每次请求、响应都是独立的
- 优点:速度快
- 缺点:多次请求间不能共享数据
Cookie和Session的区别?
- 存储问题不同
- Cookie存储在浏览器,Session存储在服务器
- 存储容量不同
- 单个Cookie保存的数据只能<=4kb,一个网站一般能保存20~50个Cookie,不同的浏览器也有区别
- Session没有上限,受限于服务器的内存
- 存储方式的数据类型不同
- Cookie只能存储字符串数据
- Session可以存储任何类型的数据
- 隐私策略不同
- Cookie对客户端是可见的,可能会出现篡改Cookie数据进行欺骗,所以它是不安全的
- Session因为是存储在服务器,不存在敏感信息泄露的风险
什么是Session的钝化和活化?
- 钝化是指服务器正常关闭后,Tomcat会自动将Session数据写入到磁盘,钝化要求Session保存的数据必须实现Serializable序列化接口
- 活化是再次启动服务器后,从磁盘中读取文件,加载数据到Session中
什么是Ajax,有什么优势?
- Ajax,就是Asynchronous JavaScript And XML,异步的JavaScript和XML
- 由前端实现异步请求服务端接口,进行通信交互
- 优势:通过异步非阻塞方式进行请求,用户不需要等待,提升用户体验
- 优化了浏览器和服务器之间的传输,减少了不必要的数据往返,减少了带宽占用,性能好(后端只返回数据,而不需要返回整个网页)
JavaWeb的三大组件及其作用
- Servlet:用于处理资源的请求和响应
- Filter:过滤器,用于拦截请求,以及统一操作每个请求的一些通用处理,例如权限控制、统一编码等
- Listener:监听器,用于监听ServletContext、Request、Session的创建和销毁,以及这3个域对象中数据变化,数据变化时进行额外的业务处理
JSP和Servlet的区别
相同点,JSP编译后,本质就是一个Servlet,由于JVM只能识别Java类,所以需要Web服务器将JSP编译为Servlet,当请求到来时,调用生命周期方法进行处理
不同点,JSP侧重于视图和展现数据,Servlet侧重于逻辑控制和获取数据
Spring的IOC、DI、AOP分别是什么?IOC和DI有什么关系?
- IOC是控制反转,将对象的创建,交给Spring容器,不再亲自new对象,而是Spring根据我们的配置文件生成对象,当我们需要对象时,再通过IOC容器进行获取
- DI是依赖注入,是运行过程中,对IOC中的对象的成员属性进行赋值
- AOP是面向切面编程,是将项目中非业务代码的抽取,进行最大程度的解耦,Spring的AOP使用JDK动态代理,或使用CGLIB进行动态代理
- IOC和DI的区别,IOC侧重于对象的创建上的解耦,主要是将对象创建交给Spring容器。而DI侧重于对象使用上的解耦,对象需要依赖哪些对象,向IOC容器进获取
Spring的Bean作用域有哪些?每种作用域是怎样的?
- Singleton,单例,是默认的作用域,IOC容器启动时就会创建该作用域的bean,每个容器只有一个对象
- prototype,多例,每次向IOC容器获取对象时,都会创建一个新的bean对象
- request,在web工程中使用,IOC容器会在每次Request请求时,创建bean对象,并设置到request域中,同一个request对象中共享一个bean对象,request结束中,bean就会销毁
- session,在web工程中使用,IOC容器在每次会话开启时,创建bean对象,并设置到session域中,同一个session会话中共享一个bean对象,会话结束后,bean对象就会销毁
- global-session,在web工程中使用,在多台web服务器中,所有的session会话共享一个bean实例
Spring的对象默认是单例还是多例?单例bean存在线程安全问题吗?
- Spring的bean的默认作用域是单例的,可以设置bean对象的scope为prototype则为多例
- 在多线程的情况下,操作单例bean的成员属性,会有线程安全问题
- 解决方案是避免在单例bean中定义成员变量,如果无法避免,则需要将成员属性设置到ThreadLocal中
MyBatis编程步骤是怎样的?
- 导入MyBatis的依赖
- 编写Mapper接口
- 编写Mapper映射XML文件
- 执行MyBatis的操作
- 创建SqlSessionFactory
- 通过该工厂类创建SqlSession
- 通过SqlSession的getMapper,创建Mapper接口的代理类,并执行数据库操作
- SqlSession提交事务
- 关闭SqlSession,释放资源
谈谈你对MyBatis的缓存机制的理解
- MyBatis有2级缓存
- 一级缓存是SqlSession级别的,默认开启
- 二级缓存是Mapper级别的,默认不开启,需要手动开启
- 一级缓存,是SqlSession级别的,一个SqlSession范围内共享该缓存,第一次查询时会将查询结果缓存,第二个查询直接返回缓存。当进行增、删、改、提交事务时,就会清空一级缓存
- 二级缓存,是Mapper级别的,多个SqlSession都可以共享该缓存,要使用二级缓存,需要做2个步骤
- 实体类实现Serializable序列化接口
- Mapper.xml中添加
<cache>
标签,才能开启二级缓存
- 当sqlSession执行提交或关闭时,写入缓存
- 执行增、删、改操作时,清空二级缓存
Spring中@Autowired和@Resource的区别
-
@Autowired
是Spring提供的,而@Resource
是javax包下的 -
@Autowired
默认按类型匹配,而@Resource
默认按名称匹配 -
@Qualifier
需要和@Autowired
一起使用,而@Resource
可以单独使用 - Java9及其以上版本,
@Resource
已被删除,所以推荐使用@Qualifier
和@Autowired
Spring的事务传播行为
-
spring事务的传播行为说的是,当多个事务同时存在的时候,Spring如何处理这些事务的行为。备注(方便记忆): propagation传播
-
require必须的/support支持/mandatory 强制托管/requires-new 需要新建/ not -supported不支持/never从不/nested嵌套的
-
PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,该设置是最常用的设置。
-
PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。
-
PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。
-
PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。
-
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
-
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
-
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。
Spring的常用注解
-
IOC注解
- @Component(任何层) @Controller @Service @Repository(dao): 用于实例化对象
- @Scope : 设置Spring对象的作用域
- @PostConstruct、@PreDestroy : 用于设置Spring创建对象在对象创建之后和销毁之前要执行的方法
- @Bean: 表在方法上,用于将方法的返回值对象放入容器
-
DI注解
- @Value: 简单属性的依赖注入
- @Autowired: 对象属性的依赖注入
- @Qualifier: 要和@Autowired联合使用,代表在按照类型匹配的基础上,再按照名称匹配。
- @Resource 按照类型和属性名称依赖注入 @Resource =@Autowired+@Qualifier
- @ComponentScan: 组件扫描
-
AOP注解
- @Before 前置通知,会在运行原有方法前面执行
- @AfterReturning 后置通知,会在运行原有方法后面执行,前提原有方法不发生异常。
- @AfterThrowing 异常通知,会在运行原有方法发生异常的时候运行
- @After 最终通知,会在运行原有方法后运行, 无论原有方法是否发生异常都会运行
- @Around 环绕通知,一个环绕就可以实现上面4个位置的增强
- @Aspect 标识当前类为切面类
- @Pointcut切入点表达式
-
事务注解
- @Transactional 此注解可以标在类上,也可以标在方法上,表示当前类中的方法具有事务管理功能。
-
其他配置
- @PropertySource: 用于引入其它的properties配置文件
- @Import: 在一个配置类中导入其它配置类的内容
- @Configuration: 被此注解标注的类,会被Spring认为是配置类。Spring在启动的时候会自动扫描并加载所有配置类,然后将配置类中bean放入容器
Spring事务的实现方式和实现原理
- Spring事务的本质其实就是
数据库对事务
的支持,没有数据库的事务支持,Spring是无法提供事务功能的。 - Spring事务会调用数据库设置
手动控制
事务set autocommit = 0
,之后通过commit
提交和ro11back
回滚,数据库底层是通过binlog
或者redolog
实现的。 - Spring事务实现主要有两种方法
-
编程式
(编码控制事务),使用Spring框架提供的事务管理器模板TransactionTemplate
相关的方法实现事务控制,会造成代码重复几余。 -
声明式
,利用注解@Transactiona
或者aop
配置
-
Spring中@Autowired和@Resource的区别
-
@Autowired
是Spring框架的,默认按照byType
自动装配,@Resource
是javax包下的和jdk8及以下版本存在,默认byName
自动装配 -
@Autowired
和@Qualifier
一起用可以自定义别名注入,@Resource
可以单独使用
Spring的Bean生命周期
- Spring的Bean生命周期,就是Bean的创建到销毁的过程
- 初始化容器阶段
- 创建Bean对象(内存分配),执行构造方法
- 执行属性注入(set方法、依赖注入)
- 执行Bean的初始化方法
- 使用Bean
- 执行业务操作
- 关闭容器
- 执行Bean销毁方法
SpringMVC中拦截器的使用步骤?
- 拦截器,可以拦截器的方法,可以做一些通用增强的功能
- 定义拦截器类
- SpringMVC为我们提供了拦截器规范的接口,创建一个类实现
HandTerInterceptor
,重写接口中的抽象方法 -
preHandle
方法:在调用处理器之前调用该方法,如果该方法返回true则请求继续向下进行,否则请求不会继续向下进行,控制器也不会调用 -
afterCompletion
方法:在前端控制器渲染页面,完成之后调用此方法
SpringMVC的有哪些主要组件
- 前端控制器
Dispatcherservlet
:接收请求、响应结果,相当于转发器,有了Dispatcherservlet
就减少了其它组件之间的耦合度 - 处理器映射器
HandlerMapping
:根据请求的URL来查找Handler
- 处理器适配器
HandlerAdapter
:负责执行Handler
- 处理器
Handler
:处理业务逻辑的Java类(Contro1ler
类) - 视图解析器
viewResolver
:进行视图的解析,根据视图逻辑名将ModeTAndview
解析成真正的视图 (view) 并跳转到视图页面
SpringMVC和SpringBoot的关系
- SpringMVC,提供了一种轻度耦合的方式来进行Web开发,它是Spring的一个模块,是一个Web层的框架
- SpringBoot,实现了自动配置,降低了Spring项目搭建的复杂度
- SpringBoot只是辅助简化Spring项目的搭建过程的,如果搭建的是Web项目,Web层采用SpringMVC,那么SpringMVC的工作原理还是跟原来一样,并没有因为使用SpringBoot而改变
SpringMVC各个组件的执行流程
- 用户发送请求到前端控制器
DispatchServlet
- 前端控制器
DispatchServlet
收到请求后,调用处理器映射器HandlerMapping
,进行查找处理器Handler
- 处理器映射器
HandlerMapping
,根据URL找到具体的处理器Handler
,以及对应的拦截器HandlerIntercepter
,将它们一起返回给前端控制器DispatchServlet
- 前端控制器
DispatchServlet
,调用处理器适配器HandlerAdapter
,进行处理 - 处理器适配器
HandlerAdapter
则调用处理器Handler
(也就是Controller)进行处理,首先将请求参数映射到处理器的方法参数上,然后调用处理方法进行处理,以及返回结果(ModelAndView
模型视图对象),交回给前端控制器DispatchServlet
- 前端控制器
DispatchServlet
则将ModelAndView
模型视图对象,交给视图解析器ViewReslover
,视图解析器则会解析视图地址
,进行请求转发
跳转到视图页面,使用视图数据进行渲染视图,最后再渲染结果交回给前端控制器DispatchServlet
- 前端控制器
DispatchServlet
,将渲染结果返回给浏览器 - 如果是
异步请求
,返回JSON数据
,那么则不需要进行视图解析,直接将Json字符串
数据返回给浏览器
SpringMVC的常用注解
-
@RequestMapping
,用于处理所有请求类型的Url映射的注解。可用于类上或方法上- 用于类上时,表示该类的所有方法都是以该地址作为父路径
- 用于方法上时,表示该方法能处理的资源路径
-
@RequestMapping
还衍生出4个常用的注解-
@GetMapping
,该注解表示处理Get
请求 -
@PutMapping
,该注解表示处理Put
请求 -
@DeleeteMapping
,该注解表示处理Delete
请求 -
@PostMapping
,该注解表示处理Post
请求
-
-
@RequestBody
,该注解表示接收Http请求传递的json格式的请求体,并将json格式请求体转换为Java对象 -
@ResponseBody
,该注解表示该controller方法返回的对象,会转换为json字符串返回给客户端 -
@PathVariable
,该注解表示方法绑定的url中的占位符参数到指定的方法参数,通过@Pathvariable("要获取的参数名")
来指定 -
@RequestParam
,该注解表示方法参数接收Http请求的参数之前,做一些限制- value属性,指定该方法参数接收哪一个请求参数
- required属性,指定该方法参数对应的请求参数是否必传
- defaultValue属性,当该方法参数对应的请求参数未传时,参数的默认值
-
@ControllerAdvice
,用于在类上,表示该类是一个全局异常处理器类 -
@ExceptionHandler(Exception.class)
,用于异常处理器的方法上,表示该方法能处理的异常类型