一线互联网公司Java高级面试题总结

技术小能手 2018-08-06

安全 算法 java innodb 性能 线程 模块 互联网 spring 排序 索引 面试题 数据结构

Java重点知识
多线程(线程状态、线程并发,Synchronized与Lock的区别和底层原理,常用的锁及其使用场景和原理,
volatile和ThreadLocal解决了什么问题,CAS在Java中的实现
线程池原理和实现,阻塞队列和线程安全队列,
线程间通信: synchronized + wait、notify/notifyAll, Lock + Condition 的多路复用,
CountDownLatch、CyclicBarrier和Semaphore的作用和用法,使用场景)
JVM内存管理机制和垃圾回收机制(内存模型、GC策略、算法、分代回收GC类型,Full GC、Minor GC作用范围和触发条件)
JVM内存调优(内存调整的6个参数,了解是怎么回事,一般做项目过程中使用较多)
设计模式(熟悉常见设计模式的应用场景,会画类图,常用:代理,2个工厂,策略,单例,观察者,适配器,组合与装饰)
JAVA集合类框架(理解框架图、HashMap、ArrayList、HashSet等的关系和区别,其中HashMap的存储机制几乎每次都有问)
HashMap的原理,底层数据结构,rehash的过程,指针碰撞问题HashMap的线程安全问题,为什么会产生这样的线程安全问题ConcurrentHashMap的数据结构,底层原理,put和get是否线程安全
JAVA的异常处理机制(异常的分类、常见的异常有哪些、Try catch finally的使用)
JVM运行机制(理解JVM是如何运行的,理解类加载机制和类的初始化顺序)
Java 的NIO 3个主要概念 Channel、Buffer、Selector,为何提高了性能?加分项:熟悉Netty
Linux基础(面试笔试中对linux也有一定的要求,建议最好搭建一个linux虚拟机,并练习常用的命令)
框架
Spring
Spring IOC原理,Bean的生成和生命周期(工厂模式 + 反射生成 + 单例),Spring用到的设计模式
Spring AOP原理和应用(动态代理与cglib代理,使用场景和代理的本质区别)
Spring如何处理高并发?高并发下,如何保证性能?
单例模式 + ThreadLocal
单例模式大大节省了对象的创建和销毁,有利于性能提高,ThreadLocal用来保证线程安全性
Spring单例模式下,用ThreadLocal来切换不同线程直接的参数,用ThreadLocal是为了保证线程安全,实际上,ThreadLocal的key就是当前线程的Thread实例
单例模式下,Spring把每个线程可能存在线程安全问题的参数值放进了ThreadLocal,虽然是一个实例,但在不同线程下的数据是相互隔离的,
因为运行时创建和销毁的bean大大减少了,所以大多数场景下,这种方式对内存资源的消耗较少,并且并发越高,优势越明显
特别注意:
Spring MVC的Controller不是线程安全的!!!
Spring MVC 是基于方法的拦截,粒度更细,而Spring的Controller默认是Singleton的,即:每个request请求,系统都会用同一个Controller去处理,
Spring MVC和Servlet都是方法级别的线程安全,如果单例的Controller或Servlet中存在实例变量,都是线程不安全的,而Struts2确实是线程安全的
优点:不用每次创建Controller,减少了对象创建和销毁
缺点:Controller是单例的,Controller里面的变量线程不安全
解决方案:
1.在Controller中使用ThreadLocal变量,把不安全的变量封装进ThreadLocal,使用ThreadLocal来保存类变量,将类变量保存在线程的变量域中,让不同的请求隔离开来
2.声明Controller为原型 scope="prototype",每个请求都创建新的Controller
3.Controller中不使用实例变量
Spring 事务管理的使用和原理?事务的传播属性
声明式事务管理,在Service之上或Service的方法之上,添加 @Transactional注解
@Transactional如何工作?
Spring在启动时,会去解析生成相关的Bean,这是会查看拥有相关注解的类和方法,并且为这些类和方法生成代理,并根据 @Transactional的相关参数进行相关配置注入,这样就在代理中把相关的事务处理掉了(开启正常提交事务,异常回滚事务)真正的数据库层,事务提交和回滚是通过binlog和redo log实现的
Spring如何解决对象的循环依赖引用?( 只支持Singleton作用域的, setter方式的循环依赖!不支持构造器方式和prototype的循环依赖)
原理:
创建Bean A时,先通过无参构造器创建一个A实例,此时属性都是空的,但对象引用已经创建创建出来,然后把Bean A的引用提前暴露出来,
然后setter B属性时,创建B对象,此时同样通过无参构造器,构造一个B对象的引用,并将B对象引用暴露出来。
接着B执行setter方法,去池中找到A(因为此时,A已经暴露出来,有指向该对象的引用了),这样依赖B就构造完成,也初始化完成,然后A接着初始化完成,循环依赖就这么解决了!
总结:先创建对象引用,再通过setter()方式,给属性赋值,层层创建对象 !!!
Bean A初始化时,先对其依赖B进行初始化,同时,通过默认无参构造器,生成自己的引用,而不调用其setter()方法,
当B对象创建时,如果还依赖C,则也通过无参构造器,生成B的引用,
C对象创建时,如果引用了A,则去对象池中查到A的引用,然后调用setter()方式,注入A,完成C对象的创建
C创建完成后,B使用setter()方式,注入C,完成B对象创建,
B对象场景完成后,A使用setter()方式,注入B,完成A对象创建,
最终,完成setter()方式的循环依赖!
数据库
InnoDB和MyISAM区别和选择
1.InnoDB不支持FULLTEXT类型的索引。
2.InnoDB 中不保存表的具体行数,也就是说,执行select count() from table时,InnoDB要扫描一遍整个表来计算有多少行,但是MyISAM只要简单的读出保存好的行数即可。注意的是,当count()语句包含 where条件时,两种表的操作是一样的。
3.对于AUTO_INCREMENT类型的字段,InnoDB中必须包含只有该字段的索引,但是在MyISAM表中,可以和其他字段一起建立联合索引。
4.DELETE FROM table时,InnoDB不会重新建立表,而是一行一行的删除。
5.LOAD TABLE FROM MASTER操作对InnoDB是不起作用的,解决方法是首先把InnoDB表改成MyISAM表,导入数据后再改成InnoDB表,但是对于使用的额外的InnoDB特性(例如外键)的表不适用。
另外,InnoDB表的行锁也不是绝对的,如果在执行一个SQL语句时MySQL不能确定要扫描的范围,InnoDB表同样会锁全表,例如update table set num=1 where name like “%aaa%”
任何一种表都不是万能的,只用恰当的针对业务类型来选择合适的表类型,才能最大的发挥MySQL的性能优势。
悲观锁和乐观锁的含义(悲观锁:真正的锁,只允许一个线程操作同一条记录,
乐观锁:一种冲突检测机制,一般通过版本号或时间戳方式实现,对性能影响较小)
索引使用及其索引原理(索引底层实现:B+树)
Query查询优化
1.explain sql查看执行效率,定位优化对象的性能瓶颈
2.永远用小结果驱动大的结果集
3.尽可能在索引中完成排序
4.只取出自己需要的column,而不是*
5.使用最有效的过滤条件
6.用表连接代替子查询
7.当只要一行数据时,使用limit 1
8.为搜索字段建立索引
9.千万不要ORDER BY RAND(),避免select *
10.尽可能使用NOT NULL
11.开启查询缓存,并为查询缓存优化查询语句
eg:
select username from user where add_time >= now()
注意:
1.这样的语句不会使用查询缓存,
2.像NOW()和RAND()或是其它的诸如此类的SQL函数都不会开启查询缓存,因为这些函数的返回是会不定的易变的。所以,你所需要的就是用一个变量来代替MySQL的函数,从而开启缓存
3.修改, 对now()进行处理,只取年月日 yyyy-MM-dd,变为一个不衣变的值
Redis的5种数据结构和使用场景
Redis的持久化机制
Redis中Hash类型的底层2种实现区别(压缩表: 省内存 和 跳跃表:查询更快)
Redis作为