取经之路就在眼前--面经备战

Android 面经收集大全

注意: 本文是通过阅读大量的博文以及其他论坛的精彩好文简化版随手录,如有侵权马上删除!

持续更新~~

文字多,难免有些错别字,最近时间忙,后续尽快更改,望谅解

文章目录

简述

<<<<<<< HEAD
但不仅限于Android方面,希望通过此方式把自己的知识台阶一步一步搭起,最后通向offer的大门,知识汇聚,知识分享,开源的力量是无穷尽的,也祝大家早些时日提取自己心满意足的offer.

PS: 本收集属于对其他各大技术论坛大佬精美好文的简短总结

数据库

1.数据库的索引和实现原理

  1. 索引就是一个查找问题,索引是一个排序的数据结构,实现通常是B树及其变种
  2. 优点
    1. 唯一索引,保证每一行数据的唯一性
    2. 加快检索速度
    3. 加速表之间的连接
    4. 减少分组和排序时间
    5. 使用优化隐藏器提高性能
  3. 缺点
    1. 创建和维护耗时
    2. 占据更多的物理空间
    3. 增删改需要动态维护索引
  4. 在什么列上需要创建索引
    1. 经常搜索的列
    2. 主键列 [唯一性]
    3. 经常连接的列 [外键]
    4. 经常需要排序的列
    5. 经常使用在where子句中的列 [条件判断]
  5. 什么列不需要索引
    1. 查询少的列
    2. 只有很少数据值的列
    3. 定义数据类型很大的列 [比如text image bit]
    4. 修改性能大于检索性能的列
  6. 存储结构 [B树,B+树,红黑树]

2.手撕MySQL分组查询

  1. 按照某个字段进行分组 group by

  2. having 指定查询的条件,对分组的内容尽心过滤

  3. 单独使用group by

    单独使用查询出来的是分组的第一条记录的值
    select * form student group by gender;
    
  4. 结合聚合函数使用

    select count(*) ,gender from student group by gender;
    
  5. 结合having一起使用

    select sum(grade),gender from student group by gender having sum(grade)<300
    

    having和where的区别:

    都是根据条件进行过滤,

    having后面可以跟随聚合函数

3.数据库中的事务理解

  1. 事务? 更新各种数据项的一个程序执行单元 [操作的集合: 这个集合从开始事务到提交事务]

    1. 保持数据库的一致性,可以恢复数据
    2. 程序操作数据的一个隔离
  2. 特性

    1. 原子性 要么全部执行,要么不执行
    2. 一致性 完整约束状态一致转移
    3. 隔离性 事物之间不影响
    4. 持久性 一旦提交,永久保存
  3. 隔离级别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nukikaE4-1581727051667)(C:\Users\Primer4\Desktop\AndroidTheInterview\TheInterviewExperienceOfAndroid\1581680424542.png)]

  4. 隔离 并发控制

    1. 乐观并发控制 一开始以乐观的心态认位没有冲突,直到真的有冲突了才去解决问题
    2. 悲观并发控制 一开始悲伤地假定必然会出现冲突,使用锁使其串行执行
  5. 锁 <还有一控制并发的就是 时间戳>

    1. 共享锁 有了共享锁只能在拥有共享锁
    2. 排他锁 明显的排斥,不再拥有其他锁
    3. 死锁 多个事务持有锁,相互等待无法执行
    4. 饥饿 一直加共享锁,不曾获得排他锁

Linux

1.Linux中断命令

  1. 中断
    1. CPU暂停当前的程序执行,转去处理突发事件,处理完后返回原程序继续执行
    2. 中断请求线: 中断唯一标识的数值
    3. 中断处理函数:内核执行对应的处理函数,中断的优先级最高
  2. 分类
    1. 中断<外部中断或异步中断>: 外设向处理器发出中断请求
    2. 异常<内部中断或同步异常>: 处理器执行指令错误
  3. 终端命令
    1. CTRL+C 强制中断程序执行
    2. CTRL+z任务中断,进程挂起
    3. CTRL+d特殊字符 EOF
    4. CTRL+\退出
    5. kill pid

扩展: 后台进程管理命令:

  1. jobs查看当前有多少在后台运行的命令
  2. fg将后台的命令调至前台执行
  3. bg将后台暂停的命令继续执行

操作系统

1.操作系统线程和进程的同步机制和通信机制

  1. 同步机制

    1. 临界区

      多线程的串行化访问公共资源,一个线程进入临界区之后其余线程必须在临界区外部等待,等到临界区被释放后,其他线程就可以抢占
      
    2. 互斥量

      互斥对象机制,只有拥有互斥对象的线程才有访问共享资源的权限,互斥对象只有一个,还能实现不同应用程序的线程之间的资源访问
      
    3. 信号量

      允许多个线程同一时刻访问同一资源,但需要限制最大线程数量,类似于操作系统的PV操作
      

      PV操作

      信号量S>0,表示可供使用的资源实体数量
      
      信号量S<0,表示等待使用资源的进程数
      
      相关操作:
      	申请资源
      					S-- [资源实体少一个]
      						if(--S >= 0){线程继续执行}
      						if(--S < 0){该进程阻塞,进入进程调度}
      	释放资源
      					s++ [资源实体多一个]
      						if(++S > 0){进程继续执行}
      						if(++S <= 0){唤醒一个等待进程或者转入进程调度}
      

      关键词解释: 进程调度

      多个进程抢用CPU使用权,操作系统究竟给哪一个进程CPU资源,由操作系统决定,这个决定的方法就是调度策略,理所当然的<进程调度>就是<操作系统使用调度策略给某个具体的进程给予CPU资源的使用权>
      
      调度算法:
      			抢占式
      							进程拥有最大的运行时间,并且这个时间是确定的,进程运行时间大于最大运行时间,该进程就会被挂起,选择调度算法挑选下一个进程运行
      			非抢占式
      							一个进程一直运行到阻塞或者自愿退出
      
    4. 事件

      使用通知操作,生产者和消费者的关系<观察者模式>
      
      
  2. 通讯机制

    1. 无名管道

      数据单向流动,是在具有亲缘关系之间的进程之间通讯,存在于内存
      
      
       2.  命名管道
      
      
      数据双向流动,无关进程之间可以数据交换
      
      
    2. 消息队列

      消息的链表,存放在内核中,独立于发送和接收线程
      
      
    3. 信号量

      计数器,实现进程间的互斥和同步,不是存储数据
      
      
    4. 共享内存

      共享一个给定的存储区,最快的方式,进程直接对内存数据进行读取
      
      

2.什么是缓存溢出

  1. 缓冲区
    1. 写入的缓冲区的数据超出缓冲区的大小,溢出的数据覆盖合法的数据 [溢出]
    2. 一段可读可写的内存区域
  2. 危害
    1. 程序崩溃,拒绝服务
    2. 执行恶意代码
    3. 缓冲区攻击 [驱使操作系统执行恶意的代码]
  3. 原因
    1. 没有检查用户的合法输入

3.操作系统分页分段管理

  1. 存储器层次结构

    1. 高速缓存

    2. 主存

    3. 磁盘

      物理内存
      			寄存器
      			内部缓存
      			外部缓存
      			主存
      			[内部和外部区别于CPU的内和外]
      
      
  2. 内存管理方式

    虚地址
    地址变换
    内存分配,回收,扩充,共享,保护
    
    
  3. 连续分配存储管理

    1. 单一连续
      1. 系统区
      2. 用户区
    2. 分区管理 [分时系统,程序并发执行,引入问题使内存碎片]
      1. 固定分区 [限制并发执行程序的数目]
      2. 动态分区 [没有内碎片,有外碎片,算法复杂]
        1. 分区分配算法
          1. 最先适配 :从头查找符合条件的第一个分区
          2. 下次适配:从上一次分配分区开始向下查找第一个符合的分区
          3. 最佳适配:查找大小相差最小的分区
          4. 最坏适配:查找最大的空闲分区
      3. 内存紧缩 [占用的分区移动到一端,其余空闲分区合成一个新的空闲分区]
      4. 覆盖技术 [较小的可用内存运行较大的程序 ,必要数据常驻内存,可选部分在外存,使用公共内存]
      5. 交换技术 [不执行的程序暂时送到外存,装入新的程序执行]
      6. 交换和覆盖的区别
        1. 交换不需要覆盖结构
        2. 交换在进程和作业之间
        3. 覆盖在同一作业或进程中
        4. 覆盖只能覆盖无关的程序段
  4. 页式存储

    1. 原理
      1. 逻辑地址划分为固定大小的
      2. 物理内存划分为相同大小的页框
      3. 将页转入页框
      4. CPU实现逻辑地址和物理地址的映射
    2. 优点
      1. 没有页外碎片,也内碎片比较小
      2. 程序不必连续存放
      3. 方便更改程序占用控件大小
    3. 缺点
      1. 一时刻装入整个程序
    4. 数据结构
      1. 进程表: 逻辑地址到物理地址的映射
      2. 物理页面表:描述物理内存的分配状况
      3. 请求表: 描述进程表的大小和位置
    5. 地址变换
      1. 使用逻辑页号最终找到物理地址
  5. 段式存储

    1. 原理
      1. 将程序地址空间划分为多个段
      2. 每个段分配一个连续的分区
      3. 各个段可以不连续在不同分区
      4. 动态分区管理物理内存
    2. 优点
      1. 分别编写和编译源程序的一个文件
      2. 没有内存碎片,外存碎片可以内存紧缩消除
      3. 内存共享
    3. 缺点
      1. 进程全部装入内存
    4. 数据结构
      1. 进程段表: 进程地址的各段
      2. 系统段表: 描述系统所有已分配的段
      3. 空闲段表:内存中所有空闲段
    5. 地址交换: 段表地址寄存器完成
  6. 区别

    1. 分页系统管理的需要
    2. 分段满足用户的需要
    3. 页使信息的物理单位
    4. 段是信息的逻辑单位
    5. 页大小固定,固定值由系统决定
    6. 段长度不固定,大小由用户程序决定
    7. 页式地址空间是一维的
    8. 段地址空间是二维的 <段名,段内地址>
    9. 段表比页表短

4.CPU时间片

  1. CPU分配给每个程序的时间,允许该程序运行的时间

  2. 程序或者线程在时间片结束前还在运行,CPU会强行把程序推出CPU,随机挑一个线程或者程序进来执行下一个时间片

  3. 若程序或者线程在时间片结束前结束,CPU立马挑选一个程序或者线程来执行,这样就不会浪费CPU资源,提高利用率

  4. 时间片的大小通常为10-100ms

    时间片轮询调度算法 抢占式的

    5.线程池

  5. 达到线程的复用,减少创建和销毁的开销

  6. 优点

    复用线程
    提高响应效率
    高效管理
    防止服务器过载
    
    
  7. 核心

    生产者和消费者模型
    ThreadPoolExecutor类	
    		corepoolsize核心池大小 ,开始线程数为0,到达核心值之后会把任务放入缓冲队列
    		maximumpoolsize池子最多可容纳线程数
    		keepalivetime没有任务执行时最长存活时间 <当线程数大于核心数时才起作用>
    		unit存活时间单位
    		workqueue阻塞的任务队列
    		threadfactory线程工厂
    		handler拒绝处理任务的策略
    		
    
    
    线程池状态:
    		一个volatile保证可见性 <最新之主存可见,可以立马获得最新值>
    任务执行:
    		execute方法提交方法 <submit方法,最后也是调用execu()>
    线程初始化:
    	创建核心线程prestartcoreThread
    	创建所有核心线程prestartallcorethread
    任务缓存队列:
    	wordqueue
    	给予数组的队列
    	基于链表的队列
    	特殊队列<直接新建线程执行新任务>
    	
    任务拒绝策略:
    	线程数到达最大值的时候执行策略
    	
    线程池关闭:
    	showdown()任务队列为空的时候关闭
      shutdownnow()立即关闭,强制清空队列缓存
      
      
    
    		
    
    

Java基础

1.Java四种引用

目的: 决定对象的生命周期利用JVM进行垃圾回收

  1. 强引用

    直接创建对象赋值,只要有引用变量就永远不被回收,宁可抛出异常;
    
    中断强引用和某个对象之间的关联,直接就是变量置null
    
    
  2. 软引用

    内存空间足够,垃圾回收器就不会回收他;
    
    否则对象会被回收,get获取对象就是null
    
    
    
  3. 弱引用

    只要垃圾回收器需要回收,弱引用必定会被回收
    
    
  4. 虚引用

    任何时候都有可能会被回收
    
    

2.Java synchronized的类锁和对象锁

  1. 对象锁

    1. 仅仅有关键字synchronized
    2. 也称实例锁
    3. 防止其他线程同时访问该实例的synchronized方法块
    4. 每个实例拥有自己的监视块
  2. 类锁

    1. static synchronized
    2. 也称全局锁,
    3. 控制类的所有实例的并发访问 [限制都线程该该类的所有实例同时访问jvm中对应的代码块]
    4. 所有实例公用一个监视块
  3. demo

    pulbic class Something(){  
        public synchronized void isSyncA(){}  
        public synchronized void isSyncB(){}  
        public static synchronized void cSyncA(){}  
        public static synchronized void cSyncB(){}  
    }
    
    
  4. 总结

    类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。
    
    

3.JavaGC机制 [hotspot为例]

    1. 新生代
      1. eden region
        1. 分配所得空间较大
        2. 垃圾回收时,eden和survivor存活的对象被复制一遍到两一个survivor中
      2. from survivor region
      3. survivor空间不够依赖老生代
      4. to survivor region
    2. 老生代
      1. 保存大对象
      2. 这个区满了就报出outofmemory异常
    3. 永久代 [一般不会被回收]
      1. 方法去
  1. 判断一个类是否无用,满足下列条件

    1. 类的所有实例被回收,队中不存在该类的任何实例
    2. 该类的classloader被回收
    3. 该类的java.lang.class没有任何地方被引用
  2. 什么时候执行GC操作

    1. Eden区空间不足,执行较小的GC
    2. 老年代空间不足,执行重大的GC
    3. 老年代连续可用空间小于新生代对象总大小,指向Full GC
  3. 对象的年龄计数器判断对象应该在新生代还是老生代 <判断依据有两个: 最大年龄 ; 相同年龄对象大小总和占比重>

  4. 确定对象是否不可用

    1. 引用计数法

      判断对象是否可用

      给对象添加引用计数器
      每引用一次计数+1
      引用失效一次-1
      当引用计数=0,对象不可用  <ps:不是对象死亡,因为相互循环引用难以解决>
        
      
      
    2. 可达性分析

      判断对象是否存活

      当一个对象没有到达GCRoots的路径就是不可达
      
      
  5. 垃圾收集算法

    1. 标记清除 : 标记需要回收的对象,清除被标记对象占用的内存 <缺点: 内存碎片严重>
    2. 复制算法: 内存一分为二,一部分用于直接存储对象,另一部分备用,解决内存碎片问题 <缺点:实际可用内存缩短一半>
    3. 标记整理:对象移动到内存的一端,清除边缘对象,标记的对象时需要移动的
    4. 分代收集:
      1. 根据对象的生命周期收集垃圾对象
      2. eden和另一个surivivor的存活对象复制到两一个surivivor中,再清除eden和surivivor区域

4.哪些对象可以作为GC ROOTS对象

GC会收集那些不是GC roots且没有被GC roots引用的对象

  1. JVM中的栈引用对象
  2. 方法区中的类静态属性引用对象
  3. 方法区中常量引用对象(final常量)
  4. 本地方法栈jni引用对象
作者:Accelerator
链接:https://www.zhihu.com/question/50381439/answer/120846441
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1.System Class   
系统类

2.JNI Local
Java 本地接口

3.JNI Global
全局变量

4.Thread Block
线程块

5.Busy Monitor
频繁的监控

6.Java Local

7.Native Stack
本地堆栈

7.Finalizable
final常量

8.Unfinalized

9.Unreachable

10.Java Stack Frame
栈帧
11.Unknown


5.volatile关键字和synchronize关键字

  1. volatile
    1. 使变量具有可见性 [可见性:我理解是有点同步的意思,就是一个变量被修改,最新的值立马更新到主存供其他线程使用最新之,以至于不会继续使用旧的值]
    2. 直接读写内存
    3. 禁止指令重排 [指令重排: 处理器优化代码,可能改变代码执行顺序,但在单线程下不会改变执行结果, 多线程上则不能]
  2. synchronize
    1. 既保证可见性 [同一时刻只有一个线程获得锁然后执行代码,释放锁之前将最新之刷新到主存]
    2. 也保证原子性 [要么执行,要么不执行]

6.Java内存模型的可见性,重排序,原子性

  1. 可见性:使用的是直接缓存到CPU中

  2. 重排序:改变指令执行顺序,优化代码,但不会改变执行结果

  3. 原子性 :操作单一不可见

计算机网络

1.OSI网络模型 [七层]

  1. 物理层

    提供物理连接
    
    关心比特流传输
    
    关心机械,电气,功能和规程特性
    
    IEEE 802.2的电器协议
    
    
  2. 数据链路层

    PPP SLIP ARPANE协议,隧道通讯协议,思科的CDP协议,地址解析协议
    
    物理寻址
    
    将原比特流转换成逻辑传输线路
    
    
    
    
  3. 网络层

    ICMP  ARP RARP IP,安全协议AH,路由协议OSPF最短路径优先
    
    外部网关EGP 内部网关IGRP IP/IPV6
    
    控制子网运行
    
    分组传输
    
    路由选择
    
    逻辑编址
    
    
  4. 传输层

    TCP  UDP
    
    分割数据
    
    保证数据有效到达端
    
    
  5. 会话层

    SMTP  DNS
    
    SSL TLS安全协议
    
    不同机器上用户之间简历管理会话
    
    
    
  6. 表示层

    SNMP TELNET
    
    信息的语法语义和之间的关联 [加密解密,转换翻译,压缩解压]
    
    
  7. 应用层

    HTTP TFTP FTP SMTP应用程序协议
    
    
  8. img

2.TCP/IP模型 [四层]

  1. 应用层: 传输协议

  2. 传输层: TCP UDP

  3. 网络层: IP ICMP

    IP层传输
    				点到点传输
    				传输IP分组
    
    TCP层传输
    				端到端的传输
    				传输TCP段
    
    
  4. 物理链路层: 根据需要选择不同的物理链路

3.网络设别工作在那一层

  1. 网卡

    物理层

  2. 中继器

    物理层 [复原网络中的信号,从新发送到其他网段]

  3. 集线器

    物理层 [连接各个物理设备]

  4. 网桥

    数据链路层的MAC子层上<介质访问控制层> [网段中相同协议传输数据包]

  5. 交换机

    数据链路层 [和网桥类似的功能]

  6. 路由器

    网络层 [分组转发和路由]

4.``HTTP,HTTPS,HTTP1,HTTP2`

  1. HTTP
    1. 明文方式发送内容
    2. 不提供加密
    3. 80端口
    4. 无状态连接
  2. HTTPS
    1. 在HTTP基础上加入了SSL协议<通讯加密,依靠证书验证服务器的身份>
    2. 用途
      1. 保证数据传输的安全
      2. 确认网站的真实性
    3. 需要申请证书
    4. 443端口
    5. 短连接
    6. 缺点
      1. 握手阶段耗时
      2. 缓存效率相对HTTP底
      3. ssl证书需要钱
      4. ssl证书需要绑定ip,不能在同一个ip上绑定多个域名
      5. 加密范围有限
  3. HTTP1.0
    1. 无状态 <不记录客户端过去的请求>
    2. 无连接 <处理完任务马上断开连接>
  4. HTTP1.1
    1. 持久连接 <默认keep-alive,避免重新连接和释放>
    2. 管道化
    3. 增加缓存
    4. host字段
    5. 断点传输
    6. <不允许同时存在两个响应>
  5. HTTP2.0
    1. 二进制分帧
      1. 流:双向字节流
      2. 消息:逻辑对应的数据帧集合
      3. 帧:最小单位,标记当前帧所属的流
    2. 多路复用
      1. TCP连接 <承载任意数量的双向数据流>
      2. 数据流以消息的形式发送 <可乱序发送,后根据帧头部标识的所属流进行重装>
      3. 并行传输
    3. 头部压缩
      1. HTTP1.x头部数据以纯文本形式传输
      2. HTTP2.0使用encoder,双方各自缓存一份header Fiedls <注意使缓存,重复利用,避免重传>
    4. 服务器推送
      1. 无需客户端明确请求,主动向客户端推送资源
  6. HTTP2和HTTPS的关系

5.三次握手及其相关

img

img

  1. 为何连接
    1. 客户端和服务器的内存里保存对方的一份信息
    2. 双方需要交换参数 <参数放在TCP头部>
    3. 可靠,面向连接,字节流,传输层服务
    4. 三次握手连接 <两次初始化序列号,两次确认> ,四次握手关闭
  2. 序列号
    1. 作用是使得一个TCP接收端可丢弃重复的报文段,记录以杂乱次序到达的报文段
  3. 标识符
    1. ACK 确认
    2. RST 重连
    3. SYN 初始化序列号
    4. FIN 发送方结束向对方发送数据

6.https加密过程

  1. 对称加密

    1. 加密和解密使用同一个密钥
  2. 非对称加密

    1. 私钥: 不让其他人知道 [解密]
    2. 公钥:任何人可以知道 [加密]
  3. 加密过程

    1. 非对称加密

    2. 结合 对称加密

    3. 结合 数字证书

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-efge8kmr-1581727051672)(C:\Users\Primer4\Desktop\AndroidTheInterview\TheInterviewExperienceOfAndroid\1581601486036.png)]

7.在浏览器中键入URL时会发生什么

一个完整的http请求

  1. 域名解析

    1. 查找浏览器本身的dns缓存,找IP地址和域名的映射表
    2. 操作系统的dns缓存
    3. 本地服务器dns缓存
    4. 根域名服务器发起请求
    5. 顶级域名服务器发起请求
    6. 权限服务器发起请求
    7. 得到IP地址
    8. 逐级返回IP地址给浏览器
  2. 发起HTTP请求

  3. 到传输层,选择TCP或者UDP,封装HTTP请求

  4. 到网络层,IP协议封装IP地址为IP数据报,使用arp协议

  5. 数据链路层,封装成mac帧

  6. 服务器响应请求资源

  7. 断开连接

8.get各post的区别

get

参数包含在url中
浏览器主动缓存请求
只能url编码
请求参数保留在历史记录中
参数长度有限
参数类型只能是ascll字符
get不安全

产生一个TCP数据包

post

参数包含在request body中

产生两个TCP数据包 <一个数据包先发送请求,第二个数据包发送数据>

设计模式

1.手写Java双重检验的单列模式

  1. 侧重考点就是在如何实现双重验证

    双重验证的逐渐演化过程

    1.单线程适用

    缺点:只能适用与单线程

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          instance = new A();
        }
        return instance;
      }
    }
    
    

    2.多线程适用 [适用synchronized方法]

    缺点:每次调用方法都需要同步的代价,实际上是只有if语句需要同步

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static synchronized A getInstance(){
        if(instance == null){
          instance = new A();
        }
        return instance;
      }
    }
    
    

    3.降低同步调用代价 [适用synchronized代码块]

    缺点: 当两个线程进入if判断之后,有一个线程进入了同步代码块,还有另外一个线程在同步块外,if语句内等待,所以当同步块线程创建完对象之后退出同步块,另一个线程没有再次判断instance是否为null

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          synchronized(A.class){
          	instance = new A();
          }
        }
        return instance;
      }
    }
    
    

    4.双重检查 [在创建对象之前再加一个if判断null]

    缺点:理想很美好,现实很骨感,不能保证在单处理器和多处理器上顺序执行,因为内存模型写入是无序的

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          synchronized(A.class){
            if(instance == null){
          		instance = new A();
            }
          }
        }
        return instance;
      }
    }
    
    

    5.解决无序写入问题 [双重同步代码块]

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          synchronized(A.class){
            A tmp = instance;
            if(tmp == null){
          		synchronized(A.class){
                tmp = new A();
              }
              instance = tmp;
            }
          }
        }
        return instance;
      }
    }
    
    

    6.优化无序写入

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          synchronized(A.class){
            A tmp = instance;
            if(tmp == null){
          		synchronized(A.class){
                instance = new A();
              }
            }
          }
        }
        return instance;
      }
    }
    
    

    7.考虑到内存模型和写入顺序,最终选择方案

    1. 使用static关键字的单例模式

      class A{
        private static A instance = new A();
        
        private A(){}
        
        public static A getinstance(){
          return instance;
        }
      }
      
      
    2. 使用同步方法

      public static synchronized A getinstance(){
        if(instance == null){
          instance = new A();
        }
        return instance;
      }
      
      

2.设计模式原则

开闭原则: 对扩展开放,对修改关闭

  1. 单一原则: 每个类尽可能单一职责
  2. 里氏替换: 父类可以出现的地方,子类可以替换
  3. 依赖倒置:依赖于抽象而不是具体,面更象接口编程
  4. 接口隔离: 接口中不存在子类用不到却必须实现的方法
  5. 迪米特法则:最少知道,具体逻辑封装在方法内部,只与直接的朋友通讯
  6. 合成复用: 稍用继承,多用合成和聚合

常用设计模式

简单工厂模式 <缺点 :依赖工厂类,没有面向接口编程>

  1. 简单工厂 <传入创建对象的指令 [字符串] >
    1. 创建接口
    2. 创建实现类
    3. 工厂类
  2. 工厂方法 <改进传入指令错误的问题>
    1. 一个工厂有具体的方法创建具体的对象
  3. 静态工厂 <解决创建工厂类的实例问题,直接调用方法创建实例>
    1. 简单的在 工厂方法上添加static关键字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hHf2NgcM-1581727051674)(C:\Users\Primer4\Desktop\AndroidTheInterview\TheInterviewExperienceOfAndroid\1581665500037.png)]

工厂方法模式

  1. 创建工厂接口
  2. 多个工厂实现类 <依赖到接口了,改变了>
  3. 一个接口,new谁的实现类接口回调就是谁的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JUOrJzpI-1581727051675)(C:\Users\Primer4\Desktop\AndroidTheInterview\TheInterviewExperienceOfAndroid\1581665732430.png)]

抽象工厂模式 <易混淆与 工厂方法模式 >

  1. 区别

    1. 工厂方法 工厂中的一条生产线,生产专一的产品
    2. 抽象工厂 真正的工厂 生产多中产品

单例模式

见上文

原型模式

  1. 对象的 复制克隆出一个新的对象
  2. 实现
    1. 实现clone able接口
    2. 腹泻clone方法
  3. 深复制 无论是基本类型还是引用类型,都是全新的创建
  4. 浅复制 基本类型重新创建,引用类型指向原来的对象

适配器模式

  1. 类适配 功能扩展到另一个类 继承被扩展的类,实现新的接口
  2. 对象适配 <这个最多> 新类组合被扩展的类,重写的方法内调用组合类的方法
  3. 接口适配 不需要实现一个接口的所有方法,分离一部分方法到新的接口

装饰模式

  1. 装饰类持有扩展类的实例 <组合>
  2. 内部调用组合对象的方法达到功能扩展

代理模式 <经典的MVP模式p层>

观察者模式 <发布订阅>

Android框架使用

1.Glide的使用 [加载图片]

  1. 特点

    1. 可以加载gif动图
    2. 播放本地MP4
    3. 加载默认图片 .fallback(id)
    4. 重置大小.resize(int,int)
    5. 裁剪图片.fitcenter()
    6. 缩放图片thumbnail(0.1f)
    7. 圆角图片bitmaptransform(new )
    8. 缓存 [自定义缓存] diskCacheStrategy
    9. 修改缓存大小,位置,图片质量
    10. 请求优先级,加载图片的优先级.priority(int)
  2. 加载网络图片

    Glide.with(context).load(url).into(imageview)

  3. 加载文件

    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"Test.jpg");
    Glide.with(context).load(file).into(imageViewFile);
    
    
  4. 根据id加载

    int resourceId = R.mipmap.ic_launcher;
    Glide.with(context).load(resourceId).into(imageViewResource);
    
    
  5. uri加载

    Glide.with(context).load(uri).into(imageViewUri);
    
    

2. EventBus的使用

  1. 发布订阅<观察者模式>的事件总线
  2. 作用
    1. 简化组件之间通讯 [两个fragment之间的]
    2. 组件和后台线程间的通讯 [网络请求]
  3. 使用
    1. event事件 [任意类型的对象]
    2. subscribe订阅者 [指定线程模型]
    3. publisher发布者 [任意线程位置发送事件post方法]
  4. 线程模型
    1. posting事件发布和事件接受在同一个线程 [默认] <避免执行耗时操作,因会阻塞事件传递>
    2. main在ui线程中处理事件,不能耗时操作,ui线程本来就不能耗时操作
    3. background在子线程中处理事件 [一般是网络请求等耗时操作] <子线程发布事件就在子线程中处理事件,ui线程发布事件就创建新的子线程处理事件>
    4. async无论在哪个线程发布事件,都将新建子线程处理事件

3.Rxjava通讯机制

  1. 扩展的观察者模式
    1. observable被观察者
    2. observer观察者
    3. subscribe订阅
  2. 事件回调方法
    1. onnext<类似于点击>
    2. oncompleted事件队列完成
    3. onerror错误
  3. 使用
    1. 创建观察者
      1. [重写三个回调方法] observer
      2. <这个的一个抽象类subscriber> 用法一致
      3. [onstart方法在所在线程处理事件,不能更新ui,更新ui需要用doonsubscribe方法中]
    2. 创建被观察者observable
      1. 从写call方法,内部调用subscriber.onnext() oncompleted()等方法,一次执行事件
      2. 还有just() from()
    3. subscribe订阅事件
      1. 把观察者和被观察者关联起来observable.subscribe(observer);
  4. Action0() Action1()将对象打包起来内部打包了不同的回调方法
  5. scheduler线程控制器 [指定一段代码运行在什么样的线程下]
    1. 默认下: 在哪一个线程调用subscriber()就在该线程产生事件,就在该线程消费事件
    2. immediate()默认情况,在当前线程运行
    3. newThread()总是创建新的线程执行代码
    4. ioio操作[网络,数据库,文件读取],内部实现有一个无数量上限的线程池
    5. computationCPU计算密集型
    6. mainthreadAndroid主线程
  6. 变换
    1. 加工整个序列,转换成不同的事件序列
    2. Func1具有返回值的包装
    3. map flatmap

Android基础

1.你常用的组件有哪些

常用组件用途
text view文本显示
edit text注册框,搜索框
button登录按钮
float action button <悬浮按钮>flutter中印象深刻的一个按钮,登录
recycle viewlist view少用了,动态列表
switch白天和黑夜模式的开关
scroll view上下滑动
fragment结合view page使用布局切换
image view显示各种图片
check box订单的选择
web view新闻网页的显示
progress bar加载进度
spinner下拉消息选项
rating bar订单评价
search view搜索框
constraint layout最喜欢的一个布局
linear layout常常使用等比例均分的属性
grid layout搜索预选择的格子
horizontal scroll view横向布局 ,切换页面
tool bar首页的顶部文字显示
bottom navigation view主界面的底部导航栏
view一些分割线

2.Android四大组件

  1. activity

    1. 单独的窗口
    2. 通过intent通讯
    3. 必须在AndroidManifest.xml配置文件中声明
  2. service

    1. 用于后台完成用户操作
    2. 启动,调用startService()
      1. 与启动服务的组件无关
      2. 可以无限期运行
      3. 销毁服务需要调用stopSelf() 或者 stopService()
    3. 绑定,调用bindService()
      1. 与绑定服务的组件相关联
    4. 配之文件需要声明service
  3. content provider

    1. 不同程序之间的数据共享
    2. 使用uri标实数据集
  4. broadcast receiver

    1. 对感兴趣的外部事件进行接受并作出响应
    2. 可以启用一个activity或者service来响应接收到的信息
    3. 也可以使用notificationmanager通知用户
    4. 动态注册
      1. 生命周期随注册的activity共存亡
    5. 静态注册
      1. 生命周期独立于程序

3. 自定义view

  1. 自绘控件

    1. 继承view或者view group

    2. 自定义view属性

      1. 在res/values创建xml定义属性
    3. 在构造方法中获取 自定义的属性

      		TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);
      
      循环获取属性并赋值给成员变量
      
      
    4. 重写onDraw方法

    5. [可选]重写onMesure方法

  2. 控件组合

4.约束布局

  1. 特点

    1. 百分比适配屏幕和控件
    2. 解决布局嵌套过多问题
    3. 添加动画
    4. 支持代码布局控件(不需要写xml)
  2. 和相对布局的区别

5.Serializable和Parcelable的理解和区别

序列化: 将一个对象转换成可存储和可传输的状态

  1. serializable
    1. Java自带
    2. 易产生大量的临时对象,容易GC
    3. 保证数据很好的持久性 <另外那个都东西是在内存中的啊>
    4. 一般还是使用这个的多,方便和持久
    5. 代码量少
    6. 使用反射,序列化过程满
  2. parcelable
    1. Android专用
    2. 将一个完整的对象分解,分解后的每一部分都是intent支持的基本数据类型
    3. 数据都在内存中,是内存的时候是很推荐的
    4. 效率高 <毕竟是专款专用>

6.假如手机只有10M内存,想要申请1M的内存是否一定成功

不一定申请成功
Java申请内存的底层算法有些是通过native方式cpp形式实现的,
1.使用系统调用,然后直接申请运行空间来实现算法;
2.使用 ptmalloc 等等这些 c 库,malloc 内存,然后实现自己的内存管理算法。
内存分配时选找一个合适大小的连续的内存块,没有合适大小并且不是连续的就会申请失败.

内存分配规则

malloc

从堆中分配内存
底层函数调用 brk()  sbrk() mmap() munmap()系统调用
malloc容易产生内存碎片 <因为malloc分配堆内存,堆从低地址到高地址,低地址没有被释放,高地址就不会被回收>

算法题

1.归并排序

2. 汉诺塔

递归实现

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
	    int n = scanner.nextInt();
	    han(n,1,2,3);
	}
	
	public static void han(int n,int a,int b,int c){
	    if (n == 1){
	       System.out.printf("Move %d from %d to %d\n",n,a,c);
	    }
	    else{
	        han(n-1,a,c,b);
	        System.out.printf("Move %d from %d to %d\n",n,a,c);
	        han(n-1,b,a,c);
	    }
	}
}

非递归实现

一个美国学者总结得到:所有的汉诺塔移动可以总结为重复的两步,我们假设现在最小的圆盘在a柱子上,柱子为a,b,c

第一步:将最小圆盘移动到下一个柱子上,也就是b

第二步:对a柱子和c柱子进行顶上最小的元素进行判断,把小一点的那个圆盘移动到大一点的那个圆盘(有空则摞在空柱子上)。

重复上述两步就可以得到答案。

注意:这样得到的最后的答案不一定是摞在c上,如果N是偶数将摞在b上,所以如果N是偶数我们就令第二个柱子为c,第三个柱子为b,这样就一定最后是摞在c上的

3.数组反转

4.给一对无序数组,给一个target整数,找出数组中两个数字相加为target,并输出下标(不能用哈希)

5.数组反转,给一个target整数,每target长度反转一次

6.最长连续子序列 O(n)

7.数组反转,给一个target整数,每target长度反转一次

8.连续最大总和

#include<iostream>
#include<algorithm>

using namespace std;
int main()
{
    
    int nums[100005];
    int n;
    cin>>n;
    for(int i = 0 ; i < n; i++){
        cin>>nums[i];
    }
    
    int dp = new int[n];
    int maxx = nums[0];
    dp[0] = nums[0];
    for(int i = 1; i < n;i++){
        if(dp[i-1] > 0){
            dp[i] = dp[i-1]+nums[i];
        }else{
            dp[i] = max(nums[i],dp[i-1]);
        }
        
        maxx = max(maxx,dp[i]);
    }
    
    cout<<maxx<<endl;    
    return 0;
}

9.最长公共子序列长度

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
    	int len1 = text1.length();
    	int len2 = text2.length();
        int dp[][] = new int[len1+1][len2+1];
    	for (int i = 1; i <= len1; i++) {
			for (int j = 1; j <= len2; j++) {
				if(text1.charAt(i-1) == text2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1]+1;
				}else {
                    dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
				}
			}
		}
    	
    	return dp[len1][len2];
    }
}



10.两个字符串是否是重排序

直接sort排序一次,比较是否相同

class Solution {
public:
	bool CheckPermutation(string s1, string s2) {
		sort(s1.begin(),s1.end());
		sort(s2.begin(),s2.end());
		return s1 == s2;
	}

};

异或相同,取模相同

class Solution {
public:
    bool CheckPermutation(string s1, string s2) {
        if(s1.length() != s2.length())
            return false;

        unsigned char xor_s1 = 0, xor_s2 = 0;
        int mod_s1 = 0, mod_s2 = 0;

        for(int i = 0; i < s1.length(); ++i){
            xor_s1 = xor_s1 ^ s1[i];
            xor_s2 = xor_s2 ^ s2[i];

            mod_s1 = (mod_s1 + s1[i]) % 128;
            mod_s2 = (mod_s2 + s2[i]) % 128;
        }

        return xor_s1 ^ xor_s2 == 0 && mod_s1 == mod_s2;
    }
};


multiset集合<一个是字母排序好,一个是统计字母个数>

class Solution {
public:
	bool CheckPermutation(string s1, string s2) {
		int length1 = s1.length();
		int length2 = s2.length();
		if (length1 !=length2)
		{
			return false;
		}

		multiset<char> sa;
		multiset<char> sb;
		for (int i = 0; i < length1; i++)
		{
			sa.insert(s1[i]);
		}
		for (int i = 0; i < length2; i++)
		{
			sb.insert(s2[i]);
		}
		

		auto ia = sa.begin();
		auto ib = sb.begin();
		while (ia!=sa.end())
		{
            if (*ia!= *ib)
			{
				return false;
			}
			if (sa.count(*ia) != sb.count(*ib))
			{
				return false;
			}

			ia++;
			ib++;
		}

		return true;
	}

};

11.字符串轮转

两个连接字符串中查找是否含有字串

class Solution {
public:
    bool isFlipedString(string s1, string s2) {
        if(s1.size() != s2.size())
            return false;
        
        return (s1 + s1).find(s2) != string::npos;
    }
};


12.最近公共祖先

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL)return NULL;
        if(root == p || root == q)return root;
        TreeNode* l = lowestCommonAncestor(root->left,p,q);
        TreeNode* r = lowestCommonAncestor(root->right,p,q);
        if(l == NULL)return r;
        else{
            if(r == NULL)return l;
        }
        return root;
    }
};

13.第k个丑数

双重循环,选择乘3 5 7 中最小的一个

class Solution {
public:
    int getKthMagicNumber(int k) {
        vector<long long> dp(k+1,0);
        dp[0] = 1;
        for(int i = 1; i < k;i++){
            long long m = LLONG_MAX;
            for(int j = 0 ; j < i; j++){
                if(dp[j]*3 > dp[i-1])m = min(m,dp[j]*3);
                if(dp[j]*5 > dp[i-1])m = min(m,dp[j]*5);
                if(dp[j]*7 > dp[i-1])m = min(m,dp[j]*7);
            }
            dp[i] = m;
        }
        return dp[k-1];
    }
};

14.到处弟k个节点

双指针走向

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int kthToLast(ListNode* head, int k) {
        ListNode* begin =head;
        ListNode* pre = head;

        for(int i = 0 ; i < k;i++){
            begin = begin->next;
        }

        while(begin != NULL){
            begin = begin->next;
            pre = pre->next;
        }

        return pre->val;
    }
};

栈的特性

class Solution {
public:
    int kthToLast(ListNode* head, int k) {
       stack<int> s;
       while(head!=NULL){
           s.push(head->val);
           head = head->next;
       }

       while(--k){
           s.pop();
       }

       return s.top();
    }
};

Android 面经收集大全

注意: 本文是通过阅读大量的博文以及其他论坛的精彩好文简化版随手录,如有侵权马上删除!

持续更新~~

文章目录

简述

<<<<<<< HEAD
但不仅限于Android方面,希望通过此方式把自己的知识台阶一步一步搭起,最后通向offer的大门,知识汇聚,知识分享,开源的力量是无穷尽的,也祝大家早些时日提取自己心满意足的offer.

PS: 本收集属于对其他各大技术论坛大佬精美好文的简短总结

数据库

1.数据库的索引和实现原理

  1. 索引就是一个查找问题,索引是一个排序的数据结构,实现通常是B树及其变种
  2. 优点
    1. 唯一索引,保证每一行数据的唯一性
    2. 加快检索速度
    3. 加速表之间的连接
    4. 减少分组和排序时间
    5. 使用优化隐藏器提高性能
  3. 缺点
    1. 创建和维护耗时
    2. 占据更多的物理空间
    3. 增删改需要动态维护索引
  4. 在什么列上需要创建索引
    1. 经常搜索的列
    2. 主键列 [唯一性]
    3. 经常连接的列 [外键]
    4. 经常需要排序的列
    5. 经常使用在where子句中的列 [条件判断]
  5. 什么列不需要索引
    1. 查询少的列
    2. 只有很少数据值的列
    3. 定义数据类型很大的列 [比如text image bit]
    4. 修改性能大于检索性能的列
  6. 存储结构 [B树,B+树,红黑树]

2.手撕MySQL分组查询

  1. 按照某个字段进行分组 group by

  2. having 指定查询的条件,对分组的内容尽心过滤

  3. 单独使用group by

    单独使用查询出来的是分组的第一条记录的值
    select * form student group by gender;
    
  4. 结合聚合函数使用

    select count(*) ,gender from student group by gender;
    
  5. 结合having一起使用

    select sum(grade),gender from student group by gender having sum(grade)<300
    

    having和where的区别:

    都是根据条件进行过滤,

    having后面可以跟随聚合函数

3.数据库中的事务理解

  1. 事务? 更新各种数据项的一个程序执行单元 [操作的集合: 这个集合从开始事务到提交事务]

    1. 保持数据库的一致性,可以恢复数据
    2. 程序操作数据的一个隔离
  2. 特性

    1. 原子性 要么全部执行,要么不执行
    2. 一致性 完整约束状态一致转移
    3. 隔离性 事物之间不影响
    4. 持久性 一旦提交,永久保存
  3. 隔离级别

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JkqlpozX-1581727052499)(C:\Users\Primer4\Desktop\AndroidTheInterview\TheInterviewExperienceOfAndroid\1581680424542.png)]

  4. 隔离 并发控制

    1. 乐观并发控制 一开始以乐观的心态认位没有冲突,直到真的有冲突了才去解决问题
    2. 悲观并发控制 一开始悲伤地假定必然会出现冲突,使用锁使其串行执行
  5. 锁 <还有一控制并发的就是 时间戳>

    1. 共享锁 有了共享锁只能在拥有共享锁
    2. 排他锁 明显的排斥,不再拥有其他锁
    3. 死锁 多个事务持有锁,相互等待无法执行
    4. 饥饿 一直加共享锁,不曾获得排他锁

Linux

1.Linux中断命令

  1. 中断
    1. CPU暂停当前的程序执行,转去处理突发事件,处理完后返回原程序继续执行
    2. 中断请求线: 中断唯一标识的数值
    3. 中断处理函数:内核执行对应的处理函数,中断的优先级最高
  2. 分类
    1. 中断<外部中断或异步中断>: 外设向处理器发出中断请求
    2. 异常<内部中断或同步异常>: 处理器执行指令错误
  3. 终端命令
    1. CTRL+C 强制中断程序执行
    2. CTRL+z任务中断,进程挂起
    3. CTRL+d特殊字符 EOF
    4. CTRL+\退出
    5. kill pid

扩展: 后台进程管理命令:

  1. jobs查看当前有多少在后台运行的命令
  2. fg将后台的命令调至前台执行
  3. bg将后台暂停的命令继续执行

操作系统

1.操作系统线程和进程的同步机制和通信机制

  1. 同步机制

    1. 临界区

      多线程的串行化访问公共资源,一个线程进入临界区之后其余线程必须在临界区外部等待,等到临界区被释放后,其他线程就可以抢占
      
    2. 互斥量

      互斥对象机制,只有拥有互斥对象的线程才有访问共享资源的权限,互斥对象只有一个,还能实现不同应用程序的线程之间的资源访问
      
    3. 信号量

      允许多个线程同一时刻访问同一资源,但需要限制最大线程数量,类似于操作系统的PV操作
      

      PV操作

      信号量S>0,表示可供使用的资源实体数量
      
      信号量S<0,表示等待使用资源的进程数
      
      相关操作:
      	申请资源
      					S-- [资源实体少一个]
      						if(--S >= 0){线程继续执行}
      						if(--S < 0){该进程阻塞,进入进程调度}
      	释放资源
      					s++ [资源实体多一个]
      						if(++S > 0){进程继续执行}
      						if(++S <= 0){唤醒一个等待进程或者转入进程调度}
      

      关键词解释: 进程调度

      多个进程抢用CPU使用权,操作系统究竟给哪一个进程CPU资源,由操作系统决定,这个决定的方法就是调度策略,理所当然的<进程调度>就是<操作系统使用调度策略给某个具体的进程给予CPU资源的使用权>
      
      调度算法:
      			抢占式
      							进程拥有最大的运行时间,并且这个时间是确定的,进程运行时间大于最大运行时间,该进程就会被挂起,选择调度算法挑选下一个进程运行
      			非抢占式
      							一个进程一直运行到阻塞或者自愿退出
      
    4. 事件

      使用通知操作,生产者和消费者的关系<观察者模式>
      
      
  2. 通讯机制

    1. 无名管道

      数据单向流动,是在具有亲缘关系之间的进程之间通讯,存在于内存
      
      
       2.  命名管道
      
      
      数据双向流动,无关进程之间可以数据交换
      
      
    2. 消息队列

      消息的链表,存放在内核中,独立于发送和接收线程
      
      
    3. 信号量

      计数器,实现进程间的互斥和同步,不是存储数据
      
      
    4. 共享内存

      共享一个给定的存储区,最快的方式,进程直接对内存数据进行读取
      
      

2.什么是缓存溢出

  1. 缓冲区
    1. 写入的缓冲区的数据超出缓冲区的大小,溢出的数据覆盖合法的数据 [溢出]
    2. 一段可读可写的内存区域
  2. 危害
    1. 程序崩溃,拒绝服务
    2. 执行恶意代码
    3. 缓冲区攻击 [驱使操作系统执行恶意的代码]
  3. 原因
    1. 没有检查用户的合法输入

3.操作系统分页分段管理

  1. 存储器层次结构

    1. 高速缓存

    2. 主存

    3. 磁盘

      物理内存
      			寄存器
      			内部缓存
      			外部缓存
      			主存
      			[内部和外部区别于CPU的内和外]
      
      
  2. 内存管理方式

    虚地址
    地址变换
    内存分配,回收,扩充,共享,保护
    
    
  3. 连续分配存储管理

    1. 单一连续
      1. 系统区
      2. 用户区
    2. 分区管理 [分时系统,程序并发执行,引入问题使内存碎片]
      1. 固定分区 [限制并发执行程序的数目]
      2. 动态分区 [没有内碎片,有外碎片,算法复杂]
        1. 分区分配算法
          1. 最先适配 :从头查找符合条件的第一个分区
          2. 下次适配:从上一次分配分区开始向下查找第一个符合的分区
          3. 最佳适配:查找大小相差最小的分区
          4. 最坏适配:查找最大的空闲分区
      3. 内存紧缩 [占用的分区移动到一端,其余空闲分区合成一个新的空闲分区]
      4. 覆盖技术 [较小的可用内存运行较大的程序 ,必要数据常驻内存,可选部分在外存,使用公共内存]
      5. 交换技术 [不执行的程序暂时送到外存,装入新的程序执行]
      6. 交换和覆盖的区别
        1. 交换不需要覆盖结构
        2. 交换在进程和作业之间
        3. 覆盖在同一作业或进程中
        4. 覆盖只能覆盖无关的程序段
  4. 页式存储

    1. 原理
      1. 逻辑地址划分为固定大小的
      2. 物理内存划分为相同大小的页框
      3. 将页转入页框
      4. CPU实现逻辑地址和物理地址的映射
    2. 优点
      1. 没有页外碎片,也内碎片比较小
      2. 程序不必连续存放
      3. 方便更改程序占用控件大小
    3. 缺点
      1. 一时刻装入整个程序
    4. 数据结构
      1. 进程表: 逻辑地址到物理地址的映射
      2. 物理页面表:描述物理内存的分配状况
      3. 请求表: 描述进程表的大小和位置
    5. 地址变换
      1. 使用逻辑页号最终找到物理地址
  5. 段式存储

    1. 原理
      1. 将程序地址空间划分为多个段
      2. 每个段分配一个连续的分区
      3. 各个段可以不连续在不同分区
      4. 动态分区管理物理内存
    2. 优点
      1. 分别编写和编译源程序的一个文件
      2. 没有内存碎片,外存碎片可以内存紧缩消除
      3. 内存共享
    3. 缺点
      1. 进程全部装入内存
    4. 数据结构
      1. 进程段表: 进程地址的各段
      2. 系统段表: 描述系统所有已分配的段
      3. 空闲段表:内存中所有空闲段
    5. 地址交换: 段表地址寄存器完成
  6. 区别

    1. 分页系统管理的需要
    2. 分段满足用户的需要
    3. 页使信息的物理单位
    4. 段是信息的逻辑单位
    5. 页大小固定,固定值由系统决定
    6. 段长度不固定,大小由用户程序决定
    7. 页式地址空间是一维的
    8. 段地址空间是二维的 <段名,段内地址>
    9. 段表比页表短

4.CPU时间片

  1. CPU分配给每个程序的时间,允许该程序运行的时间

  2. 程序或者线程在时间片结束前还在运行,CPU会强行把程序推出CPU,随机挑一个线程或者程序进来执行下一个时间片

  3. 若程序或者线程在时间片结束前结束,CPU立马挑选一个程序或者线程来执行,这样就不会浪费CPU资源,提高利用率

  4. 时间片的大小通常为10-100ms

    时间片轮询调度算法 抢占式的

    5.线程池

  5. 达到线程的复用,减少创建和销毁的开销

  6. 优点

    复用线程
    提高响应效率
    高效管理
    防止服务器过载
    
    
  7. 核心

    生产者和消费者模型
    ThreadPoolExecutor类	
    		corepoolsize核心池大小 ,开始线程数为0,到达核心值之后会把任务放入缓冲队列
    		maximumpoolsize池子最多可容纳线程数
    		keepalivetime没有任务执行时最长存活时间 <当线程数大于核心数时才起作用>
    		unit存活时间单位
    		workqueue阻塞的任务队列
    		threadfactory线程工厂
    		handler拒绝处理任务的策略
    		
    
    
    线程池状态:
    		一个volatile保证可见性 <最新之主存可见,可以立马获得最新值>
    任务执行:
    		execute方法提交方法 <submit方法,最后也是调用execu()>
    线程初始化:
    	创建核心线程prestartcoreThread
    	创建所有核心线程prestartallcorethread
    任务缓存队列:
    	wordqueue
    	给予数组的队列
    	基于链表的队列
    	特殊队列<直接新建线程执行新任务>
    	
    任务拒绝策略:
    	线程数到达最大值的时候执行策略
    	
    线程池关闭:
    	showdown()任务队列为空的时候关闭
      shutdownnow()立即关闭,强制清空队列缓存
      
      
    
    		
    
    

Java基础

1.Java四种引用

目的: 决定对象的生命周期利用JVM进行垃圾回收

  1. 强引用

    直接创建对象赋值,只要有引用变量就永远不被回收,宁可抛出异常;
    
    中断强引用和某个对象之间的关联,直接就是变量置null
    
    
  2. 软引用

    内存空间足够,垃圾回收器就不会回收他;
    
    否则对象会被回收,get获取对象就是null
    
    
    
  3. 弱引用

    只要垃圾回收器需要回收,弱引用必定会被回收
    
    
  4. 虚引用

    任何时候都有可能会被回收
    
    

2.Java synchronized的类锁和对象锁

  1. 对象锁

    1. 仅仅有关键字synchronized
    2. 也称实例锁
    3. 防止其他线程同时访问该实例的synchronized方法块
    4. 每个实例拥有自己的监视块
  2. 类锁

    1. static synchronized
    2. 也称全局锁,
    3. 控制类的所有实例的并发访问 [限制都线程该该类的所有实例同时访问jvm中对应的代码块]
    4. 所有实例公用一个监视块
  3. demo

    pulbic class Something(){  
        public synchronized void isSyncA(){}  
        public synchronized void isSyncB(){}  
        public static synchronized void cSyncA(){}  
        public static synchronized void cSyncB(){}  
    }
    
    
  4. 总结

    类锁和对象锁是两个不一样的锁,控制着不同的区域,它们是互不干扰的。同样,线程获得对象锁的同时,也可以获得该类锁,即同时获得两个锁,这是允许的。
    
    

3.JavaGC机制 [hotspot为例]

    1. 新生代
      1. eden region
        1. 分配所得空间较大
        2. 垃圾回收时,eden和survivor存活的对象被复制一遍到两一个survivor中
      2. from survivor region
      3. survivor空间不够依赖老生代
      4. to survivor region
    2. 老生代
      1. 保存大对象
      2. 这个区满了就报出outofmemory异常
    3. 永久代 [一般不会被回收]
      1. 方法去
  1. 判断一个类是否无用,满足下列条件

    1. 类的所有实例被回收,队中不存在该类的任何实例
    2. 该类的classloader被回收
    3. 该类的java.lang.class没有任何地方被引用
  2. 什么时候执行GC操作

    1. Eden区空间不足,执行较小的GC
    2. 老年代空间不足,执行重大的GC
    3. 老年代连续可用空间小于新生代对象总大小,指向Full GC
  3. 对象的年龄计数器判断对象应该在新生代还是老生代 <判断依据有两个: 最大年龄 ; 相同年龄对象大小总和占比重>

  4. 确定对象是否不可用

    1. 引用计数法

      判断对象是否可用

      给对象添加引用计数器
      每引用一次计数+1
      引用失效一次-1
      当引用计数=0,对象不可用  <ps:不是对象死亡,因为相互循环引用难以解决>
        
      
      
    2. 可达性分析

      判断对象是否存活

      当一个对象没有到达GCRoots的路径就是不可达
      
      
  5. 垃圾收集算法

    1. 标记清除 : 标记需要回收的对象,清除被标记对象占用的内存 <缺点: 内存碎片严重>
    2. 复制算法: 内存一分为二,一部分用于直接存储对象,另一部分备用,解决内存碎片问题 <缺点:实际可用内存缩短一半>
    3. 标记整理:对象移动到内存的一端,清除边缘对象,标记的对象时需要移动的
    4. 分代收集:
      1. 根据对象的生命周期收集垃圾对象
      2. eden和另一个surivivor的存活对象复制到两一个surivivor中,再清除eden和surivivor区域

4.哪些对象可以作为GC ROOTS对象

GC会收集那些不是GC roots且没有被GC roots引用的对象

  1. JVM中的栈引用对象
  2. 方法区中的类静态属性引用对象
  3. 方法区中常量引用对象(final常量)
  4. 本地方法栈jni引用对象
作者:Accelerator
链接:https://www.zhihu.com/question/50381439/answer/120846441
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

1.System Class   
系统类

2.JNI Local
Java 本地接口

3.JNI Global
全局变量

4.Thread Block
线程块

5.Busy Monitor
频繁的监控

6.Java Local

7.Native Stack
本地堆栈

7.Finalizable
final常量

8.Unfinalized

9.Unreachable

10.Java Stack Frame
栈帧
11.Unknown


5.volatile关键字和synchronize关键字

  1. volatile
    1. 使变量具有可见性 [可见性:我理解是有点同步的意思,就是一个变量被修改,最新的值立马更新到主存供其他线程使用最新之,以至于不会继续使用旧的值]
    2. 直接读写内存
    3. 禁止指令重排 [指令重排: 处理器优化代码,可能改变代码执行顺序,但在单线程下不会改变执行结果, 多线程上则不能]
  2. synchronize
    1. 既保证可见性 [同一时刻只有一个线程获得锁然后执行代码,释放锁之前将最新之刷新到主存]
    2. 也保证原子性 [要么执行,要么不执行]

6.Java内存模型的可见性,重排序,原子性

  1. 可见性:使用的是直接缓存到CPU中

  2. 重排序:改变指令执行顺序,优化代码,但不会改变执行结果

  3. 原子性 :操作单一不可见

计算机网络

1.OSI网络模型 [七层]

  1. 物理层

    提供物理连接
    
    关心比特流传输
    
    关心机械,电气,功能和规程特性
    
    IEEE 802.2的电器协议
    
    
  2. 数据链路层

    PPP SLIP ARPANE协议,隧道通讯协议,思科的CDP协议,地址解析协议
    
    物理寻址
    
    将原比特流转换成逻辑传输线路
    
    
    
    
  3. 网络层

    ICMP  ARP RARP IP,安全协议AH,路由协议OSPF最短路径优先
    
    外部网关EGP 内部网关IGRP IP/IPV6
    
    控制子网运行
    
    分组传输
    
    路由选择
    
    逻辑编址
    
    
  4. 传输层

    TCP  UDP
    
    分割数据
    
    保证数据有效到达端
    
    
  5. 会话层

    SMTP  DNS
    
    SSL TLS安全协议
    
    不同机器上用户之间简历管理会话
    
    
    
  6. 表示层

    SNMP TELNET
    
    信息的语法语义和之间的关联 [加密解密,转换翻译,压缩解压]
    
    
  7. 应用层

    HTTP TFTP FTP SMTP应用程序协议
    
    
  8. img

2.TCP/IP模型 [四层]

  1. 应用层: 传输协议

  2. 传输层: TCP UDP

  3. 网络层: IP ICMP

    IP层传输
    				点到点传输
    				传输IP分组
    
    TCP层传输
    				端到端的传输
    				传输TCP段
    
    
  4. 物理链路层: 根据需要选择不同的物理链路

3.网络设别工作在那一层

  1. 网卡

    物理层

  2. 中继器

    物理层 [复原网络中的信号,从新发送到其他网段]

  3. 集线器

    物理层 [连接各个物理设备]

  4. 网桥

    数据链路层的MAC子层上<介质访问控制层> [网段中相同协议传输数据包]

  5. 交换机

    数据链路层 [和网桥类似的功能]

  6. 路由器

    网络层 [分组转发和路由]

4.``HTTP,HTTPS,HTTP1,HTTP2`

  1. HTTP
    1. 明文方式发送内容
    2. 不提供加密
    3. 80端口
    4. 无状态连接
  2. HTTPS
    1. 在HTTP基础上加入了SSL协议<通讯加密,依靠证书验证服务器的身份>
    2. 用途
      1. 保证数据传输的安全
      2. 确认网站的真实性
    3. 需要申请证书
    4. 443端口
    5. 短连接
    6. 缺点
      1. 握手阶段耗时
      2. 缓存效率相对HTTP底
      3. ssl证书需要钱
      4. ssl证书需要绑定ip,不能在同一个ip上绑定多个域名
      5. 加密范围有限
  3. HTTP1.0
    1. 无状态 <不记录客户端过去的请求>
    2. 无连接 <处理完任务马上断开连接>
  4. HTTP1.1
    1. 持久连接 <默认keep-alive,避免重新连接和释放>
    2. 管道化
    3. 增加缓存
    4. host字段
    5. 断点传输
    6. <不允许同时存在两个响应>
  5. HTTP2.0
    1. 二进制分帧
      1. 流:双向字节流
      2. 消息:逻辑对应的数据帧集合
      3. 帧:最小单位,标记当前帧所属的流
    2. 多路复用
      1. TCP连接 <承载任意数量的双向数据流>
      2. 数据流以消息的形式发送 <可乱序发送,后根据帧头部标识的所属流进行重装>
      3. 并行传输
    3. 头部压缩
      1. HTTP1.x头部数据以纯文本形式传输
      2. HTTP2.0使用encoder,双方各自缓存一份header Fiedls <注意使缓存,重复利用,避免重传>
    4. 服务器推送
      1. 无需客户端明确请求,主动向客户端推送资源
  6. HTTP2和HTTPS的关系

5.三次握手及其相关

img

img

  1. 为何连接
    1. 客户端和服务器的内存里保存对方的一份信息
    2. 双方需要交换参数 <参数放在TCP头部>
    3. 可靠,面向连接,字节流,传输层服务
    4. 三次握手连接 <两次初始化序列号,两次确认> ,四次握手关闭
  2. 序列号
    1. 作用是使得一个TCP接收端可丢弃重复的报文段,记录以杂乱次序到达的报文段
  3. 标识符
    1. ACK 确认
    2. RST 重连
    3. SYN 初始化序列号
    4. FIN 发送方结束向对方发送数据

6.https加密过程

  1. 对称加密

    1. 加密和解密使用同一个密钥
  2. 非对称加密

    1. 私钥: 不让其他人知道 [解密]
    2. 公钥:任何人可以知道 [加密]
  3. 加密过程

    1. 非对称加密

    2. 结合 对称加密

    3. 结合 数字证书

      [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RNGPGSwt-1581727052504)(C:\Users\Primer4\Desktop\AndroidTheInterview\TheInterviewExperienceOfAndroid\1581601486036.png)]

7.在浏览器中键入URL时会发生什么

一个完整的http请求

  1. 域名解析

    1. 查找浏览器本身的dns缓存,找IP地址和域名的映射表
    2. 操作系统的dns缓存
    3. 本地服务器dns缓存
    4. 根域名服务器发起请求
    5. 顶级域名服务器发起请求
    6. 权限服务器发起请求
    7. 得到IP地址
    8. 逐级返回IP地址给浏览器
  2. 发起HTTP请求

  3. 到传输层,选择TCP或者UDP,封装HTTP请求

  4. 到网络层,IP协议封装IP地址为IP数据报,使用arp协议

  5. 数据链路层,封装成mac帧

  6. 服务器响应请求资源

  7. 断开连接

8.get各post的区别

get

参数包含在url中
浏览器主动缓存请求
只能url编码
请求参数保留在历史记录中
参数长度有限
参数类型只能是ascll字符
get不安全

产生一个TCP数据包

post

参数包含在request body中

产生两个TCP数据包 <一个数据包先发送请求,第二个数据包发送数据>

设计模式

1.手写Java双重检验的单列模式

  1. 侧重考点就是在如何实现双重验证

    双重验证的逐渐演化过程

    1.单线程适用

    缺点:只能适用与单线程

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          instance = new A();
        }
        return instance;
      }
    }
    
    

    2.多线程适用 [适用synchronized方法]

    缺点:每次调用方法都需要同步的代价,实际上是只有if语句需要同步

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static synchronized A getInstance(){
        if(instance == null){
          instance = new A();
        }
        return instance;
      }
    }
    
    

    3.降低同步调用代价 [适用synchronized代码块]

    缺点: 当两个线程进入if判断之后,有一个线程进入了同步代码块,还有另外一个线程在同步块外,if语句内等待,所以当同步块线程创建完对象之后退出同步块,另一个线程没有再次判断instance是否为null

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          synchronized(A.class){
          	instance = new A();
          }
        }
        return instance;
      }
    }
    
    

    4.双重检查 [在创建对象之前再加一个if判断null]

    缺点:理想很美好,现实很骨感,不能保证在单处理器和多处理器上顺序执行,因为内存模型写入是无序的

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          synchronized(A.class){
            if(instance == null){
          		instance = new A();
            }
          }
        }
        return instance;
      }
    }
    
    

    5.解决无序写入问题 [双重同步代码块]

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          synchronized(A.class){
            A tmp = instance;
            if(tmp == null){
          		synchronized(A.class){
                tmp = new A();
              }
              instance = tmp;
            }
          }
        }
        return instance;
      }
    }
    
    

    6.优化无序写入

    class	A{
      
      private A instance;
      
      private A(){}
      
      public static A getInstance(){
        if(instance == null){
          synchronized(A.class){
            A tmp = instance;
            if(tmp == null){
          		synchronized(A.class){
                instance = new A();
              }
            }
          }
        }
        return instance;
      }
    }
    
    

    7.考虑到内存模型和写入顺序,最终选择方案

    1. 使用static关键字的单例模式

      class A{
        private static A instance = new A();
        
        private A(){}
        
        public static A getinstance(){
          return instance;
        }
      }
      
      
    2. 使用同步方法

      public static synchronized A getinstance(){
        if(instance == null){
          instance = new A();
        }
        return instance;
      }
      
      

2.设计模式原则

开闭原则: 对扩展开放,对修改关闭

  1. 单一原则: 每个类尽可能单一职责
  2. 里氏替换: 父类可以出现的地方,子类可以替换
  3. 依赖倒置:依赖于抽象而不是具体,面更象接口编程
  4. 接口隔离: 接口中不存在子类用不到却必须实现的方法
  5. 迪米特法则:最少知道,具体逻辑封装在方法内部,只与直接的朋友通讯
  6. 合成复用: 稍用继承,多用合成和聚合

常用设计模式

简单工厂模式 <缺点 :依赖工厂类,没有面向接口编程>

  1. 简单工厂 <传入创建对象的指令 [字符串] >
    1. 创建接口
    2. 创建实现类
    3. 工厂类
  2. 工厂方法 <改进传入指令错误的问题>
    1. 一个工厂有具体的方法创建具体的对象
  3. 静态工厂 <解决创建工厂类的实例问题,直接调用方法创建实例>
    1. 简单的在 工厂方法上添加static关键字

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YeJSRQLL-1581727052505)(C:\Users\Primer4\Desktop\AndroidTheInterview\TheInterviewExperienceOfAndroid\1581665500037.png)]

工厂方法模式

  1. 创建工厂接口
  2. 多个工厂实现类 <依赖到接口了,改变了>
  3. 一个接口,new谁的实现类接口回调就是谁的方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HyHsndx6-1581727052507)(C:\Users\Primer4\Desktop\AndroidTheInterview\TheInterviewExperienceOfAndroid\1581665732430.png)]

抽象工厂模式 <易混淆与 工厂方法模式 >

  1. 区别

    1. 工厂方法 工厂中的一条生产线,生产专一的产品
    2. 抽象工厂 真正的工厂 生产多中产品

单例模式

见上文

原型模式

  1. 对象的 复制克隆出一个新的对象
  2. 实现
    1. 实现clone able接口
    2. 腹泻clone方法
  3. 深复制 无论是基本类型还是引用类型,都是全新的创建
  4. 浅复制 基本类型重新创建,引用类型指向原来的对象

适配器模式

  1. 类适配 功能扩展到另一个类 继承被扩展的类,实现新的接口
  2. 对象适配 <这个最多> 新类组合被扩展的类,重写的方法内调用组合类的方法
  3. 接口适配 不需要实现一个接口的所有方法,分离一部分方法到新的接口

装饰模式

  1. 装饰类持有扩展类的实例 <组合>
  2. 内部调用组合对象的方法达到功能扩展

代理模式 <经典的MVP模式p层>

观察者模式 <发布订阅>

Android框架使用

1.Glide的使用 [加载图片]

  1. 特点

    1. 可以加载gif动图
    2. 播放本地MP4
    3. 加载默认图片 .fallback(id)
    4. 重置大小.resize(int,int)
    5. 裁剪图片.fitcenter()
    6. 缩放图片thumbnail(0.1f)
    7. 圆角图片bitmaptransform(new )
    8. 缓存 [自定义缓存] diskCacheStrategy
    9. 修改缓存大小,位置,图片质量
    10. 请求优先级,加载图片的优先级.priority(int)
  2. 加载网络图片

    Glide.with(context).load(url).into(imageview)

  3. 加载文件

    File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES),"Test.jpg");
    Glide.with(context).load(file).into(imageViewFile);
    
    
  4. 根据id加载

    int resourceId = R.mipmap.ic_launcher;
    Glide.with(context).load(resourceId).into(imageViewResource);
    
    
  5. uri加载

    Glide.with(context).load(uri).into(imageViewUri);
    
    

2. EventBus的使用

  1. 发布订阅<观察者模式>的事件总线
  2. 作用
    1. 简化组件之间通讯 [两个fragment之间的]
    2. 组件和后台线程间的通讯 [网络请求]
  3. 使用
    1. event事件 [任意类型的对象]
    2. subscribe订阅者 [指定线程模型]
    3. publisher发布者 [任意线程位置发送事件post方法]
  4. 线程模型
    1. posting事件发布和事件接受在同一个线程 [默认] <避免执行耗时操作,因会阻塞事件传递>
    2. main在ui线程中处理事件,不能耗时操作,ui线程本来就不能耗时操作
    3. background在子线程中处理事件 [一般是网络请求等耗时操作] <子线程发布事件就在子线程中处理事件,ui线程发布事件就创建新的子线程处理事件>
    4. async无论在哪个线程发布事件,都将新建子线程处理事件

3.Rxjava通讯机制

  1. 扩展的观察者模式
    1. observable被观察者
    2. observer观察者
    3. subscribe订阅
  2. 事件回调方法
    1. onnext<类似于点击>
    2. oncompleted事件队列完成
    3. onerror错误
  3. 使用
    1. 创建观察者
      1. [重写三个回调方法] observer
      2. <这个的一个抽象类subscriber> 用法一致
      3. [onstart方法在所在线程处理事件,不能更新ui,更新ui需要用doonsubscribe方法中]
    2. 创建被观察者observable
      1. 从写call方法,内部调用subscriber.onnext() oncompleted()等方法,一次执行事件
      2. 还有just() from()
    3. subscribe订阅事件
      1. 把观察者和被观察者关联起来observable.subscribe(observer);
  4. Action0() Action1()将对象打包起来内部打包了不同的回调方法
  5. scheduler线程控制器 [指定一段代码运行在什么样的线程下]
    1. 默认下: 在哪一个线程调用subscriber()就在该线程产生事件,就在该线程消费事件
    2. immediate()默认情况,在当前线程运行
    3. newThread()总是创建新的线程执行代码
    4. ioio操作[网络,数据库,文件读取],内部实现有一个无数量上限的线程池
    5. computationCPU计算密集型
    6. mainthreadAndroid主线程
  6. 变换
    1. 加工整个序列,转换成不同的事件序列
    2. Func1具有返回值的包装
    3. map flatmap

Android基础

1.你常用的组件有哪些

常用组件用途
text view文本显示
edit text注册框,搜索框
button登录按钮
float action button <悬浮按钮>flutter中印象深刻的一个按钮,登录
recycle viewlist view少用了,动态列表
switch白天和黑夜模式的开关
scroll view上下滑动
fragment结合view page使用布局切换
image view显示各种图片
check box订单的选择
web view新闻网页的显示
progress bar加载进度
spinner下拉消息选项
rating bar订单评价
search view搜索框
constraint layout最喜欢的一个布局
linear layout常常使用等比例均分的属性
grid layout搜索预选择的格子
horizontal scroll view横向布局 ,切换页面
tool bar首页的顶部文字显示
bottom navigation view主界面的底部导航栏
view一些分割线

2.Android四大组件

  1. activity

    1. 单独的窗口
    2. 通过intent通讯
    3. 必须在AndroidManifest.xml配置文件中声明
  2. service

    1. 用于后台完成用户操作
    2. 启动,调用startService()
      1. 与启动服务的组件无关
      2. 可以无限期运行
      3. 销毁服务需要调用stopSelf() 或者 stopService()
    3. 绑定,调用bindService()
      1. 与绑定服务的组件相关联
    4. 配之文件需要声明service
  3. content provider

    1. 不同程序之间的数据共享
    2. 使用uri标实数据集
  4. broadcast receiver

    1. 对感兴趣的外部事件进行接受并作出响应
    2. 可以启用一个activity或者service来响应接收到的信息
    3. 也可以使用notificationmanager通知用户
    4. 动态注册
      1. 生命周期随注册的activity共存亡
    5. 静态注册
      1. 生命周期独立于程序

3. 自定义view

  1. 自绘控件

    1. 继承view或者view group

    2. 自定义view属性

      1. 在res/values创建xml定义属性
    3. 在构造方法中获取 自定义的属性

      		TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);
      
      循环获取属性并赋值给成员变量
      
      
    4. 重写onDraw方法

    5. [可选]重写onMesure方法

  2. 控件组合

4.约束布局

  1. 特点

    1. 百分比适配屏幕和控件
    2. 解决布局嵌套过多问题
    3. 添加动画
    4. 支持代码布局控件(不需要写xml)
  2. 和相对布局的区别

5.Serializable和Parcelable的理解和区别

序列化: 将一个对象转换成可存储和可传输的状态

  1. serializable
    1. Java自带
    2. 易产生大量的临时对象,容易GC
    3. 保证数据很好的持久性 <另外那个都东西是在内存中的啊>
    4. 一般还是使用这个的多,方便和持久
    5. 代码量少
    6. 使用反射,序列化过程满
  2. parcelable
    1. Android专用
    2. 将一个完整的对象分解,分解后的每一部分都是intent支持的基本数据类型
    3. 数据都在内存中,是内存的时候是很推荐的
    4. 效率高 <毕竟是专款专用>

6.假如手机只有10M内存,想要申请1M的内存是否一定成功

不一定申请成功
Java申请内存的底层算法有些是通过native方式cpp形式实现的,
1.使用系统调用,然后直接申请运行空间来实现算法;
2.使用 ptmalloc 等等这些 c 库,malloc 内存,然后实现自己的内存管理算法。
内存分配时选找一个合适大小的连续的内存块,没有合适大小并且不是连续的就会申请失败.

内存分配规则

malloc

从堆中分配内存
底层函数调用 brk()  sbrk() mmap() munmap()系统调用
malloc容易产生内存碎片 <因为malloc分配堆内存,堆从低地址到高地址,低地址没有被释放,高地址就不会被回收>

算法题

1.归并排序

2. 汉诺塔

递归实现

import java.util.Scanner;

public class Main {
	public static void main(String[] args) {
		Scanner scanner = new Scanner(System.in);
	    int n = scanner.nextInt();
	    han(n,1,2,3);
	}
	
	public static void han(int n,int a,int b,int c){
	    if (n == 1){
	       System.out.printf("Move %d from %d to %d\n",n,a,c);
	    }
	    else{
	        han(n-1,a,c,b);
	        System.out.printf("Move %d from %d to %d\n",n,a,c);
	        han(n-1,b,a,c);
	    }
	}
}

非递归实现

一个美国学者总结得到:所有的汉诺塔移动可以总结为重复的两步,我们假设现在最小的圆盘在a柱子上,柱子为a,b,c

第一步:将最小圆盘移动到下一个柱子上,也就是b

第二步:对a柱子和c柱子进行顶上最小的元素进行判断,把小一点的那个圆盘移动到大一点的那个圆盘(有空则摞在空柱子上)。

重复上述两步就可以得到答案。

注意:这样得到的最后的答案不一定是摞在c上,如果N是偶数将摞在b上,所以如果N是偶数我们就令第二个柱子为c,第三个柱子为b,这样就一定最后是摞在c上的

3.数组反转

4.给一对无序数组,给一个target整数,找出数组中两个数字相加为target,并输出下标(不能用哈希)

5.数组反转,给一个target整数,每target长度反转一次

6.最长连续子序列 O(n)

7.数组反转,给一个target整数,每target长度反转一次

8.连续最大总和

#include<iostream>
#include<algorithm>

using namespace std;
int main()
{
    
    int nums[100005];
    int n;
    cin>>n;
    for(int i = 0 ; i < n; i++){
        cin>>nums[i];
    }
    
    int dp = new int[n];
    int maxx = nums[0];
    dp[0] = nums[0];
    for(int i = 1; i < n;i++){
        if(dp[i-1] > 0){
            dp[i] = dp[i-1]+nums[i];
        }else{
            dp[i] = max(nums[i],dp[i-1]);
        }
        
        maxx = max(maxx,dp[i]);
    }
    
    cout<<maxx<<endl;    
    return 0;
}

9.最长公共子序列长度

class Solution {
    public int longestCommonSubsequence(String text1, String text2) {
    	int len1 = text1.length();
    	int len2 = text2.length();
        int dp[][] = new int[len1+1][len2+1];
    	for (int i = 1; i <= len1; i++) {
			for (int j = 1; j <= len2; j++) {
				if(text1.charAt(i-1) == text2.charAt(j-1)) {
                    dp[i][j] = dp[i-1][j-1]+1;
				}else {
                    dp[i][j] = Math.max(dp[i-1][j],dp[i][j-1]);
				}
			}
		}
    	
    	return dp[len1][len2];
    }
}



10.两个字符串是否是重排序

直接sort排序一次,比较是否相同

class Solution {
public:
	bool CheckPermutation(string s1, string s2) {
		sort(s1.begin(),s1.end());
		sort(s2.begin(),s2.end());
		return s1 == s2;
	}

};

异或相同,取模相同

class Solution {
public:
    bool CheckPermutation(string s1, string s2) {
        if(s1.length() != s2.length())
            return false;

        unsigned char xor_s1 = 0, xor_s2 = 0;
        int mod_s1 = 0, mod_s2 = 0;

        for(int i = 0; i < s1.length(); ++i){
            xor_s1 = xor_s1 ^ s1[i];
            xor_s2 = xor_s2 ^ s2[i];

            mod_s1 = (mod_s1 + s1[i]) % 128;
            mod_s2 = (mod_s2 + s2[i]) % 128;
        }

        return xor_s1 ^ xor_s2 == 0 && mod_s1 == mod_s2;
    }
};


multiset集合<一个是字母排序好,一个是统计字母个数>

class Solution {
public:
	bool CheckPermutation(string s1, string s2) {
		int length1 = s1.length();
		int length2 = s2.length();
		if (length1 !=length2)
		{
			return false;
		}

		multiset<char> sa;
		multiset<char> sb;
		for (int i = 0; i < length1; i++)
		{
			sa.insert(s1[i]);
		}
		for (int i = 0; i < length2; i++)
		{
			sb.insert(s2[i]);
		}
		

		auto ia = sa.begin();
		auto ib = sb.begin();
		while (ia!=sa.end())
		{
            if (*ia!= *ib)
			{
				return false;
			}
			if (sa.count(*ia) != sb.count(*ib))
			{
				return false;
			}

			ia++;
			ib++;
		}

		return true;
	}

};

11.字符串轮转

两个连接字符串中查找是否含有字串

class Solution {
public:
    bool isFlipedString(string s1, string s2) {
        if(s1.size() != s2.size())
            return false;
        
        return (s1 + s1).find(s2) != string::npos;
    }
};


12.最近公共祖先

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        if(root == NULL)return NULL;
        if(root == p || root == q)return root;
        TreeNode* l = lowestCommonAncestor(root->left,p,q);
        TreeNode* r = lowestCommonAncestor(root->right,p,q);
        if(l == NULL)return r;
        else{
            if(r == NULL)return l;
        }
        return root;
    }
};

13.第k个丑数

双重循环,选择乘3 5 7 中最小的一个

class Solution {
public:
    int getKthMagicNumber(int k) {
        vector<long long> dp(k+1,0);
        dp[0] = 1;
        for(int i = 1; i < k;i++){
            long long m = LLONG_MAX;
            for(int j = 0 ; j < i; j++){
                if(dp[j]*3 > dp[i-1])m = min(m,dp[j]*3);
                if(dp[j]*5 > dp[i-1])m = min(m,dp[j]*5);
                if(dp[j]*7 > dp[i-1])m = min(m,dp[j]*7);
            }
            dp[i] = m;
        }
        return dp[k-1];
    }
};

14.到处弟k个节点

双指针走向

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    int kthToLast(ListNode* head, int k) {
        ListNode* begin =head;
        ListNode* pre = head;

        for(int i = 0 ; i < k;i++){
            begin = begin->next;
        }

        while(begin != NULL){
            begin = begin->next;
            pre = pre->next;
        }

        return pre->val;
    }
};

栈的特性

class Solution {
public:
    int kthToLast(ListNode* head, int k) {
       stack<int> s;
       while(head!=NULL){
           s.push(head->val);
           head = head->next;
       }

       while(--k){
           s.pop();
       }

       return s.top();
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值