秋招网易Java开发凉经(2020年9月25日

在这里插入图片描述

一面

自我介绍

面试官: 你先做个简单的自我介绍吧
我:简单的介绍了学校,专业,在校期间参与的比赛与项目.(我说了大学期间主要打比赛,项目比较薄弱.

项目介绍

面试官: 介绍一下你的项目及其背景吧
我: 巴拉巴拉
面试官: 你这个项目的难点是什么
我: 我做的东西比较简单,主要是解决了自己的实际需求以及练手了一下实际的JavaWeb项目的开发流程.
面试官: 好的,了解了,那我们先做两道题吧。
我: 好

手写算法

链表反转

面试官: 写一个链表反转吧
我:

1
2
3
4
5
6
7
8
9
10
11
12
13
public Node reverse(Node head) {
if(head == null)
return head;
Node now = head;
Node pre = null;
while(now != null){
Node tmp = now.next;
now.next = pre;
pre = now;
now = tmp;
}
return pre;
}

面试官: 你解释一下你的代码吧
我: 逐行给他解释了一下, 就不断的改变当前节点的next指针的指向,直到当前节点为空节点为止。
面试官: 好的

二叉树的最大深度

面试官: 二叉树的最大深度 你先说一下思路吧
我: 这个我们可以递归做 递归出口就是当前到达了空节点 返回0 否则 返回1 + 左右子树深度的较大值即可
面试官: 你写一下吧
我:

1
2
3
4
5
public static int dfs(TreeNode root){
        if(root == null)
            return 0;
        return 1 + Math.max(dfs(root.left), dfs(root.right));
    }

面试官: 你解释一下吧
我: 就是当前到达空节点, 返回0 否则返回当前层 + 左右子树的较大深度。

二分查找

面试官:你说一下二分查找吧
我: 二分查找一般应用于某一条件可以将数据分为两部分,如大于,小于等条件。 一般二分查找作用于有序的数组中,我们可以根据targer于arr[mid]的关系,将数据分为两部分,进而加速我们查找的过程。朴素的遍历需要o(n)的复杂度,而二分可以降到O(logn)的级别。大概就是这样,需不需要我手写一下?
面试官: 不用了,二分查找比较简单,我们接下来说一下Java基础吧.
我: 好的

Java基础

for循环遍历和迭代器遍历有啥区别

面试官: 我们遍历List集合,用for和 冒号for 有啥区别
我: 我没太理解您意思,您能举个例子吗? (确实是我菜
面试官: 就比如

1
2
3
	for(int i = 0; i < list.size(); i++)

for(Object o : list)

这两种方式有什么区别
我: 这个我不太知道. 我只知道用定义i这种底层也是调用get方式进行返回并输出,冒号for这种实现了Iterator接口. (这里应该是实现了Iterable接口, 重写了Iterator方法
面试官: 你后期可以去了解一下

后期补充答案

  • foreach适用于只是进行集合或数组遍历,for则在较复杂的循环中效率更高。
  • foreach不能对数组或集合进行修改(添加删除操作),如果想要修改就要用for循环。

说一下HashMap中put一个值的具体过程

面试官: 你说一下HashMap中put一个值的具体过程
我: 好的,HashMap put的时候会根据key值计算其HashCode,并通过按位与length-1确定下标.因为底层是数组+链表或者数组+红黑树来实现的。如果所加到的index没有元素则直接插入,否则会判断当前有没有这个key,有的话覆盖,没有的话插入。( 完全是瞎说…

后期补充答案

以下内容针对jdk8

  • put(key, value)中直接调用了内部的putVal方法,并且先对key进行了hash操作;
  • putVal方法中,先检查HashMap数据结构中的索引数组表是否位空,如果是的话则进行一次resize操作;
  • 以HashMap索引数组表的长度减一与key的hash值进行与运算,得出在数组中的索引,如果索引指定的位置值为空,则新建一个k-v的新节点;
  • 如果不满足的3的条件,则说明索引指定的数组位置的已经存在内容,这个时候称之碰撞出现
  • 在上面判断流程走完之后,计算HashMap全局的modCount值,以便对外部并发的迭代操作提供修改的Fail-fast判断提供依据,于此同时增加map中的记录数,并判断记录数是否触及容量扩充的阈值,触及则进行一轮resize操作;
  • 在步骤4中出现碰撞情况时,从步骤7开始展开新一轮逻辑判断和处理;
  • 判断key索引到的节点(暂且称作被碰撞节点)的hash、key是否和当前待插入节点(新节点)的一致,如果是一致的话,则先保存记录下该节点;如果新旧节点的内容不一致时,则再看被碰撞节点是否是树(TreeNode)类型,如果是树类型的话,则按照树的操作去追加新节点内容;如果被碰撞节点不是树类型,则说明当前发生的碰撞在链表中(此时链表尚未转为红黑树),此时进入一轮循环处理逻辑中;
  • 循环中,先判断被碰撞节点的后继节点是否为空,为空则将新节点作为后继节点,作为后继节点之后并判断当前链表长度是否超过最大允许链表长度8,如果大于的话,需要进行一轮是否转树的操作;如果在一开始后继节点不为空,则先判断后继节点是否与新节点相同,相同的话就记录并跳出循环;如果两个条件判断都满足则继续循环,直至进入某一个条件判断然后跳出循环;
  • 步骤8中转树的操作treeifyBin,如果map的索引表为空或者当前索引表长度还小于64(最大转红黑树的索引数组表长度),那么进行resize操作就行了;否则,如果被碰撞节点不为空,那么就顺着被碰撞节点这条树往后新增该新节点;
  • 最后,回到那个被记住的被碰撞节点,如果它不为空,默认情况下,新节点的值将会替换被碰撞节点的值,同时返回被碰撞节点的值(V)。

在这里插入图片描述

面试官: 那我们put可变对象会不会出现问题?
我: 我没太理解您意思,你能举一个例子吗?
面试官: 就比如我put一个学生之后,这个学生的姓名改变了,会出现问题吗?
我: 这个… 我猜他的HashCode值变了,可能无法get到已put过的key值了
面试官: 这个你后期去了解一下吧

后期补充答案

HashMap而言,其get和put会依赖于对象的HashCode和equals方法。若是可变对象,我们修改了某些属性,那么对于其的HashCode必然会发生改变。此时会出现

  • get不到已经put过的value,因为HashMap会根据hash(key) & length - 1,确定下标位置,HashCode的改变致使桶的下标位置不同,从而找不到已经put的value值
    其实这样也是可以接受的,毕竟对象重新设置了某些字段的值,其就算是一个新的对象了,如果这样考虑的话,那么还会出现一种情况
  • 没有put过的对象,可以获取到value,试想 当重新设置了字段的值之后,当新的HashCode与原来的HashCode值相等,那么 就会满足 HashCode相等而且 key也是同一块地址。 致使原来put进去的值被返回.

面试官: (他可能感觉到我的水平太差了,) 说那你并发编程这块和锁这块cas之类的也不熟悉是吧
我: 并发只在本机测试过一些机制,没在项目中使用过.
面试官: 好的,了解了

看我Java基础太菜了 又问了一道Linux的问题

你熟悉的Linux命令有哪些

面试官: 你在Linux环境下会那些操作
我: 主要是 安装 卸载软件, 已经vi的编程。 因为学生机是单核的,会测试一些在并发编程下与本机的区别
面试官: 那你说一下 我要在某个目录下搜索文件 命令应该是咋样写的?
我: ….. 就只记得是find 后面的参数忘了
面试官: 好的

后期补充答案

  • 查找目录:find /(查找范围) -name ‘查找关键字’ -type d
  • 查找文件:find /(查找范围) -name 查找关键字 -print

是在太菜了 就有让我写了两道题

手写算法

给定两棵树,询问一棵树是否完全可以覆盖另一颗树

面试官:你先说一下思路吧
我: 跑一下a树的所有节点,然后判断当前跑的这个节点是否和b树是相同的
面试官: 那你实现一下吧
我:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public static boolean fun(TreeNode a, TreeNode b){
        if(a == null && b == null){
            return true;
        } else if(a == null && b != null){
            return true;
        } else if(a != null && b == null){
            return false;
        } else {
            return check (a, b) || fun(a.left, b) || fun(a.right, b);
        }
    }
    public static boolean check(TreeNode root1, TreeNode root2){
        if(root1 == null && root2 == null){
            return true;
        } else if(root1 != null && root2 != null){
            if(root1.val == root2.val){
                return check(root1.left, root2.left) && check(root1.right, root2.right);
            } else {
                return false;
            }
        } else {
            return false;
        }
    }

面试官: 解释一下吧
我: 大概解释了一下,中途出现了某些问题 然后改了一下, 面试官是 嗯 可以了 应该不会有大的问题 可能有些特殊情况还是有问题.

一道dp

面试官: 最后写一个题吧, 可能对你就比较容易了。有一个三角形a,b,c三个点, 初始在a点,走一步只能到b点或者c点。走两步可以回到a点,即a->b->a或者a->c->a。 然后询问走100步有多少种办法回到a点,

我: 我想一下啊 这肯定是要用dp来写, 然后大概推了下 dp[i][j] 表示第i步走到j点的方案数.
0代表a点,1代表b点,2代表c点;
那么

a点 c点 c点
dp[1][0] = 1 dp[1][1] = 1 dp[1][2] = 1
dp[2][0] = dp[1][1] + dp[1][2] = 2 dp[2][1] = dp[1][0] + dp[1][2] = 1 dp[2][2] = dp[1][0] + dp[1][1] = 1

所以转移方程就是 dp[i][j] = sum(dp[i - 1][!j]);
初始条件就是 dp[0][0] = 1. 然后转移即可
面试官: 好了 可以了 你有什么想问我的

反问

面试官: 好了 可以了 你有什么想问我的
我: 我确实太菜了 可能还是要春招了 您对我复习有什么建议吗?

面试官: 作为校招生 你要加强基础知识,如刚才问你的for和冒号for的区别。如果是社招的话直接卡,但是校招的话 我们还是考虑你的潜力,在集合 并发 以及锁这块要深入的理解 多看底层源码 框架方面的话 如Spring 有经验的话更好,但是相对而言还是把计算机基础打扎实了更好。

我: 好的,谢谢你
面试官: 那我们今天就到这里

一面总结

经历了好几场面试的一轮游,网易甚至不想面了。
面试官是一个很好的面试官,问到不会的也就不会深挖了,挑我熟悉的算法题去做。最后的反问环节给了我很多建议,也讲了并不是面试造火箭,入职拧螺丝。确实是不能做api调用者,有时候我们必须要修改源码 或者 自己造轮子。我们必要要拥有这个能力。 确实感受到自己的实力不足,好好学习,备战春招

二面

自我介绍

面试官: 你先做个简单的自我介绍吧
我:简单的介绍了学校,专业,在校期间参与的比赛与项目.(我说了大学期间主要打比赛,项目比较薄弱.

为什么从C++转Java? 你感觉C++和Java的区别? 为什么不考研

项目

面试官:大文件如何在不同主机间传输? 效率、文件的完整性

面试官:Http缓存 比如图片它是如果确定是要重新获取还是加载缓存?

面试官:不同主机如何传输目录 保证接收端的目录结构

面试官:给定一个字符串 只有加减运算 考虑所有异常 返回结果

二面总结

真的被网易的面试官圈粉了,一个个都太强了。最后建议加强基础知识,以及劝我考研。面试官很年轻啊,感觉30不到,但是真的太强了。除了太强了无话可说。

-------------本文结束,感谢您的阅读!-------------