当前位置: 首页 > article >正文

记录遇到的面试题

不止一次被问到,你觉得自己哪方面了解比较深,而我的回答是 不知道。

面试就是推销自己,但是当自己都不知道怎么介绍自己的优势时,也就注定了失败。

飘得时候,还是要出去承受下风吹雨打。太依赖框架,依赖GPT,忘记了基础才是一个程序员赖以生存的护城河。

目录

HashMap 的 put 存值方法,发生哈希碰撞之后的处理逻辑。

Android 抓包的逻辑,原理,(网页上输入一个网址后发生的事情)

抓包的原理

抓包的逻辑

输入一个网址发生了什么:

1. 用户输入网址

2. URL 解析

3. DNS 解析

4. 建立 TCP 连接

5. 发送 HTTP 请求

6. 服务器处理请求

7. 服务器发送 HTTP 响应

8. 浏览器接收并渲染响应

Android 程序ANR报错怎么定位

1. 查看 ANR 日志

2. 分析 trace.txt 文件

3. 检查主线程耗时操作

4. 使用 StrictMode 检测

6. 检查广播接收器和服务

7. 优化布局和绘制

8. 避免死锁和同步问题

组件化和普通模式有什么区别。

组件化(Modularization)

普通模式(Monolithic)

比较

结论

包体积优化,页面卡顿优化,内存优化 

Android Studio Profiler 生成的报告阅读

手写单利


HashMap 的 put 存值方法,发生哈希碰撞之后的处理逻辑。

         这部分应该是检验面试者 数据结构,源码逻辑,在问深点 就是红黑树的计算。

  • 计算哈希值:首先,HashMap 使用键的 hashCode 方法计算哈希值,然后通过进一步的扰动函数将哈希值分布得更均匀。

  • 定位桶:使用哈希值和数组长度来确定键值对应的桶(bucket)位置。

  • 检查桶中的节点

    • 如果桶为空,则直接将键值对插入到该位置。
    • 如果桶不为空,则遍历桶中的链表或红黑树结构,以检查是否存在相同的键。
  • 处理哈希碰撞

    • 链表:如果桶中节点数量较少(小于阈值 TREEIFY_THRESHOLD),则使用链表存储冲突的键值对。新的键值对会添加到链表的末尾。如果找到相同的键,则更新其值。
    • 红黑树:如果桶中节点数量较多(达到阈值 TREEIFY_THRESHOLD),则将链表转换为红黑树,以提高查询和插入效率。新的键值对会插入红黑树中。如果找到相同的键,则更新其值。
public class HashMapPutExample<K, V> {

    static final int TREEIFY_THRESHOLD = 8;

    static class Node<K, V> {
        final int hash;
        final K key;
        V value;
        Node<K, V> next;

        Node(int hash, K key, V value, Node<K, V> next) {
            this.hash = hash;
            this.key = key;
            this.value = value;
            this.next = next;
        }
    }

    Node<K, V>[] table;

    public V put(K key, V value) {
        int hash = key.hashCode();   // 计算哈希值
        int index = (table.length - 1) & hash;  // 定位桶
        Node<K, V> first = table[index];

        if (first == null) {   // 检查桶中节点
            table[index] = new Node<>(hash, key, value, null);  // 为空直接存
        } else {   // 不为空
            Node<K, V> node = first;
            if (first.hash == hash && (first.key == key || (key != null && key.equals(first.key)))) {
                node.value = value;
                return value;
            }

            for (int binCount = 0; ; ++binCount) {
                Node<K, V> next = node.next;
                if (next == null) {    // 存入链表
                    node.next = new Node<>(hash, key, value, null);
                    if (binCount >= TREEIFY_THRESHOLD - 1) {   判断链表是否达到阀值,达到后转换成红黑树  
                        // Convert to tree
                    }
                    break;
                }
                if (next.hash == hash && (next.key == key || (key != null && key.equals(next.key)))) {
                    next.value = value;
                    return value;
                }
                node = next;
            }
        }
        return null;
    }

记得当时回答:HashMap是数组加链表的形式。当发生哈希碰撞的时候,值会以链表的形式存入table(应该是数组)角标下的链表中。——然后面试官在让我确定一下,我就回答成了 角标的倍数的位置.... 记混了,应该是脑子里想起了 数组的动态扩容的概念。 被自己蠢哭;

正确回答:HashMap是一个组合容器,是由数组+链表 红黑树组成的。存入一个值首先是拿key进行hashCode方法计算出哈希值,在经过&运算将哈希值均匀分布(有兴趣可以了解下,其设计的巧妙 &运算 比对后四位就可以完成 均匀的分布 ,这也是设计初始长度是16,系数0.75的原因)然后定位桶,取出链表,检查链表长度,超过定义的阀值,把链表转换成红黑树 ,不超过或者为空,直接存入对应的链表。

Android 抓包的逻辑,原理,(网页上输入一个网址后发生的事情)

          这个问题应该是考察面试网络通讯原理的理解。因为输入一个网址后续执行了什么~ 

抓包的原理

  • 网络数据包(Packet):网络数据包是网络通信的基本单位,它包含了数据传输的元信息(如源地址、目标地址、协议类型等)和实际传输的数据。

  • 捕获网络数据包:抓包工具通过在网络接口上监听传输的数据包来捕获它们。对于 Android 设备,这通常涉及在设备上安装一个抓包应用或使用中间人(MITM)代理。

  • 解码和分析:捕获到的数据包需要被解码和分析,以便人类可以理解。抓包工具会解析数据包的结构,并显示其各个部分,如头信息和数据负载。

抓包的逻辑

  • 设置网络代理:在 Android 设备上,抓包通常通过设置网络代理来实现。通过将设备的网络请求重定向到代理服务器,代理服务器可以记录并转发这些请求和响应。

  • 安装根证书:很多抓包工具需要安装根证书,以便能够解密 HTTPS 流量。这样,代理服务器就可以解密和查看应用的加密通信。

  • 捕获流量:抓包工具开始监听和捕获通过网络接口传输的所有数据包。它们可以过滤特定类型的流量,或记录所有流量以供后续分析。

  • 分析和调试:开发者可以使用抓包工具提供的界面来查看和分析捕获到的网络数据包。他们可以检查请求的 URL、头信息、响应数据等,以调试网络通信问题。

其实一大堆,我觉得最重要的是两个词, SSL证书, 重定向方式。而我说的是类似okhttp拦截 ~ 

输入一个网址发生了什么:

1. 用户输入网址

用户在浏览器的地址栏中输入网址,并按下回车键。

2. URL 解析

浏览器解析用户输入的 URL,并将其分解成不同的部分,例如协议(http 或 https)、域名(example.com)和路径(如果有)。

3. DNS 解析

浏览器需要将域名转换为 IP 地址,这个过程称为 DNS(域名系统)解析。步骤如下:

  • 浏览器首先检查本地缓存中是否已有该域名的 IP 地址。
  • 如果本地缓存没有命中,浏览器会向操作系统请求该域名的 IP 地址。
  • 操作系统会检查其缓存,如果没有命中,则向本地配置的 DNS 服务器发送查询请求。
  • DNS 服务器会查找并返回该域名对应的 IP 地址。

4. 建立 TCP 连接

获取到 IP 地址后,浏览器会与服务器建立 TCP(传输控制协议)连接。通常,这个过程包括三次握手:

  1. 客户端向服务器发送一个 SYN(同步)包。
  2. 服务器响应一个 SYN-ACK(同步-确认)包。
  3. 客户端发送一个 ACK(确认)包,连接建立成功。

5. 发送 HTTP 请求

TCP 连接建立后,浏览器会向服务器发送一个 HTTP(或 HTTPS)请求。请求包含以下内容:

  • 请求方法(如 GET、POST)
  • 请求的 URL 路径
  • HTTP 版本
  • 请求头(如用户代理、接受的内容类型等)
  • (可选)请求体(通常在 POST 请求中包含)

6. 服务器处理请求

服务器接收到请求后,会处理请求并生成响应。处理过程可能包括:

  • 检查请求头和参数
  • 查询数据库或读取文件
  • 执行应用程序逻辑

7. 服务器发送 HTTP 响应

服务器生成响应后,会通过 TCP 连接将响应发送回客户端。响应包括:

  • HTTP 状态码(如 200、404、500)
  • 响应头(如内容类型、缓存控制等)
  • (可选)响应体(如 HTML、JSON 数据等)

8. 浏览器接收并渲染响应

回答:第一步先进性 URL解析获取协议,域名,路径 ;第二步进行 DNS解析根据域名先从缓存——操作系统—— 访问本地配置的DNS服务器 ,找IP,直至命中。 第三步建立网络连接,也就是三次握手协议。四五六 就是 通讯 渲染 四次挥手。嗯,直接问你三次握手 你肯定知道,因为八股文里有,多多少少背过。但是换个问法就懵逼了, 

Android 程序ANR报错怎么定位

这个就是做过的就做过,没做过的啥也说不出来,你回答一句日志里不是有吗?人家难道不知道日志里可以定位吗?

在 Android 应用程序中,ANR(Application Not Responding)错误通常是由于主线程(UI 线程)被阻塞时间过长,无法处理用户输入或绘制界面。为了定位和解决 ANR 问题,可以采取以下步骤:

1. 查看 ANR 日志

ANR 错误会在 logcat 日志中显示详细信息。通过 logcat 日志,可以找到 ANR 发生的时间和原因。你可以使用 Android Studio 的 Logcat 工具查看日志,或使用命令行工具:

adb logcat

在 logcat 中,你可以搜索 ANR 关键字来查找相关日志。

2. 分析 trace.txt 文件

当 ANR 发生时,系统会生成一个名为 trace.txt 的文件,其中包含了所有线程的堆栈跟踪。你可以通过以下命令获取 trace.txt 文件:

adb pull /data/anr/traces.txt

分析 trace.txt 文件,查找主线程(通常是 main 线程)的堆栈跟踪,确定导致阻塞的方法或代码段。

3. 检查主线程耗时操作

确保在主线程中没有进行耗时操作,如网络请求、数据库操作、大量计算等。如果需要执行这些操作,请使用后台线程或异步任务(如 AsyncTask 或 HandlerThread)。

4. 使用 StrictMode 检测

StrictMode 是 Android 提供的一个工具,用于检测应用中的潜在性能问题。你可以在应用的 Application 类或主要的 Activity 中启用 StrictMode:

MyApplication.javav1

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
            StrictMode.setThreadPolicy(new StrictMode.ThreadPolicy.Builder()
                    .detectAll()
                    .penaltyLog()
                    .penaltyDeath()
                    .build());
            StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder()
                    .detectAll()
                    .penaltyLog()
                    .penaltyDeath()
                    .build());
        }
    }
}

5. 分析和优化 UI 线程任务

使用工具(如 Android Studio Profiler)分析应用的性能,找出 UI 线程上的瓶颈并进行优化。确保尽可能将耗时任务移到后台线程执行。

6. 检查广播接收器和服务

确保广播接收器和服务(尤其是前台服务)在处理任务时不会阻塞主线程。如果广播接收器或服务需要执行耗时任务,请在后台线程中进行处理。

7. 优化布局和绘制

复杂的布局和频繁的重绘也可能导致 ANR。使用 Android Studio 的 Layout Inspector 和 Profile GPU Rendering 工具检查布局性能,优化布局层次结构和绘制操作。

8. 避免死锁和同步问题

确保在多线程编程中避免死锁和同步问题,这些问题可能导致主线程长时间等待,从而引发 ANR。

通过这些步骤,你可以有效地定位和解决 Android 应用中的 ANR 问题,提高应用的响应性和用户体验。如果你有具体的 ANR 日志或代码片段,请提供更多信息,以便进一步诊断和解决问题。

正确回答:日常开发中遇到Anr可以根据 logcat检查Anr日志,或者 分析系统自动生成的 trace文件。提测前可以根据profiler 分析应用性能,找出UI线程上的瓶颈。上线后根据工具bugly 解决Anr问题。 

其实我想了想,面试官主要想引申出 profiler 这个。看下你是否使用过,是否会根据profile文件定位问题。

组件化和普通模式有什么区别。

组件化(Modularization)和普通模式(Monolithic)是两种不同的软件架构模式。它们在应用程序的设计和实现方面有显著的区别。

组件化(Modularization)

组件化是一种将应用程序划分为多个独立模块的设计方法。每个模块具有明确的职责和边界,可以独立开发、测试和部署。这种方法有以下几个优点:

  1. 可维护性:每个模块的代码相对独立,修改某个模块的代码不会影响其他模块,从而提高了代码的可维护性。
  2. 可重用性:模块可以在不同的项目中重用,从而减少重复代码,提高开发效率。
  3. 可测试性:每个模块可以独立测试,从而更容易发现和修复问题。
  4. 并行开发:不同的开发人员或团队可以并行开发不同的模块,从而加快开发进度。
  5. 灵活性:可以根据需要增加或删除模块,从而更灵活地应对需求变化。

在 Android 开发中,常见的组件化实现方式包括通过 Gradle 的多模块项目来管理不同的功能模块。以下是一个简单的组件化项目结构示例:

Code

MyApplication/
├── app/                  // 主应用模块
├── feature1/             // 功能模块1
├── feature2/             // 功能模块2
└── common/               // 公共模块

普通模式(Monolithic)

普通模式,也称为单体架构,是指将所有功能和代码都放在一个单一的代码库中。所有的功能模块紧密耦合,通常在同一个进程中运行。这种方法有以下特点:

  1. 简单性:单体架构的项目结构简单,所有代码都在一个项目中,容易理解和管理。
  2. 部署方便:单体应用打包成一个部署单元,部署过程相对简单。
  3. 性能优化:由于所有功能在同一个进程中运行,可以更容易进行性能优化。

然而,单体架构也有一些缺点:

  1. 可维护性差:随着项目规模的增大,代码库变得庞大且复杂,维护变得困难。
  2. 开发效率低:多个开发人员在同一个代码库中工作,容易产生代码冲突,影响开发效率。
  3. 扩展性差:难以单独扩展或替换某个功能模块,限制了系统的扩展性和灵活性。
  4. 测试困难:由于各个功能模块紧密耦合,单元测试和集成测试变得更加复杂。

比较

特点组件化(Modularization)普通模式(Monolithic)
可维护性
可重用性
可测试性
开发效率
灵活性
部署复杂度
性能优化较难较易

结论

选择组件化还是普通模式取决于项目的规模和需求。对于大型项目,组件化可以带来更高的可维护性和开发效率。而对于小型项目,普通模式可能更简单和直接。

这个没有正确回答,就是两个架构之间的比较。组件结构依赖等等,懂就是懂,你可能是懂但是说不出来,但是你不懂指望背八股肯定说不清楚。而我是前者,我确实用过,而且自己搭建过组件化的项目。但就是说不出来~ 

包体积优化,页面卡顿优化,内存优化 

回答:首先说下包体积的优化,整个apk占用大小,纯代码只占用了体积的十分之一左右,其余都是一些资源占用。所以包体积的优化其实就是对资源的的优化;在项目结束阶段,可以使用工具检测未使用资源,删除。然后可以减少非必要图片资源,或者选择合适的图片格式svg等方式。其次尽量少用大型库,选用轻量级代替,比如图片库Gilie代替Picasso等,采用混淆方式减少必要库占用体积。最终方式采用动态组件方式 把不常用的功能拆分到独立模块,减少初始包的体积大小。或者采用分包形式。

页面卡顿:

内存优化:

Android Studio Profiler 生成的报告阅读

手写单利


http://www.kler.cn/a/570869.html

相关文章:

  • FPGA开发,使用Deepseek V3还是R1(1):应用场景
  • android 系统 wms详解
  • HONOR荣耀MagicBook 15 2021款 独显(BOD-WXX9,BDR-WFH9HN)原厂Win10系统
  • 为什么深度学习选择Tensor而非NumPy数组?核心优势深度解析
  • 8295智能座舱弹窗点击问题,点击window之外的区域,window不消失的问题。touchableRegion的问题分析(android 13)
  • DNS 详细过程 与 ICMP
  • aiohttp、httpx 和 requests 的区别
  • 五分钟快速学习优秀网站的HTML骨架布局设计
  • 比亚迪“灵鸢”来袭,汽车+无人机梦幻联动!
  • Qt:day1
  • LeetCode 热题 100 链表章节
  • 爬虫:一文掌握WebSocket爬虫案例实战
  • Kotlin 类委托与属性委托
  • 大白话跨域问题的原理与多种解决方法的实现
  • mac Homebrew安装、更新失败
  • 广义线性模型下的数据分析(R语言)
  • Android -- 使用Sharepreference保存List储存失败,原因是包含Bitmap,drawable等类型数据
  • UI自动化框架介绍
  • SpringBoot集成Netty实现Ws和Tcp通信
  • 【mysql】有索引和没有索引字段更新时锁的不同