春招:我居然三天就拿到了offer?(1)

  • 可避免的内存拷贝,直接传递对象,到C层是一个jobject的指针,可以使用jmethodID和jfiledID访问方法和字段,无需进行内存拷贝,使用直接缓冲区也可以避免内存拷贝.
  • 无法避免的内存拷贝,基本类型数组,无法避免拷贝,因为JVM不信任C层的任何内存操作,特别是字符串操作,因为Java的字符串与C/C++的字符串所使用的数据类型是不一样的C/C++使用char一个字节(1字节=8位)或wchar_t是四字节.而jstring和jchar使用的是UTF-16编码使用双字节.(Unicode是兼容ASCII,但不兼容GBK,需要自己转换)
  • 自己创建的局部引用一定要释放,否则一直持有内存泄漏
  • 非局部引用方法返回后就会失效,除非创建全局引用,jclass是一个jobject,方法外围使用时需要创建全局引用,jmethodID和jfiledID不需要.
  • JNI是通过Java方法映射到C函数实现的,如果使用这种方法,函数必须以C式接口导出(因为C++会对名字做修饰处理),当然也可以在JNI_OnLoad方法中注册.
  • JNIEnv是线程独立的,JNI中使用pthread创建的线程没有JNIEnv,需要AttachCurrentThread来获取JNIEnv,不用时要DetachCurrentThread

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

  • 数组中找到重复元素
  • n个长为n的有序数组,求最大的n个数
  • 用O(1)的时间复杂度删除单链表中的某个节点 把后一个元素赋值给待删除节点,这样也就相当于是删除了当前元素,只有删除最后一个元素的时间为o(N)平均时间复杂度仍然为O(1)

public void deleteNode(ListNode node) {
ListNode next = node.next;
node.val = next.val;
node.next = next.next;
}

  • 删除单链表的倒数第N个元素 两个指针,第一个先走N步第二个再走,时间复杂度为O(N),参考link

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

  • 从长序列中找出前K大的数字
  • 用数组实现双头栈

public static class Stack
{

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

  • 两个链表求和,返回结果也用链表表示 1 -> 2 -> 3 + 2 -> 3 -> 4 = 3 -> 5 -> 7

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

  • 找出数组中和为给定值的两个元素,如:[1, 2, 3, 4, 5]中找出和为6的两个元素。

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 list) {
    if (list == null || list.isEmpty()) {
    return;
    }
    quickSort(list, 0, list.size() - 1);
    }

private static void quickSort(List 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 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 提问

不要问诸如:

  • 面试官是哪个组的?

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

  • XXX今年是否真的缺人?招聘策略是什么?
  • 面试官认为我存在哪些不足(从性格和技术两方面)?
  • 如果面试没通过能不能告诉我挂掉我的原因,这样既可以帮助到我也可以帮助到我带的学弟学妹们,而且在我分享我的面经的时候也能帮助XX招到更好的人.
  • XXX需要我这样的同学吗?

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

3.1 积极准备、不断试错

  • 机会都是留给有准备的人的,千万不要想着不准备上战场就能成功.
  • 多看面经,面经就是面试官们的招聘导向,透过阅读大量的面经,你能够感受得到面试官想要找到什么样的人,并且你可以有针对性地去准备.
  • 因为我们作为学生,往往没有什么实际的项目经验,所以操作系统,计算机网络,数据结构与算法,计算机组成原理这四大基础就是春招考察的重点.
  • 《Android开发艺术探索》这本书之于Android求职者的重要性,我觉得全世界都应该知道了,如果你还没有看过,建议真的好好看一下吧.
  • 不断地去面试,如果你这次面试失败了,那一定要好好总结其中的原因,一定要想方设法地从面试官口中套出自己的不足,这样你下次面试成功的概率就会增加.

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

往往有很多同学明明觉得自己已经准备得很好了已经很复读了,可最后还是不没拿到offer,那么你可能需要考虑以下的问题了.
就像我一开始说的,春招是运气>性格>三观>技术的,项目与基础固然很重要,但是想在短短的数小时能完全掌握一个人所有的基本情况实在是太难了,面试官看到的往往只是你的冰山一角,所以在面试的时候,你可能还要在意以下的问题:

  • 随机应变: 准备一些圆场的话,比如在讲原码时,如果觉得自己讲得不完全对,最后就补充一句——可能我说得不完全准确,但是我觉得看源码不是背书,而是学习他的一种思想并用到自己的代码中去.
  • 知己知彼: 深入地了解目标公司的企业文化,并准备一些夸他们的话,最好能够让对方会心一笑,这样绝对加分.

结语

  • 现在随着短视频,抖音,快手的流行NDK模块开发也显得越发重要,需要这块人才的企业也越来越多,随之学习这块的人也变多了,音视频的开发,往往是比较难的,而这个比较难的技术就是NDK里面的技术。
  • 音视频/高清大图片/人工智能/直播/抖音等等这年与用户最紧密,与我们生活最相关的技术一直都在寻找最终的技术落地平台,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。
  • 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
  • OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。


《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
,以前是windows系统,而现在则是移动系统了,移动系统中又是以Android占比绝大部分为前提,所以AndroidNDK技术已经是我们必备技能了。

  • 要学习好NDK,其中的关于C/C++,jni,Linux基础都是需要学习的,除此之外,音视频的编解码技术,流媒体协议,ffmpeg这些都是音视频开发必备技能,而且
  • OpenCV/OpenGl/这些又是图像处理必备知识,下面这些我都是当年自己搜集的资料和做的一些图,因为当年我就感觉视频这块会是一个大的趋势。所以提前做了一些准备。现在拿出来分享给大家。

[外链图片转存中…(img-pv9G00wj-1715098373632)]

[外链图片转存中…(img-G1ShH3hJ-1715098373633)]
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值