Java面试总结

年初时面试的一些总结,作为存货,现在发布出来吧。


面试了两周,大大小小的公司也都去试了一遍,大概的套路也都摸索出来了一些,基本会考察什么点,如何去考察,都大概了解了些。这两周收获很多,心情也跌宕起伏过。现在唯一感觉遗憾的就是,后悔没有早出来面试几家,先了解都考察哪些问题,这样再回去有针对性的准备准备,肯定效果要好的很多!现在这种集中面试,发现了问题,虽然能查缺补漏,但是大的问题已经没有时间和机会去解决了,只能硬上了。这里先按照这几天出现频次多的重点内容做一个整理吧。

1.集合类

Java集合的知识,是要作为基础重点考察的,而HashMap更是首当其冲。

HashMap

首先HashMap的结构要明白,是数组加链表。put方法、get方法的原理(注意equals和hashcode方法)、hash冲突的解决、扩容相关的知识(扩容扩的是什么)、和HashTable,comcurrentmap的差别、comcurrentmap是否可以替代HashTable、如何实现线程安全等问题。自己实现一个map(应该是先考察对HashMap的了解,之后是对实现map接口,重写equals和hashcode方法的考察)。

其他的集合类

其他的集合类没有太多的考察,只是有问:ArrayList与linkedlist区别?当然,如果有时间好好准备的话,其他的集合类可以稍微深入的了解一下,如果时间不够充裕,至少都要简单的了解一下。

2.数据库

数据库是必考察的,其中索引更是重中之重。

索引

都了解哪些索引?聚簇索引和非聚簇索引?什么情况下索引会生效/失效?为什么用B+树索引?一个表有员工名称、性别、年龄、部门四个字段,索引加到那一列比较好?数据库索引的原理?

优化

常见的数据库优化有哪些方式?

其他

MySQL的innodb和myISAM引擎的区别?数据库保存数据的原理?当然都是保存成文件,具体的呢?数据库查询慢的原因?数据库连接池原理?如何自己设计?数据库是是如何实现查询的?原理?结构化数据库与nosql数据库差别?数据库读写分离?

3.JVM

Java虚拟机是必须知道的,但是考察范围目前看来大多仅限于Java内存模型和GC原理。Java的内存模型简单讲一下(画一下)?讲一下GC是怎么实现的?什么情况下会发生GC?GCROOT都在内存的哪个部分?为什么分为新生代和老年代,只有老年代不行吗?常见的JVM调优?(只有搜狐和百度问到这个地步了,其他的小厂小公司没有这么深入)

4.Spring

令我奇怪的是,对于Spring本身的考察倒是不多,主要对AOP做了深入的考察。

Spring AOP

AOP的几个概念:切面?切点?通知?AOP是怎么实现的?动态代理是怎么实现的?AOP有什么缺陷?除了动态代理,Spring的AOP还有其他的实现方式吗?

Spring

简单介绍一下Spring?Spring的事务是怎么实现的?实现的原理(百度问的真深)?Springbean单例?什么时候不用单例?SpringMVC一个请求的过程?

Spring boot

优点和缺点?常用的配置熟悉吗?

5.多线程

多线程也是必须考察的,而且互联网公司对于并发多线程很看重。没办法,工作中没有接触过,只能是硬背知识点了。进程与线程介绍一下?线程的几个状态简单介绍一下,画一下图?有几种方式实现多线程?线程池了解吗?不了解的话,如果你设计应该怎么设计呢?synchronized关键字?volatile关键字?二者的区别?都是如何实现的?一个类里,static方法和非static方法加synchronized,是同一个锁吗?线程之间的通信,写一个生产消费者的模型?ThreadLocal?

6.设计模式

设计模式问的不是很多,但是必须要掌握的是,最简单的单例模式是要懂得,AOP涉及到的代理模式也是要懂的,二者都应该手写出来。
- 手写单例模式,注意volatile。
- 动态代理
- 生产者消费者模式
- 工厂模式

7.消息队列

主要考察了kafka(因为简历上写的使用过kafka)
kafka简单介绍一下?kafka为什么是高吞吐的?其他的消息队列了解吗?和其他的消息队列相比有什么区别?partition和consumer?offset的控制与保存?

8.redis

经过几家公司的面试,发现redis是现在很主流使用的。redis的工作原理?为什么快?什么时候会做持久化?redis单线程了解吗?redis集群了解吗?redis的瓶颈在哪里?和kafka相比,redis用于队列,有什么优点和缺点?Redis和memcached区别?redis的几种数据类型?那redis那么好,为什么不直接替代memcached呢?

9.RPC

简单介绍一下dubbo。dubbo原理,就是说消费者是怎么调用服务提供者的?其他的rpc了解吗?有什么差别呢?

10.算法

一般面试考察的算法相关的题目,都是很基础常见简单的。稍微整理一下考察到的算法:
- 二分查找,以及变种,就是如果查找值重复则返回全部位置(两次)
- 快速排序
- 二叉树的结构,二叉树中查找是否存在某节点,四种遍历算法。

11.Elasticsearch(我的简历中有,就记录一下)

为什么快?了解es的索引吗?常见的聚合查询?分片了解吗?评分机制了解吗?

12.网络协议与web相关

网络相关的东西问的不多,浏览器访问一个url的过程问到了两次。
浏览器输入一个url后的过程,都发生了什么?能说多少说多少?TCP的四次挥手(估计应该也要知道三次握手的的状态)?如何保存一个用户的登录状态?

13.手写代码解决问题

主要考察把解决问题的思路转换成代码的能力。
记录一下遇到的手写代码要解决的问题:
1. 一个无序数组,返回前M个最大值。(两次考察)
2. 模拟一个机器人在方格阵中的行进。
3. 像abcabcbc这个字符串,找到最大重复子串。
4. 链表反转(两次考察猫眼和搜狐)
5. 用两个栈模拟一个队列。
6. 设计一个栈,保证取最大值的时候,时间复杂度是o(1)。(思路:其中一个栈作为栈,另一个栈保存最大值)
7. 一个数组,获取过半重复的值?
8. N个数字的数组,输出全部的质数。
9. 一只股票的价格每天都在波动,给一段历史区间的每天价格,求什么时候买入,什么时候卖出收益最大?(扫一遍数组就可以)。之后又问如果两次交易呢?也是相当于,选一天划分为两段,之后算各自的最大,加起来总的最大。

非技术性问题

你做的项目,哪个是最满意的/认为是亮点的?

这个应该准备好回答,说一个项目就行,之后我们就可以带节奏了,面试官肯定会根据你说的往下问。比如说,我这个项目数据库优化的好。那么问题来了,你是怎么优化的呢?然后顺着引入到这类知识点的考察。

一点建议

  1. 不要裸辞之后去准备,但是也不是不推荐裸辞。而是最好先请假去面试几家公司,摸清当前市场价格以及常见考察点,之后回来根据自己目前的技术水平进行查缺补漏。
  2. 面试官考察,首先肯定是要根据简历上写了的技术点进行考察,所以,简历上有的知识点,一定要确保了解掌握。
  3. 敢于要价,hr一般不会因为要价高不给发offer,顶多会压价。但是一般hr手里会有一些余地,所以可以看情况怎么把hr手里的那一丢丢争取过来咯。

附录:几个基础的手写代码题

二分查找

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;
}

无序数组返回前M个最大值(参照快排思路)

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;// 反转后新链表的头结点  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白码上飞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值