1、说出三个常用的视图类
InternalResourceView、JstlView、RedirectView
2、简述REST中HiddenHttpMethodFilter 过滤器的作用
该过滤器主要负责转换客户端请求的方式,当浏览器的请求方式为POST,并且在请求中能通过method获取到请求参数值。该过滤器就会进行请求方式的转换。
一般在REST中,都是将POST请求转换为对应的DELETE或者是PUT。
3、简述Springmvc中InternalResourceViewResolver 解析器的工作机制
使用prefix+方法的返回值+suffix生成一个物理视图路径。
4、简述Spring中切面中常用的几种通知,并简单解释
前置通知
在目标方法执行之前执行。
后置通知
在目标方法执行之后执行,不管目标方法有没有抛出异常。
返回通知
在目标方法成功返回之后执行,可以获取到目标方法的返回值。
异常通知
在目标方法抛出异常后执行。
环绕通知
环绕着目标方法执行。
5、SpringMVC的工作原理
(1)用户向服务器发送请求,请求被springMVC前端控制器DispatchServlet 捕获;
(2)DispatcherServle对请求URL进行解析,得到请求资源标识符(URL),然后根据该URI调用HandlerMapping将请求映射到处理器HandlerExcutionChain;
(3)DispatchServlet 根据获得Handler 选择一个合适的HandlerAdapter 适配器处理;
(4)Handler 对数据处理完成以后将返回一个ModelAndView()对象给DisPatchServlet;
(5)Handler 返回的ModelAndView()只是一个逻辑视图并不是一个正式的视图,DispatcherSevlet 通过ViewResolver 试图解析器将逻辑视图转化为真正的视图View;
(6)DispatcherServle 通过model解析出ModelAndView()中的参数进行解析最终展现出完整的view并返回给客户端;
6、谈谈你对Spring的理解
Spring是一个开源框架,为简化企业级应用开发而生。Spring可以是使简单的JavaBean实现以前只有EJB才能实现的功能。Spring是一个IOC和AOP容器框架。
Spring容器的主要核心是:
控制反转(IOC)
传统的java开发模式中,当需要一个对象时,我们会自己使用new或者getInstance等直接或者间接调用构造方法创建一个对象。而在spring开发模式中,spring容器使用了工厂模式为我们创建了所需要的对象,不需要我们自己创建了,直接调用spring提供的对象就可以了,这是控制反转的思想。
依赖注入(DI)
spring使用javaBean对象的set方法或者带参数的构造方法为我们在创建所需对象时将其属性自动设置所需要的值的过程,就是依赖注入的思想。
面向切面编程(AOP)
在面向对象编程(oop)思想中,我们将事物纵向抽成一个个的对象。而在面向切面编程中,我们将一个个的对象某些类似的方面横向抽成一个切面,对这个切面进行一些如权限控制、事物管理,记录日志等公用操作处理的过程就是面向切面编程的思想。AOP底层是动态代理,如果是接口采用JDK动态代理,如果是类采用CGLIB方式实现动态代理。
7、 Spring中常用的设计模式
(1)代理模式
spring中两种代理方式,若目标对象实现了若干接口,spring使用jdk的java.lang.reflect.Proxy类代理。若目标兑现没有实现任何接口,spring使用CGLIB库生成目标类的子类。
(2)单例模式
在spring的配置文件中设置bean默认为单例模式。
(3)模板方式模式
用来解决代码重复的问题。
比如:Rest Template、Jms Template、JpaTemplate。
(4)工厂模式
在工厂模式中,我们在创建对象时不会对客户端暴露创建逻辑,并且是通过使用同一个接口来指向新创建的对象。Spring中使用beanFactory来创建对象的实例。
8、List 和Set的区别
List,Set 都是继承自collection 接口。
List 特点:
元素有放入顺序,元素可重复。
Set 特点:
元素无放入顺序,元素不可重复,重复元素会覆盖掉,(元素虽然无放入顺序,但是元素在set中的位置是有该元素的Hashcode决定的,其位置其实是固定的,加入Set的object必须定义equals()方法,另外list支持for循环,也就是通过下标来遍历,也可以用迭代器,但是set只能用迭代,因为他无序,无法用下标来取得想要的值。)
Set和List对比
Set:检索元素效率低下,删除和插入效率高,插入和删除不会引起元素位置改变。
List:和数组类似,List可以动态增长,查找元素效率高,插入删除元素效率低,因为会引起其他元素位置改变。
9、对象的四种引用
强引用
只要引用存在,垃圾回收器永远不会回收。
可直接通过obj取得对应的对象如obj.eque1s(new object());而这样obj对象对后面 new object的一个强引用,只有当obj这个引用被释放之后,对象才会被释放掉,这也是我们经常所用到的编码形式。
软引用
非必须引用,内存溢出之前进行回收,可以通过以下代码实现
这时候sf是对obi的一个软引用,通过sf.get)方法可以取到这个对象,当然,当这个对象被标记为需要回收的对象时,则返回null;软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。
弱引用
第二次垃圾回收时回收,可以通过如下代码实现
弱引用是在第二次垃圾回收时回收,短时间内通过弱引用取对应的数据,可以取到,当执行过第二次垃圾回收时,将返回nul。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued 方法返回对象是否被垃圾回收器标记。
ThreadLocal中有使用到弱引用
虚引用
垃圾回收时回收,无法通过引用取到对象值,可以通过如下代码实现
虚引用是每次垃圾回收的时候都会被回收,通过虚引用的get方法永远获取到的数据为null,因此也被成为幽灵引用。虚引用主要用于检测对象是否已经从内存中删除。
10、Java获取反射的三种方法
(1)通过new对象实现反射机制。
(2)通过路径实现反射机制。
(3)通过类名实现反射机制。
11、Java反射机制
Java反射机制是在运行状态中,对于任意一个类,都能够获得这个类的所有属性和方法,对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为Java的反射机制。
Class类与java.lang.reflect 类库一起对反射的概念进行了支持,该类库包含了Field,Method,Constructor类(每个类都实现了Member接口)。这些类型的对象时由JVM在运行时创建的,用以表示未知类里对应的成员。
这样你就可以使用Constructor 创建新的对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods()和getConstructors()等很便利的方法,以返回表示字段,方法,以及构造器的对象的数组。这样匿名对象的信息就能在运行时被完全确定下来,而在编译时不需要知道任何事情。
12、Arrays.sort 和Collections.sort实现原理和区别
Collection和Collections区别
java.util.collection是一个集合接口。它提供了对集合对象进行基本操作的通用接口方法。
java.util.collections是针对集合类的一个帮助类,他提供一系列静态方法实现对各种集合的搜索、排序、线程安全等操作。然后还有混排(Shufling)、反转(Reverse)、替换所有的元素(fill)、拷贝(copy)、返回Collections中最小元素(min)、返回Collections中最大元素(max)、返回指定源列表中最后一次出现指定目标列表的起始位置(1astIndexofsubList)、返回指定源列表中第一次出现指定目标列表的起始位置(IndexofsubList)、根据指定的距离循环移动指定列表中的元素(Rotate);
事实上Collections.sort方法底层就是调用的array.sort方法,
legacyMergesort(a):归并排序。
ComparableTimsort.sort():Timsort 排序。
Timsort 排序是结合了合并排序(merge sort)和插入排序(insertion sort)而得出的排序算法。
Timsort的核心过程
TimSort 算法为了减少对升序部分的回溯和对降序部分的性能倒退,将输入按其升序和降序特点进行了分区。排序的输入的单位不是一个个单独的数字,而是一个个的块-分区。其中每一个分区叫一个run。针对这些run序列,每次拿一个run出来按规则进行合并。每次合并会将两个run合并成一个run。合并的结果保存到栈中。合并直到消耗掉所有的run,这时将栈上剩余的run合并到只剩一个run为止。这时这个仅剩的run便是排好序的结果。
综上述过程,Timsort算法的过程包括
(1)如何数组长度小于某个值,直接用二分插入排序算法。
(2)找到各个run,并入栈。
(3)按规则合并run。
13、深拷贝(深复制)和浅拷贝(浅复制)
深拷贝(深复制)和浅拷贝(浅复制)是两个比较通用的概念,尤其在C++语言中,若不弄懂,则会在delete的时候出问题,但是我们用的是Java。虽然Java自动管理对象的回收,但对于深拷贝(深复制)和浅拷贝(浅复制),我们还是要给予足够的重视,因为有时这两个概念往往会给我们带来不小的困惑。
浅拷贝是指拷贝对象时仅仅拷贝对象本身(包括对象中的基本变量),而不拷贝对象包含的引用指向的对象。
深拷贝不仅拷贝对象本身,而且拷贝对象包含的引用指向的所有对象。
举例:
对象A1中包含对B1的引用,B1中包含对C1的引用。浅拷贝A1得到A2,A2中依然包含对B1的引用,B1中依然包含对C1的引用。深拷贝则是对浅拷贝的递归,深拷贝A1得到A2,A2中包含对B2(B1的copy)的引用,B2中包含对C2(C1的copy)的引用。
若不对clone()方法进行改写,则调用此方法得到的对象即为浅拷贝。
14、异常分类以及处理机制
总体上我们根据Javac对异常的处理要求,将异常类分为二类:
非检查异常(unckecked exception):
Error 和RuntimeException 以及他们的子类。javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用try…catch…finally)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理。这样的异常发生的原因多半是代码写的有问题。如除0错误 ArithmeticException,错误的强制类型转换错误classcastException,数组索引越界ArrayIndexoutofBoundsException,使用了空对象Nu11PointerException 等等。
检查异常(checked exception):
除了Error 和RuntimeException 的其它异常。avac 强制要求程序员为这样的异常做预备处理工作(使用try…catch…finally 或者throws)。在方法中要么用try-catch 语句捕获它并处理,要么用throws子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如SQLException,IOException,ClassNotFoundException等。
注意:检查和非检查是对于javac来说的。
15、wait 和sleep的区别
源码如下:
Sleep源码:
Wait源码:
(1)sleep来自Thread类,和wait 来自object类。
(2)最主要是sleep方法没有释放锁,而wait方法释放了锁,使得其他线程可以使用同步控制块或者方法。
(3)wait,notify和notifyA11只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)。
(4)sleep必须捕获异常,而wait,notify和notifyAl1不需要捕获异常。
1)sleep方法属于Thread类中方法,表示让一个线程进入睡眠状态,等待一定的时间之后,自动醒来进入到可运行状态,不会马上进入运行状态,因为线程调度机制恢复线程的运行也需要时间,一个线程对象调用了sleep方法之后,并不会释放他所持有的所有对象锁,所以也就不会影响其他进程对象的运行。但在sleep的过程中过程中有可能被其他对象调用它的interrupt(),产生InterruptedException异常,如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态,如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally 语句块)以及以后的代码。
注意sleep( )方法是一个静态方法,也就是说他只对当前对象有效,通过t.sleep( )让t对象进入sleep,这样的做法是错误的,它只会是使当前线程被s1eep而不是t线程
2)wait 属于object 的成员方法,一旦一个对象调用了wait方法,必须要采用notify()和notifyAll( )方法唤醒该进程:如果线程拥有某个或某些对象的同步锁,那么在调用了wait( )后,这个线程就会释放它持有的所有同步资源,而不限于这个被调用了wait( )方法的对象。wait()方法也同样会在wait的过程中有可能被其他对象调用interrupt()方法而产生。
16、数组在内存中如何分配
静态初始化
初始化时由程序员显式指定每个数组元素的初始值,由系统决定数组长度,如:
动态初始化
初始化时由程序员显示的指定数组的长度,由系统为数据每个元素分配初始值,如:
因为Java数组变量是引用类型的变量,所以上述几行初始化语句执行后,三个数组在内存中的分配情况如下图所示:
由上图可知,静态初始化方式,程序员虽然没有指定数组长度,但是系统已经自动帮我们给分配了,而动态初始化方式,程序员虽然没有显示的指定初始化值,但是因为Java数组是引用类型的变量,所以系统也为每个元素分配了初始化值nul1,当然不同类型的初始化值也是不一样的,假设是基本类型int类型,那么为系统分配的初始化值也是对应的默认值0。