后端面试常见题目及知识点(四)---其他

系列文章目录

  1. 操作系统(Linux)
  2. 计算机网络
  3. 数据库
  4. 其他

设计/架构

设计模式(常用的5个,S.O.L.I.D.)

  • Single Responsibility(单一职责)
  • Open-Close(开放封闭)— 拓展性开放,更改性封闭(如:vector, 泛型)
  • Liskov Substitution(替换原则)— 子类应该可以替换父类出现在父类可以出现的地方
  • Interface Segregation(接口分离)— 比起一个大的interface,应该分离成多个小的
  • Dependency Inversion(依赖原则)— 上层模块不应该依赖于下层模块的具体实现,而应该依赖于他的抽象,如manager class包含SDE,SDM等多个成员就属于依赖具体实现,而应该含有一个List<Employee>,抽象下层对象

面向对象有哪些特征(3大特性)

  • 封装 — 封装成类
  • 继承 — 不用重新编码的前提下对现有类进行功能拓展
  • 多态 — 允许将子类类型的指针赋值给父类类型的指针,虚函数(子类覆盖父类同名函数)

负载均衡算法

  • 轮询法
  • 随机法
  • 源地址哈希法(同一IP总会被映射到同一个主机)
  • 加权轮询法(根据不同服务器的性能进行加权)
  • 加权随机法
  • 最小连接数法

微服务

由“单体式”结构演变升级而来的一种新的结构,升级系统架构目的(基本所有架构问题都可以套这个):满足系统可靠性并发量以及快速开发的需求。

  • 可靠性
    • 单体式所有服务严重耦合在一起,一旦有一个服务出错,整个系统崩溃,而且单体式很难增加冗余;
    • 微服务,各个服务组件相互独立,可以方便的增加冗余,并且单个组件出错不影响整个系统;同时我们还可以对每个组件配置独立的防火墙等安全措施
  • 并发量
    • 单体式需要将整个系统部署到服务器上面,可能会为了满足某个瓶颈服务的并发量要求,造成过多盈余;
    • 微服务可以以组件为单位进行部署,可以方便的进行扩容
  • 快速开发
    • 单体式所有开发人员一起开发同一款软件,分工/界线不清,很容易造成无人维护;同时可能会在不同模块有过多代码重复(如:移动端和网页端代码,都包含了支付模块)
    • 微服务,每个开发人员明确开发目标,负责从头到尾的开发,部署,维护一系列工作

微服务缺点:

  • 组件化,测试困难,可以单独测试每一个组件,但是组件间integration test困难;
  • 难以定位出错点,传统单体式整个系统在一起,可以通过查看日志等方式快速定位bug,但是微服务+分布式会使得定位错位难度增加

平衡二叉树 (AVL vs Red-Black)

  • Balanced BST
    • AVL — 严格平衡(两个子节点间高度相差不超过1)
      • 常规的递归插入/删除操作,插入/删除完后,更新每个节点的height,一旦发现unbalance,利用旋转restore
    • Red-black(四个不变量)— 非严格平衡,相比于AVL,旋转较少,插入删除效率相对较高
      • 1)每个节点要么是红色要么是黑色
      • 2)根节点一定是黑色的
      • 3)如果一个节点是红色的,那么子节点一定是黑色的(没有连续两个红色节点)
      • 4)从根节点到NULL的每一天路径上拥有相同数量的黑色结点
      • 新插入的一定是红色节点,如果一个节点两个孩子均为红色,则将他变为红色,孩子变为黑色,如果违反了规则3则旋转

Java相关内容

我也不知道为啥,但反正就面试中经常会问Java…

HashMap,HashTable,ConcurrentHashMap,1.7-1.8的变化

  • HashMap(1.7)
    • 底层:数组+链表(一个数组的Entry,Entry是一个链表节点结构)
    • 插入:头插法(找到对应的Entry list,然后新的Entry指向原来的链表)
    • threashold = capacity * loadFactor
    • 位置 = hash % length(hash值取余数组长度),取余可以用&来替换
    • resize之后会调用transfer方法拷贝数据,注意此时会重新计算每一个元素的位置(扩容的目的就是为了尽可能平均元素的分配),所以此时多线程可能会造成循环链表
  • HashMap(1.8)
  • HashMap 1.7 —> 1.8的变化
    • 默认初始容量(16 — 0)
    • 引入了红黑树(长度超过8时,转换为红黑树)
    • 插入方法(头插法 — 尾插法)
    • 优化hash算法
    • resize计算索引位置算法优化
    • 先插入后扩容
    • Entry改为Node(内容还是一样的)
  • HashMap 和 HashTable的区别
    • HashTable是线程安全的(每个函数都有synchronized修饰),HashMap不是
    • 性能HashMap > HashTable(因为要加锁)
    • 初始容量:HashMap(16),HashTable(11)
    • 扩容机制:HashMap翻倍,HashTable +1
  • ConcurrentHashMap(1.7)
    • 将整个HashMap分区,每个segment单独上锁
    • 每个segment类似于一个小的HashMap,独自处理扩容等
  • ConcurrentHashMap(1.8)
    • 减小锁的粒度,不再有segment的概念,直接锁每个桶的头结点(first Node),并使用CAS来更新

ConcurrentHashMap/HashMap的扩容机制

LinkedHashMap 底层数据结构?使用场景?

  • 除了有一个HashMap一模一样的散列链表之外,还单独维护了一个双向链表

  • 利用双向链表,保证了迭代输出时候是有序的

  • LinkedHashMap的Entry继承自HashMap的Entry,并增加了before和after两个节点(原来还有一个next节点)

  •   private static class Entry<K,V> extends HashMap.Entry<K,V> {
          // These fields comprise the doubly linked list used for iteration.
          Entry<K,V> before, after;
          Entry(int hash, K key, V value, HashMap.Entry<K,V> next) {
              super(hash, key, value, next);
          }
      }
    
  • 使用场景:LRU(重写removeEldestEntry函数)

LinkedHashMap和TreeMap

  • TreeMap实现的是sortMap接口,实现了根据键值的排序;
  • LinkedHashMap本质上还是一个HashMap,只是增加了两个指针,可以根据插入/访问顺序排序

其他

hash冲突怎么办

  • chaining — 链表
  • open addressing — 开放寻址,地址+1,+2…

静态编译,动态编译

  • 区别在于动态链接库有没有被编译到目标可执行文件里面
  • 静态 — 有,不依赖于环境里的动态库(因为全编译到了可执行文件内)
  • 动态 — 无,编译更快,可执行文件体积更小,但是需要运行环境里面有对应的动态库

const int等

  • int* - pointer to int
  • int const * - pointer to const int
  • int * const - const pointer to int
  • int const * const - const pointer to const int

一个小技巧,从左往右看(运算顺序)+const TT const是等价的

  • int const * —> (int const) * —> (const int) *,指向常量int的指针
  • int * const —> (int *) const —> const (int *),一个常量指针,指向int
  • int const * const —> ((int const) *) const —> const (const int) *),一个常量指针指向一个常量int
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值