面试复盘 | 小米一二面(已定SP)+百度一二三面(意向)

两家投的都是安卓,主要问的内容Java和计算机相关的基础。小米base南京,百度base上海。

写在前面:大家好,把最近拿到意向的两家面经分享出来,主要是想回馈牛客,之前看其他同学的面经收获很大。整理花了很多时间,后面我想做一个公众号分享非科班转码学习过程中的一些思考与经验,目前还在筹备阶段,第一阶段的计划是求职经历分享和主管面、HR面相关的问题,后面会出一些技术专题的文章,希望能做出高质量的内容帮助到未来求职的朋友。
求支持~公众号名字《新生代农民工胖虎

小米一面8.28

  • 深挖项目和竞赛
    主要是实验室智能机器人相关的项目(系统设计、机器人视觉、算法落地);
    经典被问为什么研究算法相关的,工作要找开发(算法太难了TT);
    竞赛主要是数学建模(两次国赛两次省赛)。
  • 对安卓系统有哪些理解
    一开始答的activity运行起来之后的一系列过程,答偏了,面试官想问自己对安卓系统的理解,坦诚没有思考过。(问点Java基础吧)
  • JAVA中的引用类型
    1. 强引用,任何时候都不会被;垃圾回收器回收,如果内存不足,宁愿抛出OutOfMemoryError
      使用场景:我们平常大部分使用的场景都是使用了强引用,比如new创建对象,反射获得一个对象等。
    2. 软引用,只有在内存将满的时候才会被垃圾回收器回收,如果还有可用内存,垃圾回收器不会回收。
      软引用可以和一个引用队列进行关联,如果这个软引用的对象被垃圾回收,就会将这个软引用加入到关联的队列中去。 可用于高速缓存。
    3. 弱引用(WeakReference),生命周期更短,只要垃圾回收器运行,就肯定会被回收,不管还有没有可用内存。
      使用场景: 弱引用用于生命周期更短的,对内存更敏感的场景中,比如占用内存很大的Map,java api中就提供了WeakHashMap使用,就会是的大Map被及时清理掉。
    4. 虚引用(PhantomReference),虚引用等于没有引用,任何时候都有可能被垃圾回收。虚引用必须和引用队列联合使用,引用队列的作用和软弱引用一样。
      使用场景: 我觉得他的使用场景应该在判断一个对象是否被垃圾回收了,什么时候引用队列有新的引用入队了,就说明他被回收了。
  • 栈和队列,应用场景
    FILO,FIFO。使用场景答得比较随意,提了一些栈的应用(方法调用过程对应入栈出栈、安卓activity任务栈),队列主要用在一些需要公平处理的场景,消息队列。阻塞队列。
    这个问题被问过很多次,每次都是随意回答一下,希望大佬们评论区指点一波怎么答最好。
  • 生产者消费者问题中的锁的传递,PV操作
    生产者:每次执行时先判断当前库存是否超过负载(最大库存),超过的话就wait,释放锁;否则库存自增,然后唤醒其他线程。
    消费者:每次执行时先判断当前库存是否为零,是的话就wait,释放锁;否则库存自减,然后唤醒其他线程。
    PV操作当时没答上来,后来查了一下应该是基于信号量的方式实现消费者生产者问题。因自己不能确保理解正确了,先丢个链接Java实现PV操作 ,有大佬理解的可以评论区讨论一下!
  • 宏观的描述垃圾回收的思路
    先判断哪些对象需要回收,然后根据对象特性确定相应的回收算法,选用合适的垃圾回收器。
  • 怎么判断对象该不该回收,说说自己的理解。如果出现循环引用怎么办
    引用计数法、可达性分析法
  • 可达性分析法中淘汰的对象一定会被回收吗?
    即使在可达性分析算法中不可达的对象,也并非"非死不可"的,这时候他们暂时处在"缓刑"阶段。要宣告一个对象的真正死亡,至少要经历两次标记过程: 如果对象在进行可达性分析之后发现没有与GC Roots相连接的引用链,那它将会被第一次标记并且进行一次筛选,筛选的条件是此对象是否有必要执行finalize()方法。当对象没有覆盖finalize()方法或者finalize()方法已经被JVM调用过,虚拟机会将这两种情况都视为"没有必要执行",此时的对象才是真正"死"的对象。
    如果这个对象被判定为有必要执行finalize()方法,那么将会被放置在一个叫做F-Queue的队列之中,并在稍后由一个虚拟机自动建立的、低优先级的Finalizer线程去执行它(这里所说的执行指的是虚拟机会触发finalize()方法)。finalize()方法是对象逃脱死亡的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模标记,如果对象在finalize()中成功拯救自己(只需要重新与引用链上的任何一个对象建立起关联关系即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量),那在第二次标记时它将会被移除出"即将回收"的集合;如果对象这时候还是没有逃脱,那基本上它就是真的被回收了。
  • 具体怎么做呢,挑一个垃圾回收算法谈谈自己的理解
    说了复制算法。
  • 复制算有什么问题
    仅针对新生代效果好,浪费内存(只能用一半)转移存活对象时会导致用户线程无法定位引用对象。
  • tcp怎么保证可靠性
  1. 序列号和确认号机制:
    TCP 发送端发送数据包的时候会选择序列号,接收端收到数据包后会检测数据包的完整性,如果检测通过会响应确认号表示收到了数据包。
  2. 超时重发机制:
    发送端发送了数据包后会启动一个定时器,如果一定时间没有收到接收端的确认,将会重新发送该数据包。
  3. 对乱序数据包重新排序:
    从 IP 网络层传输到 TCP 层的数据包可能会乱序,TCP 层会对数据包重新排序再发给应用层。
  4. 丢弃重复数据:
    从 IP 网络层传输到 TCP 层的数据包可能会重复,TCP 层会丢弃重复的数据包。
  5. 流量控制:
    TCP 发送端和接收端都有一个固定大小的缓冲空间,为了防止发送端发送数据的速度太快导致接收端缓冲区溢出,发送端只能发送接收端可以接纳的数据,为了达到这种控制效果,TCP 用了流量控制协议(可变大小的滑动窗口协议)来实现。
    【滑动窗口详解】
    滑动窗口通俗来讲就是一种流量控制技术。
    它本质上是描述接收方的TCP数据报缓冲区大小的数据,发送方根据这个数据来计算自己最多能发送多长的数据,如果发送方收到接收方的窗口大小为0的话,那么发送方将停止发送数据,等到接收方发送窗口大小不为0的数据报的到来。首次发送数据时的窗口是链路带宽决定。
  6. 拥塞控制:在数据传输过程中,可能由于网络状态的问题,造成网络拥堵,此时引入拥塞控制机制,在保证TCP可靠性的同时,提高性能,具体为慢启动、拥塞避免、快重传与快恢复……
  • 算法题:给定一个多叉树,返回这棵树哪一层所有的节点和最大,返回层号。
    本质上就是层序遍历,维护一个层节点和。多叉树第一次写hhh
    import java.util.*;
    public class Duochashu {
      public static void main(String[] args) {
          TreeNode root = new TreeNode(0);
          root.children.add(new TreeNode(1));
          root.children.add(new TreeNode(2));
          root.children.add(new TreeNode(3));
          System.out.println(new Duochashu().getMaxLayerVal(root));
      }
      private int getMaxLayerVal(TreeNode root) {
          Deque<TreeNode> queue = new LinkedList<>();
          int max_layer = 0;
          queue.offer(root);
          while (!queue.isEmpty()) {
              int n = queue.size();
              int tmp = 0;
              for(int i = 0; i < n; i++) {
                  TreeNode node = queue.poll();
                  tmp += node.val;
                  for(TreeNode child : node.children) {
                      queue.offer(child);
                  }
              }
              max_layer = Math.max(max_layer, tmp);
          }
          return max_layer;
      }
    }
    class TreeNode {
      int val;
      List<TreeNode> children;
      public TreeNode() {
      }
      public TreeNode(int val, List<TreeNode> children) {
          this.val = val;
          this.children = children;
      }
      public TreeNode(int val) {
          this.val = val;
          children = new ArrayList<>();
      }
    }

    小结:小米一面是我秋招以来面试中受到鼓励最强烈的一次,面试官会认真听我说为什么放弃原来的研究方向选择做开发,为什么简历写得意向后端但是投了安卓,并且对我的想法表示支持;我在描述自己硕士期间做的课题时他非常耐心,并且表示他作为一个“外行”能够听懂这个项目的意义和我做的工作,一个劲的夸我。最后说:作为非科班的学生,我非常看好你!(一面应该给了我非常好的面评,因此最后定了SP)

小米二面9.9

  • 为啥要选择安卓
    跟一面面试官问了一样的问题hhh,经典再现(后面我会单独整理一份HR面、主管面相关的问题
  • 创建线程的方式
    继承Thread类;实现Runnable接口;实现Callable接口(JDK1.5);线程池方式创建
  • 多线程的死锁产生条件
    互斥条件:一个资源一次只能被一个线程使用
    请求与保持条件:一个线程因请求资源而阻塞时,对已获得资源保持不放
    不剥夺条件:线程获得的资源,在未完全使用完之前,不能强行剥夺
    循环等待条件:若干进程之间形成一种头尾相接的环形等待资源关系
  • final关键字的作用
    修饰变量:背final修饰的变量可以当作常量,不能被修改,需要初始化;
    修饰方法:方法不能被重写;
    修饰类:类不能被继承,类的方法也被默认修饰为final
  • synchronized锁的作用
    使一段代码块或者一个方法在多线程操作访问时保证安全,具有原子性、可见性与有序性。
  • 如果两个线程被synchronized修饰了,线程A拿到了锁,线程B会怎么样?那如果线程A这时候阻塞了,线程B会怎么样
    (1)A拿到锁,B阻塞;
    (2)A阻塞了,B还是继续阻塞。
  • 说说看jvm是啥,有啥用
    (答得比较随意,面试官说别背八股文hhh)他就是来解释Java源码编译出来的字节码文件的,针对每种OS,JVM有单独的映射规范来解释,因此java可以做到跨平台。JVM负责内存里怎么给对象分配空间、怎么回收分配出去的空间……
  • 反射有什么用,反射是怎么实现的
    盲区。面试后补习了反射相关的知识。
    反射就是一种动态获取类的信息并调用的机制。运行状态中,对任意一个类,都可以知道它的成员变量与方法,并且可以调用。关于怎么实现的,我觉得可以说通过Class.forName()方法获取类对象,然后通过类对象的newInstance()方法可以创建实例,通过getMethod()方法可以获取类的成员方法对象,成员方法对象通过invoke()实习调用该方法。。不过这么答可能有些浅了,《反射是怎么实现的》求教一波。
  • 算法题:合并有序数组(这道题百度二面也遇到了,小米面试官没有要求,百度要求只用两次循环语句)
    class Solution {
      public void merge(int[] nums1, int m, int[] nums2, int n) {
          int p1 = 0, p2 = 0;
          int[] sorted = new int[m + n];
          int cur;
          while (p1 < m || p2 < n) {
              if (p1 == m) {
                  cur = nums2[p2++];
              } else if (p2 == n) {
                  cur = nums1[p1++];
              } else if (nums1[p1] < nums2[p2]) {
                  cur = nums1[p1++];
              } else {
                  cur = nums2[p2++];
              }
              sorted[p1 + p2 - 1] = cur;
          }
          for (int i = 0; i != m + n; ++i) {
              nums1[i] = sorted[i];
          }
      }
    }

    小结:二面问题还是比较基础的,不过当时自己困得很,很多应该背熟的八股说的结结巴巴,不过面试官也没有刁难,全程笑着问。

百度一面9.12(9点开始,持续60min,精神抖擞)

一面纯八股(致秋招遇到的第一个开视频的女面试官温柔姐姐人美心善带着口罩爱眯眼笑)

  • 面向对象三大特性,怎么理解多态
    继承 封装 多态
    多态 可以分为两种:编译时多态即重载。指java允许方法名相同而参数不同(返回值可以相同也可以不同),同一个类中允许存在多个同名函数,只要参数类型或参数个数不同即可。
    运行时多态即重写,指在继承体系中,子类重写父类方法,JVM运行时根据调用该方法的类型决定调用那个方法。
    Java实现多态的三个必要条件:继承、重写和向上转型:
    • 继承就是多态中必须存在继承关系的父子类
    • 重写就是子类对父类的某些方法重新定义,
    • 向上转型是说让父类引用指向子类对象,这样该引用既可以调用父类方法,也可以调用子类方法。

  • 抽象类和接口分别在什么场景使用
    图片说明

  • String是基础类型还是引用类型
    引用类型

  • String对象在进行一个+操作时,会发生什么
    运行时, 两个字符串str1, str2的拼接首先会调用 String.valueOf(obj),这个Obj为str1,而String.valueOf(Obj)中的实现是return obj == null ? “null” : obj.toString(), 然后产生StringBuilder, 调用的StringBuilder(str1)构造方法, 把StringBuilder初始化,长度为str1.length()+16,并且调用append(str1)!接下来调用StringBuilder.append(str2), 把第二个字符串拼接进去, 然后调用StringBuilder.toString返回结果。

  • “+”原本是运算符,为什么可以用来拼接字符串?
    运算符重载。
    (这里是有争议的,Java本身是不支持运算符重载的,String的+操作实际上String类作者设计的语法糖。还可以追问为啥Java不给支持运算符重载?我猜是性能问题)

  • new一个对象的背后发生了什么
    Java在new一个对象的时候,会先查看对象所属的类有没有被加载到内存,如果没有的话,就会先通过类的全限定名来加载。
    加载并初始化类完成后,再进行对象的创建工作。我们先假设是第一次使用该类,这样的话new一个对象就可以分为两个过程:加载并初始化类和创建对象。
    创建对象:
    (1)在堆区分配对象需要的内存
    分配的内存包括本类和父类的所有实例变量,但不包括任何静态变量
    (2)对所有实例变量赋默认值
    将方法区内对实例变量的定义拷贝一份到堆区,然后赋默认值
    (3)执行实例初始化代码
    初始化顺序是先初始化父类再初始化子类,初始化时先执行实例代码块然后是构造方法
    如果有类似于Child c = new Child()形式的c引用的话,在栈区定义Child类型引用变量c,然后将堆区对象的地址赋给它

  • java的泛型了解吗,一般用在什么场景
    泛型就是把类型参数化,在编译的时候才会确定具体的参数。可以用在类、接口、方法中。
    场景:我觉得当类 方法 接口 这些 当我们不确定使用的对象的类型是啥 或者 可能存在多种类型的可能的时候,可以使用泛型。最熟悉的就是集合类的实现都用到了泛型,这样我们在用的时候可以在<>中指定自己的需求,而不针对每种类型设计一个新的类。用上泛型可以提高类型的安全性,避免强转等。
    (有官方回答的求指导)

  • 程序运行时能够得到泛型对象的具体类型吗
    不能,编译的时候类型已经擦除了,看到的都是Object类

  • 谁来负责泛型的处理?
    答的JVM,她笑了笑,编译器?

  • 数组和list的区别
    长度固定与否?可以存引用类型/基础类型?list实现类有很多,方法很丰富,底层有数组 有链表,又有相应的特点。这题大家应该都会

  • 排序算法的稳定是什么含义?哪些算法是稳定的
    原序列中的值相等的元素的相对顺序,排序之后还能保持的,就是稳定。
    冒泡 插入 归并 计数 桶排序……

  • 哪些算法可以做到,每一趟(每一次循环)都能把某个元素放到它最终应该在的位置?

    1. 冒泡:每一趟都把最大的冒到最后去
    2. 选择排序:取出当前无序序列中最小值与第一位置的元素互换位置
    3. 快排:每一趟都把比基准小的移到基准左边去,比基准大的移到基准右边去,所以基准到了最终位置;
    4. 堆排序:每一趟循环后根节点就是最大的,拿它跟末尾元素交换
  • 快排每一次循环干了什么事情,堆排序呢
    如上

  • 网络分层模型 每一层的功能
    图片说明

  • http在哪一层(应用)

  • http状态码302是什么意思(临时重定向)

  • 算法题:二叉树的最大深度

    import java.util.*;
    public class Main {
      public static void main(String[] args) {
         TreeNode root = new TreeNode(1);
          root.left = new TreeNode(2);
          root.left.left = new TreeNode(3);
          System.out.println(depthOfTree(root));
      }
      public static int depthOfTree(TreeNode root) {
          if(root == null) {
              return 0;
          }
          return Math.max(depthOfTree(root.left), depthOfTree(root.right)) + 1;
      }
    }
    class TreeNode{
      int val;
      TreeNode left;
      TreeNode right;
      TreeNode() {}
    
      TreeNode(int val) {
          this.val = val;
      }
    }

    百度二面9.12(10.20开始,持续55min,口干舌燥)

  • String、StringBuilder和StringBuffer的区别
    执行速度,在这方面运行速度快慢为:StringBuilder > StringBuffer > String
    可变不可变,String为字符串常量,而StringBuilder和StringBuffer均为字符串变量,即String对象一旦创建之后该对象是不可更改的,但后两者的对象是变量,是可以更改的。
    线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的(String不可变当然线程安全)

  • 什么场景用String,什么场景用StringBuffer
    看需求 是否存在线程安全问题,空间限制,时间限制

  • JVM怎么判断对象是否可以回收(可达性分析法)

  • 可达性分析法中一般可以选哪些对象作为GC root,常量可以吗?
    虚拟机栈(栈帧中的本地变量表)中引用的对象。
    方法区中静态属性引用的对象。
    方法区中常量引用的对象。
    本地方法栈中(Native方法)引用的对象

  • 常见的回收算法,复制算法的优缺点(不写了大家应该都会)

  • 复制算法中,使用的空间和未使用的空间比例默认是多少
    针对新生代,里面又细分为eden和两个survivor区,默认8:1:1

后面问了一堆安卓的八股和场景题,答得稀烂,先留个坑 日后再填。。。

  • 安卓四大组件

  • 为什么要有content provider

  • 四大组件有哪些共同点

  • activity的生命周期

  • 有一个activity1在运行,这时候来了一个activity2,是一个透明主题界面,再回到activity1,这期间的两个activity的生命周期状态是怎么转换的?

  • 安卓开发常见的布局,你用过哪些

  • 如果要实现一个滚动界面功能,其中屏幕底部有一个按钮(如确认)不动,其他内容随着用户下滑滚动,怎么做?

  • 算法题1:合并两个有序链表(见小米二面)

  • 算法题2:字符串转整型atoi

    import java.util.*;
    public class Solution {
      public int atoi (String str) {
          // write code here
          char[] sc = str.toCharArray();
          int n = sc.length;
          int index = 0;
          while(index < n && sc[index] == ' ') {
              index++;
          }
          if(index == n) return 0;
          int sign = 1;
          char fc = sc[index];
          if(fc == '+'){
              index++;
          }else if(fc == '-') {
              index++;
              sign = -1;
          }
          int ans = 0;
          while(index < n) {
              char cur = sc[index];
              if(cur > '9' || cur < '0'){
                  break;
              }
    
              if(ans > Integer.MAX_VALUE/10 || (ans == Integer.MAX_VALUE/10 && (cur - '0') > Integer.MAX_VALUE%10)){
                  return Integer.MAX_VALUE;
              }
              if(ans < Integer.MIN_VALUE/10 || (ans == Integer.MIN_VALUE/10 && (cur - '0') > -Integer.MIN_VALUE%10)){
                  return Integer.MIN_VALUE;
              }
    
              ans = ans*10 + sign*(cur - '0');
              index++;
          }
          return  ans;
      }
    }

百度三面9.12(11.45开始,持续65min,饿坏了)

  • 介绍硕士课题(智能机器人相关)
  • 其中的困难、最大的成果、团队合作、自己的职责
  • 安卓项目简介(做的太基础,不感兴趣)
  • 算法1:搜索插入位置(二分法)
    import java.util.*;
    public class Solution {
      public int searchInsert (int[] A, int target) {
          // write code here
          int l = 0, r = A.length - 1;
          int mid = -1;
          while(l <= r) {
              mid = l + (r-l)/2;
              if(A[mid] == target){
                  return mid;
              }else if(A[mid] > target){
                  r = mid -1;
              }else{
                  l = mid + 1;
              }
          }
          return l;
      }
    }
  • 算法2:最小的K个数
    import java.util.*;
    public class Solution {
      public ArrayList<Integer> GetLeastNumbers_Solution(int [] input, int k) {
          ArrayList<Integer> ans = new ArrayList<>();
          if(k == 0) return ans;
          PriorityQueue<Integer> queue = new PriorityQueue<>(new Comparator<Integer>(){
              //@override
              public int compare(Integer o1, Integer o2){
                  return o2 - o1;
              }
          });
          for(int i = 0; i < k; i++) {
              queue.offer(input[i]);
          }
          for(int i = k; i < input.length; i++) {
              if(queue.peek() > input[i]){
                  queue.poll();
                  queue.offer(input[i]);
              }
          }
          for(int i = 0; i < k; i++) {
              ans.add(queue.poll());
          }
          return ans;
      }
    }
  • sql:每个人最近的登录日期(没做对)
    原题

小结:百度的面试整体还是问的比较基础的,很注重编码能力,三轮连环面,从9点到下午1点。。。

以上就是小米和百度的面经了,回答的不好的问题如下:

  1. 操作系统的PV操作描述(生产者消费者问题为例)
  2. 反射实现的机制/原理
  3. 安卓相关八股有大佬整理吗,求分享

最后再求一波关注~
图片说明

#面试复盘##百度##小米##面经#
全部评论
反射机制我觉得可以从底层的class类文件来理解,每个类都会在方法区存储一个类文件信息,反射通过getClass来获取。
2 回复 分享
发布于 2021-09-25 17:31
1 回复 分享
发布于 2021-09-19 12:11
沾沾喜气
1 回复 分享
发布于 2021-09-19 17:52
楼主这么强为什么投安卓了呀!
1 回复 分享
发布于 2021-09-19 22:12
为啥我的面试从来没提到评级。。。。都已经发意向书了,难道就是默认白菜价了吗?一名产品飘过。。。
1 回复 分享
发布于 2021-09-20 05:58
楼主小米sp给了多少?
点赞 回复 分享
发布于 2021-09-19 13:27
好棒的面经,学习了
点赞 回复 分享
发布于 2021-09-19 14:39
小米谈薪了?为啥我还没有🤣
点赞 回复 分享
发布于 2021-09-19 15:34
大兄弟小米二面多久后收到通过的消息
点赞 回复 分享
发布于 2021-09-19 16:44
用心的面经
1 回复 分享
发布于 2021-09-19 18:01
哈喽 请问被问研究算法为什么找开发应该怎么回答比较好啊 一直被问回答都很尬
点赞 回复 分享
发布于 2021-09-19 18:05
sp是什么
点赞 回复 分享
发布于 2021-09-19 18:35
老哥,小米Hr是直接打电话谈薪吗?有没有发邮件什么的?
点赞 回复 分享
发布于 2021-09-19 18:38
太棒啦!
点赞 回复 分享
发布于 2021-09-19 19:26
点赞 回复 分享
发布于 2021-09-19 19:54
楼主 你的小米意向是什么时候呀?
点赞 回复 分享
发布于 2021-09-19 21:53
沾沾喜气,期待谈薪
点赞 回复 分享
发布于 2021-09-19 21:59
沾沾喜气
点赞 回复 分享
发布于 2021-09-19 22:22
恭喜老哥
点赞 回复 分享
发布于 2021-09-19 22:35
老哥的小米问的比我难🤣,哥们你小米两方签了么
点赞 回复 分享
发布于 2021-09-19 23:17

相关推荐

10-30 23:23
已编辑
中山大学 Web前端
去B座二楼砸水泥地:这无论是个人素质还是专业素质都👇拉满了吧
点赞 评论 收藏
分享
91 462 评论
分享
牛客网
牛客企业服务