Android面试指南(八)

目录

1、Java语言相关

1.1、String的intern方法

1.2、HashMap的扩容

1.3、Java数组不支持泛型

1.4、泛型类型保留到运行时

1.5、匿名内部类使用的外部变量需要加final

2、Kotlin语言相关

3、设计模式

1、Java语言相关

1.1、String的intern方法

    1)、String的存储机制

    String内存模型的核心特点包括:

    • 不可变性:String实例一旦创建不可修改
    • 双重存储机制:实例可能存储在堆或字符串常量池
    • intern方法作用:将字符串显式存入字符串池中

    String类的特殊性体现在:

    • 不可变性:与基本数据类型封装类相同
    • 声明语法特殊:唯一可通过字面量创建的引用类型
    • 存储位置特殊:实例可能存在于字符串常量池而非堆

    ①、String不可变性的实现机制:

    • final类声明:禁止继承
    • private final char[]存储数据:外部无法修改
    • 方法设计原则:比如substring等操作均返回新实例

    ②、String实例不可变的表现:

    • 任何修改操作(如拼接、截取)均生成新实例
    • 原实例内容始终保持不变
    • 必须通过改变引用指向新实例实现"修改"效果

    2)、字符串常量池

    • 字符串池与常量池的区别:常量池存在于每个class文件的逻辑结构中,存储以字面量方式声明的字符串;运行时常量池是class文件常量池加载到内存后的形态。
    • 字符串池功能:全局存储字符串(非每个类独立),HotSpot虚拟机通过哈希表实现。
    • 关联性:常量池中的string类型常量通过查询字符串池解析,二者通过引用保持一致性,导致概念易混淆。

    字符串常量池的优化原理:

    • 相同字面量的多个String实例可共享
    • intern方法实现实例复用
    • 哈希值相同且equals为true的实例可合并

    内存优化示例:字面量均为"ABC"的name1和name2引用可通过intern指向同一实例。

    3)、总结

    string的不可变性使字符串池复用成为可能,JDK1.7+的字符串池存储堆引用,相关面试题多围绕内存模型展开,需理解存储机制。

    • JDK 1.7以下(不包括1.7),字符串池在永久代,intern方法的含义是:
      • 如果字符串池中存在与当前字符串内容相同的缓存,则返回缓存实例;
      • 否则,在字符串池中创建相应实例,并返回。
    • JDK1.7及以上,字符串池在堆中,字符串池中,存储的是字符串堆引用,intern方法的含义是:
      • 如果字符串池中存在与当前字符串内容相同的缓存,则返回缓存的引用;
      • 否则,在字符串池中存储当前字符串的引用。

    1.2、HashMap的扩容

    HashMap容量选择二的N次方的原因如下:

    • 计算数组下标时可通过位运算替代取余操作,从而提升运算性能
    • 扩容时无需重新计算所有节点位置,仅需检查新参与运算的最高位即可

    1)哈希算法

    散列算法将任意长度输入转换为固定长度输出,要求相同输入必产生相同输出。哈希碰撞指不同输入产生相同输出的情况。理想哈希算法应最大限度避免碰撞,简单取余算法因碰撞概率高不适用于高性能场景。

    哈希碰撞:哈希表中元素位置通过哈希码对容量取余确定。碰撞发生时元素会被放入相同桶中,该机制是哈希表定位元素的基础算法。

    2)HashMap

    HashMap主体结构是初始长度16的数组,数组存储Node类型元素。传统桶结构采用链表实现,JDK1.8新增红黑树实现(TreeNode为Node子类)。数组与红黑树会根据元素数量动态转换,红黑树在数据量大时具有性能优势。

    ①、put方法

    put方法实际调用putVal实现存储,过程中会通过hash()方法重新计算键的哈希码。

    ②、putVal方法

    putVal方法执行流程如下:

    • 检测数组是否为空,空则触发resize扩容
    • 计算元素数组下标(非直接取余)
    • 下标为空时新建节点插入
    • 下标非空时判断键是否相等,相等则覆盖值
    • 不相等时根据节点类型(链表/红黑树)遍历查找
    • 链表插入后检查是否需转为红黑树
    • 最终检查是否触发扩容

    计算元素在数组中的下标:下标计算采用(n-1)&hash替代取余操作,二者数学等价但位运算效率更高。以容量16为例:

    • 16-1=15(二进制1111)
    • 与哈希码按位与操作可保留后四位
    • 该优化的前提是容量必须为2的n次幂,此为设计原因之一。

    ③、hash方法

    hash()方法通过扰动函数解决低位碰撞问题:

    • 将哈希码无符号右移16位使高位参与运算
    • 与原值异或融合高位特征到低位
    • JDK 1.8的扰动逻辑较1.7简化但原理一致,核心目标是提升低位随机性。

    ④、resize方法

    扩容机制特性分析:

    维度

    JDK 1.7前

    JDK 1.8优化

    容量变化

    翻倍为2^n

    保持2^n特性

    重定位方式

    全量重新计算下标

    仅检查新增最高位

    元素移动

    全部迁移

    原位置或原位置+旧容量

    优化依据:扩容后元素新位置仅取决于新增运算位(0保持原位,1偏移旧容量值)。此特性同样依赖2^n容量设计。

    1.3、Java数组不支持泛型

    Java数组不支持泛型的说法需明确两点:泛型仅在编译期提供有限类型安全保证,且实际限制在于无法创建泛型类型数组。核心原因包括:

    • 类型擦除机制使运行时泛型信息丢失
    • 数组协变性与泛型设计理念冲突
    • Java泛型实现受历史兼容性制约

    Java数组不支持泛型的原因在于数组与泛型在设计理念上存在根本性冲突。数组是JVM底层实现的特殊实例类型,自Java早期版本就已存在;而泛型在Java 1.5版本才引入。两者核心矛盾在于:数组具有协变性(父类型数组引用可指向子类型数组实例),而泛型默认具有不变性(泛型类之间无继承关系)。这种设计差异导致无法通过编译期类型安全检查创建泛型数组实例。

    1)、Java泛型的实现方式

    类型擦除机制特点:

    • 泛型类型参数仅在编译期有效,字节码中替换为原始类型
    • 生成桥接方法保证多态性
    • 历史原因:保持与Java1.5前版本的二进制兼容性

    2)、泛型的类型擦除机制

    泛型类型擦除机制实现方式可通过反编译字节码观察。new指令及构造方法调用均不包含泛型信息,创建的ArrayList实际为Object类型。绕过编译器报错可将任意类型元素放入ArrayList,此现象解释了字符串元素存入非泛型集合的可能性。

    类型擦除:类定义的泛型经类型擦除后固化为Object。子类继承父类时给定的泛型参数需具体化,例如实现change方法时参数与返回值被具体化为Apple类型。由于父类方法签名中为Object,编译器自动生成桥接方法以维持重写关系。类型擦除导致运行时无法创建泛型数组,且泛型无法在运行时保证内存安全。总结:

    • 字节码创建数组需明确类型
    • 类型擦除使泛型无法延续至运行时
    • Java泛型无法实现运行时类型安全检查

    3)、Kotlin中的数组

    Kotlin数组支持泛型通过两种实现方式:

    数组类型

    实现特性

    对应字节码指令

    Java等效类型

    类集合语法数组

    泛型实现类似List,不具备协变性

    anewarray

    包装类类型数组

    基本类型数组

    兼容Java基本数据类型

    newarray

    基本数据类型数组

    Kotlin通过IntArray等类封装基本类型数组,反编译显示其字节码同样不包含泛型信息,证实Kotlin泛型基于类型擦除机制实现。

    4)、Kotlin为何支持创建泛型数组

    Kotlin虽采用类型擦除机制,但通过强制泛型类型声明消除安全隐患。由于无需兼容历史字节码或源码,集合与数组API设计均要求显式指定泛型类型,此设计规避了Java中存在的类型安全问题。

    1.4、泛型类型保留到运行时

    1)、使用Class<T>

    <T> T create(Class<T> kind) {
    return new kind.newInstance();
    } // TODO catch
    

    通过Class<T>参数传递类型信息。该方案特点:

    • 支持反射创建实例
    • 局限性:
      • 反射效率低
      • 无法处理嵌套泛型(如List<String>.class非法)

    2)、通过子类把泛型类型具体化(reified)

    public class AppleBasket extends Basket<Apple>{
    ... // 这里Apple具体化
    }
    public class GenericsTypeToken<T> {
    public Type type = null;
    public GenericsTypeToken() {
    type =
    ((ParameterizedType)this.getClass().getGenericSuperclass())
    .getActualTypeArguments()[0];
    }
    }
    new GenericsTypeToken<Basket<Apple>>() {}.type;
    

    字节码签名保留泛型信息。实现步骤:

    • 继承具体化泛型类
    • 通过getGenericSuperclass()获取签名属性 - 解析Type接口获取实际类型
    • Gson等库采用类似TypeToken机制实现泛型反序列化。
    Type type = new TypeToken<List<User>>() {}.getType();
    List<User> list = new Gson().fromJson(userJson, userListType);

    1.5、匿名内部类使用的外部变量需要加final

    • 匿名内部类, 持有外部类的引用;
      • 以编译器自动生成的成员变量的形式持有;
      • 通过编译器自动生成的构造方法传入。
      • 匿名内部类, 通过这个引用访问外部类的成员变量和方法。
    • 匿名内部类, 访问外部局部变量时, 其实是访问自身的一个成员变量;
      • 这个成员变量, 是编译器自动生成的(val$变量);
      • 这个成员变量, 由编译器自动生成的构造方法初始化;
      • 为了保证这个成员变量和外部局部变量时刻保持一致性, 二者必须都是final的。

    2、Kotlin语言相关

    推荐阅读:一文带你快速掌握Kotlin核心技能

    3、设计模式

    设计模式:是前人通过试错总结的最佳实践,用于解决特定问题(如提高代码复用性、可读性、健壮性)。那我们遇到过哪些设计模式的使用案例呢?

    ①、模板方法模式

    • Android Activity生命周期:开发者通过重写onCreate、onResume等方法响应状态变更,无法干预状态流转逻辑。
    • 核心要点:
      • 父类定义算法框架,子类实现细节。
      • 决策权在高层模块(如父类控制调用时机)。
      • 与策略模式区别:模板方法通过继承封装算法,策略模式通过组合封装。

    ②、责任链模式

    责任链模式通过链式节点处理请求,经典案例有:

    • Android View事件分发:事件沿视图层级传递直至被消费。
    • OkHttp拦截器:请求通过拦截器链逐层处理。

    责任链模式适用于多个处理者具备处理相同事件能力且需筛选最符合要求或优先级最高的场景。核心特征包括:

    • 职责差异化:责任链各节点具备不同职责
    • 顺序依赖性:节点间存在明确先后顺序
    • 扩展优势:通过接口实现新责任并加入责任链即可完成功能扩展
    • 动态调整:支持运行时修改责任链节点构成

    ③、装饰者模式

    装饰者模式的核心在于动态扩展类的能力,通过组合而非继承实现功能扩展。

    安卓平台的ContextWrapper类采用装饰者模式。通过包装ContextImpl实现动态功能扩展,避免直接继承带来的局限性。

    ④、代理模式

    代理模式与装饰者模式结构相似,均实现接口并持有接口引用。代理模式通过替身控制对象访问,代理模式的要点包括远程对象代理和虚拟代理。与装饰者模式目的不同,前者控制访问,后者扩展行为。

    典型场景:安卓框架层通过Binder跨进程实现代理模式。ActivityManager通过IActivityManager接口代理跨进程访问。

    ⑤、Android中的其它设计模式

    安卓平台常见设计模式包括:

    • 观察者模式:广播、Adapter的notifyDataSetChanged
    • 适配器模式:ListView的Adapter
    • 构建者模式:AlertDialog.Builder
    • 备忘模式:Activity的onSaveInstanceState
    • 迭代器模式:Cursor遍历
    • 原型模式:Intent的clone方法
    • 享元模式:线程池、连接池
    • 命令模式:Handler消息循环

    OK,今天的内容就这么多了,下期再会!

    本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
    如若转载,请注明出处:http://www.pswp.cn/bicheng/96428.shtml
    繁体地址,请注明出处:http://hk.pswp.cn/bicheng/96428.shtml

    如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

    相关文章

    7、Matplotlib、Seaborn、Plotly数据可视化与探索性分析(探索性数据分析(EDA)方法论)

    学习目标&#xff1a;掌握数据可视化的原理和工具&#xff0c;培养通过图表洞察数据规律的能力&#xff0c;建立数据驱动的分析思维数据可视化是数据科学的重要组成部分&#xff0c;它将抽象的数字转化为直观的图形&#xff0c;让我们能够快速识别模式、趋势和异常。从基础的柱…

    Next系统学习(二)

    SSR生命周期与实现详细解答 19. 如果不使用框架&#xff0c;如何从零用React/VueNode.js实现一个简单的SSR应用? React Node.js SSR实现步骤&#xff1a; 项目结构搭建 /project/client - 客户端代码/server - 服务端代码/shared - 共享代码服务端基础设置 // server/index…

    零代码入侵:Kubernetes 部署时自动注入 kube-system UID 到 .NET 9 环境变量

    在现代化 .net9 应用部署阶段&#xff0c;零代码入侵模式&#xff0c;自动获取 kubernetes 命名空间 kube-system 的 UID&#xff0c;并其作为变量配置到应用。 以下是几种实现方式&#xff1a; 方法一&#xff1a;使用 InitContainer Downward API 您可以通过 Kubernetes 的 …

    基于Redis设计一个高可用的缓存

    本文为您介绍&#xff0c;如何逐步设计一个基于Redis的高可用缓存。 目录 业务背景 步骤一&#xff1a;写一个最简单的缓存设计 存在的问题&#xff1a;大量冷数据占据Redis内存 解决思路&#xff1a;让缓存自主释放 步骤二&#xff1a;为缓存设置超时时间 存在的问题&a…

    从原理到实践:LVS+Keepalived构建高可用负载均衡集群

    从原理到实践&#xff1a;LVSKeepalived构建高可用负载均衡集群 文章目录从原理到实践&#xff1a;LVSKeepalived构建高可用负载均衡集群一、为什么需要LVSKeepalived&#xff1f;二、核心原理&#xff1a;Keepalived与VRRP协议1. VRRP的核心思想2. Keepalived的三大功能三、LV…

    iOS混淆工具实战 在线教育直播类 App 的课程与互动安全防护

    近年来&#xff0c;在线教育直播类 App 已成为学生与培训机构的重要工具。无论是 K12 教育、职业培训&#xff0c;还是兴趣学习&#xff0c;App 中承载的课程视频、题库与互动逻辑都是极高价值的内容资产。 然而&#xff0c;教育直播应用同样面临多重安全风险&#xff1a;课程视…

    第2节-过滤表中的行-BETWEEN

    摘要: 在本教程中&#xff0c;您将学习如何在 WHERE 子句中使用 PostgreSQL 的 BETWEEN 运算符来检查某个值是否在两个值之间。 PostgreSQL BETWEEN 运算符 BETWEEN运算符是一种比较运算符&#xff0c;如果某个值介于两个值之间&#xff0c;则返回true。 以下是 BETWEEN 运算符…

    Windows 11 手动下载安装配置 uv、配置国内源

    Windows 11 手动下载安装配置 uv、配置国内源 本文对应的讲解视频链接&#xff1a;https://www.bilibili.com/video/BV1WnYTzZEpW 文章目录Windows 11 手动下载安装配置 uv、配置国内源1. 下载、安装、配置 uv2. 参考信息重要声明&#xff1a; uv 的安装有很多种方式&#xff…

    平板热点频繁断连?三步彻底解决

    平板反复断开热点连接是一个非常常见且令人烦恼的问题。这通常不是单一原因造成的&#xff0c;而是多种因素叠加的结果。 我们可以从热点发射设备&#xff08;手机等&#xff09;、平板本身、以及环境因素三个方面来排查和解决。 一、 热点发射端&#xff08;通常是手机&#x…

    Qt文件操作的学习(三)

    一、实现简易文本编辑器 主要用到带菜单栏的窗口&#xff0c;而非单一窗口。QT已经写好相关操作&#xff0c;就不在重新造轮子了功能设计&#xff1a;新建文本文档&#xff0c;打开文件&#xff0c;保存文件&#xff0c;另存为 这次不同于之前直接可以在控件上面右击槽了&…

    ArcGIS学习-20 实战-县域水文分析

    水文分析任务提取区域内水流方向、汇流累积量、河网、流域、子流域前置操作环境更改加载数据检查投影坐标系河网分析洼地填充限制默认为空&#xff0c;认为所有洼地都是需要填充的&#xff0c;这里更正一下Fill_DEM需要加上后缀.tif流向分析得到流量分析得到这里的黑色代表非河…

    本地 Docker 环境 Solr 配置 SSL 证书

    一、简介 在本地开发环境中为 Solr 配置 SSL 证书,是提升开发与测试一致性的关键步骤。尤其是在涉及安全传输需求的场景中,本地环境的 HTTPS 配置能有效避免因环境差异导致的问题。本文将详细介绍如何利用 Docker 容器,快速为 Solr 服务配置自签名 SSL 证书,实现本地 HTTP…

    MacOS 运行CosyVoice

    CosyVoic主要特点&#xff1a;1、支持中文、英文、上海话、天津话、四川话等方言。语音非常自然。2、支持3秒语音零样本克隆&#xff0c;效果非常好。3、克隆时间比较长&#xff08;取决于GPU性能&#xff0c;使用H20以满足低延迟输出&#xff09;&#xff0c;L4 克隆默认文本需…

    我不是挂王-用python实现燕双鹰小游戏3

    在前两个版本的更新后,越来越多内容,操作和运行也不方便,优化第三版本窗口可视化界面 本次版本更新使得可读性和可操作性大幅度增加,前面2版本可分别参考 我不是挂王-用python实现燕双鹰小游戏 和 我不是挂王-用python实现燕双鹰小游戏2 一.燕双鹰窗口可视化(燕双鹰3.0) 新燕双…

    装饰(Decorator)模式可以在不修改对象外观和功能的情况下添加或者删除对象功能

    试题&#xff08;35&#xff09;、&#xff08;36&#xff09;某系统中的文本显示类&#xff08;TextView&#xff09;和图片显示类&#xff08;PictureView&#xff09;都继承了组件类&#xff08;Component&#xff09;&#xff0c;分别显示文本和图片内容&#xff0c;现需要…

    深度学习基础概念【持续更新】

    1. 梯度消失如果网络中某一层的激活函数&#xff08;如 sigmoid 或 tanh&#xff09;在输入较大的情况下有很小的梯度&#xff08;比如接近零&#xff09;&#xff0c;那么当这些小的梯度通过多层反向传播时&#xff0c;它们会逐渐变得更小。这意味着在深层网络的前面几层&…

    上下文工程:AI应用成功的关键架构与实践指南

    在AI应用开发中&#xff0c;模型能力只决定性能上限&#xff0c;而上下文质量决定性能下限——上下文工程正是确保AI系统理解用户意图、生成准确响应的核心工程技术&#xff0c;已成为区分普通AI应用与卓越AI应用的关键因素。一、上下文工程&#xff1a;AI应用的新核心竞争力 1…

    数据传输优化-异步不阻塞处理增强首屏体验

    背景&#xff1a;主 project 页面中会将视频存储到云端后获得 ID &#xff0c;然后用 ID 调用 后端API POST到数据库后拿到挂载页面URL&#xff0c;接着传入视频分享组件&#xff08;由于视频分享子组件的目标是分享视频挂载页面&#xff0c;所以前置步骤不能少&#xff09;con…

    【芯片设计-信号完整性 SI 学习 1.0 -- SI 介绍】

    文章目录一、SoC 设计验证阶段的 SI 测试主要工作举例二、芯片 Bringup 阶段的 SI 测试主要工作举例三、SI-PI 联合仿真主要内容举例四、整体总结一、SoC 设计验证阶段的 SI 测试 在 前硅阶段&#xff08;pre-silicon&#xff09;&#xff0c;设计团队需要确保 SoC 与外设接口…

    C语言链表设计及应用

    链表链表节点设计链表项目链表中的传址调用检查申请空间链表尾插链表头插链表尾部删除链表头部删除链表的查找指定位置之前插入指定位置之后插入数据删除指定位置&#xff08;节点&#xff09;数据删除指定位置&#xff08;节点&#xff09;之后的数据链表的销毁前面学习了顺序…