送你一份P6级Java面试题(一)

导读

作者:瞿云康,英文名jacksonKang,是一名努力成长中的Java爱好者

原文:http://mayiyk.cn/article/6

本文为一个java面试题集锦系列首篇,主要把一些常见的java面试题目整理发给大家,希望能给大家以后面试中提供一些帮助。

一、String stringBuffer和stringBuilder

String:适用于少量字符串操作的情况,为字符串常量,即对象一旦创建之后对象是不可更改的。

StringBuffer:适用多线程下字符缓冲区进行大量操作的情况。属于线程安全。

StringBuilder:适用于单线程下在字符缓冲区进行大量操作的情况。属于线程不安全。

StringBuffer与StringBuilder均为字符串变量,对象是变量,即可以更改。StringBuilder所有方面都没有被synchronized修饰,它的效率比StringBuffer要高。

二、HashMap的底层实现原理

HashMap底层是数组+链表实现的,它是一个entry类的数组,entry中包含key和value的值,允许key、value可以为null,通过key的hashcode计算在这个数组所在位置,遍历这个链表从而查询到值,hashMap默认的初始化容器大小为16,之后每次扩充为原来的2倍。属于线程不安全的。

在JDK1.7及以前,HashMap中维护着Entry,Entry中维护着key,value以及hash和next指针,而整个HashMap实际就是一个Entry数组

当向HashMap 中put 一对键值时,它会根据key的hashCode 值计算出一个位置,该位置就是此对象准备往数组中存放的位置。

如果该位置没有对象存在,就将此对象直接放进数组当中;如果该位置已经有对象存在了,则顺着此存在的对象的链开始寻找(为了判断是否是否值相同,map不允许<key,value>键值对重复),如果此链上有对象的话,再去使用equals方法进行比较,如果对此链上的每个对象的equals 方法比较为false,则将该对象放到数组当中,然后将数组中该位置以前存在的那个对象链接到此对象的后面。

get方法类似,通过key取hash找到数组的某个位置,然后遍历这个数组上的每个Entry,直到key值equals则返回。

如果Hash碰撞严重,那么JDK1.7中的实现性能就很差,因为每次插入都要遍历完整条链去查看key值是否重复,每次get也要遍历整个链,在JDK1.8中,由于链表的查找复杂度为O(n),而红黑树的查找复杂度为O(logn),JDK1.8中采用链表/红黑树的方式实现HashMap,达到某个阀值时,链表转成了红黑树。

三、HashMapConcurrent HashMap区别,Concurrent HashMap 线程安全吗,ConcurrentHashMap如何保证线程安全?

HashMap不是线程安全的,ConcurrentHashMap是线程安全的,HashMap内部维护着一个Entry数组,而ConcurrentHashMap内部有一个Segment段,它将大的HashMap切分成若干个段(小的HashMap),然后让数据在每一段上Hash,这样多个线程在不同段上的Hash操作一定是线程安全的,所以只需要同步同一个段上的线程就可以了,这样实现了锁的分离,大大增加了并发量。ConcurrentHashMap的实现中还使用了不变模式final和volatile来保障线程安全

四、HashMapput方法做了哪些操作

他会根据key的hashcode重新计算hash值,根据hash值得到这个元素在数据中的位置,如果数组在该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放在链尾。

五、HashMap的缺点

HashMap不支持并发操作,所以不适用于多线程环境,在高并发状态下,如果产生同时put操作,并且在put时刚好遇上要扩容,可能会形成链环,如果get的key的hashcode值刚好在链环的位置,而这个key对应的值为null或不存在就会进入死循环,耗尽cpu内存。

六、ConcurrentHashMap

底层是数组+红黑树+链表实现,可以替代HashTable,因为使用了多个锁代替hashTable中的单个锁,也就是锁分离技术,hashTable是锁住了整个数组导致效率特别低,属于线程安全。

七、Springmvc的工作原理

用户发送请求至前端控制器DispatcherServlet,DispatcherServlet收到请求调用handlerMapping处理器映射器,解析请求对应的handler,开始由handlerAdapter适配器处理请求,并处理相应的业务逻辑并返回一个ModelAndView对象,根据返回的modelAndView选择一个合适视图解析器返回给DispatcherServlet,视图解析器根据view和model渲染页面。

八、集合的父类及各子类的区别

Collection

├List 允许重复值、有序容器,保持了每个元素的插入顺序

│├LinkedList 线程不安全增删改速度快,基于链表数据结构

│├ArrayList 线程不安全查询速度快,基于动态数组结构

│└Vector 线程安全

│└Stack

├set 不允许重复值、无序容器,无法保证元素的存储顺序,可以通过TreeSet的comparator或者comparable维护一个排序顺序

│├HashSet 线程不安全无序(存入与取出时顺序不同)不重复,无索引,底层hash表结构查询删除快,增改慢。

│├TreeSet 是sortedSet接口的唯一实现,可以进行排序

九、什么是线程安全与不安全

线程安全是多个线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问,直到该线程读取完,其他线程才可使用,不会出现数据不一致或者数据污染。

线程不安全就是不提供数据访问保护,有可能出现多个线程后更改数据造成所得到数据时脏数据。

十、为什么使用reids,用redis都做了些什么

Redis是一个开源的key-value型数据库,运行在内存,速度快,同时支持持久化,支持的数据结构丰富(String,list,set,sorted set,hash),支持订阅发布功能。

String:(常用命令get,incr,decr,mget)常规的key-value缓存应用。统计访问次数,关注人数。

Hash(常用命令hget,hset,hgetall)省市区联动,用户信息

List(常用命令 Ipush,rpush,Ipop)用户关注列表

Set 注册用户用户名不能重复,使用set记录注册用户

十一、Jvm如何调优

VisualVM:jdk自带,功能强大

堆信息查看:观察内存释放情况、集合类检查、对象树-----(查看堆空间的大小分配【年轻代、年老代、持久代分配】、提供即时的垃圾回收功能、垃圾监控),可以解决年老代年轻代大小划分是否合理、内存泄漏、垃圾回收算法设置是否合理。

线程监控:可以查看线程在系统中的数量,各个线程都处在什么状态下、死锁检查

热点分析:cpu热点---检查系统哪些方面占用大量cpu时间,内存热点-------检查哪些对象在系统中的数量最大。

内存泄漏检查:在错误的使用下导致使用完毕的资源无法回收,引起系统错误。常表现年老代空间被占满(java heap space)一般根据垃圾回收前后情况对比,同时根据对象引用情况分析,基本可以找到泄漏点。

持久代被沾满:无法为新的class分配存储空间而引发的异常,在java大量的反射的使用会造成,大量动态反射生成的类不断被加载。解决:-XX:MaxPermSize=16m

堆栈溢出:一般是递归没返回,或者循环调用造成

线程池栈满:java中一个线程空间大小是有限的,在jdk5以后这个值是1m。解决:增加线程栈大小,-Xss2m

十二、GC垃圾回收机制

什么时候:eden满了minor gc,升到老年代的对象大于老年代剩余空间full gc,或者小于时被HandlePromotionFailure参数强制full gc

对什么东西:从root搜索不到,而且经过第一次标记,清理后仍然没有复活的对象。

做什么事情:删除不使用的对象,腾出内存空间。

算法

1、标记-清除:分为标记和清除两个阶段,首先标记出所有需要回收的对象,标记完成后统一回收所有被标记的对象。

2、复制算法:它将可用的内存分为两块,每次只用其中一块,当这一块内存用完了,就将还存活的对象复制到另一块上,然后再把已经使用过的内存空间一次性清理掉。

3、标记-整理算法:是让所有存活的对象都向一端移动,然后直接清理掉边界以外的内存。

十三、 JVM如何GC,新生代,老年代,持久代,都存储哪些东西?

JVM通过可达性(可触及性)分析算法标记出哪些对象是垃圾对象,然后将垃圾对象进行回收,在新生代中采用复制算法,在老年代中采用标记清理或标记压缩算法。新生代存储了新new出的对象,老年代存储了大的对象和多次GC后仍然存在的老年对象,持久代存储了类信息,常量(JDK7中String常量池被移到堆中),静态变量(JDK7中被移到了Java堆),类方法

十四、 强引用、软引用、弱引用、虚引用的区别

强引用:是最难被GC回收的,宁可虚拟机抛出异常,中断程序,也不会去回收该对象。(Object o=new Object)

软引用:非必须引用,内存溢出之前进行回收。软引用主要用户实现类似缓存的功能,在内存足够的情况下直接通过软引用取值,无需从繁忙的真实来源查询数据,提升速度;当内存不足时,自动删除这部分缓存数据,从真正的来源查询这些数据。

弱引用:第二次垃圾回收时回收。弱引用主要用于监控对象是否已经被垃圾回收器标记为即将回收的垃圾,可以通过弱引用的isEnQueued方法返回对象是否被垃圾回收器标记。

虚引用:垃圾回收时回收,无法通过引用取到对象值。虚引用主要用于检测对象是否已经从内存中删除。

十五、Spring的原理

内部核心是ioc,动态注入,让一个对象的创建不用new了,可以自动生成,这其实就是利用了java里的反射,反射其实就是在运行时动态的去创建、调用对象,spring就是在运行时,跟xml spring的配置文件来动态的创建对象,和调用对象的方法。

Spring还有一个核心就是AOP面向切面编程,可以为某一类对象进行监督和控制从而达到堆一个模块扩充的功能。这些都是通过配置类达到的。

十六、Aop如何实现

静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行时,代理类.class文件就已经存在了。

动态代理:在程序运行时,运用发射机制动态创建而成。

Cglib动态代理:针对类实现的代理。它的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。

JDK的动态代理依靠接口实现,如果有些类没有实现接口,则不能使用jdk代理,就要使用cglib代理了。

十七、MySQL如何优化

使用查询缓存优化查询。(例如NOW,RAND或其他SQL函数都不会开启查询缓存,因为返回是会不定的易变得,所以你需要的就是一个变量来替代mysql的函数,从而开启缓存)

使用EXPLANIN关键字检测查询(可以使我们知道MYSQL是如何处理SQL语句的,帮助分析查询语句或是表结构性能瓶颈;索引主键是如何被利用的,数据表是如何被搜索或排序的,语句格式:EXPLAIN+SELECT语句)

当只有一行数据时使用LIMIT 1(可以增加性能,会查到第一条数据后停止搜索)

为搜索字段建立索引(普通索引INDEX:适用于name、email等一般属性,唯一索引UNIQUE:要求索引字段值在表中是唯一的,唯一索引允许有空值。适用于身份证号码、用户账户等,全文索引:适用于VARCHAR和TEXT类型字段)

在jion表的时候使用相当类型的列,并将其索引(存在很多jion查询时,保证两个表中jion的字段时被建立索引的,这样mysql会启动优化JION的sql语句机制)

避免使用select *

永远为每一张表设置一个ID主键

尽可能的不要赋值为NULL(会占用存储空间,程序判断更加复杂,索引不存储null值,使用not null约束以及默认值。)

固定长度的表会更快(容易计算下一个数据的偏移量,容易被缓存和重建。)

垂直分割:是一种把数据库中的表按列变成几张表的方法,这样可以降低表的复杂度和字段数目

拆分大的delete或insert(这两个大操作会锁表,这样别的操作就进不来了,可以使用LIMIT控制操作记录的数量)

关联推荐:

知数堂面试宝典——《叶问》专辑:https://zhishutang.com/Z4z

END返回搜狐,查看更多

责任编辑:

本文来自网络,不代表新锐科技自媒体博客平台立场,转载请注明出处: https://www.chaoqi.net/xinchao/2019/1115/271591.html

作者:

这家伙太懒了,什么都没有留下!

发表评论

联系我们

联系我们

0898-88881688

在线咨询: QQ交谈

邮箱: email@wangzhan.com

工作时间:周一至周五,9:00-17:30,节假日休息

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部