2018年Java面试题搜集
一、Servlet执行流程(浏览器访问servlet的过程容器)
客户端发起http请求,web服务器将请求发送到servlet容器,servlet容器解析url并根据web.xml找到相对应的servlet,并将request、response对象传递给找到的servlet,
servlet根据request就知道谁发出的请求、请求信息以及其他信息,当servlet处理完业务逻辑后会将信息放入到response,并响应客户端;
二、SpringMVC执行流程
SpringMVC是有DispatchServlet为核心的分层控制框架,首先客户端发出一个请求,web服务器解析请求的url并匹配DispatchServlet的映射url,如果匹配的上就将这个请求放入到DispatchServlet,
DispatchServlet根据mapping映射配置去寻找相对应的Handle,然后把处理权给找到的Handle,Handle封装了处理业务逻辑的代码,当Handle处理完后会返回一个逻辑视图ModelAndView给DispatchServlet,
此时ModelAndView是一个逻辑试图不是一个正式试图,所以DispatchServlet会通过ViewResource视图资源去解析ModelAndView,然后将解析后的参数放到View中返回到客户端并展现;
三、Java设计模式思想(单列模式,工厂模式,策略模式,共23种设计模式)
1,单例模式
单例模式核心只要new一个实例对象的模式;比如数据库的连接,在线人数统计等;
一些网站上的在线人数统计就是通过单例模式实现的,把一个计时器放在数据库或者内存中,当有人登录的时候取出来加一然后再放回去;但是当有两个人同时登录的时候,会出现同时取出计时器,同时加一,
同时放回去,这样就的话数据就会出错,所以我们需要一个全局变量给全部人用,只需要new出一个实例对象,这就是单例模式应用,并且单例模式节省资源,因为它控制这实例对象的个数,有利于GC回收;
2,策略模式
策略模式就是将几个类中公共的方法提取到一个新的类中,从而使扩展更容易,保证代码的可移植性,可维护性强;
比如有一个需求是写鸭子对象,鸭子有叫、飞、外形这三种方法;如果每个鸭子类都写这三个方法,会出现代码冗余;这时候我们可以把鸭子的叫、飞、外形这三个方法提取出来,放到鸭父类中,让每个鸭子都继承这个鸭父类,重写这三个方法,这样封装的代码可移植性强;
当客户提出新的需求,比如鸭子会游泳,那么我们只要在鸭父类中,添加游泳方法,即可使使得所有的鸭子会游泳,让会游泳的鸭子重新鸭父类的游泳方法即可;
3,工厂模式
简单的工厂模式主要是统一提供对象实例的引用,通过工厂模式接口获取实例对象的引用。
比如一个登录功能,后端有三个类:Controller类、Interface类、实现接口的实现类;当客户端发出一个请求,当请求传送到Controller类中时,Controller类获取接口的引用对象,而实现接口的实现类的实现类中封装好的登录业务逻辑代码。当你需要加一个注册需求的时候,只需要在接口中增加一个注册方法,实现类中实现方法,Controller类获取接口的引用对象即可,不需要改动原来的代码,实现可拓展性强;
四、冒泡排序和二分查找
1,冒泡排序
public static void mp(int a[]){
int swap = 0;
for(int i=0;i<a.length;i++){
for(int j=i;j<a.lenrth;j++){
if(a[j]>a[i]){
swap = a[j];
a[j] = a[i];
a[i] = swap;
}
}
}
System.out.println(""+Arrays.toString(a));
}
2,二分查找
//a[]是一个升序的数值数组
public static int ef(int a[],int tag){
int first = 0;
int end = a.length;
for(int i=0;i<a.length;i++){
int middle = (first + end)/2;
if(tag = a[middle]){
return middle;
}
if(tag > a[niddle]){
first = middle + 1;
}
if(tag < a[middle]){
end = middle -1;
}
}
return 0;
}
五、关于Ajax技术理解
Ajax为异步请求,局部刷新技术;
在传统的页面中,用户需要点击按钮或者事件触发请求来实现页面的刷新;而异步技术不需要用户点击即可触发事件,这样使得用户体验感增强;
比如商城的购物车的异步加载,当你点击商品时无需请求后台而直接动态修改参数;
六、父类与子类之间的调用顺序
(1)父类静态代码块
(2)子类静态代码块
(3)父类构造方法
(4)子类构造方法
(5)子类普通方法
(6)重写父类的方法(重写后的方法)
七、内部类和外部类的调用
(1)内部类可以直接调用外部类包括private类,使用外部类引用的this关键字调用即可;
(2)外部类调用内部类需要建立内部类对象;
八、多线程
(1)一个进程是一个独立的运行环境,可以看作是一个程序;而线程可以看作是进程的一个任务;比如QQ是一个进程,一个QQ窗口是一个线程;
(2)在多线程程序中,多线程并发可以提高程序的效率,CPU不会因为某个线程等待资源而进入空闲状态,它会把资源让给其他的线程;
(3)用户线程就是我们开发程序时创建的线程,而守护线程为系统进程,如JVM虚拟中的GC;
(4)线程的有限级别:每一个线程都有优先级别,有限级别高的可以先获取CPU资源使该线程从就绪状态转为运行状态;也可以自定义有限级别;
(5)死锁:至少两个以上线程争夺两个以上CPU资源,避免死锁就避免使用嵌套锁,只需要在他们需要同步的地方加锁和无限等待;
九、AOP与IOC的概念(Spring核心)
(1)AOP:面向切面编程,主要是管理系统层的业务,比如日志、权限、事物等;
AOP是将封装好的对象剖开,找出其中对多个对象产生的影响的公共行为,并将其封装为一个可重用的模块,这个模块命名为切面(Aspect);
切面将那些与业务逻辑无关的,却被业务模块共同调用的逻辑提取并封装起来,减少了系统中的重复代码,降低了模块的耦合度,同时提高了系统的可维护性;
(2)IOC:Spring是开源框架,使用框架可以减少我们的工作量,提高工作效率并且它是分层结构,即相对应的层处理相对应的业务逻辑,减少代码的耦合度;而Spring的核心IOC控制反转和AOP面向切面编程。
IOC控制反转主要强调的是程序之间的关系是由容器控制的;容器控制对象,控制了对外部资源的获取;
而反转即为,在传统的编程中都是由我们创建对象获取依赖对象,正式容器帮我们查找和注入对象,对象是被获取,所以叫反转;
十、Hibernate的核心思想
Hibernate的核心思想是ROM对象关系映射机制;它是将表与表之间的操作映射成对象与对象之间的操作;也就是从数据库中提取的信息会自动按照你设置的映射要求封装成待定的对象;所以Hibernate就是通过将数据库表和实体类映射,使得对对象的修改即对数据对应的行进行修改;
十一、Struts1和Struts2的区别
Struts2是一个兼容Struts1和WebWork的MVC框架;
两者的区别:
(1)Action类
- Struts1要求Action类继承一个抽象基类。Struts1的一个普遍问题是使用抽象类编程而不是接口。
- Struts 2 Action类可以实现一个Action接口,也可实现其他接口,使可选和定制的服务成为可能。Struts2提供一个ActionSupport基类去实现 常用的接口。Action接口不是必须的,任何有execute标识的POJO对象都可以用作Struts2的Action对象。
(2)线程模式
- Struts1 Action是单例模式并且必须是线程安全的,因为仅有Action的一个实例来处理所有的请求。单例策略限制了Struts1 Action能作的事,并且要在开发时特别小心。Action资源必须是线程安全的或同步的。
- Struts2 Action对象为每一个请求产生一个实例,因此没有线程安全问题。(实际上,servlet容器给每个请求产生许多可丢弃的对象,并且不会导致性能和垃圾回收问题)
(3)Servlet依赖
- Struts1 Action 依赖于Servlet API ,因为当一个Action被调用时HttpServletRequest 和 HttpServletResponse 被传递给execute方法。
- Struts 2 Action不依赖于容器,允许Action脱离容器单独被测试。如果需要,Struts2 Action仍然可以访问初始的request和response。但是,其他的元素减少或者消除了直接访问HttpServetRequest 和 HttpServletResponse的必要性。
(4)可测性
- 测试Struts1 Action的一个主要问题是execute方法暴露了servlet API(这使得测试要依赖于容器)。一个第三方扩展--Struts TestCase--提供了一套Struts1的模拟对象(来进行测试)。
- Struts 2 Action可以通过初始化、设置属性、调用方法来测试,“依赖注入”支持也使测试更容易。
(5)捕获输入
- Struts1 使用ActionForm对象捕获输入。所有的ActionForm必须继承一个基类。因为其他JavaBean不能用作ActionForm,开发者经常创建多余的类捕获输入。动态Bean(DynaBeans)可以作为创建传统ActionForm的选择,但是,开发者可能是在重新描述(创建)已经存在的JavaBean(仍然会导致有冗余的javabean)。
- Struts 2直接使用Action属性作为输入属性,消除了对第二个输入对象的需求。输入属性可能是有自己(子)属性的rich对象类型。Action属性能够通过web页面上的taglibs访问。Struts2也支持ActionForm模式。rich对象类型,包括业务对象,能够用作输入/输出对象。这种ModelDriven 特性简化了taglib对POJO输入对象的引用。
(7)表达式语言
- Struts1 整合了JSTL,因此使用JSTL EL。这种EL有基本对象图遍历,但是对集合和索引属性的支持很弱。
- Struts2可以使用JSTL,但是也支持一个更强大和灵活的表达式语言--"Object Graph Notation Language" (OGNL).
(8)绑定值到页面(view)
- Struts 1使用标准JSP机制把对象绑定到页面中来访问。
- Struts 2 使用 "ValueStack"技术,使taglib能够访问值而不需要把你的页面(view)和对象绑定起来。ValueStack策略允许通过一系列名称相同但类型不同的属性重用页面(view)。
(9)类型转换
- Struts 1 ActionForm 属性通常都是String类型。Struts1使用Commons-Beanutils进行类型转换。每个类一个转换器,对每一个实例来说是不可配置的。
- Struts2 使用OGNL进行类型转换。提供基本和常用对象的转换器。
(10)校验
- Struts 1支持在ActionForm的validate方法中手动校验,或者通过Commons Validator的扩展来校验。同一个类可以有不同的校验内容,但不能校验子对象。
- Struts2支持通过validate方法和XWork校验框架来进行校验。XWork校验框架使用为属性类类型定义的校验和内容校验,来支持chain校验子属性
(11)Action执行控制
- Struts1支持每一个模块有单独的Request Processors(生命周期),但是模块中的所有Action必须共享相同的生命周期。
- Struts2支持通过拦截器堆栈(Interceptor Stacks)为每一个Action创建不同的生命周期。堆栈能够根据需要和不同的Action一起使用。
十二、最优删除字符串中的某个字符
//String.replace()方法用法 String b = "abcabcabc"; b=b.replace("b","");
十三、Arraylist和Linkedlist的区别
二者都是实现List接口的列表;Arraylist是基于数组的数据结构,Linkedlist是基于链表的数据结构;
当获取特定元素时,Arraylist效率比较快,它通过数组小标即可获取;而Linkedlist需要移动指针;
当存储和删除元素时,Linkedlist效率快,只需要将指针移动指定位置增加或者删除即可;而Arraylist需要移动数据;
十四、Mybatis与Ibatis的区别
随着开发团队转投Google Code旗下,ibatis3.x正式更名为Mybatis;
Mybatis与Ibatis2.x区别:
(1)Mybatis实现了接口绑定(最重要的改进)
在Ibatis2.x中我们需要在DAO的实现类中指定具体对应哪个xml映射文件,而Mybatis实现了DAO接口与xml映射文件的绑定,自动为我们生成接口的具体实现,使用起来变得更加省事和方便。
注意: 虽然Mybatis支持在接口中直接使用annotation的配置方式来简化配置,不过强烈建议仍然使用xml配置的方式。毕竟annotation的配置方式功能有限且代码入侵性太强。使用xml配置方式才能体现出Mybatis的优势所在;
(2)对象关系映射的改进(效率更高)
在使用ibatis2.x的朋友并没有通过ibatis的xml映射文件来实现对象间的关系映射。其实也确实没有必要那么做,因为ibatis2.x采用的是“嵌套查询”的方式将对象之间的关系通过查询语句的直接拼装来实现,其效果和在DAO或Service中自行封装是一样的。
不过这种方式存在“N+1查询问题”。
概括地讲,N+1查询问题可以是这样引起的:
你执行了一个单独的SQL语句来获取结果列表(就是+1)。
对返回的每条记录,你执行了一个查询语句来为每个加载细节(就是N)。
这个问题会导致成百上千的SQL语句被执行。这通常不是期望的。
而在Mybatis中,除了兼容ibatis2.x中的“嵌套查询”方式外,还提供了直接“嵌套结果”的方式,其效果相当于直接通过一句sql将查询出的dto对象自动封装成所需的对象。
不过实际上这一改进所带来的好处也是很有限的。因为这一方式在使用分页的时候并不起作用,或者说嵌套对象的结果集是不允许进行分页的。这一点在Mybatis框架中已经做出了明确的限制org.apache.ibatis.executor.resultset.NestedResultSetHandler里34行),而实际项目中需要分页的情况又特别多……
仔细一想,一对多映射确实不能通过配置文件来分页,因为这时查询出的记录数并不等于实际返回对象的size,不过一对一映射为什么也不允许就不太明白了。可能是因为一对一是一对多的特例,而在设计框架的时候并没有考虑去处理或是难于处理这一特例吧。
(3)MyBatis采用功能强大的基于OGNL的表达式来消除其他元素
MyBatis采用OGNL表达式简化了配置文件的复杂性,使用起来更简洁。
十五、数据库优化
(1)选择合适的字段,比如邮箱字段可以设为char(6),尽量把字段设置为not null,这样查询的时候数据库就不需要比较null值
(2)使用关联查询( left join on)查询代替子查询
(3)使用union联合查询手动创建临时表
(4)开启事物,当数据库执行多条语句出现错误时,事物会回滚,可以维护数据库的完整性
(5)使用外键,事物可以维护数据的完整性但是它却不能保证数据的关联性,使用外键可以保证数据的关联性
(6)使用索引,索引是提高数据库性能的常用方法,它可以令数据库服务器以比没有索引快的多的速度检索特定的行,特别是对于max,min,order by查询时,效果更明显
(7)优化的查询语句,绝大多数情况下,使用索引可以提高查询的速度,但如果sql语句使用不恰当的话,索引无法发挥它的特性。
十六、Tomcat服务器优化(内存,并发连接数,缓存)
(1)内存优化:主要是对Tomcat启动参数进行优化,我们可以在Tomcat启动脚本中修改它的最大内存数等等。
(2)线程数优化:Tomcat的并发连接参数,主要在Tomcat配置文件中server.xml中配置,比如修改最小空闲连接线程数,用于提高系统处理性能等等。
(3)优化缓存:打开压缩功能,修改参数,比如压缩的输出内容大小默认为2KB,可以适当的修改。
十七、HTTP协议
(1)常用的请求方法有get、post
(2)Get与post的区别:传送数据,get携带参数与访问地址传送,用户可以看见,这的话信息会不安全,导致信息泄露。而post则将字段与对应值封装在实体中传送,这个过程用户是不可见的。Get传递参数有限制,而post无限制。
十八、TCP/UDP协议
(1)TCP协议:面向连接(如打电话需要先拨号连接);
UDP协议:无连接,即发送数据之前不需要先建立连接;
(2)TCP协议:提供可靠的服务,即通过TCP传输的数据,无差错,不丢失,不重复,且按序到达;
UDP协议:尽最大努力交付,即不保证可靠交付;
(3)TCP协议:TCP面向字节流,实际上是TCP把数据看出一串无结构的字节流;
UDP协议:UDP是面向报文的,UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话、视频会议等);
(4)TCP协议:每一条TCP连接只能点到点;
UDP协议:UDP支持一对一、一对多、多对一、多对多的交互通讯;
(5)TCP协议:TCP首部开销20个字节;
UDP协议:UDP首部开销8个字节;
(6)TCP协议:TCP的逻辑通信信道是全双工的可靠信道;
UDP协议:是不可靠信道;
十九、Java集合类框架的基本接口有哪些
1,Collection接口:元素集合,单列集合根接口;
(1)List接口:继承Collection接口,有序元素集合,元素可重复;
<1> ArrayList类:实现List接口,基于数组的数据结构,适合查询,不适合增删;
<2> LinkedList类:实现List接口,基于链表的数据结构(底层是双向循环链表),适合增删,不适合查询;
<3> Vector类:实现List接口;
① Stack类:继承Vector类;
(2)Set接口:继承Collection接口,无序元素集合,元素不可重复;
<1> HashSet类:实现Set接口,根据对象的哈希值确定元素在集合中的位置;
<2> SortedSet类:实现Set接口;
① TreeSet类:继承SortedSet类,以二叉树的方式存储元素,实现对集合中的元素进行排序;
2,Map接口:键值对集合,双列集合的根接口;
(1)hashMap类:实现Map接口,性能好;
(2)HashTable类:实现Map接口,线程安全;
(3)SortedMap类:实现Map接口,用于存储键值的映射关系,不能出现重复的键(key);
① TreeMap类:继承SortedMap类,用于存储键值的映射关系,不能出现重复的键(key),所有的键按照二叉树的方式排列;
java.util.Collection [I]
|—java.util.List [I]
|—java.util.ArrayList [C]
|—java.util.LinkedList [C]
|—java.util.Vector [C]
|—java.util.Stack [C]
|—java.util.Set [I]
|—java.util.HashSet [C]
|—java.util.SortedSet [I]
|—java.util.TreeSet [C]
java.util.Map [I]
|—java.util.SortedMap [I]
|—java.util.TreeMap [C]
|—java.util.Hashtable [C]
|—java.util.HashMap [C]
|—java.util.LinkedHashMap [C]
|—java.util.WeakHashMap [C]
二十、类加载的过程
遇到一个新的类时,首先会在方法区去找class文件,如果没有找到就会去硬盘中找class文件,找到后返回将class文件加载到方法区中;在类加载的时候,静态成员变量会被分配到方法区的静态区域;非静态成员变量分配到非静态区;然后开始给静态成员变量初始化,赋默认值;赋完默认值后,会根据静态成员变量的书写位置赋显示值,然后执行静态代码;当所有的静态代码执行完,类加载才算完成;
二十一、对象的创建
对象创建的过程:
(1)遇到一个新类时,会进行类加载,定位到class文件;
(2)对所有静态成员变量初始化,静态代码块也会执行,而且只在类加载的时候执行一次;
(3)NEW对象时,JVM会在堆中分配一个足够大的存储空间;
(4)存储空间清空,为所有的变量赋默认值,所有对象的引用赋值为NULL;
(5)根据书写的位置给变量一些初始化的操作;
(6)调用构造器方法(没有继承);
二十二、JVM优化
(1)设置参数,设置JVM的最大内存数;
(2)垃圾回收器的选择;
二十三、高并发处理
(1)代码层次:Java的同步锁,典型的就是同步关键字Synchronized;
(2)数据库层次:悲观锁和乐观锁;
<1> 悲观锁:是指对数据被外界(包括本系统当前的其他事物,以及来自外部系统的事物处理)修改保持保守态度,因此,在整个数据处理过程中,将数据处于锁定状态;
悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据);
<2> 乐观锁:乐观锁机制采取了更加宽松的加锁机制,大多数基于数据版本(Version)记录机制实现;
乐观锁的实现,是将数据表增加一个字段版本(Version),对于不同的人员操作数据库时,都会有个版本好1,2,3这样的数字;
当数据库接受更新数据的时候,会根据“提交数据版本大 于数据库记录当前版本”来判断是否更新或者驳回的乐观锁策略;
由于整个过程没有对数据库进行数据加锁,所以大大提高了大并发量下的系统整体性能表现;
乐观锁机制往往基于系统中的数据存储逻辑,因此有一定的局限性(外部系统对数据的更新操作,我们可以采用将乐观锁策略在数据存储过程中实现来解决,开放存储过程更新路径,不直接开放数据表);
(3)常见案例
<1> 案例一:订票系统,某航班只有一张机票,假定有1W人打开你的网站来订票,问你如何解决并发问题(可以扩展到任何高并发网站要考虑的并读写问题)
问题分析:a:1W人来访问,票没出去前要保证大家都能看到有票,不可能一个人在看到票的时候其他人就都看不到了;到底谁能抢到,那得看网速;
b:并发问题,1W人同时点击购买,到底谁能成交?总共就一张票;
解决方案:采用乐观锁;乐观锁是在不锁表的情况下,利用业务的控制来解决并发问题,这样既保证了数据的并发可读性又保证了数据的排他性,保证性能的同时解决了并发带来的脏数据问题;
实现思路:Hibernate实现乐观锁;
前提:在表中增加一个冗余字段,Version版本号,long类型;
原理:只有当前版本号>=数据库版本号,才能提交;提交成功后,版本号Version++;
代码:在ormpping增加一属性optimistic-lock = "version"即可,
<hibernate-mapping>
<class name = "com.insigma.stock.ABC" optimistic-lock = "version" table = "T_Stock" schema = "STOCK">
</hibernate-mapping>
<2> 案例二:股票交易系统,银行系统,大数据量你是如何考虑
问题分析:Oracle数据单张表达到100万记录后,查询性能就很差;
解决方案:大数据量系统必须考虑表拆分(表名字不一样,但结构完全一样)
a:按业务分:比如手机号的表,我们可以根据前3位数来拆分表;
b:利用Oracle的表拆分机制做分表;
c:如果是交易系统,我们可以考虑采用时间轴来拆分;
注意:还要考虑缓存:hibernate本身提供的一级二级缓存、内存缓存;
比如经常搜索的商品,将信息放在内存缓存中,可以大大的提高性能;专业的独立缓存框架memcached等,可独立部署一个缓存服务器;
二十四、事物的理解
事物具有原子性、一致性、持久性、隔离性;
(1)原子性:是指一个事物中,要么全部执行,要么全部失败回滚;
(2)一致性:事物执行之前和执行之后都出于一致性状态;
(3)持久性:事物对数据的操作是永久性;
(4)隔离性:当一个事物正在对数据进行操作时,另一个事物是不可以对数据进行操作,也就是多个事物之间是相互隔离的;
二十五、Struts工作流程
(1)客户端发出一个请求到servlet容器;
(2)请求经过一些列过滤FilterDispatch调用,FilterDispatch通过ActionMapper去找相对应的Action;
(3)ActionMapper找到相对应的Action返回给FilteDispatch,Dispatch把处理权交给ActionProxy
(4)ActionProxy通过配置文件找到对应的Action类;
(5)ActionProxy创建一个ActionLinvocation的实例处理业务逻辑;
(6)Action处理完毕,ActionInocation负责根据struts.xml的配置找到对应的返回结果(返回结果通常是Jsp页面);