-
- 前言
- 正文
- 自我介绍
- 数据结构和算法
- Java篇
- Java EE知识点储备
- 计算机网络
- 操作系统
- 数据库相关
- XML
- 常识性知识
- 总结
前言
准备了接近两个月的面试笔试,现在终于是可以休息下了。真真是应了那句老话“台上一分钟, 台下十年功。”。
人嘛,越努力,才会越幸运。机会总是留给有准备的人的。
下面分享一下我的Java实习生准备所看过的材料,(虽然至今还有些依然看不懂地方。) 希望对这方面的同学有点帮助。
正文
自我介绍
先针对自己的情况写段自我介绍,真实一些就好了,这方面我倒是没有什么其他的建议。我就写了我自己的真实的情况,比如喜欢写博客,喜欢学习新技术,做过哪些小工具什么的。
但是有一点,那就是别作假,否则的话很容易被发现的,而且后果一般会很严重。
数据结构和算法
这段时间自己也总结了关于数据结构和算法相关的一些例子。也看了几本书,总的来说《剑指Offer》挺好,就我不多的面试经验来看,大部分面试官都是面试的上面的题,所以有时间的同学可以好好参考参考。
原书是使用C++实现的,我又用Python实现了其中大部分的内容,有兴趣的话可以参考我的GitHub: https://github.com/guoruibiao/sword-to-offer
由于本人经验,技术能力有限,有不恰当,不正确的地方还望批评指正。觉得还可以的也可以给我点个star,(^__^) 嘻嘻……
阿里的那个《技术之瞳》我也看了,里面内容比较多,但是也比较乱。知识面很广,但是我感觉深度上还是不太够。不是很适合我。
其他的类似于Java面试宝典啊这些的,复习的时候认真看一遍,就可以扔一边了。但是前提是认真看了,因为基础没打牢的话,更别提生层建筑了。于乎微处见真章。
Java篇
- Java 虚拟机,这个专栏强烈推荐,讲的真的是太好了。
- 深入Java语言,看这个就够了
- 经典面试100题
- Java面试题汇总
- Java 面试须知
- Java面试集锦1
- Java面试集锦2
- Java泛型深入理解
- ThreadLocal底层原理浅析
- concurrencyHashMap 底层原理,好在哪?
分段锁原理,对有竞争的区块实现同步,区块内维护hashEntry - final and finally
- 集合详解
- 集合类的线程安全总结:
- 安全的: Vector, Hashtable, concurrentHashMap…
- 不安全的:HashMap, ArrayList, TreeMap, linkedList,HashSet(底层基于HashMap实现)
- 容量相关:
- Vector默认初始容量为10, 自增长量为0。翻倍增长拓展机制。
- ArrayList:默认为10,最大上限为Integer。MAX_SIZE-8;拓展机制:newCapacity = oldCapacity + (oldCapacity >> 1);
- Hashtable:默认大小为11, 装载因子为0.75
- HashMap: 默认16,装载因子0.75, 最大上限1<<30
- 集合类的线程安全总结:
- Comparator和Comparable的区别
一个类实现了Camparable接口则表明这个类的对象之间是可以相互比较的,这个类对象组成的集合就可以直接使用sort方法排序。
Comparator可以看成一种算法的实现,将算法和数据分离,Comparator也可以在下面两种环境下使用:
Comparable 接口以提供自然排序顺序。
对于那些没有自然顺序的类、或者当您想要一个不同于自然顺序的顺序时,您可以实现
Comparator 接口来定义您自己的排序函数。可以将Comparator传递给Collections.sort或Arrays.sort。
Comparator接口
当一个类并未实现Comparable,或者不喜欢缺省的Comaparable行为。可以实现Comparator接口
直接实现Comparator的compare接口完成自定义比较类。
例:Arrays.sort(results, new Comparator<RepDataQueryResultVO/>() 数组排序 RepDataQueryExecutor
例:Collections.sort(lst,new Comparator<TaskPrintSchemeVO/>()
- Java NIO 相关:
- Java 的instanceof关键字底层实现:
维护了主要超类型(继承深度)小于7的主数组, 和次要超类型(判断的时候需要super链遍历查找);在字节码使用特殊指令对常量池中的相关符号引用进行判断,来决定true和false。
- Java内存模型
- 年轻代,: 伊甸区,两个存活区(总有一个为空,轮询转移)。 (逐级晋升机制)
- 年老代: 在年轻代中经历了N次垃圾回收后依然存活的生命期较长的对象。
- 永久代: 存放静态文件,如Java类,方法等。持久代对垃圾回收没有显著影响。
- GC 触发机制:
- 新生代GC: 在新对象生成且在伊甸区申请空间失败的时候会触发一次对伊甸区的GC,把
存活的对象放置到存活区。通常来说伊甸区的GC会比较频繁。 - Full GC: 对整个堆进行整理,速度慢,次数少。触发的时机: 年轻代被写满, 持久代被写满,系统GC被调用。
- 新生代GC: 在新对象生成且在伊甸区申请空间失败的时候会触发一次对伊甸区的GC,把
- GC 回收标准:
- 引用计数算法: 可能导致循环引用导致GC效果不好,代替方式有引用标记清理方式
- 根搜索算法: 那些对象可以作为GC Root? 答案是虚拟机栈中引用的对象,方法区中的类静态属性引用的对象
方法区中的常量引用的对象, 本地方法栈中JNI的引用对象 - 引用状态:强(被引用着)软(还有些有那个但不是必须, 二次回收)弱(非必须对象,下次就回收)虚(告知被引用的对象,要被回收啦,作用不大): 程度依次减小,
- 方法区回收: JVM没有强调方法区的回收,但是也是非常有用的,对于效率要求较高而言。对于废弃常量比较好处理
直接判断有没有相关的引用即可。对于无效类对象而言有下面几种方式。该类的实例都被回收;该类的classLoader被回收;该类对应的字节码对象Class没有被任何地方引用,无法在任何地方通过反射来访问该类的方法。
- GC 算法:
- 标记清除算法: 对要进行回收的对象进行标记,在下次GC的时候予以回收。但是碎片化严重,对下次的大对象的分配效率不高。
- 复制算法: 为了解决效率问题而出现,将内存分为可用的两块,对于存活的对象
- 进行复制转移,碎片化现象减轻。但是代价高啊,可用的只有一半,对于新生代内存区采用比较频繁,
- 标记整理算法: 对于老年代对象存活率高,这样复制算法不适用,而采用标记整理算法。将老年代中存活的对象移动到一侧,对另外的区域进行GC。
综上所述,对不同的代区采用不同的GC算法,会使得GC的效率得到进一步的提升。
- 垃圾收集器: 针对比较常用的HotSpot虚拟机而言,支持不同类型的垃圾收集器。
- Serial收集器。新生代中采用单线程的复制算法的垃圾收集器。Client端默认
- Parallel收集器。新生代中采用多线程的复制算法的垃圾收集器。Server端默认,高吞吐量。
- Serial Old收集器,采用单线程的标记-整理算法的垃圾收集器,Client端使用。
- Parallel Old收集器, 采用多线程的标记-整理算法的垃圾收集器,Server端使用。
- CMS(Concurrent-Mark-Sweep),以一种获取最短回收停顿为时间目标的老年代收集器,采用标记-清除算法。
- G1(Garbage-First),对新生代和老年代不予区分,而是对内存堆空间划分区块,分配不同的优先级,使用更有效率的垃圾回收算法。
- 锁相关:
- 乐观锁机制
- CAS(Compare And Swap)方法
- 悲观锁:
Synchronized就是悲观锁的一种,也称之为独占锁,加了synchronized关键字的
代码基本上就只能以单线程的形式去执行了,它会导致其他需要该资源的线程挂起,直到前面的线程执行完毕释放所资源。而另外一种乐观锁是一种更高效的机制,它的原理就是每次不加锁去执行某项操作,如果发生冲突则失败并重试,直到成功为止,其实本 质上不算锁,所以很多地方也称之为自旋。
- ClassLoader相关:
双亲委派模型,以及唯一性的好处。
有哪些类加载器 (Bootstrap ClassLoader[C语言实现,很底层], Ext.., Application)
- 一些原则:
- 小黄鸭测试法
- happen-before原则
- fail-fast原则
- fail-safe原则
- 开闭原则
Java EE知识点储备
- Spring中 IOC的生命周期:
(<3/>init-method,<1/>intilizingbean接口方法<2/>afterPropertiesSet的先后顺序)等。
//详情参考
Bean factory implementations should support the standard bean lifecycle interfaces as far as possible. The full set of initialization methods and their standard order is:
1. BeanNameAware's setBeanName
2. BeanClassLoaderAware's setBeanClassLoader
3. BeanFactoryAware's setBeanFactory
4. ResourceLoaderAware's setResourceLoader (only applicable when running in an application context)
5. ApplicationEventPublisherAware's setApplicationEventPublisher (only applicable when running in an application context)
6. MessageSourceAware's setMessageSource (only applicable when running in an application context)
7. ApplicationContextAware's setApplicationContext (only applicable when running in an application context)
8. ServletContextAware's setServletContext (only applicable when running in a web application context)
9. postProcessBeforeInitialization methods of BeanPostProcessors
10. InitializingBean's afterPropertiesSet
11. a custom init-method definition
12. postProcessAfterInitialization methods of BeanPostProcessors
On shutdown of a bean factory, the following lifecycle methods apply:
1. DisposableBean's destroy
2. a custom destroy-method definition
大致可以这么来理解:
bean容器创建–通过反射实现bean开始实例化–通过注入信息设置属性–可以被调用啦–容器被销毁/bean中设置的destory-method方法被调用实现bean的销毁。
我自己的理解如下:
- Strtus:
首先是客户端发起一个指向servlet容器的请求,被容器上一系列的过滤器获得主要是ActionContextCleanUp
然后被FilterDispatcher拦截到,经过查询ActionMapper之后决定调用哪个action
FilterDispatcher将请求转发给ActionProxy,ActionMapper内部经过查询ConfigurationManager找到要调用的Action类
ActionProxy代理产生一个ActionInvocation实例,回调action的execute方法,通过返回的串调用相关的jsp页面
最后反向经过一系列的拦截器,释放不需要的如ThreadLocal里面的数据,对象信息。 - SpringMVC:
前端管家DispatcherServlet接收来自客户端的请求,然后通过handlerMapping查找到相应的处理器。转交给handler处理相应的业务逻辑。处理结束后返回一个ModelAndView,返回给客户端以响应。 - Servlet:
web服务器接收到客户端请求之后,将请求指向一个与url对应的servlet的class,如果已实例化则调用init,service,destory。如果未创建,则通过查询ServletConfig获取到相关的字节码信息,进行实例化,接着进行上面的操作。
- Hibernate一二级缓存,以及lazy-load
- 一级缓存: Hibernate内置,默认,切不可卸载
- 二级缓存你:指SessionFactory的外部缓存,可配置可更改,可卸载。常用插件
- EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。
memcache等。
- EhCache:可作为进程范围的缓存,存放数据的物理介质可以是内存或硬盘,对Hibernate的查询缓存提供了支持。
- 惰性加载: 通过延迟加载技术可以避免过多的,过早的加载表里面的数据,从而降低系统的内存开销。Hibernate中使用代理来实现这一个效果,要想使用在核心配置文件中对lazy-load属性设值为true即可。
- Spring AOP解决了什么问题?怎么实现的?aop与cglib,与asm的关系。
- AOP实现了面向切面编程。连接点(可以被增强的方法),切入点(被增强的方法),增强(业务中增强的具体逻辑),切面(将增强应用到切入点的过程)
- AOP中借助于动态代理或者cglib实现。动态代理需要接口,cglib是子类增强机制,不能增强final类。
- ASM是一个字节码操作框架,直接修改产生字节码数据流,效率高,但是人工需要对字节码非常的熟悉。
- Spring中的事务传播属性:
- REQUESTED: 默认,当前不存在则新增一个事务。
- MANDATORY:没有事务则失败
- NEVER: 有事务则失败
- NOT_SUPPORT: 以非事务的形式执行,如果当前存在事务,则挂起
- SUPPORT: 支持当前事务,如果当前没有事务,则以非事务形式执行
- NESTED:支持当前事务,新增savepoint,与当前事务同步提交或者回滚,但是内部事务出现异常时,不会影响当前事务的回滚。简单来说就是内层操作依赖于外层的事务。
- Spring中的BeanFactory和FactoryBean的区别:
- BeanFactory使得管理任何性质的类得以实现,ApplicationContext是BeanFactory的增强。
- FactoryBean提供一个工厂方法,用来得到其他的bean实例。从FactoryBean上得到的bean与普通的bean的配置不同,因为不是由容器产生,所以不需要提供class属性。如ProxyFactoryBean用于创建代理(根据advisor生成TargetBean的代理,得以实现增强行为)。
计算机网络
- TCP趣文:
- HTTP协议, http1.0和http2.0区别:
- http2的二进制压缩流,完全的多路传输。使用报头压缩降低了开销;实现了推送,降低了客户端多次请求。
- 报文结构—请求部分:
请求行:请求方法+空格+URL+空格+协议版本+回车+换行符
请求头: 什么referer,userAgent啦等等
空行: 回车符+换行符
请求数据: 来自客户端的请求的数据。 - 报文结构—响应部分:
状态行:协议版本, 状态码啊啥的
消息报头:时间, content-type, content-length啥的
响应正文: 正文数据流。
- 推送模式:
- 长连接: 减少了客户端轮询的计算,但是对于服务器而言有一定压力,且服务对象会变少。
- 轮询: 可以很快的监听到数据变化,但是计算代价较高。
- websocket: 事件队列模型,解决了上面的问题,服务器和客户端都可作为主动方。
但是总的来说有两大方向: Pull 和 Push; Push对于客户端而言流量消耗更少;Pull对服务器端而言压力稍小。
- WebService浅析:
- soap:简单对象访问协议,基于xm,可以和诸多应用层协议一起工作,成本高,代价大。
- rest:表属性状态转移,是一个抽象的对于资源访问的一套设计思路。安全性相比soap较低,但是更流行,成本低。
- WSDL:web service description language.也就是哪个服务器以什么方式提供什么服务的意思,也即服务发现。工作流程:
根据WSDL构造一条soap语句发给服务器,获取返回的soap数据并解码为WSDL,获取相应服务。
对二者的思考,就是现在的rest也只是soap模式在rest下的借尸还魂,并没有彻底的改变,而rest缺少一套标准,实现的方式也比较乱,各大厂商也不一致。需要时间的沉淀。
操作系统
- 分页和分段:
操作系统分页是为了更好的提高利用率,一个系统页面大小是一定的,不可改变。而分段则是用户决定的,来方便用户
- 页面置换算法: FIFO, LRU, LRU的二次标记法来给次机会。
- 进程调度算法: 短作业优先, 优先级策略, 轮询, 分级别的整合法。
数据库相关
- Memcache和Redis:
- IO模型:memcache是多线程非阻塞IO,分为主监听和工作线程;redis则是单线程事件驱动,效率更高一点。
- 内存分配: memcache是内存预分配,碎片少,但有空间浪费之嫌;
redis是现场分配,碎片化现象存在,但是非临时数据不会踢到磁盘,仍会留在内存,素以更适合存储而不是cache. - 数据一致性: memcache采用cas命令可以保证数据一致性;redis使用原子操作保证事务正常运行。
- 存储方式: memcache使用key-value; redis除此之外还可以存储list,map等数据结构。
总结:
redis的最佳使用方式时全部数据in-memory.
redis更多场景是作为memcache的替代品来使用。
当需要除了key-value之外的数据结构支持的时候,redis更适合。
当存储的数据不鞥呗剔除的时候,使用redis更合适。
- 数据库事务以及隔离级别:
- 四大特性: ACID(原子性[要么都成功,要么全失败], 一致性[事务从一个状态变成另一个一致性的状态], 隔离性[一个事务中的行为不会影响到另一个事务], 持久性[改变了数据就不能再撤销了])。
- 不考虑事务的时候有可能在读取数据库时发生如下问题:
脏读: 一个事务中读取了另一个事务中未提交的数据。
不可重复读: 相同的sql语句,两次读取的结果不一致。
幻读: 一个事务读取到了另一个事务提交后的结果,或者改变了本次事务的数据的结果。也称为虚读。 - 隔离级别:
- 未提交读: 最低级别,任何情况都不能保证。
- 提交读: 可避免脏读。
- 重复读: 可避免脏读,不可重复读(MySQL默认支持!)
- 序列化: 最强设置。
事务的隔离级别越高,执行的效率就会越低!在JDBC代码中可以对connection对象设置相应的隔离级别。
- 数据库数据结构:
-
- mysql存储引擎中索引的实现机制;使用B-Tree优势在于出度大,磁盘预读效果好,与之相比,二叉树,红黑树则不好。理论上来讲,出度越大,索引的效率越高。尽量采用自增字段作为索引,因为按页存储的时候不需要移动数据块,而不重复的字段则类似于随机方式,需要移动数据块,效率会差点。这就是因为innodb的聚集特性(数据在磁盘上的存储顺序和索引顺序一致)决定的。
- 2.数据库事务的几种粒度;表锁(不会产生死锁,但是并发度低), 页锁(有可能产生死锁,但是用的不多), 行锁(并发度高,但是有可能产生死锁)。
- 3.行锁,表锁;乐观锁,悲观锁
悲观锁:在读取数据时锁住那几行,其他对这几行的更新需要等到悲观锁结束时才能继续
乐观锁:读取数据时不锁,更新时检查是否数据已经被更新过,如果是则取消当前更新
一般在悲观锁的等待时间过长而不能接受时我们才会选择乐观锁
-
- Mysql是怎么实现repeable read的,(next-key)?
- innode的锁,死锁和 索引相关的东西。
- 隔离级别: 未提交读,提交读, 重复读,序列化(一次只有一个人可以操作)
- innodb默认采用行锁,对数据采用repeatable read,也就是事务开始后读取到的结果会一致,这和oracle的committed read(只能读取提交的事务数据,有可能产生两次结果不一致的问题)不同。
- 行锁并发度高,不易产生死锁;表级锁并发度低,不会产生死锁。
- 锁本身又分为读锁和写锁; 按照共享与否大致分为共享锁和排它锁等四个。
- mysql死锁: 行级锁并不是直接锁行记录,而是锁索引(主键索引和非主键索引)。
- 一条sql语句如操作了主键索引,则会锁住此主键索引;操作了非主键索引,则先锁住非主键索引,再锁住主键索引。
- 在update,delete操作时,mysql还会锁住where下相邻的记录,实现next-key locking.
可以很好的解决“幻读”问题。
- SQL 优化入门
- 使用内部函数; 避免select*; 使用表的别名,提高效率和准确度; 用[not]exist 代替[not]in; 使用索引;
- 分表,分库,精简表结构,字段结构;等等。
- SQL 优化详解
XML
DTD(Document Type Define,文档定义类型), schema( XSD, XML Schema Document)之间的区别和联系
- DTD:采用非XML语法规范,拓展性差。命名冲突不易解决。
- schema: 采用XML语法规范
常识性知识
- 查找Linux中占用磁盘最大的文件:
du path -type f -size +NumberG | sort -n -r | head -n 10
找出给定目录下以G为单位的最大的10个文件,并排序输出。
- 一个大文件4G,里面一行行的数字,这时内存只有256M,如果做排序?
http://blog.csdn.net/ztkhhhhhd/article/details/53138631
- 全局唯一ID问题:
时间戳,加去中心化,加逻辑分片,机器号等
- 什么是布隆过滤器,其实现原理是? False positive指的是?
- 布隆过滤器就是使用一个很大的数组,根据K个哈希函数得到K个位置,在数组中将这K个位置对应的值设置为1;
- 查询的时候,仍旧采用这K个哈希函数对查询串进行哈希运算,判断对应的K歌位置是否为1;有可能因为其他的串的
- 插入导致这K个位置全部为1,导致误判现象的产生,不过这种情况发生的可能性很小,毕竟要与K个哈希位置都一致。
- RPC的负载均衡、服务发现怎么做的
- 单点LB: 容易出现单点失效问题,且需要配合DNS服务,性能开销略大
- LB集成到客户端: 对于开发成本较大,平台限制。但是对于服务发现和健康检查而言,服务器端压力减小。
- LB作为主机单进程,相当于将LB进程和服务进程隔离开,一个主机挂了只影响单个主机服务,但是配置较为麻烦,环节多,出错调试较为困难。
- Linux使用及问题排查:
- grep,awk,sed; 是否自己写过shell脚本;
- 常见的cpu load过高,us过高,一般是什么问题。引申出是否用过top,jstat,jstack等。
可以先ps -aux (或者top, htop)找到占比高的进程号,然后使用ps -Lp 122427 cu找到对应的java进程的每个线程的CPU使用率,追踪线程内部运行的状况。
常见的内存问题一般有哪些。 引申出是否用过free,top, jmap等。
总结
林林总总的一万多字了,但是这还远远不能覆盖全部。而且距离一个合格的Java程序员仅仅知道这些还远远不够,我们能做的就是尽可能的让自己接近那个标准吧。
上面这些链接也好,总结也好,大家还是需要有自己的理解。
“尽信书,则不如无书!”,其实也是这么个道理,带着思考来阅读,效率,效果都可能会更好。
最后,希望大家都能找到自己心仪的offer。