1、Object类的hashCode函数有什么作用?
答:当类对象被放入哈希表等容器中时,以hashCode值作为桶的散列函数,进行散列。与Object.equals函数的关系是,如果equals方法返回的判定两个对象相等,则其hashCode应相等,否则对象放入哈希容器进行存储时,将无法正确访问。哈希容器(如HashSet)存储或访问内部元素时,第一步是利用对象的hashCode()函数进行散列,第二步才是进行相应处理;而判断equals()时,也是先进行散列。因此,才会有这样的规约,即,当覆盖equals方法,一定要重写类的hashCode方法,否则哈希系列容器存储或访问对象时,将会出错。
2、volatile关键字的用法?
答:volatile是一种轻量级的同步机制,它主要有两个特性:一是保证共享变量对所有线程的可见性;二是禁止指令重排序优化。同时需要注意的是,volatile对于单个的共享变量的“读/写”具有原子性,但是像num++这种(包含多次读/写的)复合操作,volatile无法保证整体的其原子性,此时只能使用synchronized、锁或Atom*原子操作类。
3、如何让一个Java类无法被实例化?
答:在使用私有构造器的基础下,再在构造方法中返回一个异常,因为虽然外部类无法实例化该类,但是内部类可以实例化该类。如果只是通过私有化构造器,那么通过反射的方式,还是可以实例化该类。必须在私有构造器中添加一个异常,这样,当执行构造方法的时候,就会抛出异常,从而停止实例化。
public class MyUtil { private MyUtil(){ throw new IllegalArgumentException(); } }
4、如何实现一个分布式锁?
答:有三类分布式锁实现方法,基于数据库、 基于redis缓存、基于zookeeper的分布式锁实现方法。
1)基于数据库的分布式锁实现。建立以方法名为主键的数据表,获取锁时,尝试往同一表中插入被调用的方法名,如果插入成功则获取锁成功,而由于主键索引的唯一性,其它线程无法插入成功;而释放锁就是向数据表中删除该方法名。
2)基于redis的实现。利用setnx操作指令,向缓存中插入(k-v)的方式获取锁;释放锁,在Redis里面,通常的做法是运行一段Lua脚本,脚本里面做值v的判断和删除,而Redis将这段脚本作为一个原子操作执行。
3)基于zookeeper实现。创建一个目录mylock; 线程A想获取锁就在mylock目录下创建临时顺序节点; 获取mylock目录下所有的子节点,然后获取比自己小的兄弟节点,如果不存在,则说明当前线程顺序号最小,获得锁; 线程B获取所有节点,判断自己不是最小节点,设置监听比自己次小的节点; 线程A处理完,删除自己的节点,线程B监听到变更事件,判断自己是不是最小的节点,如果是则获得锁。
5、是否可以自定义一个java.lang.String类?
不可以。由于jvm加载类时的“双亲委派机制”,如果自定义一个java.lang.String类时,只会加载当前jdk自带的String类。所谓双亲委派是指,每次收到类加载请求时,先将请求委派给父类加载器完成(所有加载请求最终会委派到顶层的Bootstrap ClassLoader加载器中),如果父类加载器无法完成这个加载(该加载器的搜索范围中没有找到对应的类),子类尝试自己加载。
双亲委派好处:1)避免同一个类被多次加载;2)每个加载器只能加载自己范围内的类;
6、简述SpringMvc请求解析工作流程?
流程
1、用户发送请求至前端控制器DispatcherServlet
2、DispatcherServlet收到请求调用HandlerMapping处理器映射器。
3、处理器映射器找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
4、DispatcherServlet调用HandlerAdapter处理器适配器
5、HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
6、Controller执行完成返回ModelAndView
7、HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8、DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9、ViewReslover解析后返回具体View
10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
11、DispatcherServlet响应用户