年初时面试的一些总结,作为存货,现在发布出来吧。
面试了两周,大大小小的公司也都去试了一遍,大概的套路也都摸索出来了一些,基本会考察什么点,如何去考察,都大概了解了些。这两周收获很多,心情也跌宕起伏过。现在唯一感觉遗憾的就是,后悔没有早出来面试几家,先了解都考察哪些问题,这样再回去有针对性的准备准备,肯定效果要好的很多!现在这种集中面试,发现了问题,虽然能查缺补漏,但是大的问题已经没有时间和机会去解决了,只能硬上了。这里先按照这几天出现频次多的重点内容做一个整理吧。
Java集合的知识,是要作为基础重点考察的,而HashMap更是首当其冲。
首先HashMap的结构要明白,是数组加链表。put方法、get方法的原理(注意equals和hashcode方法)、hash冲突的解决、扩容相关的知识(扩容扩的是什么)、和HashTable,comcurrentmap的差别、comcurrentmap是否可以替代HashTable、如何实现线程安全等问题。自己实现一个map(应该是先考察对HashMap的了解,之后是对实现map接口,重写equals和hashcode方法的考察)。
其他的集合类没有太多的考察,只是有问:ArrayList与linkedlist区别?当然,如果有时间好好准备的话,其他的集合类可以稍微深入的了解一下,如果时间不够充裕,至少都要简单的了解一下。
数据库是必考察的,其中索引更是重中之重。
都了解哪些索引?聚簇索引和非聚簇索引?什么情况下索引会生效/失效?为什么用B+树索引?一个表有员工名称、性别、年龄、部门四个字段,索引加到那一列比较好?数据库索引的原理?
常见的数据库优化有哪些方式?
MySQL的innodb和myISAM引擎的区别?数据库保存数据的原理?当然都是保存成文件,具体的呢?数据库查询慢的原因?数据库连接池原理?如何自己设计?数据库是是如何实现查询的?原理?结构化数据库与nosql数据库差别?数据库读写分离?
Java虚拟机是必须知道的,但是考察范围目前看来大多仅限于Java内存模型和GC原理。Java的内存模型简单讲一下(画一下)?讲一下GC是怎么实现的?什么情况下会发生GC?GCROOT都在内存的哪个部分?为什么分为新生代和老年代,只有老年代不行吗?常见的JVM调优?(只有搜狐和百度问到这个地步了,其他的小厂小公司没有这么深入)
令我奇怪的是,对于Spring本身的考察倒是不多,主要对AOP做了深入的考察。
AOP的几个概念:切面?切点?通知?AOP是怎么实现的?动态代理是怎么实现的?AOP有什么缺陷?除了动态代理,Spring的AOP还有其他的实现方式吗?
简单介绍一下Spring?Spring的事务是怎么实现的?实现的原理(百度问的真深)?Springbean单例?什么时候不用单例?SpringMVC一个请求的过程?
优点和缺点?常用的配置熟悉吗?
多线程也是必须考察的,而且互联网公司对于并发多线程很看重。没办法,工作中没有接触过,只能是硬背知识点了。进程与线程介绍一下?线程的几个状态简单介绍一下,画一下图?有几种方式实现多线程?线程池了解吗?不了解的话,如果你设计应该怎么设计呢?synchronized关键字?volatile关键字?二者的区别?都是如何实现的?一个类里,static方法和非static方法加synchronized,是同一个锁吗?线程之间的通信,写一个生产消费者的模型?ThreadLocal?
设计模式问的不是很多,但是必须要掌握的是,最简单的单例模式是要懂得,AOP涉及到的代理模式也是要懂的,二者都应该手写出来。
- 手写单例模式,注意volatile。
- 动态代理
- 生产者消费者模式
- 工厂模式
主要考察了kafka(因为简历上写的使用过kafka)
kafka简单介绍一下?kafka为什么是高吞吐的?其他的消息队列了解吗?和其他的消息队列相比有什么区别?partition和consumer?offset的控制与保存?
经过几家公司的面试,发现redis是现在很主流使用的。redis的工作原理?为什么快?什么时候会做持久化?redis单线程了解吗?redis集群了解吗?redis的瓶颈在哪里?和kafka相比,redis用于队列,有什么优点和缺点?Redis和memcached区别?redis的几种数据类型?那redis那么好,为什么不直接替代memcached呢?
简单介绍一下dubbo。dubbo原理,就是说消费者是怎么调用服务提供者的?其他的rpc了解吗?有什么差别呢?
一般面试考察的算法相关的题目,都是很基础常见简单的。稍微整理一下考察到的算法:
- 二分查找,以及变种,就是如果查找值重复则返回全部位置(两次)
- 快速排序
- 二叉树的结构,二叉树中查找是否存在某节点,四种遍历算法。
为什么快?了解es的索引吗?常见的聚合查询?分片了解吗?评分机制了解吗?
网络相关的东西问的不多,浏览器访问一个url的过程问到了两次。
浏览器输入一个url后的过程,都发生了什么?能说多少说多少?TCP的四次挥手(估计应该也要知道三次握手的的状态)?如何保存一个用户的登录状态?
主要考察把解决问题的思路转换成代码的能力。
记录一下遇到的手写代码要解决的问题:
1. 一个无序数组,返回前M个最大值。(两次考察)
2. 模拟一个机器人在方格阵中的行进。
3. 像abcabcbc这个字符串,找到最大重复子串。
4. 链表反转(两次考察猫眼和搜狐)
5. 用两个栈模拟一个队列。
6. 设计一个栈,保证取最大值的时候,时间复杂度是o(1)。(思路:其中一个栈作为栈,另一个栈保存最大值)
7. 一个数组,获取过半重复的值?
8. N个数字的数组,输出全部的质数。
9. 一只股票的价格每天都在波动,给一段历史区间的每天价格,求什么时候买入,什么时候卖出收益最大?(扫一遍数组就可以)。之后又问如果两次交易呢?也是相当于,选一天划分为两段,之后算各自的最大,加起来总的最大。
这个应该准备好回答,说一个项目就行,之后我们就可以带节奏了,面试官肯定会根据你说的往下问。比如说,我这个项目数据库优化的好。那么问题来了,你是怎么优化的呢?然后顺着引入到这类知识点的考察。
public void erfen() {
int iwantnum = 8;
int[] list = {
0, 1, 2, 3, 4, 5, 6, 7};
int low = 0;
int high = list.length;
while (low < high) {
int mid = (int)Math.ceil((low + high) / 2);
if (list[mid] == iwantnum) {
System.out.println("位置为:" + mid);
return;
} else if (list[mid] > iwantnum) {
high = mid - 1;
} else {
low = mid + 1;
}
}
System.out.println("找不到呀");
}
/**
* 快排
* @param array
* @param low
* @param high
*/
public void sort(int[] array, int low, int high) {
if (low >= high) {
return;
}
int index = position(array, low, high);
sort(array, low, index-1);
sort(array, index + 1, high);
}
/**
* 快排返回位置
* @param array
* @param low
* @param high
* @return
*/
public int position(int[] array, int low, int high) {
int index = array[low];
while (low < high) {
while (low < high && array[high] >= index) {
high--;
}
array[low] = array[high];
while (low < high && array[low] <= index) {
low++;
}
array[high] = array[low];
}
array[high] = index;
return high;
}
public void findMaxM(int[] array, int start, int end, int M) {
int index = array[start];
int low = start;
int high = end;
while (low < high) {
while (low < high && array[high] >= index) {
high--;
}
array[low] = array[high];
while (low < high && array[low] <= index) {
low++;
}
array[high] = array[low];
}
array[high] = index;
if (end - high + 1 == M) {
return;
}else if (end - high + 1 > M) {
System.out.println(end - high + 1);
findMaxM(array,high+1,end,M);
}else{
findMaxM(array,start,high-1,M-(end - high + 1));
}
}
class Node{
public int value;
public Node left;
public Node right;
public Node(int v){
this.value=v;
this.left=null;
this.right=null;
}
}
/**
* @param root 树根节点
* 递归先序遍历,根节点-左子树-右子树
*/
public static void preOrderRec(Node root){
if(root!=null){
System.out.println(root.value);
preOrderRec(root.left);
preOrderRec(root.right);
}
}
/**
* @param root 树根节点
* 递归中序遍历,左子树-根节点-右子树
*/
public static void inOrderRec(Node root){
if(root!=null){
inOrderRec(root.left);
System.out.println(root.value);
inOrderRec(root.right);
}
}
/**
* @param root 树根节点
* 递归后序遍历,左子树-右子树-根节点
*/
public static void postOrderRec(Node root){
if(root!=null){
postOrderRec(root.left);
postOrderRec(root.right);
System.out.println(root.value);
}
}
public class Singleton {
//避免指令重排序
private volatile static Singleton instance;
private static Object lock = new Object();
private Singleton() {
}
public static Singleton GetInstance() {
if (instance == null) {
synchronized (lock) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
public interface HelloService {
public void SayHello();
}
public class HelloServiceImpl implements HelloService {
@Override
public void SayHello() {
System.out.println("大家好");
}
}
public class ProxyHello implements InvocationHandler {
private Object object;
public Object bind(Object object){
this.object = object;
return Proxy.newProxyInstance(object.getClass().getClassLoader(),
object.getClass().getInterfaces(), this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object resultObj = null;
System.out.println("我先说句话!");
resultObj = method.invoke(object, args);
System.out.println("我说完了!");
return resultObj;
}
}
public void test() {
ProxyHello proxyHello = new ProxyHello();
HelloService helloService = new HelloServiceImpl();
helloService = (HelloService) proxyHello.bind(helloService);
helloService.SayHello();
}
public class Solution {
// 求解字符串中的最长重复子串
public static String maxRepat(String input) {
// 参数检查
if (input == null || input.length() == 0) {
return null;
}
// 重复子串的最长长度
int max = 0;
// 最长重复子串的起始位置
int first = 0;
int k = 0;
for (int i = 1; i < input.length(); i++) {
for (int j = 0; j < input.length() - i; j++) {
if (input.charAt(j) == input.charAt(i + j)) {
k++;
} else {
k = 0;
}
if (k > max) {
max = k;
first = j - k + 1;
}
}
}
if (max > 0) {
System.out.println(max);
return input.substring(first, first + max);
}
return null;
}
public static void main(String[] args) {
String str1 = "hellowdhelloko";
String result = Solution.maxRepat(str1);
System.out.println(result);
}
}
public static Node Reverse1(Node head) {
// head看作是前一结点,head.getNext()是当前结点,reHead是反转后新链表的头结点
if (head == null || head.getNext() == null) {
return head;// 若为空链或者当前结点在尾结点,则直接还回
}
Node reHead = Reverse1(head.getNext());// 先反转后续节点head.getNext()
head.getNext().setNext(head);// 将当前结点的指针域指向前一结点
head.setNext(null);// 前一结点的指针域令为null;
return reHead;// 反转后新链表的头结点
}