Android开发经验谈

Android面试小技巧:随机应变,知己知彼,别太诚实,手握筹码

2020-10-12  本文已影响0人  进阶程序员007

作者:晨曦
链接:https://juejin.im/post/6844903848813789191

1 回顾我的时间线

在本文的开头,先分享一下自己的春招经历吧:

各位掘友大家好,我是练习时长快一年的Android小蔡鸡,喜欢看源码,逛掘金,写技术文章......

好了好,不开玩笑了OWO,本人大三,今年春招投了许多简历的,都是实习岗,但是被捞的只有阿里,头条和美团,一路下来挺不容易的.

个人认为在春招中运气>性格>三观>技术.

1.1 阿里

1.2 头条

1.3 美团

1.4 小结

2 把自己训练成HashMap和复读机

这次春招给我最大的感触就是,当你觉得自己像复读机能把面试题给复读出来并且对面试官所提的问题能像HashMap一样在常数时间内找到答案的时候,你就离成功很近了.

下面是我在准备面试的时候收集的一些知识点:

2.1 Java

2.1.1 volatile理解,JMM中主存和工作内存到底是啥?和JVM各个部分怎么个对应关系?

参考link

2.1.2 序列化

Serializable在序列化时使用了反射,从而导致GC的频繁调用,参考link

2.1.3 可见性,原子性,有序性(必考)

2.1.4 Java锁机制

java锁机制其实是锁总线,同步关键字和Lock接口的优劣.

2.1.5 Java的常量池?不同String赋值方法,引用是否相等?

字面值是常量,在字节码中使用id索引,equals相等引用不一定相等,Android上String的构造函数会被虚拟机拦截,重定向到StringFactory

2.1.6 HashMap的实现?树化阈值?负载因子?

数组加链表加红黑树,默认负载因子0.75,树化阈值8,这部分比较常考,建议专门准备.(打个小广告OWO,你也可以关注我的专栏,里面有一篇文章分析了HashMap和ArrayMap)

2.1.7 Java实现无锁同步

CAS的实现如AtomicInteger等等

2.1.8 单例模式

public class Singleton {

    private static volatile Singleton singleton;

    private Singleton() {}

    public static Singleton getInstance() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }
}
复制代码

2.1.9 锁的条件变量

信号量要与一个锁结合使用,当前线程要先获得这个锁,然后等待与这个锁相关联的信号量,此时该锁会被解锁,其他线程可以抢到这个锁,如果其他线程抢到了这个锁,那他可以通知这个信号量,然后释放该锁,如果此时第一个线程抢到了该锁,那么它将从等待处继续执行(应用场景,将异步回调操作封装为变为同步操作,避免回调地狱)

信号量与锁相比的应用场景不同,锁是服务于共享资源的,而信号量是服务于多个线程间的执行的逻辑顺序的,锁的效率更高一些.

2.1.10 ThreadLocal原理

线程上保存着ThreadLocalMap,每个ThreadLocal使用弱引用包装作为Key存入这个Map里,当线程被回收或者没有其他地方引用ThreadLocal时,ThreadLocal也会被回收进而回收其保存的值

2.1.11 软引用,弱引用,虚引用

2.1.12 ClassLoader双亲委派机制

简单来说就是先把加载请求转发到父加载器,父加载器失败了,再自己试着加载

2.1.13 GC Roots有这些

通过System Class Loader或者Boot Class Loader加载的class对象,通过自定义类加载器加载的class不一定是GC Root:

2.1.14 GC算法

名称 描述 优点 缺点
标记-清除算法 暂停除了GC线程以外的所有线程,算法分为“标记”和“清除”两个阶段,首先从GC Root开始标记出所有需要回收的对象,在标记完成之后统一回收掉所有被标记的对象。 标记-清除算法的缺点有两个:首先,效率问题,标记和清除效率都不高。其次,标记清除之后会产生大量的不连续的内存碎片,空间碎片太多会导致当程序需要为较大对象分配内存时无法找到足够的连续内存而不得不提前触发另一次垃圾收集动作
复制算法 将可用内存按容量分成大小相等的两块,每次只使用其中一块,当这块内存使用完了,就将还存活的对象复制到另一块内存上去,然后把使用过的内存空间一次清理掉 这样使得每次都是对其中一块内存进行回收,内存分配时不用考虑内存碎片等复杂情况,只需要移动堆顶指针,按顺序分配内存即可,实现简单,运行高效 复制算法的缺点显而易见,可使用的内存降为原来一半
标记-整理算法 标记-整理算法在标记-清除算法基础上做了改进,标记阶段是相同的,标记出所有需要回收的对象,在标记完成之后不是直接对可回收对象进行清理,而是让所有存活的对象都向一端移动,在移动过程中清理掉可回收的对象,这个过程叫做整理。 标记-整理算法相比标记-清除算法的优点是内存被整理以后不会产生大量不连续内存碎片问题。复制算法在对象存活率高的情况下就要执行较多的复制操作,效率将会变低,而在对象存活率高的情况下使用标记-整理算法效率会大大提高
分代收集算法 是java的虚拟机的垃圾回收算法.基于编程中的一个事实,越新的对象的生存期越短,根据内存中对象的存活周期不同,将内存划分为几块,java的虚拟机中一般把内存划分为新生代和年老代,当新创建对象时一般在新生代中分配内存空间,当新生代垃圾收集器回收几次之后仍然存活的对象会被移动到年老代内存中,当大对象在新生代中无法找到足够的连续内存时也直接在年老代中创建

2.2 Android

2.2.1 Handler、MessageQueue等一套东西讲一下,详细说了下源码。为什么主线程loop不会ANR?

2.2.2 View事件以及View体系相关知识

建议看《Android开发艺术探索》,这玩意三言两语讲不清楚

2.2.3 Android中使用多线程的方法

2.2.4 RecyclerView复用缓存

建议看一下,这个可能会被问,不过我运气好没被问到.

2.2.5 Activity启动流程

网上有很多相关的文章,可以自己结合源码去看一下,如果能讲个大概的话也是很加分的.

2.2.6 JNI(除非你自己说你会,否则不是很常考)

2.3专业课

2.3.1 TCP和UDP的根本区别?

数据报,流模式,TCP可靠,包序不对会要求重传,UDP不管,甚至不能保证送到

2.3.2 TCP三次握手

这个被问的几率非常的大,几乎等于必问,建议专门花时间去看.

2.3.3 Http和Https

CA证书,中间机构,公钥加密对称秘钥传回服务端,一个明文一个加密,SSL层,中间人攻击,参考link

2.4 ACM

对于ACM,比较常考链表的题,不常刷算法的同学一定不要对其有抵触心理.

你可能会问为什么要ACM?网上答案说的什么提高代码质量,能够更好地阅读别人的代码这些理由有一定道理,但对于我们去面试的人而言最重要的是ACM是面试官考察你编码能力的最直接的手段,所以不用说这么多废话刷题就够了.

刷题的话,建议去刷leetcode,题号在200以内的,简单和中等难度,不建议刷困难,因为面试的时候基本就不会出,没人愿意在那里等你想一个半个小时的.

在面试官面前现场白板编程时,可以先把思路告诉面试官,写不写得出来是另外一回事,时间复杂度和空间复杂度是怎么来的一定要搞清楚.在编码时也不一定要写出最佳的时间和空间的算法,但推荐你写出代码量最少,思路最清晰的,这样面试官看得舒服,你讲得也舒服.

下面是我在网上收集或者是在实际中遇到过的ACM题,基本上在leetcode上也都有类似的.

2.4.1 数组、链表

    public ListNode reverseList(ListNode head)
    {
        if (head == null)
        {
            return null;
        }
        if (head.next == null)
        {
            return head;
        }
        ListNode prev = null;
        ListNode current = head;
        while (current != null)
        {
            ListNode next = current.next;
            current.next = prev;
            prev = current;
            current = next;
        }
        return prev;
    }
复制代码
    public int removeDuplicates(int[] nums)
    {
        int length = nums.length;
        if (length == 0 || length == 1)
        {
            return length;
        }
        int size = 1;
        int pre = nums[0];
        for (int i = 1; i < length; )
        {
            if (nums[i] == pre)
            {
                i++;
            } else
            {
                pre = nums[size++] = nums[i++];
            }
        }
        return size;
    }
复制代码
      public void deleteNode(ListNode node) {
          ListNode next = node.next;
          node.val = next.val;
          node.next = next.next;
      }
复制代码
      public ListNode removeNthFromEnd(ListNode head, int n) {
          if (head == null)
          {
              return null;
          }
          if (head.next == null)
          {
              return n == 1 ? null : head;
          }
          int size = 0;
          ListNode point = head;
          ListNode node = head;
          do
          {
              if (size >= n + 1)
              {
                  point = point.next;
              }
              node = node.next;
              size++;
          } while (node != null);
          if (size == n)
          {
              return head.next;
          }
          node = point.next;
          point.next = node == null ? null : node.next;
          return head;
      }
复制代码
  public static class Stack<T>
      {

          public Stack(int cap)
          {
              if (cap <= 0)
              {
                  throw new IllegalArgumentException();
              }
              array = new Object[cap];
              left = 0;
              right = cap - 1;
          }

          private Object[] array;
          private int left;
          private int right;

          public void push1(T val)
          {
              int index = left + 1;
              if (index < right)
              {
                  array[index] = val;
              }
              left = index;
          }

          @SuppressWarnings("unchecked")
          public T pop1()
          {
              if (left > 0)
              {
                  return (T)array[left--];
              }
              return null;
          }

          public void push2(T val)
          {
              int index = right - 1;
              if (index > left)
              {
                  array[index] = val;
              }
              right = index;
          }

          @SuppressWarnings("unchecked")
          public T pop2()
          {
              if (right < array.length)
              {
                 return (T)array[right++];
              }
              return null;
          }
      }
复制代码
      public ListNode addTwoNumbers(ListNode node1, ListNode node2)
      {
          ListNode head = null;
          ListNode tail = null;
          boolean upAdd = false;
          while (!(node1 == null && node2 == null))
          {
              ListNode midResult = null;
              if (node1 != null)
              {
                  midResult = node1;
                  node1 = node1.next;
              }
              if (node2 != null)
              {
                  if (midResult == null)
                  {
                      midResult = node2;
                  } else
                  {
                      midResult.val += node2.val;
                  }
                  node2 = node2.next;
              }
              if (upAdd)
              {
                  midResult.val += 1;
              }
              if (midResult.val >= 10)
              {
                  upAdd = true;
                  midResult.val %= 10;
              }
              else
              {
                  upAdd = false;
              }
              if (head == null)
              {
                  head = midResult;
                  tail = midResult;
              } else
              {
                  tail.next = midResult;
                  tail = midResult;
              }
          }
          if (upAdd)
          {
              tail.next = new ListNode(1);
          }
          return head;
      }
复制代码
      public ListNode swapPairs(ListNode head)
      {
          if (head == null)
          {
              return null;
          }
          if (head.next == null)
          {
              return head;
          }
          ListNode current = head;
          ListNode after = current.next;
          ListNode nextCurrent;
          head = after;
          do
          {
              nextCurrent = after.next;
              after.next = current;
              if (nextCurrent == null)
              {
                  current.next = null;
                  break;
              }
              current.next = nextCurrent.next;
              after = nextCurrent.next;
              if (after == null)
              {
                  current.next = nextCurrent;
                  break;
              }
              current = nextCurrent;
          } while (true);
          return head;
      }
复制代码
      public int[] twoSum(int[]mun,int target)
      {
          Map<Integer, Integer> table = new HashMap<>();
          for (int i = 0; i < mun.length; ++i)
          {
              Integer value = table.get(target - mun[i]);
              if (value != null)
              {
                  return new int[]{i, value};
              }
              table.put(mun[i], i);
          }
          return null;
      }
复制代码

2.4.2 树

2.4.3 排序

  public static void quickSort(Node head, Node tail) {
        if (head == null || tail == null || head == tail || head.next == tail) {
            return;
        }

        if (head != tail) {
            Node mid = getMid(head, tail);
            quickSort(head, mid);
            quickSort(mid.next, tail);
        }
    }

    public static Node getMid(Node start, Node end) {
        int base = start.value;
        while (start != end) {
            while(start != end && base <= end.value) {
                end = end.pre;
            }
            start.value = end.value;
            while(start != end && base >= start.value) {
                start = start.next;
            }
            end.value = start.value;
        }
        start.value = base;
        return start;
    }

    /**
     * 使用如内部实现使用双向链表的LinkedList容器实现的快排 
     */
    public static void quickSort(List<Integer> list) {
        if (list == null || list.isEmpty()) {
            return;
        }
        quickSort(list, 0, list.size() - 1);
    }

    private static void quickSort(List<Integer> list, int i, int j) {
        if (i < j) {
            int mid = partition(list, i, j);
            partition(list, i, mid);
            partition(list,mid + 1, j);
        }
    }

    private static int partition(List<Integer> list, int i, int j) {
        int baseVal = list.get(i);
        while (i < j) {
            while (i < j && baseVal <= list.get(j)) {
                j--;
            }
            list.set(i, list.get(j));
            while (i < j && baseVal >= list.get(i)) {
                i++;
            }
            list.set(j, list.get(i));
        }
        list.set(i, baseVal);
        return i;
    }
复制代码

2.5 项目

2.5.1 视频聊天使用什么协议?

不要答TCP,答RTMP实时传输协议,RTMP在Github也有很多开源实现,建议面这方面的同学可以去了解一下.

2.5.2 你在项目中遇到的一些问题,如何解决,思路是什么?

这一块比较抽象,根据你自己的项目来,着重讲你比较熟悉,有把握的模块,一般面试官都会从中抽取一些问题来向你提问.

2.6 提问

不要问诸如:

这种问题一点价值都没有,因为你即使问了也不能从他那里获得额外的信息,也不能够影响他对你的判断,要问就要问面试官对你的感受与评价,还要体现出你想要加入的心情以及你问题的深度.

3 最后说一些个人认为比较重要的事

3.1 积极准备、不断试错

3.2 真正重要并且最容易忽视的一些问题

往往有很多同学明明觉得自己已经准备得很好了已经很复读了,可最后还是不没拿到offer,那么你可能需要考虑以下的问题了.

就像我一开始说的,春招是运气>性格>三观>技术的,项目与基础固然很重要,但是想在短短的数小时能完全掌握一个人所有的基本情况实在是太难了,面试官看到的往往只是你的冰山一角,所以在面试的时候,你可能还要在意以下的问题:

3.3 结语

透露一下,本人是双非二本,自从高考失利以后还以为自己要一直这么平凡下去QAQ,没想到过了三年终于又给我一个机会让我重新证明了自己,能有机会去到美团这样的大厂工作,真的倍感荣幸.最后的最后还是惯例啦,如果喜欢我的文章别忘了给我点个赞,拜托了这对我来说真的很重要.

有一句老话说的好:“比你优秀的对手在学习,你的仇人在磨刀,你的闺蜜在减肥,隔壁老王在练腰,我们必须不断学习,否则我们将被学习者超越。”当然一个人学习是枯燥的,还需要一个良好的学习氛围,因此我组建了一个学习交流探讨的社群,欢迎大家一起来交流探讨共同进步。还有一些收集整理的资料,感兴趣的可以加群,一起学习,共同进步!

这边把我收录整理的一些资料拿出来分享给大家,一方面是希望能够帮助大家提高,一方面也是警醒自己,要不断学习、不断提升,进阶才是王道!
附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

本文在开源项目:【GitHub 】中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

如果需要PDF版本可以在群文件夹里,自行领取!

上一篇 下一篇

猜你喜欢

热点阅读