面试题目记录
西域科技公司电话面试题目记录:
①JAVA中重载和重写分别是什么,以及区别
解答:
重载:参考链接:http://www.cnblogs.com/bluestorm/archive/2012/03/01/2376236.html
(1)重载是指方法重载,方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。重载Overloading是一个类中多态性的一种表现。
(2)Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。
(3)调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。重载的方法的返回值都是一样的
参考链接:http://zhycaf.iteye.com/blog/980334
注意点:
1. 方法重载只可以通过方法名和方法参数来区别,即“方法签名”
2. 不能通过访问权限,返回类型,异常列表进行重载
3.方法异常类型和数目不会对重载造成影响。
重写:简单说来重写就是子类对父类(层级上)中非私有成员方法的重新功能定义
(1)父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。
但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。
方法重写又称方法覆盖。
(2)若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
(3)子类函数的访问修饰权限不能少于父类的,重写方法访问修饰符一定要大于被重写方法访问修饰符(public > protected > default > private)
(4)重写和被重写方法返回值必须相同
(5)重写方法抛出的异常必须和被重写方法抛出异常一致,或者是其子类
(6)静态方法不存在重写概念,重写是面向对象特性,静态方法是类方法,用类名直接访问。同样,父类中private方法不能被子类重写,因为private只有父类本类可见。
②JAVA内部类的解释,内部类中为什么要使用final关键字(问题没有理解清楚)
解答:转载自http://www.cnblogs.com/dolphin0520/p/3811445.html(非常感谢,学习到了很多知识)
在Java中,可以将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类。广泛意义上的内部类一般来说包括这四种:成员内部类、局部内部类、匿名内部类和静态内部类。具体4中类的定义以及差别查看上述转载链接。
内部类中为什么要使用final关键字(问题没有理解清楚)应该是问的:
为什么局部内部类和匿名内部类只能访问局部final变量?
解答:这是由于局部变量的生命周期导致的。
参考链接:http://blog.csdn.net/craigyang/article/details/4680506以及上面的转载链接(运用JAVA反编译方式在进行说明)
③JAVA ArrayList和LinkedList分别是什么,以及区别
转载自:http://blog.csdn.net/qianzhiyong111/article/details/6678035
http://www.cnblogs.com/xrq730/p/5005347.html(私认为这篇的解读更好)
1.ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。
补充:
ArrayList 是一个可改变大小的数组.当更多的元素加入到ArrayList中时,其大小将会动态地增长.内部的元素可以直接通过get与set方法进行访问,因为ArrayList本质上就是一个数组.
LinkedList 是一个双链表,在添加和删除元素时具有比ArrayList更好的性能.但在get与set方面弱于ArrayList.
当然,这些对比都是指数据量很大或者操作很频繁的情况下的对比,如果数据和运算量很小,那么对比将失去意义.
2.对于随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。
3.对于新增和删除操作add和remove,LinedList比较占优势,因为ArrayList要移动数据。
这一点要看实际情况的。若只对单条数据插入或删除,ArrayList的速度反而优于LinkedList。但若是批量随机的插入删除数据,LinkedList的速度大大优于ArrayList. 因为ArrayList每插入一条数据,要移动插入点及之后的所有数据。 这一点我做了实验。在分别有200000条“记录”的ArrayList和LinkedList的首位插入20000条数据,LinkedList耗时约是ArrayList的20分之1。
附上简单的测试代码,比较损耗机器。。。
1 for(int m=0;m<20000;m++){ 2 linkedlist.add(m,null); //当在200000条数据之前插入20000条数据时,LinkedList只用了1125多ms.这就是LinkedList的优势所在 3 } 4 long time4 = new Dte().getTime(); 5 System.out.print("batch linkedlist add:"); 6 System.out.println(time4 - time3); 7 for(int n=0;n<20000;n++){ 8 arraylist.add(n, null); //当在200000条数据之前插入20000条数据时,ArrayList用了18375多ms.时间花费是arraylist的近20倍(视测试时机器性能) 9 } 10 long time5 = new Date().getTime(); 11 System.out.print("batch arraylist add:"); 12 System.out.println(time5 - time4);
4.查找操作indexOf,lastIndexOf,contains等,两者差不多。
5.随机查找指定节点的操作get,ArrayList速度要快于LinkedList.
④如何实现一个线程安全的单例模式(第一次听到这种东西。。。惭愧)
参考链接:http://www.cnblogs.com/coffee/archive/2011/12/05/inside-java-singleton.html
http://blog.chenzuhuang.com/archive/13.html
首先是单例模式:单例模式下分为五种:懒汉式,双重校验锁DCL(double checked locking),饿汉式,IoDH(Initialization Demand Holder)——通过静态内部类实现线程安全的单例模式以及枚举单例。下面简单介绍一下这几种:
懒汉式(Lazy Singleton):
非线程安全
优点:lazy,初次使用时实例化单例,避免资源浪费
缺点:lazy,如果实例初始化非常耗时,初始使用时,可能造成性能问题
非线程安全。多线程下可能会有多个实例被初始化。
(注:通过添加synchronized可以使线程互斥访问获取对象实例的方法,可以保证线程安全,但消耗了较多的性能)
双重校验锁DCL(double checked locking):在懒汉式的基础上扩展
线程安全
(注:在引用的博文中有一篇提及到了需要JDK-1.5的volatile关键字修饰达到DCL稳定的效果)
饿汉式:单例模式的饿汉式,在定义自身类型的成员变量时就将其实例化,使得在Singleton单例类被系统(姑且这么说)加载时就已经被实例化出一个单例对象,从而一劳永逸地避免了线程安全的问题。
线程安全
IoDH(Initialization Demand Holder)——通过静态内部类实现线程安全的单例模式:
通过静态内部类的方法就实现了lazy loading,很好地将懒汉式和饿汉式结合起来,既实现延迟加载,保证系统性能,也能保证线程安全。(并且代码简单。。)
(解释一下,因为java机制规定,内部类SingletonHolder只有在getInstance()方法第一次调用的时候才会被加载(实现了lazy),而且其加载过程是线程安全的(实现线程安全)。内部类加载的时候实例化一次instance。)
枚举单例:
比如你的Singleton类实现了序列化,又或者想避免通过反射来破解单例模式的话,单例模式还可以有另一种形式。那就是枚举单例。枚举类型在JDK1.5被引进。这种方式也是《Effective Java》作者Josh Bloch 提倡的方式,它不仅能避免多线程的问题,而且还能防止反序列化重新创建新的对象、防止被反射攻击。
public enum EnumSingleton { INSTANCE{ @Override protected void work() { System.out.println("你好,是我!"); } }; protected abstract void work(); //单例需要进行操作(也可以不写成抽象方法) }
在外部,可以通过EnumSingleton.INSTANCE.work()来调用work方法。默认的枚举实例的创建是线程安全的,但是实例内的各种方法则需要程序员来保证线程安全。
总的来说,使用枚举单例模式,有三个好处:1.实例的创建线程安全,确保单例。2.防止被反射创建多个实例。3.没有序列化的问题。
⑤Java虚拟机JVM原理,类加载机制
这个问题算是面试中的经典题了,这里不过多描述了,我相信很多人都看过《深入理解Java虚拟机》一书,对JVM的原理也能了解一二。只是回答这个问题非常考验面试者的逻辑分析以及语言表述能力,很多时候我们理解了原理,但再去表述给面试官却比较困难,往往出现措辞不当,思绪混乱等,因此在这里我也不能给大家找到或者总结一个很好的答案。(而且个人认为《深入理解Java虚拟机》一书实在有点晦涩难懂,需要耐心的多看几次。。。)
⑥JAVA设计模式种类
设计模式通常有23种
⑦Struts的原理,工作流程,Struts2和Struts1的一些差异
参考链接:http://www.cnblogs.com/langtianya/archive/2013/04/09/3011090.html
工作原理:
1 客户端初始化一个指向Servlet容器(例如Tomcat)的请求
2 这个请求经过一系列的过滤器(Filter)(这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin)
3 接着FilterDispatcher(现已过时)被调用,FilterDispatcher询问ActionMapper来决定这个请是否需要调用某个Action
4 如果ActionMapper决定需要调用某个Action,FilterDispatcher把请求的处理交给ActionProxy
5 ActionProxy通过Configuration Manager询问框架的配置文件,找到需要调用的Action类
6 ActionProxy创建一个ActionInvocation的实例。
7 ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。
8 一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果。返回结果通常是(但不总是,也可 能是另外的一个Action链)一个需要被表示的JSP或者FreeMarker的模版。在表示的过程中可以使用Struts2 框架中继承的标签。在这个过程中需要涉及到ActionMapper
在上述过程中所有的对象(Action,Results,Interceptors,等)都是通过ObjectFactory来创建的。
二 工作流程
1、客户端浏览器发出HTTP请求.
2、根据web.xml配置,该请求被FilterDispatcher接收
3、根据struts.xml配置,找到需要调用的Action类和方法, 并通过IoC方式,将值注入给Aciton
4、Action调用业务逻辑组件处理业务逻辑,这一步包含表单验证。
5、Action执行完毕,根据struts.xml中的配置找到对应的返回结果result,并跳转到相应页面
6、返回HTTP响应到客户端浏览器
至于struts2和struts1的差别,由于没用过struts1所以附上链接:http://blog.csdn.net/john2522/article/details/7436307
⑧redirect和forward的区别和差异
区别:转载自:http://zhulin902.iteye.com/blog/939049
HttpServletResponse.sendRedirect方法和RequestDispatcher.forward方法
1.从地址栏显示来说
forward是服务器请求资源,服务器直接访问目标地址的URL,把那个URL的响应内容读取过来,然后把这些内容再发给浏览器.浏览器根本不知道服务器发送的内容从哪里来的,所以它的地址栏还是原来的地址.
redirect是服务端根据逻辑,发送一个状态码,告诉浏览器重新去请求那个地址.所以地址栏显示的是新的URL.
2.从数据共享来说
forward:转发页面和转发到的页面可以共享request里面的数据.
redirect:不能共享数据.
3.从运用地方来说
forward:一般用于用户登陆的时候,根据角色转发到相应的模块.
redirect:一般用于用户注销登陆时返回主页面和跳转到其它的网站等.
4.从效率来说
forward:高.
redirect:低.
⑨Hibernate中对象的三种状态,游离态后对象是否还存在数据库里
参考链接:https://segmentfault.com/a/1190000002510323
http://blog.csdn.net/fg2006/article/details/6436517
(1)瞬时态:简单的说,new一个实例对象,这个实例对象就是瞬时态。且不和 Session 实例关联,数据库里也没有相应的记录
(2)持久态:持久化对象就是已经被保存进数据库的实体对象,并且这个实体对象现在还处于Hibernate的缓存管理之中。这是对该实体对象的任何修改,都会在清理缓存时同步到数据库中,已被session所管理了
(3)游离态:当一个持久化对象,脱离开Hibernate的缓存管理后,它就处于游离状态,游离对象和自由对象的最大区别在于,游离对象在数据库中可能还存在一条与它对应的记录,只是现在这个游离对象脱离了Hibernate的缓存管理,而自由对象不会在数据库中出现与它对应的数据记录
所以说:游离态后对象是还存在数据库里
注:三种状态之间的转换关系在上述链接里有说明
⑩Spring IOC的实现原理(如何用JAVA来实现Spring IOC的机制)
个人技术暂时只停留在使用Spring的地步上,原理的话也只是略懂一二,只了解最简单的IOC DI等。找了几篇博文附上链接:
http://www.zhihu.com/question/21346206
http://www.cnblogs.com/ITtangtang/p/3978349.html#a2
当然,Spring实在是非常庞大的一个框架,短时间内了解其核心内幕比较困难。。。共勉
11.JAVA多线程知识,如何启动一个新的线程,Run和Start的区别
启动一个新线程,很明显用的是start方法。《JAVA核心技术卷一》里面有详细介绍
用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面的代码。通过调用Thread类的start()方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到spu时间片,就开始执行run()方法,这里方法run()称为线程体,它包含了要执行的这个线程的内容,Run方法运行结束,此线程随即终止。
run()方法只是类的一个普通方法而已,如果直接调用Run方法,程序中依然只有主线程这一个线程,其程序执行路径还是只有一条,还是要顺序执行,还是要等待run方法体执行完毕后才可继续执行下面的代码,这样就没有达到写线程的目的。
总结:调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行。
补充:找到了一个个人觉得比较好的线程面试问题汇总:http://www.cnblogs.com/dolphin0520/p/3958019.html
(如果有任何版权疑问可以随时私我立刻删除)
12.Hibernate中Session和SessionFactory的区别,一级缓存和二级缓存的区别
参考链接:http://blog.csdn.net/defonds/article/details/2308972