银⾏java开发笔试⾯试题13道(含答案)
gzip编码1、在多线程环境中使⽤HashMap会有什么问题?在什么情况下使⽤get()⽅法会产⽣⽆限循环?
HashMap本⾝没有什么问题,有没有问题取决于你是如何使⽤它的。⽐如,你在⼀个线程⾥初始化了⼀个HashMap然后在多个其他线程⾥对其进⾏读取,这肯定没有任何问题。有个例⼦就是使⽤HashMap来存储系统配置项。当有多于⼀个线程对HashMap进⾏修改操作的时候才会真正产⽣问题,⽐如增加、删除、更新键值对的时候。因为put()操作可以造成重新分配存储⼤⼩(re-sizeing)的动作,因此有可能造成⽆限循环的发⽣,所以这时需要使⽤Hashtable或者ConcurrentHashMap,⽽后者更优。
老头滚动条是什么2、不重写Bean的hashCode()⽅法是否会对性能带来影响?
这个问题⾮常好,每个⼈可能都会有⾃⼰的体会。按照我掌握的知识来说,如果⼀个计算hash的⽅法写得不好,直接的影响是,当向HashMap中添加元素的时候会更频繁地造成冲突,因此最终增加了耗时。但是⾃从Java 8开始,这种影响不再像前⼏个版本那样显著了,因为当冲突的发⽣超出了⼀定的限度之后,链表类的实现将会被替换成⼆叉树(binary tree)实现,这时你仍可以得到O(logN)的开销,优于链表类的O(n)。
3、对于⼀个不可修改的类,它的每个对象是不是都必须声明成final的?
不尽然,因为你可以通过将成员声明成⾮final且private,并且不要在除了构造函数的其他地⽅来修改它。不要为它们提供setter⽅法,同时不会通过任何函数泄露出对此成员的引⽤。需要记住的是,把对象声明成final仅仅保证了它不会被重新赋上另外⼀个值,你仍然可以通过此引⽤来修改引⽤对象的属性。这⼀点是关键,⾯试官通常喜欢听到你强调这⼀点。
4、String的substring()⽅法内部是如何实现的?
⼜⼀个Java⾯试的好问题,你应该答出“substring⽅法通过原字符串创建了⼀个新的对象”,否则你的回答肯定是不能令⼈满意的。这个问题也经常被拿来测试应聘者对于substring()可能带来的内存泄漏风险是否有所了解。直到Java 1.7版本之
前,substring会保存⼀份原字符串的字符数组的引⽤,这意味着,如果你从1GB⼤⼩的字符串⾥截取了5个字符,⽽这5个字符也会阻⽌那1GB内存被回收,因为这个引⽤是强引⽤。mysql面试题常问
5、你在写存储过程或者在Java⾥调⽤存储过程的时候如何来处理错误情况?
这是个很棘⼿的Java⾯试题,答案也并不固定。我的答案是,写存储过程的时候⼀旦有操作失败,则⼀定要返回错误码。但是在调⽤存储过程的时候出错的话捕捉SQLException却是唯⼀能做的。
6、Java 中新的 Lock 接⼝相对于同步代码块(synchronized block)有什么优势?如果让你实现⼀个
⾼性能缓存,⽀持并发读取和单⼀写⼊,你如何保证数据完整性。
多线程和并发编程中使⽤ lock 接⼝的最⼤优势是它为读和写提供两个单独的锁,可以让你构建⾼性能数据结构,⽐如ConcurrentHashMap 和条件阻塞。
这道 Java 线程⾯试题越来越多见,⽽且随后的⾯试题都基于⾯试者对这道题的回答。
我强烈建议在任何 Java 多线程⾯试前都要多看看有关锁的知识,因为如今电⼦交易系统的客户端和数据交互中,锁被频繁使⽤来构建缓存。
7、Executor.submit()和ute()这两个⽅法有什么区别?
前者返回⼀个Future对象,可以通过这个对象来获得⼯作线程执⾏的结果。
当我们考察异常处理的时候,⼜会发现另外⼀个不同。当你使⽤execute提交的任务抛出异常时,此异常将会交由未捕捉异常处理过程来处理(uncaught exception handler),当你没有显式指定⼀个异常处理器的话,默认情况下仅仅会通过打印出错误堆栈。当你⽤submit来提交⼀个任务的时候,这个任务⼀旦抛出异常(⽆论是否是运⾏时异常),那这个异常是任务返回对象的⼀部分。对这样⼀种情形,当你调⽤()⽅法的时候,这个⽅法会重新抛出这个异常,并且会使⽤ExecutionException进⾏包装。
8、能否写⼀段⽤Java 4或5来遍历⼀个HashMap的代码?
事实上,⽤Java可以有四种⽅式来遍历任何⼀个Map,⼀种是使⽤keySet()⽅法获取所有的键,然后遍历这些键,再依次通过get()⽅法来获取对应的值。第⼆种⽅法可以使⽤entrySet()来获取键值对的集合,然后使⽤for each语句来遍历这个集合,遍历的时候获得的每个键值对已经包含了键和值。这种算是⼀种更优的⽅式,因为每轮遍历的时候同时获得了key和value,⽆需再调⽤get()⽅法,get()⽅法在那种如果bucket位置有⼀个巨⼤的链表的时候的性能开销是O(n)。第三种⽅法是获取entrySet之后⽤iterator依次获取每个键值对。第四种⽅法是获得key set之后⽤iterator依次获取每个key,然后再根据key来调⽤get⽅法。
9、你在什么时候会重写hashCode()和equals()⽅法?
当你需要根据业务逻辑来进⾏相等性判断、⽽不是根据对象相等性来判断的时候你就需要重写这两个函数了。例如,两个Employee对象相等的依据是它们拥有相同的emp_id,尽管它们有可能是两个不同的Object对象,并且分别在不同的地⽅被创建。同时,如果你准备把它们当作HashMap中的key来使⽤的话,你也必须重写这两个⽅法。现在,作为Java中equals-hashcode的⼀个约定,当你重写equals的时候必须也重写hashcode,否则你会打破诸如Set, Map等集合赖以正常⼯作的约定。你可以看看我的另外⼀篇博⽂来理解这两个⽅法之间的微妙区别与联系。
10、如果不重写hashCode⽅法会有什么问题?
如果不重写equals⽅法的话,equals和hashCode之间的约定就会被打破:当通过equals⽅法返回相等的两个对象,他们的hashCode也必须⼀样。如果不重写hashCode⽅法的话,即使是使⽤equals⽅法返回值为true的两个对象,当它们插⼊同⼀个
map的时候,因为hashCode返回不同所以仍然会被插⼊到两个不同的位置。这样就打破了HashMap的本来⽬的,因为Map本⾝不允许存进去两个key相同的值。当使⽤put⽅法插⼊⼀个的时候,HashMap会先计算对象的hashcode,然后根据它来到存储位置(bucket),然后遍历此存储位置上所有的Map.Entry对象来查看是否与待插⼊对象相同。如果没有提供hashCode的话,这些就都做不到了。
11、HashMap,在调⽤get()⽅法的时候equals()和hashCode()⽅法都起了什么样的作⽤?
应聘者应该知道的是,⼀旦你提到了hashCode()⽅法,⼈们很可能要问HashMap是如何使⽤这个函数的。当你向HashMap插⼊⼀个key的时候,⾸先,这个对象的hashCode()⽅法会被调⽤,调⽤结果⽤来计算将要存储的位置(bucket)。
因为某个位置上可能以链表的⽅式已经包含了多个Map.Entry对象,所以HashMap会使⽤equals()⽅法来将此对象与所有这些Map.Entry所包含的key进⾏对⽐,以确定此key对象是否已经存在。
12、在Java中如何避免死锁?
你可以通过打破互相等待的局⾯来避免死锁。为了达到这⼀点,你需要在代码中合理地安排获取和释放锁的顺序。如果获得锁的顺序是固定的,并且获得的顺序和释放的顺序刚好相反的话,就不会产⽣出现死锁的条件了。
13、说说ClassLoader.loadClass()与Class.forName()的区别
ClassLoader.loadClass()与Class.forName()⼤家都知道是反射⽤来构造类的⽅法,但是他们的⽤法还是有⼀定区别的。
在讲区别之前,我觉得很有不要把类的加载过程在此整理⼀下。
在Java中,类装载器把⼀个类装⼊Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接⼜可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要⼯作如下:
装载:查和导⼊类或接⼝的⼆进制数据;
链接:执⾏下⾯的校验、准备和解析步骤,其中解析步骤是可以选择的;
校验:检查导⼊类或接⼝的⼆进制数据的正确性;
准备:给类的静态变量分配并初始化存储空间;
解析:将符号引⽤转成直接引⽤;
初始化:激活类的静态变量的初始化Java代码和静态Java代码块。千锋教育php清华版
于是乎我们可以开始看2者的区别了。
Class.forName(className)⽅法,其实调⽤的⽅法是Class.forName(className,true,classloader);注意看第2个boolean参数,它表⽰的意思,在loadClass后必须初始化。⽐较下我们前⾯准备jvm加载类的知识,我们可以清晰的看到在执⾏过此⽅法后,⽬标对象的 static块代码已经被执⾏,static参数也已经被初始化。
再看ClassLoader.loadClass(className)⽅法,其实他调⽤的⽅法是ClassLoader.loadClass(className,false);还是注意看第2个 boolean参数,该参数表⽰⽬标对象被装载后不进⾏链接,这就意味这不会去执⾏该类静态块中间的内容。因此2者的区别就显⽽易见了。
最后还有必要在此提⼀下new⽅法和newInstance⽅法的区别
newInstance: 弱类型。低效率。只能调⽤⽆参构造。
new: 强类型。相对⾼效。能调⽤任何public构造。
例如,在JDBC编程中,常看到这样的⽤法,Class.forName("sql.jdbc.Driver"),如果换成了
getClass().getClassLoader().loadClass("sql.jdbc.Driver"),就不⾏。
为什么呢?打开sql.jdbc.Driver的源代码看看,
java类和对象的基本概念static {
try {
java.isterDriver(new Driver());linux系统架构图
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
Driver在static块中会注册⾃⼰到java.sql.DriverManager。⽽static块就是在Class的初始化中被执⾏。所以这个地⽅就只能⽤Class.forName(className)。
以上就是本⽂的全部内容,希望对⼤家的学习有所帮助,也希望⼤家多多⽀持。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论