猿辅导—春招-Android开发—一面

📍面试公司:猿辅导

🕐面试时间:4.20

💻面试岗位:Android开发工程师

🙌面试感想:看邮件里面只约了30min,以为没有什么难度,然后就掉以轻心了,因为确实是不太熟悉Android相关的,想说问到就摆烂了,所以还是挺吃力的,最后还是面了1h左右。

❓面试问题:

0.面试官介绍业务(挺好的,很大程度上帮助了我对这个部门的业务了解)——3min

1.自我介绍(经典背书环节)——5min

2.问项目。

因为本人的实习经历和项目比较杂乱,所以他确定了一下我的技术栈。可能后续便于针对这个技术栈提问八股(大概10min)

——————————————————————————————————

八股环节:

3.C++中的多态

多态就是同一个接口,不同实现,调用时会自动匹配对应对象的行为。

C++ 多态主要分为两类:

  1. 静态多态(编译期多态)发生在编译阶段典型:函数重载、运算符重载、模板优点:效率高,无运行时开销
  2. 动态多态(运行期多态)发生在运行阶段核心:基类指针 / 引用 指向 派生类对象实现条件:类之间有继承关系基类中有虚函数派生类重写(override)虚函数作用:提高代码扩展性、可维护性,符合开闭原则

4.C++中是如何实现使用基类指针能够调动派生类的虚函数的?

核心依靠 虚函数表 + 虚指针 实现。

实现机制:

  1. 包含虚函数的类,编译器会生成一张虚函数表(vtable),存放类的虚函数地址
  2. 每个对象会生成一个虚指针(vptr),指向所属类的虚函数表
  3. 派生类重写虚函数时,会用自己的虚函数地址覆盖虚函数表中对应位置
  4. 基类指针 / 引用调用虚函数时:不看指针类型,看指向对象的实际类型通过对象的虚指针找到虚表再从虚表中找到对应函数地址执行

一句话总结:虚表存地址,虚指针找表,运行时查表调用

5.安卓的启动过程

安卓启动分为 5 个核心阶段:

  1. BootLoader 引导:硬件初始化,加载 Linux 内核
  2. Linux 内核启动:启动内核进程,挂载文件系统,启动 init 进程
  3. init 进程:安卓第一个用户进程,启动各种守护进程,启动 Zygote
  4. Zygote 进程:安卓所有应用进程的父进程初始化虚拟机、注册系统服务孵化 SystemServer 进程
  5. SystemServer 进程:系统核心进程启动 AMS、WMS、PMS 等系统服务启动桌面 Launcher,用户看到桌面,启动完成

6.安卓的四大组件,以及他们所能完成的任务

Activity

  • 负责界面展示与用户交互
  • 一个页面通常对应一个 Activity
  • 生命周期:onCreate -> onStart -> onResume -> onPause -> onStop -> onDestroy

2. Service

  • 后台无界面任务
  • 用于执行耗时操作:播放音乐、后台下载、数据同步
  • 分启动型、绑定型两种

3. BroadcastReceiver

  • 全局消息监听 / 广播接收
  • 接收系统 / 应用广播:开机、网络变化、电量低、自定义消息
  • 轻量级,不适合做耗时操作

4. ContentProvider

  • 跨进程数据共享
  • 为不同应用提供数据访问接口
  • 底层基于 Binder 实现

7.安卓的binder机制

Binder 是安卓跨进程通信(IPC) 的核心方案。

优点:

  • 相比传统管道、Socket:效率高、开销小、安全
  • 一次拷贝,传统 IPC 需两次拷贝

核心结构:

  1. Client:请求方
  2. Server:服务提供方
  3. ServiceManager:服务注册、查询(类似 DNS)
  4. Binder 驱动:内核层,负责进程间数据传递

工作流程:

  1. Server 向 ServiceManager 注册服务
  2. Client 向 ServiceManager 获取服务代理
  3. Client 通过代理向 Binder 驱动发请求
  4. 驱动转发给 Server,执行后返回结果

一句话:Binder 让安卓跨进程通信高效、安全、稳定

8.C++的内存管理机制

C++ 内存分为 5 个区域:

  1. 栈区(stack)存放局部变量、函数参数系统自动分配 / 释放,效率高
  2. 堆区(heap)new/malloc 分配,delete/free 释放手动管理,容易内存泄漏、野指针
  3. 全局 / 静态区全局变量、static 变量程序运行期间一直存在
  4. 常量区字符串常量、const 常量
  5. 代码区存放程序执行代码

内存管理核心:

  • 栈:自动管理
  • 堆:手动管理,现代 C++ 推荐用智能指针自动释放

9.多线程需要注意的问题

  • 线程安全共享变量必须加锁(mutex)保护
  • 死锁多个线程互相持有对方需要的锁,循环等待
  • 竞态条件多线程同时修改共享数据导致结果异常
  • 内存可见性线程缓存导致变量修改不能及时同步
  • 线程开销频繁创建销毁线程成本高,推荐线程池
  • 异常处理线程内部崩溃可能导致整个进程崩溃
  • 10.如何避免死锁

    死锁四个必要条件:互斥、持有并等待、不可抢占、循环等待

    避免方案:

    1. 按固定顺序加锁(破坏循环等待)
    2. 一次性申请所有锁(破坏持有并等待)
    3. 设置锁超时(破坏不可抢占)
    4. 避免锁嵌套
    5. 减少锁粒度,用原子操作代替锁
    6. 使用上层工具:智能锁、线程池、协程

    11.嵌套锁如何解决

    嵌套锁 = 同一个线程多次加同一把锁,容易导致死锁或逻辑混乱。

    解决方案:

    1. 使用递归锁(std::recursive_mutex)允许同一线程多次加锁解锁次数 = 加锁次数才会真正释放
    2. 代码重构抽离公共逻辑,避免锁内调用加锁函数
    3. 减小锁范围不要在加锁代码里调用其他可能加锁的方法

    12.智能指针的底层结构

    智能指针本质是封装了裸指针的类,利用 RAII 机制自动管理内存。

    通用结构:

    1. 封装原生指针 T*
    2. 析构函数中自动释放内存
    3. 重载 *-> 运算符,使用像普通指针

    不同智能指针:

    • auto_ptr:废弃,不安全
    • unique_ptr:独占指针,不能拷贝,效率最高
    • shared_ptr:共享指针,引用计数 + 原生指针
    • weak_ptr:辅助 shared_ptr,解决循环引用,不增加计数

    13.共享智能指针如何销毁

    shared_ptr 靠引用计数(reference count) 管理生命周期。

    销毁规则:

    1. 每拷贝一次 shared_ptr,引用计数 +1
    2. 每析构一次、或重置(reset),引用计数 -1
    3. 引用计数变为 0 时:自动调用 delete 释放托管对象再释放引用计数对象本身

    注意:

  • 循环引用会让计数永远不为 0,造成内存泄漏
  • 必须搭配 weak_ptr 解决
  • 14.内存碎片是什么?如何避免

    内存碎片

    已释放的、不连续的小内存块,总空间足够,但无法分配给大块内存

    分为:

    1. 外部碎片:空闲内存分散,无法使用
    2. 内部碎片:分配内存比实际使用大,浪费空间

    如何避免

    1. 内存池:预先分配一大块内存,重复使用
    2. 减少频繁 new/delete
    3. 使用智能指针规范管理
    4. 尽量使用栈内存,少用堆内存
    5. 按需分配,避免一次性申请过大内存

    15.epoll的特性,对比poll、select

    共同作用

    I/O 多路复用:一个线程监听多个文件描述符

    性能对比

    1. select最大监听 1024 个 fd每次需遍历全部 fd内核态 / 用户态频繁拷贝,效率低
    2. poll无 1024 限制仍需遍历全部 fd,效率一般
    3. epoll(Linux 最优)无最大连接限制事件驱动,只返回活跃 fd无需遍历,效率 O (1)支持 ET(边缘触发)+ LT(水平触发)

    epoll 核心特性

    • 高效、高并发
    • 零拷贝、事件回调
    • 适合百万连接高并发服务器

    16.coding部分(快排)

    #include <iostream>
    #include <vector>
    using namespace std;
    
    // 分区函数
    int partition(vector<int>& nums, int left, int right) {
        // 选基准值
        int pivot = nums[left];
        while (left < right) {
            // 从右往左找小于 pivot 的数
            while (left < right && nums[right] >= pivot) {
                right--;
            }
            nums[left] = nums[right];
            
            // 从左往右找大于 pivot 的数
            while (left < right && nums[left] <= pivot) {
                left++;
            }
            nums[right] = nums[left];
        }
        // 基准值归位
        nums[left] = pivot;
        return left;
    }
    
    // 快排递归
    void quickSort(vector<int>& nums, int left, int right) {
        if (left >= right) return;
        int mid = partition(nums, left, right);
        quickSort(nums, left, mid - 1);
        quickSort(nums, mid + 1, right);
    }
    
    // 测试
    int main() {
        vector<int> nums = {3,1,4,2,5};
        quickSort(nums, 0, nums.size()-1);
        for(int x : nums) cout << x << " ";
        return 0;
    }
    

    总结

    因为最近可能有点懈怠,然后导致代码其实不太会写,写得磕磕碰碰的,不过好在写出来了,但是调试了可能有10min,手生了。。。

    #牛客AI配图神器#

    #发面经攒人品##我的求职进度条#
    鼠鼠的面筋摊 文章被收录于专栏

    记录面试的面经

    全部评论
    太强了哥,还会安卓
    1 回复 分享
    发布于 04-21 02:01 安徽
    太强了哥,还会安卓
    点赞 回复 分享
    发布于 04-22 14:04 江苏
    佬面的是啥部门呀,一面通过了嘛
    点赞 回复 分享
    发布于 04-22 13:55 江苏
    太强了哥,还会安卓
    点赞 回复 分享
    发布于 04-21 17:43 浙江

    相关推荐

    发个面经积攒人品。1.&nbsp;&nbsp;(开场)请做一个简单的自我介绍。2.&nbsp;&nbsp;(算法题)实现一个时间复杂度最低的排序算法(给定正整数且已知最大值)。3.&nbsp;&nbsp;(Java基础)Java的基本数据类型有哪些?4.&nbsp;&nbsp;(Java基础)byte类型的取值范围是多少?5.&nbsp;&nbsp;(Java基础)int占几个字节?6.&nbsp;&nbsp;(Java基础)你知道Java的拆箱和装箱吗?7.&nbsp;&nbsp;(Java基础)拆箱和装箱会带来什么问题?8.&nbsp;&nbsp;(Java基础)它(指Integer的缓存机制)一定会有拆箱和装箱的开销吗?9.&nbsp;&nbsp;(Java集合)Java里面的集合类分为哪些类型?10.&nbsp;(Java集合)List里面的ArrayList和LinkedList有什么区别?11.&nbsp;(Java集合)为什么会有这个区别?(指上一个问题中两个List的不同特性)12.&nbsp;(Java集合)ArrayList扩容是怎么扩的?13.&nbsp;(Java集合)为什么(ArrayList)扩容1.5倍?14.&nbsp;(Java集合)HashMap你了解吗?它是什么结构?15.&nbsp;(数据结构)为什么红黑树的查询性能(比链表)好?16.&nbsp;(数据结构)二叉(搜索)树的查询效率一定是O(log&nbsp;n)吗?17.&nbsp;(数据结构)那为什么不做一个完全平衡的(AVL)二叉树?18.&nbsp;(Java集合)HashMap的哈希算法是怎么样的?19.&nbsp;(Java并发)Java里面多线程编程,为了保证线程安全,有哪些技术?20.&nbsp;(Java并发)刚刚说的Atomic是怎么去实现线程安全的?21.&nbsp;(Java集合)HashMap是线程安全的吗?22.&nbsp;(Java集合)有哪些线程安全的Map实现?23.&nbsp;(Java并发)ConcurrentHashMap是怎么去实现(线程安全)的?24.&nbsp;(Android)安卓里面的Handler机制你了解吗?25.&nbsp;(Android)你刚刚提到的epoll机制,它是什么样的(通信机制)?26.&nbsp;(Android)安卓的那个RecycleView你了解过吗?27.&nbsp;(Android)安卓里面进程间通信的方式(有哪些)?28.&nbsp;(Android)你有了解Binder机制吗?29.&nbsp;(框架)看你简历上有提到Retrofit框架,你用过这个框架吗?30.&nbsp;(框架)Retrofit的框架是怎么去实现它接口调用的(机制)?31.&nbsp;(反问前)你那边有问题要问我吗?
    查看30道真题和解析
    点赞 评论 收藏
    分享
    避雷⚠️&nbsp;线下面试,不报销车费的,另外我是内推来面试的。技术和项目问题问的很简单,我有五个月的实习经历,但是一概不问,唯独喜欢逮着成绩单问,另外还问学校同学家人等毫无意义的问题,非常恶心。另外笔试涵盖很多嵌入式的题目,难度整体不高,只是走个流程。为什么说避雷呢,首先上来面试官就迟到一个半小时,让我另一场面试被迫推迟,来了以后非常趾高气昂,作为技术岗面试官不怎么问技术问题,就爱问个人问题,看不上你就各种挑刺找茬。感觉不像是技术官在面试,是我有史以来面试体验感最差的一次😅😅😅。面完对我来说毫无长进,反问环节已经不想和他废话了,直接赶着回去准备另一场面试,鉴定为拉完了。面试时长20分钟左右,仅有的几个技术问题如下:1.安卓开发流程?2.你平时是怎么用ai来coding和fix&nbsp;bug的?3.说说mvvm模式和mvp模式?4.kotlin相比Java有哪些优势?然后问了一点项目相关的问题,其余全是个人问题刁难你,家里几口人,同学成绩怎么样之类的😓😓😓后续:鼠鼠下午面的很好,直接当场发的offer,一共有四个offer了,这家公司一直还没后续,估计凉了。感想:鼠鼠是内推的,也当成KPI刷,无语。。。。。。有一点我不明白,我投的是伯泰克的岗位,面试来的都是埃泰克的人,最近听其他同学也遇到了和我相似的经历。所以避雷避雷再避雷!!!
    查看5道真题和解析
    点赞 评论 收藏
    分享
    评论
    3
    10
    分享

    创作者周榜

    更多
    牛客网
    牛客网在线编程
    牛客网题解
    牛客企业服务