Java面试题大全
时间:2022-08-11 09:30:02
一.JavaSE 部分
基础部分
Java基本数据有哪些类型?
byte:8位,最大存储数据量为255,存储数据范围为-128~127之间。
short:16位,
int:32位,最大数据存储容量为2的32次减1,数据范围为负2的31次减1。
long:64位,最大数据存储容量为2的64次减1,数据范围为负的63次减1。
float:32位,
double:64位,
boolean:只有true和false两个取值。
char:16位,存储Unicode代码,用单引号赋值。
Integer 和 int的区别
int它是一种直接存储在变量中的基本数据类型化时值是0
Integer是引用数据类型,对象的引用存储在变量中,变量初始时值null
Integer是int包装类型,将int封装成Integer,符合java面向对象的特征可以使用各种方法,如与其他数据类型的转换
Integer和int深度对比:
- 两个通过new生成的Integer由于堆中地址不同,对象永远不会相等
- int和Integer相比之下,只要数值相等,结果就会相等,因为当比较包装类和基本数据类型时,箱子会自动拆卸,Integer转化为int
- 通过new生成的Integer对象和非通过new生成的Integer与对象相比,由于前者存放在堆中,后者存放在堆中Java所以常量池永远不会相等
- 两个非通过new生成的Integer对象比较时,如果两个变量的值相等,且在-128-127之间,则结果相等。这是因为给Integer对象赋一个int值,java静态方法将在编译过程中自动调用valueOf(),根据java api中对Integer类型的valueOf对于-128到127之间的整数,将进行缓存,如果下次赋予相同的值将直接从缓存中取出,即享元模式
String和StringBuilder和StringBuffer区别
三者底层都是char[]存储数据,JDK1.9后使用byte[] ,因为我们经常使用短字符串来存储它们byte这样可以节省更多的空间。
由于String底层的char[]有final所以每次都是对的String所有的操作都会在内存中开辟空间,生成新的对象,所以String不可变
StringBuilder和StringBuffer是可变字符串,没有final此外,修改适用于字符串拼接StringBuffer线程安全,方法有synchronized但性能较低,StringBuilder线程不安全,方法不安全synchronized性能,性能高
String a = "A" 和 String a = new String("A") 创建字符串的区别
String c = "A" 首先常量池找 “A,如果有,a将指向对象的地址 ,如果没有,在栈中创建char型的值'A在堆中创造一个String对象object,值为"A",接着object它将在字符串常量池中,最后将a指向对象的地址
new String("A") : 如果常量池中有A将首先创建上述相同的流程A然后在堆里创造一个String对象,它现有的值共享栈char值“A”。
以下代码创建了几个对象
- String s = "a" "b" "c" "d";这句话创造了多少对象?
创建了一个对象,因为相对于字符串常量相加的表达式,编译器会在编译期间进行优化,直接将其编译成常量相加的结果。
- String s; 创建几个对象?没有创建对象。
- String a = "abc"; String b = "abc"; 创建了几个对象
创建了一个对象,只是在第一条语句中创建了一个对象,a和b都指向相同的对象"abc",引用不是对象
== 和 equals 的区别是什么
== 比较对象比较的是地址,对于Object对象中的equals 方法使用的也是 == ,比较的是对象的地址,默认情况下使用对象的equals比较Object中的equals方法,也就是比较地址,如果要实现自己的比较方式需要复写equals 方法。
对于包装类比如:Integer都是复写过equals方法,比较的是int 值。
final 和 finally 和 finalize 的区别
当用final修饰类的时,表明该类不能被其他类所继承。当我们需要让一个类永远不被继承,此时就可以用final修饰
finally作为异常处理的一部分,它只能用在try/catch语句中,并且附带一个语句块,表示这段语句最终一定会被执行(不管有没有抛出异常),经常被用在需要释放资源的情况下
finalize()是在java.lang.Object里定义的,也就是说每一个对象都有这么个方法。这个方法在gc启动,该对象被回收的时候被调用。其实gc可以回收大部分的对象(凡是new出来的对象,gc都能搞定,一般情况下我们又不会用new以外的方式去创建对象),所以一般是不需要程序员去实现finalize的。
JDK 和 JRE 有什么区别?
JRE(Java Runtime Enviroment) :是Java的运行环境,JRE是运行Java程序所必须环境的集合,包含JVM标准实现及 Java核心类库
JDK(Java Development Kit) :是Java开发工具包,它提供了Java的开发环境(提供了编译器javac等工具,用于将java文件编译为class文件)和运行环境(提 供了JVM和Runtime辅助包,用于解析class文件使其得到运行)。JDK是整个Java的核心,包括了Java运行环境(JRE),一堆Java工具tools.jar和Java标准类库 (rt.jar)。
面向对象四大特性
抽象 : 是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面,抽象只关注对象的哪些属性和行为,并不关注这此行为的细节是什么 - 举例:定义一个persion类,了就是对人这种事物的抽象
封装:对数据的访问只能通过已定义的接口,封装就是隐藏一切可隐藏的东西,只向外界提供最简单的编程接口,比如在Java中,把不需要暴露的内容和实现细节隐藏起来,或者private修饰,然后提供专门的访问方法,如JavaBean。 - 生活举例:电脑主机就是把主板等封装到机壳,提供USB接口,网卡接口,电源接口等。 JavaBean就是一种封装。
继承:新类(子类,派生类)继承了原始类的特性,子类可以从它的父类哪里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。
多态:多态是指允许不同类的对象对同一消息做出响应。对象的多种形态,当编译时类型和运行时类型不一样,就是多态,意义在于屏蔽子类差异
方法覆盖和重载
方法的覆盖是子类和父类之间的关系,方法的重载是同一个类中方法之间的关系。覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。覆盖要求参数列表相同;重载要求参数列表不同。
普通类和抽象类
抽象类不能被实例化, 需要通过子类实例化抽象类可以有构造函数,被继承时子类必须继承父类一个构造方法,抽象方法不能被声明为静态。抽象方法只需申明,而无需实现,抽象类中可以允许普通方法有主体含有抽象方法的类必须申明为抽象类抽象的子类必须实现抽象类中所有抽象方法,否则这个子类也是抽象类
接口和抽象类
定义接口使用interface,定义抽象类使用abstract class
接口由全局常量,抽象方法,(java8后:静态方法,默认方法)
抽象类由构造方法,抽象方法,普通方法
接口和类是实现关系,抽象类和类是继承关系
IO流
你知道BIO,NIO,AIO么?讲一下你的理解
BIO (Blocking I/O):同步阻塞I/O 模式,以流的方式处理数据,数据的读取写入必须阻塞在一个线程内等待其完成。适用于连接数目比较小且固定的架构
NIO (New I/O):同时支持阻塞与非阻塞模式,以块的方式处理数据,适用于连接数目多且连接比较短(轻操作)的架构,比如聊天器
AIO ( Asynchronous I/O):异步非阻塞I/O 模型,适用于连接数目多且连接比较长(重操作)的架构
java 中四大基础流
InputStream : 输入字节流, 也就是说它既属于输入流, 也属于字节流 ,
OutputStream: 输出字节流, 既属于输出流, 也属于字节流
Reader: 输入字符流, 既属于输入流, 又属于字符流
Writer: 输出字符流, 既属于输出流, 又属于字符流
读文本用什么流,读图片用什么流
文本用字符输入流,读图片用字节输入流
字符流和字节流有什么区别
字符流适用于读文本,字节流适用于读图片,视频,文件等。
字节流操作的基本单元为字节;字符流操作的基本单元为Unicode码元。
字节流默认不使用缓冲区;字符流使用缓冲区。
字节流通常用于处理二进制数据,实际上它可以处理任意类型的数据,但它不支持直接写入或读取Unicode码元;字符流通常处理文本数据,它支持写入及读取Unicode码元
BufferedInputStream 用到什么设计模式
主要运用了俩个设计模式,适配器和装饰者模式
带缓冲区的流
BufferedInputStream 带缓冲区的字节输入
BufferedOutputStream 带缓冲区的输出流
BufferedReader : 带缓冲区的字符输入流
BufferedWriter : 带缓冲区的字符输出流
怎么实现一张图片拷贝
需要一个FileInputStream指向读取的文件,然后把它包装到BufferInputStream,使用BufferInputStream#read方法去读byte[],然后创建一个FileOutputStream指向输出文件,然后把它包装到BufferOutputStream,使用BufferOutputStream#write方法写byte[]到另外一个文件
怎么实现文本拷贝
和文件拷贝思路一样,只不过读的时候需要使用BufferedReader和FileReader,使用readline来读 , 写的时候需要BufferedWriter和 FileWriter,用wite来写
集合篇
List和Set和Map的区别
- List:一个有序(元素存入集合的顺序和取出的顺序一致)容器,元素可以重复,可以插入多个null元素,元素都有索引。常用的实现类有 ArrayList、LinkedList 和 Vector。
- Set:一个无序(存入和取出顺序有可能不一致)容器,不可以存储重复元素,只允许存入一个null元素,必须保证元素唯一性。Set 接口常用实现类是 HashSet、LinkedHashSet 以及 TreeSet。
- Map是一个键值对集合,存储键、值和之间的映射。 Key无序,唯一;value 不要求有序,允许重复。Map没有继承于Collection接口,从Map集合中检索元素时,只要给出键对象,就会返回对应的值对象。
Map 的常用实现类:HashMap、TreeMap、HashTable、LinkedHashMap、ConcurrentHashMap
说一下Java中的集合体系
Collection接口
List:
- ArrayList:底层数据结构是数组,查询性能高,增删性能低
- Vector:底层数据结构是数组,查询性能高,增删性能低
- LinkedList:底层数据结构是双向链表,查询性能低,增删性能高
Set:
- HashSet:无序不重复的,使用HashMap的key存储元素,判断重复依据是hashCode()和equals()
- TreeSet:有序不重复的,底层使用TreeMap的key存储元素,排序方式分为自然排序,比较器排序
Map接口
- HashMap:key的值没有顺序,线程不安
- TreeMap:key的值可以自然排序,线程不安全
- HashTable:它的key和value都不允许为null,线程安全
- Properties:它的key和value都是String类型的,线程安全
HashMap和HashTable的区别
HashMap和HashTable都是实现了Map接口的集合框架,他们的区别
- HashTable是线程安全的,它的实现方法都加了synchronized关键字,因此它的性能较低
- HashMap是线程不安全的,它实现方法没有加synchronized,因此它的性能较高
- HashMap的key和value都允许为null,HashTable中的key和value都不能为null,如果不考虑线程安全,建议使用HashMap,如果需要考虑线程安全的高并发实现,建议使用ConcurrentHashMap
ArrayList和LinkedList区别
都属于线性结构,ArrayList是基于数组实现的,开辟的内存空间要求联系,可以根据索引随机访问元素性能高,但是插入和删除元素性能差,因为这会涉及到移位操作
LinkedList是基于双链表实现的,开配的内存空间不要求连续,因此不支持索引,查找元素需要从头查找,因此性能差,但是添加删除只需要改变指针指向即可,性能高. LinkedList会增加内存碎片化,增加内存管理难度
根据实际需要,如果项目中使用查找较多,使用ArrayList,如果使用增删较多,请使用LinkedList
ArrayList和Vector区别
ArrayList是线程不安全的,Vector相反是线程安全的,方法加了同步锁,线程安全但是性能差,ArrayList底层数组容量不足时,会自动扩容0.5倍,Vector会自动扩容1倍
一个User的List集合,如何实现根据年龄排序
第一种方式,让User类实现Comparable接口,覆写compareTo方法,方法中自定义根据年龄比较的算法
第二种方式,调用Collections.sort方法,传入一个比较器,覆写compare方法,方法中自定义根据年龄比较的算法
哪些集合类是线程安全的?
- Vector:就比Arraylist多了个 synchronized (线程安全),因为效率较低,现在已经不太建议使用。
- hashTable:就比hashMap多了个synchronized (线程安全),不建议使用。
- ConcurrentHashMap:它是HashMap的线程安全,支持高并发的版本
在jdk1.7中,它是通过分段锁的方式来实现线程安全的。意思是将哈希表分成许多片段Segment,而Segment本质是一个可重入的互斥锁,所以叫做分段锁。
在jdk1.8中,它是采用了CAS操作和synchronized来实现的,而且每个Node节点的value和next都用了volatile关键字修饰,保证了可见性
二.JavaEE&框架&中间件
数据库基础
平局值用什么,分组用什么
统计平局值:avg , 分组:group by
两个相同列的结果集求并集用什么
union 并集 , union all(允许重复并集)
完整查询SQL中的关键字的定义顺序
SELECT 列名 FROM 表1 JOIN 表2 ON 条件 WHERE 条件 GROUP BY 列名 HAVING 条件 ORDER BY 列名 LIMIT
完整的多表JOIN查询,SQL中关键字的执行顺序
FROM --> ON --> JOIN --> WHERE --> GROUP BY --> HAVING --> ORDER BY --> LIMIT
SQL面试题
员工表employee字段有: id, username, amount ,deptname .
- 求每个部门总人数怎么做 ,
select 部门名,count(id) from employee group by deptname
- 求每个部门总工资怎么做?
select 部门名,sum(amount) from employee group by deptname
Javaweb基础
常见Http状态码
200 成功返回状态
301 永久重定向,被请求的资源永久移动到新位置
302 临时重定向,被请求的资源临时移动到新的位置,项目中使用了oauth2,对目标资源访问无权限时就会见到,它是会重定向到授权地址
401 无权限访问
403 禁止访问,服务器已经接收到请求,但拒绝执行
404 找不到该资源
500 服务器内部错误 zuul找不到服务名就会见到
503 服务器内部错误 服务器维护或者过载
504 网关超时
Servlet的生命周期
Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:
- Servlet 初始化后调用 init () 方法。
- Servlet 调用 service() 方法来处理客户端的请求。
- Servlet 销毁前调用 destroy() 方法。
- 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。
什么是过滤器?怎么创建一个过滤器
过滤器:在请求发送之后,处理之前对请求的一次拦截,可以更改请求状态或者参数值等。
创建过滤器:实现filter接口,重写doFilter方法,最后在web.xml中配置过滤器
讲一下Session的工作原理
服务端的session id会自动写入客户端的cookie中,每次请求客户端回自动把cookie带入后台,后台自动根据cookie中的sessionid就能找到session
Session和cookie有什么区别
session和cookie都是为了弥补http协议的无状态特性,解决会话问题
session是以ConcurrentHashMap结构存储在服务器端,同时生成一个sessionid返回客户端并存放到cookie中
cookie是将数据存储在客户浏览器端
session占用服务器的性能,但安全性较高,使用cookie减轻服务器的压力,但有被用户篡改风险因此安全性较低
说说preparedStatement和Statement的区别
statement的sql语句使用字符串拼接,很容易出错,而preparedStatement使用?作为占位符,不容易出错易于维护
statement不对sql语句作处理,直接交给数据库,而preparedStatement支持预编译,事先将编译好的sql语句放到数据库端,相当于缓存,因此效率更高
statement有sql注入风险,preparedStatement没有sql注入风险
请求转发和重定向的区别
转发是一次请求,可以共享同一组request和response,重定向是多次请求,不能共享同一组request和response
转发地址栏不会发生变化,重定向地址栏会发生变化
转发不能到外部应用,重定向可以到尾部应用
如果我们需要数据共享,使用转发,如果需要访问内部资源(WEB-INF),使用转发,如果需要跨域到外部资源,必须使用重定向
get和post请求的区别
最直观的区别,get把参数包含在url中,post是把参数放到request body中
post相对于get更安全,post发送的数据更大,get有url的长度限制
post更发送更多的数据类型,get只能发送ASCII字符
在restful中,get一般用户查询搜索数据,post一般用户添加或者修改数据
JSP的原理
jsp的本质就是servlet,每个JSP文件都回被编译成一个Serverlet去执行,在该Serverlet会对JSP中的动态内容进行替换,静态部分是标准的html,动态部分是java程序
Spring部分
介绍一下Spring
Spring是一个开源的轻量级控制反转和面向切面编程的容器框架。轻量级是说它开发使用简单,功能强大。控制反转是指将对象的创建,销毁控制交给ioc容器,方便解耦合,降低维护难度,面向切面编程是指将相同的逻辑横向抽取出来,可以对一些通用业务如事务,日志进行集中管理
说下Spring框架的组成
- CoreContain核心容器模块:
- spring-core:提供框架的基本组成部分,包括 IoC 和依赖注入功能
- spring-beans:提供 BeanFactory,工厂模式
- context:提供国际化,事件传播,资源加载等功能
- spring-ExpressionLanguage:提供表达式语言
- Web模块
- Web:提供面向web的基本功能和面向web的应用上下文
- Web-MVC:为web应用提供模型视图控制(MVC)
- Web-Socket:在 web 应用程序中提供客户端和服务器端之间通信的方式
- Web-Portlet:模块提供了用于Portlet环境的MVC实现
- 数据/集成模块
- JDBC:包含了Spring对JDBC数据访问进行封装的所有类
- ORM:为对象-关系映射提供交互层
- OXM:提供对Object/XML映射实现的抽象层
- JMS:主要包含了一些制造、消费和消息的功能
- Transaction:为实现特殊接口类以及所有的 POJO 支持编程式和声明式的事务管理
- 其他模块
- AOP:提供了面向切面编程相关实现
- Aspects:模块提供了与AspectJ的集成,是一个功能强大的AOP框架
- Instrumentation:提供了class instrumentation 的支持和类加载器classloader的实现
- Messaging:为 STOMP 提供支持
- Test:支持使用JUnit和TestNG对Spring组件进行测试
什么是Spirng的IOC
IOC控制反转,把对象的创建,属性设置,初始化,销毁等工作交给Spirng的IOC容器去管理,解放程序员的劳动力。
对象被注册到Spring的IOC容器中,使用的时候从容器中获取即可,非常方便。
它通过依赖注入,将需要的外部资源注入到组件中,使用IOC使得对象之间的耦合度降低,资源变得容易管理,从而使得代码更加优雅
你对AOP的理解
AOP,Aspect Oriented Programming 英文首字母缩写,意为面向切面编程,是Spring的核心思想之一
AOP是对OOP(面向对象编程)的一种补充,能够做到很多面向对象无法做到的事情,比如需要在所有方法执行前开启事务,打印日志,如果使用面向对象来编程,将会产生大量重复代码,而使用AOP,可以将那些与业务无关,却为业务模块所共同调用的逻辑封装起来,一次解决这些问题。而这些重复的代码,一般统称为横切逻辑代码
使用AOP,在不改变原有业务逻辑的情况下,实现解耦合,避免横切逻辑代码重复
AOP的使用场景包括日志记录,性能统计,安全控制,事务处理,异常处理等等
AOP的原理(使用什么设计模式)
它是基于动态代理实现的,分为JDK动态代理和CGLIB动态代理。JDK动态代理只支持实现了接口的类 ,CGLIB支持没有实现接口的类。Spring默认使用JDK动态代理,如果被代理类没有实现接口,会选择CGLIB动态代理
另外Spring的AOP还用到了执行链模式。
Spring的Bean懒加载和非懒加载有什么区别
懒加载:需要使用对象的时候才创建,节省资源,但不利于提前发现错误
非懒加载,也叫迫切加载,容器启动时就创建对象,消耗资源,但有利于提前发现错误
spring中默认时迫切加载,即在项目启动时,spring会扫描符合条件的所有bean并将其初始化
如果需要懒加载,可以使用@Lazy注释或者xml中配置属性default-lazy-init="true"
Spring的依赖注入方式有哪些
方式一:setter方式注入,通过反射调用无参构造方法生成对象,再通过对于的setter方法注入配置的值,支持注解和xml两种实现方式
方式二:构造器方式注入,通过反射调用有参构造方法生成对象,支持注解和xml两种实现方式
注解实现方式:@Autowired,它是默认按类型匹配的、@Resource,它是默认按名字匹配的
说一下定义切面相关的注解
@Aspect:定义切面
@Pointcut:定义切点 = cn.xx.service.*
@Before:前置通知,在目标方法运行之前运行
@After:后置通知,在目标方法运行结束之后运行(无论方法正常结束还是异常结束)
@AfterReturning:返回通知,在目标方法正常返回之后运行
@AfterThrowing:异常通知,在目标方法出现异常以后运行
@Around:动态代理,手动推进目标方法运行
Bean的四种注册方式
方式一:普通注册方式,直接通过class注册
方式二:简单静态工厂方式注册
方式三:简单实例工厂方式注册
方式四:FactoryBean方式注册
注册Bean的注解有哪些
@Controller/@RestController 一般用于定义控制层的类
@Service 一般用于定义服务层的类
@Repository 一般用于定义持久层类
@Component 定义一般类
@Configuration 定义配置类
单例多例的区别
单例和多例属于对象模式,单例模式指对象在整个系统中只存在一份,多例模式则可以有多个实例。
在spring的ioc容器中的bean默认都是单例的,如果需要使用多例,可以通过修改scope属性:scope="prototype"
如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个ConcurrentHashMap中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。
如果是多例(prototype)模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。
Bean被指定为prototype以及singleton有什么区别
这两者分别指的是多例和单例模式,singleton即单例模式,指对象在整个系统中只存在一份;prototype即多例模式系统中可以有多个实例。
如果一个bean是单例模式的,在处理多次请求的时候,在ioc容器中只实例化一个bean,这个对象会被保存在一个ConcurrentHashMap中,当有请求来的时候,会先从map中查看,如果有就直接使用这个对象,没有才会实例化新的对象。
如果是多例模式的bean,每次请求来的时候,会直接实例化新的bean,没有map缓存的过程。
在spring的ioc容器中的bean默认都是单例的,如果需要使用多例,可以指定scope属性:scope="prototype"
BeanFactory和ApplicationContext有什么区别
BeanFactory接口是IOC容器的核心接口,定义了管理bean的最基本方法,比如实例化,配置,管理,获取bean的方法
ApplicationContext接口是BeanFactory接口的子接口,除了继承BeanFactory中所有管理bean的方法,还拥有环境、国际化、资源、事件等服务相关的接口
BeanFactory是延迟加载,ApplicationContext是迫切加载
Spring事务传播行为(机制)
事务传播行为指的是一个方法调用另外一个方法事务是怎么进行传递的在Spring中规定了7种类型的事务传播行为
PROPAGATION_REQUIRED |
如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。这是最常见的选择。- 默认 |
PROPAGATION_SUPPORTS |
支持当前事务,如果当前没有事务,就以非事务方式执行。 |
PROPAGATION_MANDATORY |
使用当前的事务,如果当前没有事务,就抛出异常。 |
PROPAGATION_REQUIRES_NEW |
新建事务,如果当前存在事务,把当前事务挂起。 |
PROPAGATION_NOT_SUPPORTED |
以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。 |
PROPAGATION_NEVER |
以非事务方式执行,如果当前存在事务,则抛出异常。 |
PROPAGATION_NESTED |
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作 |
事务失效的场景
- 未启用spring事务管理功能
- 方法不是public类型的
- 数据源未配置事务管理器
- 自身调用问题,比如:入口方法A没有事务,在方法内部调用该类中的B方法,B方法的事务会失效,因为入口方法没有事务就是在非代理情况下执行,那么B方法也会非代理执行。
- 异常类型错误
- 异常被吞了
- 业务和spring事务代码必须在一个线程中
SpringMVC部分
SpringMVC怎么样设定重定向和转发的
重定向是指将用户从当前请求重新定向到一个视图页面,或者是一个handler处理请求,以前的request域中信息全部失效,同时地址栏会发生变化,它是客户端行为
转发是指将用户从当前请求转发给另一个视图页面或者handler处理请求,以前的request域可以共享,地址栏不会发生变化,它是服务器行为
springmvc默认是使用转发方式跳转的,且会默认经过视图解析器,我们也可以通过指定,转发时在返回值前面加"forward:",重定向时在返回值前面加"redirect:",且此时就不会再经过视图解析器了
SpringMVC如何对时间格式的参数进行格式化
第一种需求,后台接收前台页面返回的string类型时间,要转换成的Date类型数据,可以使用@DateTimeFormat注解来接收参数
第二种需求,后台将Date类型数据返回给前台页面,默认是返回时间戳,如果想要优雅的格式,可以在模型的Date字段或get方法上使用@JsonFormat注解
SpringMVC常用的注解有哪些
@Controller:用来标识一个类是控制器类
@RequestMapping:用来映射请求路径和参数
@ResponseBody:将返回值放到responsebody中,通常返回json或者xml格式数据
@RequestBody:将前台请求参数转换成对象
@PathVariable:接收路径参数,通常用在restful接口中
@RestController:@Controller和@ResponseBody的组合注解
@ControllerAdvice:运用aop的思想,对全局做一些处理,比如结合@ExceptionHandler做全局异常捕获
如何定义SpringMVC的拦截器
SpringMVC 的拦截器主要用于拦截用户的请求并做相应的处理,通常应用在权限验证、判断登录等功能上
第1步,定义拦截器:可以实现 HandlerInterceptor 接口来自定义拦截器,接口定义了三个方法,preHandler方法是在请求到达处理器之前执行,postHandler方法是在请求经过处理器之后、解析试图之前执行,afterCompletion方法是在视图渲染之后、返回客户端之前执行
第2步,配置拦截器:在springmvc的配置文件xml中,配置所有拦截路径,以及需要放行的路径
HandlerInterceptor和HandlerInterceptorAdapter的区别
HandlerInterceptor是接口,我们可以实现该接口来定义拦截器,HandlerInterceptorAdapter是抽象类,它实现了HandlerInterceptor接口的子接口AsyncHandlerInterceptor,我们可以继承该类来定义拦截器,它简化拦截器的实现,默认preHandler返回true
SpringMVC的执行原理
1.Http请求:客户端请求提交到DispatcherServlet-前端控制器
2.寻找处理器:由DispatcherServlet调用HandlerMapping-处理器映射器,根据url找到对应的的Handler
3.调用处理器:DispatcherServlet指定HandlerAdapter-处理器适配器去调用Handler
4.调用业务处理和返回结果:Handler调用业务逻辑处理完成后,返回ModelAndView
5.处理视图映射并返回模型: DispatcherServlet查询一个或多个ViewResoler-视图解析器,找到ModelAndView指定的视图
6.Http响应:将结果显示到客户端
SpringMVC的Controller是单例还是多例,有没有并发安全问题,如何解决
在spring中,bean默认都是单例的,controller也是交给spring容器管理的一个bean,因此它也是单例的。
单例的好处是减少了创建对象和垃圾回收的时间,节省了内存资源,但同时单例会造成线程不安全的问题,因为当所有请求访问同一个controller实例,controller中的成员变量是所有线程公用的,某个线程如果修改了这个变量,别的请求再来拿这个变量就编程修改后的值了
要解决这个问题,最直接有效的方式就是不要在controller中定义成员变量,如果你非要定义成员变量,两种方式
第一种,可以给controller上加注解@Scope("prototype"),将controller设置为多例模式,每次请求都重新实例化一个controller
第二种,使用ThreadLocal变量,让每一个线程都有自己独立的变量
RequestMapping 和 GetMapping有什么区别