字节跳动测开实习面经

目录

  • 一、C++
    • 1.多态
    • 2.重写与重载的区别
    • 3.堆和栈的区别
  • 二、操作系统
    • 1.操作系统的特征
    • 2.中断处理
    • 3.什么是进程?什么是线程?有什么区别?
    • 4.进程的状态与转换
    • 5.进程间通信
    • 6.处理器调度
    • 7.进程同步
    • 8.什么是死锁?如何预防并处理
    • 9.如何查看进程和线程的具体信息?
  • 三、计算机网络
    • 1.网络七层协议栈
    • 2.TCP和UDP的区别以及应用场景
    • 3.TCP三次握手
    • 4.TCP四次挥手
    • 5.SYN攻击
    • 6.TCP的拥塞控制
    • 7.DNS劫持
    • 8.DNS域名解析过程
    • 9.输入一个URL
    • 10.接口和端口有什么区别?
  • 四、数据库
    • 1.事务与其特性
    • 2.索引
    • 3.多表查询的连接方式
    • 4.并发一致性问题
    • 5.事务隔离级别
  • 五、图书管理系统
    • 1.项目需求
    • 2.需求分析
    • 3.MVC架构
    • 4.JDBC链接数据库
    • 5.前端
    • 6.程序细节设计
  • 六、Java
    • 1.垃圾回收机制
    • 2.java多线程实现方法
    • 3.使用锁实现java多线程的安全
  • 七、设计模式
    • 1.单例模式
    • 2.适配器模式
  • 八、测试
    • 1.黑盒测试
    • 2.白盒测试
    • 3.微信红包功能怎么测试
    • 4.微信朋友圈的点赞功能怎么测试
    • 5.抖音的登录功能怎么测试
    • 6.点击登录之后等待时间过长可能是什么原因?如何判断是客户端的问题还是服务端的问题?

一、C++

1.多态

多态性是面向对象程序设计的又一重要特性,它指的是同样的消息被不同类型的对象接收时导致不同行为的现象。多态分为动态多态与静态多态。
(1)静态多态是指程序在编译时系统就能够确定要调用的是哪个函数,通过函数的重载来实现,也包括运算符的重载。
(2)动态多态是指通过虚函数来实现的,它是在函数运行时,系统动态地在同名函数中寻找匹配函数。具体的操作为:基类定义一个函数并声明为虚函数,在派生类中重新定义与基类同名的函数,并且使用基类指针或者基类引用来访问这个同名函数。虚函数具有继承性,默认派生类中的重写函数是具有virtual。

动态多态(虚函数)的底层实现

1.编译器为每个含有虚函数的类在声明虚函数时,创建一个一维的数组被叫做虚函数表(vtable),在这个虚表中存放每个虚函数的地址。
2.编译器为每个对象提供了一个虚表指针(即vptr),这个指针指向对象所属类的虚表的开始。在程序运行时,根据对象的类型去初始化vptr,从而让vptr正确的指向了所属类的虚表。
字节跳动测开实习面经_第1张图片

2.重写与重载的区别

  1. 重载是指同一可访问区内被声明的几个具有不同参数列(参数的类型,个数,顺序不同)的同名函数,根据参数列表确定调用哪个函数,重载不关心函数返回类型。
  2. 重写是指派生类中存在重新定义的函数。其函数名,参数列表,返回值类型,所有都必须同基类中被重写的函数一致,只有函数体不同。派生类调用时会调用派生类的重写函数,不会调用基类的被重写函数。重写的基类中被重写的函数必须有virtual修饰。

3.堆和栈的区别

程序内存布局场景下:

堆与栈表示两种内存管理方式。

栈:

  1. 管理方式:栈由操作系统自动分配释放 ,用于存放函数的参数值、局部变量等,其操作方式类似于数据结构中的栈。
  2. 内存空间大小:栈是一块连续的内存区域,大小是操作系统预定好的,windows下栈大小一般是2M。
  3. 内存管理机制:只要栈的剩余空间大于所申请空间,系统为程序提供内存,否则报异常提示栈出。
  4. 分配方式:栈有静态分配和动态分配, 静态分配由编译器完成(如局部变量分配),动态分配由alloca函数分配,但栈的动态分配的资源由编译器进行释放,无需程序员实现。
  5. 生长方向:栈向下,向低地址方向增长,内存连续,每次递减一个数据单位。

堆:

  1. 管理方式:堆由开发人员分配和释放, 若开发人员不释放,程序结束时由操作系统回收,其操作类似于数据结构中的链表。
  2. 内存空间大小:堆是不连续的内存区域,堆的空间比较大和灵活。
  3. 内存管理机制:系统有一个记录空闲内存地址的链表,当系统收到程序申请时,遍历该链表,寻找第一个空间大于申请空间的堆结点,删除空闲结点链表中的该结点,并将该结点空间分配给程序(大多数系统会在这块内存空间首地址记录本次分配的大小,这样delete才能正确释放本内存空间,另外系统会将多余的部分重新放入空闲链表中)。
  4. 分配方式:动态分配,new与delete。
  5. 生长方向:堆向上,向高地址方向增长。

数据结构场景下,堆与栈表示两种常用的数据结构:

  1. 栈是一种运算受限的线性表,其限制是指只仅允许在表的一端进行插入和删除操作,这一端被称为栈顶(Top),相对地,把另一端称为栈底(Bottom)。把新元素放到栈顶元素的上面,使之成为新的栈顶元素称作进栈、入栈或压栈(Push);把栈顶元素删除,使其相邻的元素成为新的栈顶元素称作出栈或退栈(Pop)。这种受限的运算使栈拥有“先进后出”的特性(First In Last Out),简称FILO。
  2. 栈分顺序栈和链式栈两种。栈是一种线性结构,所以可以使用数组或链表(单向链表、双向链表或循环链表)作为底层数据结构。使用数组实现的栈叫做顺序栈,使用链表实现的栈叫做链式栈,二者的区别是顺序栈中的元素地址连续,链式栈中的元素地址不连续。
  3. 堆是一种常用的树形结构,是一种特殊的完全二叉树,当且仅当满足所有节点的值总是不大于或不小于其父节点的值的完全二叉树被称之为堆。堆的这一特性称之为堆序性。因此,在一个堆中,根节点是最大(或最小)节点。如果根节点最小,称之为小顶堆(或小根堆),如果根节点最大,称之为大顶堆(或大根堆)。堆的左右孩子没有大小的顺序。

二、操作系统

1.操作系统的特征

  1. 并发性:两个或多个事件在同一时间间隔发生
  2. 共享性:系统中的资源可供内存中多个并发执行的过程共同使用。
  3. 虚拟性:把一个物理上的实体变成若干个逻辑上的对应物。
  4. 异步性:由于资源有限,进程的执行并不是一贯到底的,而以不可预知的速度向前推进。

2.中断处理

  1. 关中断
  2. 保存断点
  3. 中断服务程序寻址
  4. 保存现场和屏蔽字
  5. 开中断
  6. 执行中断服务程序
  7. 关中断
  8. 回复现场和屏蔽字
  9. 开中断,中断返回

3.什么是进程?什么是线程?有什么区别?

  1. 进程是资源分配的基本单位,线程是处理器调度的基本单位。
  2. 进程是程序的一次执行过程,线程是进程的一部分,一个线程只属于一个进程,但一个进程可以有多个线程。
  3. 每个进程都有独立的代码和数据空间,因此进程间切换开销很大。但同一进程的不同线程共享代码块和数据空间,只独立享有自己的运行栈和程序计数器,因此不同线程间切换开销较小。

4.进程的状态与转换

  1. 进程的状态有运行态、就绪态、阻塞态、创建态和结束态。
  2. 处于就绪态的进程被调度后,获得处理器资源,于是进程由就绪态转为运行态。处于运行态的进程在时间片用完后,会让出处理机,从而进程由运行态转换为就绪态。进程请求某一资源的使用和分配或等待某一事件的发生时,它就从运行态转换为阻塞态。进程等待的事件到来时,如I/O操作结束或中断结束时,中断处理程序必须把相应进程的状态由阻塞态转换为就绪态

5.进程间通信

高级的通信方法主要有共享存储、消息传递、管道通信

  1. 共享存储:在通信的进程之间存在一块可以直接访问的共享空间,通过对这片共享空间进行读写操作实现进程之间的信息交换。一般是通过系统调用的方式实现进程的共享空间。需要使用信号量用来同步对共享存储的访问。
  2. 消息传递:在消息传递系统中,进程间的数据交换是以格式化的消息为单位,进程通过系统提供的发送消息和接收消息两个原语进行数据交换。有直接通信方式和间接通信方式。
  3. 管道通信:管道通信是消息传递的一种特殊方式,管道是一个连接读进程和写进程以实现它们通信的一个共享文件。
  4. 套接字:与其它通信机制不同的是,它可用于不同机器间的进程通信。

6.处理器调度

处理器调度是多道程序操作系统的基础,是操作系统设计的核心问题。一个作业从提交到完成往往要经历以下三级调度:作业调度、中级调度和进程调度。

三级调度的联系

  1. 作业调度从外存的后备队列中选择一批作业进入内存,为它们建立进程,这些进程被送入就绪队列
  2. 进程调度从就绪队列中选出一个进程,并把其状态改为运行态,把CPU分配给它。
  3. 中级调度是为了提高内存的利用率,系统将那些暂时不能运行的进程调至外存等待(挂起),当内存空间宽松时,通过中级调度选择具备运行条件的进程将其唤醒。

进程调度方式

  1. 剥夺式:有更为重要或紧迫的进程需要使用处理机,立即分配。
  2. 非剥夺式:有更为重要或紧迫的进程需要使用处理机,但仍让当前进程继续执行。

经典的调度算法

  1. 先来先服务调度算法:选择最先进入队列的
  2. 短作业优先调度算法:选择完成时间最短的
  3. 优先级调度算法:选择优先级别最高的
  4. 高响应比调度算法:选择响应比最高的
  5. 时间片轮转调度算法:总是选择就绪队列中的第一个进程,但仅能运行一个时间片
  6. 多级反馈队列:时间片轮转调度算法和优先级调度算法的综合和发展

7.进程同步

  • 在多道程序系统中,进程与进程是并发执行的,不同进程之间存在同步与互斥的相互制约关系,为了协调进程之间的相互制约关系,引入了进程同步的概念。
  • 同步是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调他们工作次序而等待、传递信息所产生的制约关系。
  • 互斥是指当一个进程进入临界区使用临界资源时,另一个进程必须等待,当占用临界资源的进程退出临界区后,另一进程才允许去访问这些临界资源。临界区互斥的原则:空闲让进、忙则等待、有限等待、让权等待。

进程同步基本方法:

  1. 软件方法:单标志法、双标志法
  2. 硬件方法:中断屏蔽、硬件指令
  3. 信号量:利用PV操作实现互斥
    信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。

PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:
P(S):①将信号量S的值减1,即S=S-1;
②如果S>0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。
V(S):①将信号量S的值加1,即S=S+1;
②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。
一般来说,信号量S>0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;若S<0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去。

  1. 管程:信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。管程把对共享资源的操作封装起来。每次只允许一个进程进入管程、从而实现进程的互斥。

8.什么是死锁?如何预防并处理

  1. 死锁是在多道程序系统中,多个进程因竞争资源而造成的一种相互等待,若无外力作用,这些进程都将无法向前推进。
  2. 通常系统中拥有的不可剥夺资源,其数量不足以满足多个进程运行的需要,使得进程在运行过程中,会因为争夺资源而陷入僵局,如磁带机、打印机等。只有对不可剥夺资源的竞争才可能产生死锁,对可剥夺资源的竞争是不会引起死锁的。
  3. 进程在运行过程中,请求和释放资源的顺序不当,也同样会导致死锁。信号量使用不当也会造成死锁。进程间彼此等待对方发来的消息,也会使这些进程间无法继续推进。
  4. 死锁产生的必要条件:互斥条件、不剥夺条件、请求并保持条件、循环等待条件。
  5. 为使系统不发生死锁,必须设法破坏产生死锁的4个必要条件之一,或允许死锁产生,但当死锁产生时能检测出死锁,并有能力实现恢复。
    一般有死锁的预防、死锁避免、死锁的检测与恢复三种方法。
    (1) 死锁预防:破坏导致死锁必要条件中的任意一个就可以预防死锁。例如,要求用户申请资源时一次性申请所需要的全部资源,这就破坏了保持和等待条件;将资源分层,得到上一层资源后,才能够申请下一层资源,它破坏了环路等待条件。预防通常会降低系统的效率。
    (2) 死锁避免:避免是指进程在每次申请资源时判断这些操作是否安全,例如,使用银行家算法。死锁避免算法的执行会增加系统的开销。
    (3) 死锁检测:死锁预防和避免都是事前措施,而死锁的检测则是判断系统是否处于死锁状态,如果是,则执行死锁解除策略。死锁解除的主要方法有:资源剥夺法、撤销进程法、进程回退法。

9.如何查看进程和线程的具体信息?

三、计算机网络

1.网络七层协议栈

  1. 应用层(HTTP,FTP,DNS) 文件传输,电子邮件,文件服务,虚拟终端
  2. 表示层(JPEG,ASII)数据格式化,代码转换,数据加密
  3. 会话层(RPC,NFS) 解除或建立与别的接点的联系
  4. 传输层(TCP,UDP) 提供端对端的接口
  5. 网络层 (IP,ARP,ICMP) 为数据包选择路由
  6. 数据链路层(MAC,VLAN,PPP) 传输有地址的帧以及错误检测功能
  7. 物理层(IEEE)传输数据的媒介,这一层数据叫比特

2.TCP和UDP的区别以及应用场景

简单说,tcp,复杂,可靠,速度慢;udp正相反

  1. TCP面向连接(如打电话要先拨号建立连接);UDP是无连接的,即发送数据之前不需要建立连接。
  2. TCP提供可靠的服务。也就是说,通过TCP连接传送的数据,无差错,不丢失,不重复,且按序到达;UDP尽最大努力交付,即不保证可靠交付。
  3. TCP面向字节流,实际上是TCP把数据看成一连串无结构的字节流;UDP是面向报文的。UDP没有拥塞控制,因此网络出现拥塞不会使源主机的发送速率降低(对实时应用很有用,如IP电话,实时视频会议等)。
  4. 每一条TCP连接只能是点到点的;UDP支持一对一,一对多,多对一和多对多的交互通信
  5. TCP首部开销20字节;UDP的首部开销小,只有8个字节
  6. TCP的逻辑通信信道是全双工的可靠信道,UDP则是不可靠信道
  7. 应用场景
    tcp:文件下载,收发邮件,远程登录
    udp:视频通讯(要求速度,但是对稳定性要求一般),广播通信

3.TCP三次握手

同步位SYN,确认位ACK,终止位FIN

  1. 客户端向服务器发送连接请求报文段,这个特殊的报文段中不含有应用层数据,其首部的SYN同步位置为1,同时随机选择一个起始序号seq=x。
    SYN=1,seq=x
  2. 服务器同意建立连接则发回确认,给该段TCP连接分配缓存和变量。SYN和ACK位都被置为1,确认号字段ack=x+1,并且服务器随机产生起始序号seq=y,确认报文段同样不包含应用层数据。
    SYN=1,ACK=1,seq=y,ack=x+1
  3. 客户端收到确认报文段后,向服务器给出确认,给该段TCP连接分配缓存和变量。ACK标志位置为1,序号字段为x+1,确认字段为y+1。
    ACK=1,seq=x+1,ack=y+1

三次握手原因:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。

4.TCP四次挥手

  1. 客户端向服务器,FIN=1,seq=u(前面已传送的数据的最后一个字节的序号加1)
  2. 服务器向客户端,ACK=1,ack=u+1,seq=v(前面已传送的数据的最后一个字节的序号加1)
  3. 服务器向客户端,FIN=1,ACK=1,ack=u+1,seq=w
  4. 客户端向服务器,ACK=1,seq=u+1,ack=w+1

四次挥手原因:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,“你发的FIN报文我收到了”。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送,故需要四次挥手。

5.SYN攻击

SYN 洪水攻击利用 TCP 连接的握手过程发动攻击。

  1. 正常情况下,TCP 连接将完成三次握手以建立连接。首先,客户端向服务器发送 SYN 数据包以发起连接。 接着,服务器通过 SYN/ACK 数据包对该初始数据包做出响应,以便确认通信。 最后,客户端返回 ACK 数据包以确认接到服务器发出的数据包。完成这一系列数据包发送和接收操作后,TCP 连接将处于打开状态并且能够发送和接收数据。

  2. 为发起拒绝服务攻击,攻击者需利用这样一项事实:收到初始 SYN 数据包后,服务器将通过一个或多个 SYN/ACK 数据包做出回应,等待完成握手过程的最后一步。

  3. 工作方式如下:攻击者通常使用伪造的 IP 地址向目标服务器发送大量 SYN 数据包。 然后,服务器分别对每一项连接请求做出响应,并确保打开的端口做好接收响应的准备。 在服务器等待最后一个 ACK 数据包(永远不会到达)的过程中,攻击者将继续发送更多 SYN 数据包。每当有新的 SYN 数据包到达,服务器都会临时打开一个新的端口并在一段特定时间内保持连接;用遍所有可用端口后,服务器将无法正常运行。

  4. 在网络中,如果服务器连接处于打开状态但另一端的机器连接未打开,则视为半开连接。在此类 DDoS 攻击中,目标服务器将使连接一直处于打开状态,静待各个连接超时,避免再次开放端口。因此,此类攻击可视为“半开连接攻击”。

  5. SYN Cookie 此策略要求服务器创建 Cookie。为避免在填充积压工作时断开连接,服务器使用 SYN-ACK 数据包响应每一项连接请求,而后从积压工作中删除 SYN 请求,同时从内存中删除请求,保证端口保持打开状态并做好重新建立连接的准备。如果连接是合法请求并且已将最后一个 ACK 数据包从客户端机器发回服务器,服务器将重建(存在一些限制)SYN 积压工作队列条目。虽然这项缓解措施势必会丢失一些 TCP 连接信息,但好过因此导致对合法用户发起拒绝服务攻击。

6.TCP的拥塞控制

TCP 发送方要维持一个拥塞窗口(cwnd)的状态变量。拥塞控制窗口的大小取决于网络的拥塞程度,并且动态变化。发送方让自己的发送窗口取为拥塞窗口和接收方的接受窗口中较小的一个。TCP的拥塞控制采用了四种算法,即慢开始、拥塞避免、快重传和快恢复。在网络层也可以使路由器采用适当的分组丢弃策略(如主动队列管理 AQM),以减少网络拥塞的发生。

  1. 慢开始:慢开始算法的思路是当主机开始发送数据时,如果立即把大量数据字节注入到网络,那么可能会引起网络阻塞,因为现在还不知道网络的符合情况。经验表明,较好的方法是先探测一下,即由小到大逐渐增大发送窗口,也就是由小到大逐渐增大拥塞窗口数值。cwnd初始值为1,每经过一个传播轮次,cwnd加倍。
  2. 拥塞避免:拥塞避免算法的思路是让拥塞窗口cwnd缓慢增大,即每经过一个往返时间RTT就把发送放的cwnd加1.
  3. 快速重传与快恢复:在 TCP/IP 中,快速重传和恢复(fast retransmit and recovery,FRR)是一种拥塞控制算法,它能快速恢复丢失的数据包。没有 FRR,如果数据包丢失了,TCP 将会使用定时器来要求传输暂停。在暂停的这段时间内,没有新的或复制的数据包被发送。有了 FRR,如果接收机接收到一个不按顺序的数据段,它会立即给发送机发送一个重复确认。如果发送机接收到三个重复确认,它会假定确认件指出的数据段丢失了,并立即重传这些丢失的数据段。有了 FRR,就不会因为重传时要求的暂停被耽误。 当有单独的数据包丢失时,快速重传和恢复(FRR)能最有效地工作。当有多个数据信息包在某一段很短的时间内丢失时,它则不能很有效地工作。

7.DNS劫持

域名劫持是互联网攻击的一种方式,通过攻击域名解析服务器(DNS),或伪造域名解析服务器(DNS)的方法,把目标网站域名解析到错误的IP地址从而实现用户无法访问目标网站的目的或者蓄意或恶意要求用户访问指定IP地址(网站)的目的。

劫持的方法:

  1. 本机DNS劫持:攻击者通过某些手段使用户的计算机感染上木马病毒,或者恶意软件之后,恶意修改本地DNS配置,比如修改本地hosts文件,缓存等

  2. 路由DNS劫持:很多用户默认路由器的默认密码,攻击者可以侵入到路由管理员账号中,修改路由器的默认配置 3.攻击DNS服务器 直接攻击DNS服务器,例如对DNS服务器进行DDOS攻击,可以是DNS服务器宕机,出现异常请求,还可以利用某些手段感染dns服务器的缓存,使给用户返回来的是恶意的ip地址

DNS的防范:

  1. 加强本地计算机病毒检查,开启防火墙等,防止恶意软件,木马病毒感染计算机
  2. 改变路由器默认密码,防止攻击者修改路由器的DNS配置指向恶意的DNS服务器
  3. 企业的话可以准备两个以上的域名,一旦一个域名挂掉,还可以使用另一个
  4. 用HTTP DNS 代替 Local DNS

8.DNS域名解析过程

DNS是一个应用层协议,用于将主机名转换为便于机器处理的IP地址,运行在UDP之上,采取客户机服务器模式。假定在浏览器中输入y.abc.com,域名解析的过程如下:

  1. 浏览器会首先查看本地硬盘的 hosts 文件,看看其中有没有和这个域名对应的规则,如果有的话就直接使用 hosts 文件里面的 ip 地址。

  2. 如果在本地的 hosts 文件没有能够找到对应的 ip 地址,浏览器会发出一个 DNS请求到本地DNS服务器 。本地DNS服务器一般都是你的网络接入服务器商提供,比如中国电信,中国移动。

  3. 本地DNS服务器收到请求后,会首先查询它的缓存记录,如果缓存中有此条记录,就可以直接返回结果,如果没有,本地DNS服务器则以客户的身份向根域名服务器发出解析请求。此过程是递归的方式进行查询。

  4. 根域名服务器收到请求后,判断该域名属于.com域,将对应的顶级域名服务器dns.com的IP地址返回本地域名服务器。本地域名服务器向根域名服务器的查询采用迭代查询。

  5. 本地DNS服务器继续向顶级域名服务器解析请求报文,顶级域名服务器dns.com收到请求后,判断该域名属于abc.com因此将对映的授权域名服务器dns.abc.com的IP地址返回给本地域名服务器

  6. 最后,本地DNS服务器向授权域名服务器dns.abc.com发起解析请求报文,授权域名服务器接到请求后将查询结果返回给本地域名服务器。这时就能收到一个域名和IP地址对应关系,本地DNS服务器不仅要把IP地址返回给用户电脑,还要把这个对应关系保存在缓存中,以备下次别的用户查询时,可以直接返回结果,加快网络访问。

9.输入一个URL

  1. 根据域名到DNS中找到IP

  2. 根据IP建立TCP连接(三次握手)

  3. 连接建立成功发起http请求

  4. 服务器响应http请求

  5. 浏览器解析HTML代码并请求html中的静态资源(js,css)

  6. 关闭TCP连接(四次挥手)

  7. 浏览器渲染页面

10.接口和端口有什么区别?

四、数据库

1.事务与其特性

事务指的是满足 ACID 特性的一组操作,可以通过 Commit 提交一个事务,也可以使用 Rollback 进行回滚。数据库事务可以包含一个或多个数据库操作,但这些操作构成一个逻辑上的整体。构成逻辑整体的这些数据库操作,要么全部执行成功,要么全部不执行。

  1. 原子性:事务所有操作在数据库中要么全部执行,要么全部不执行;
  2. 一致性:事务多次执行,其结果应一致;
  3. 隔离性:事务与事务之间隔离,并发执行透明;
  4. 持续性:事务完成后,数据改变必须是永久的。

2.索引

索引是对数据库表中的一列或者多列的值进行排序的一种数据结构,如果把数据库中的表比作一本书,索引就是这本书的目录,通过目录可以快速查找到书中指定内容的位置。索引最大的好处是提高查询速度,缺点是更新数据时效率低,因为要同时更新索引。

索引的实现:B+树:

一个m阶的B+树具有如下几个特征:
1.有k个子树的非叶子节点上是不存储数据的,包含有k个元素,每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。
之所以这么做是因为在数据库中页的大小是固定的,InnoDB 中页的默认大小是 16KB。
如果不存储数据,那么就会存储更多的索引,相应的树的阶数(节点的子节点树)就会更大,树就会更矮更胖,如此一来我们查找数据进行磁盘的 IO 次数又会再次减少,数据查询的效率也会更快。
2.所有的叶子结点中包含了全部元素的信息,及指向含这些元素记录的指针,且叶子结点本身依关键字的大小自小而大顺序链接。
字节跳动测开实习面经_第2张图片

索引查找过程中就要产生磁盘I/O消耗,主要看IO次数,和磁盘存取原理有关:根据B-Tree的定义,可知检索一次最多需要访问h个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入局部性原理与磁盘预读。

3.多表查询的连接方式

主要分为三种:内连接、外连接、交叉连接

关键字为:

内连接:inner join

外连接:left join、right join、outer join

交叉连接:cross join

4.并发一致性问题

(1)丢失修改:丢失修改指一个事务的更新操作被另外一个事务的更新操作替换。一般在现实生活中常会遇到,例如:T1 和 T2 两个事务都对一个数据进行修改,T1 先修改并提交生效,T2 随后修改,T2 的修改覆盖了 T1 的修改。

(2)脏读:脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。当一个事务正在多次修改某个数据,而在这个事务中这多次的修改都还未提交,这时一个并发的事务来访问该数据,就会造成两个事务得到的数据不一致。例如:用户A向用户B转账100元,对应SQL命令如下

update account set money=money+100 where name=’B’;  (此时A通知B)

update account set money=money - 100 where name=’A’;

当只执行第一条SQL时,A通知B查看账户,B发现确实钱已到账(此时即发生了脏读),而之后无论第二条SQL是否执行,只要该事务不提交,则所有操作都将回滚,那么当B以后再次查看账户时就会发现钱其实并没有转。

(3)不可重复读:不可重复读是指在对于数据库中的某个数据,一个事务范围内多次查询却返回了不同的数据值,这是由于在查询间隔,被另一个事务修改并提交了。例如事务T1在读取某一数据,而事务T2立马修改了这个数据并且提交事务给数据库,事务T1再次读取该数据就得到了不同的结果,发送了不可重复读。

不可重复读和脏读的区别是,脏读是某一事务读取了另一个事务未提交的脏数据,而不可重复读则是读取了前一事务提交的数据。

(4)幻读::幻读是事务非独立执行时发生的一种现象。例如事务T1对一个表中所有的行的某个数据项做了从“1”修改为“2”的操作,这时事务T2又对这个表中插入了一行数据项,而这个数据项的数值还是为“1”并且提交给数据库。而操作事务T1的用户如果再查看刚刚修改的数据,会发现还有一行没有修改,其实这行是从事务T2中添加的,就好像产生幻觉一样,这就是发生了幻读。

幻读和不可重复读都是读取了另一条已经提交的事务(这点就脏读不同),所不同的是不可重复读查询的都是同一个数据项,而幻读针对的是一批数据整体(比如数据的个数)。

5.事务隔离级别

  1. 未提交读(READ UNCOMMITTED)
    事务中的修改,即使没有提交,对其它事务也是可见的。
  2. 提交读(READ COMMITTED)
    一个事务只能读取已经提交的事务所做的修改。换句话说,一个事务所做的修改在提交之前对其它事务是不可见的。
  3. 可重复读(REPEATABLE READ)
    保证在同一个事务中多次读取同一数据的结果是一样的。
  4. 可串行化(SERIALIZABLE)
    强制事务串行执行,这样多个事务互不干扰,不会出现并发一致性问题。

以上四种隔离级别最高的是Serializable级别,最低的是Read uncommitted级别,当然级别越高,执行效率就越低。

五、图书管理系统

1.项目需求

利用JAVA开发工具Eclipse和MySQL数据库来开发这个图书管理系统。该系统要解决的图书管理所要解决的问题,可以满足图书管理基本要求,包括添加、管理等功能。该系统能根据用户的需求,快捷方便的为读者提供借阅服务 图书管理系统应有以下功能:

  1. 读者库管理
  2. 书库管理
  3. 借阅管理
  4. 读者信息查询

2.需求分析

  1. 图书管理员登入系统 图书管理员需使用账号和密码登入。
  2. 新增读者资料 新增读者资料,如姓名、性别、职位等。
  3. 新增书籍资料 新增书籍资料,如书名、价格、种类等。
  4. 读者库管理 选中读者库里的读者信息,即可更新和删除。
  5. 书库管理 可以按“书编号查询”和“书名模糊查询”,继而选中书籍信息,即可更新和删除书籍分为在库和借出
  6. 借阅管理 输入读者编号,即可检阅读者的数据和借阅的图书,检阅顾客的数据和购买纪录。
  7. 读者登入系统 读者需使用账号和密码登入。
  8. 借书 按书名模糊搜索,选中即可借阅图书
  9. 还书 检阅自己的读者信息以及已经借阅的图书,点击归还即可,不允许更改或删除读者数据,只允许查询,更改和删除功能由管理员负责。
    以下是使用 实体联系模型-Entity Relationship来分析。

3.MVC架构

  1. Database用于存储数据库链接操作
  2. View用于存储各个窗体界面
  3. Model用于存储各个实体(表)对应的数据模型
  4. Dao用于存储操作数据库的增删改查方法

4.JDBC链接数据库

  1. 创建一个java project
  2. JDK不提供类库链接数据库,因此使用第三方jar包提供驱动。将第三方jar包复制到Java project下的一个文件中,并添加工程路径。这样就得到了jar包编译好的类库,可以使用这些类库进行数据库链接
  3. 创建一个类获取数据库的链接对象
  4. 提供数据库的IP地址端口号、用户名、密码,定义一个数据库的链接变量connection用于存放对象
  5. 定义一个静态的初始化块来初始化connection对象,首先加载驱动类,然后使用DriverManager类的getconnection方法获取数据库链接对象,最后返回connection。
  6. 关闭数据库链接:connection.close();
    字节跳动测开实习面经_第3张图片

5.前端

使用Swing GUI工具包,Swing是JAVA基础类的一部分。它包括了图形用户界面(GUI)器件如:文本框,按钮,分隔窗格和表。它们用纯Java写成,所以同Java本身一样可以跨平台运行。

6.程序细节设计

  1. 登陆界面的密码回显,伴有跳转动画(此处利用了多线程,控制线程存活时间)
  2. 表格直接选中即可修改数据,更新数据时候默认有原始数据,可按不同方式搜索图书
  3. 新增借阅信息时候,自动加入当前时间,并计算归还时间。(SQL函数NOW())
  4. 界面按钮,背景用Photoshop的重新设计,图形用户界面友好
  5. 数据库设计达到第三范式,去除了所有非主属性对任何候选关键字的传递信依赖,冗余度低。
  6. 变量和方法命名符合规范,可读性强
  7. 不同的Model实体(表)对应不同的SqlTools操作,分开存放,程序复用性好,易扩展。 使用
  8. 将SQL语句导入,字符集选utf8,不然有可能显示不了中文,数据库名称为library create database library;
  9. 推荐使用Mysql Front这个MySQL的前台,支持多句sql语句一起执行,百度第一个链接即可下载。 http://dlsw.baidu.com/sw-search-sp/soft/6c/17997/MySQL-Front_V5.3.4.214_Setup.1435658094.exe
  10. Java环境中加入数据库的驱动,源程序里database包里DatabaseTools.java文件是有关数据库连接的操作,源程序的用户名和密码皆为root,数据库名为:library不同电脑上运行需要稍微改一下这个代码。

六、Java

1.垃圾回收机制

java中对象的引用:

  1. 强引用(Strong Reference):如“Object obj = new Object()”,这类引用是Java程序中最普遍的。只要强引用还存在,垃圾收集器就永远不会回收掉被引用的对象。

  2. 软引用(Soft Reference):它用来描述一些可能还有用,但并非必须的对象。在系统内存不够用时,这类引用关联的对象将被垃圾收集器回收。JDK1.2之后提供了SoftReference类来实现软引用。

  3. 弱引用(Weak Reference):它也是用来描述非须对象的,但它的强度比软引用更弱些,被弱引用关联的对象只能生存到下一次垃圾收集发生之前。当垃圾收集器工作时,无论当前内存是否足够,都会回收掉只被弱引用关联的对象。在JDK1.2之后,提供了WeakReference类来实现弱引用。

  4. 虚引用(Phantom Reference):最弱的一种引用关系,完全不会对其生存时间构成影响,也无法通过虚引用来取得一个对象实例。为一个对象设置虚引用关联的唯一目的是希望能在这个对象被收集器回收时收到一个系统通知。JDK1.2之后提供了PhantomReference类来实现虚引用。

判断对象是否是垃圾的算法:

Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:(1)找到所有存活对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用。

  1. 标记-清除算法:
    标记-清除算法对根集合进行扫描,对存活的对象进行标记。标记完成后,再对整个空间内未被标记的对象扫描,进行回收。
    优点:实现简单,不需要进行对象进行移动。
    缺点:标记、清除过程效率低,产生大量不连续的内存碎片,提高了垃圾回收的频率。
  2. 复制算法:
    这种收集算法解决了标记清除算法存在的效率问题。它将内存区域划分成相同的两个内存块。每次仅使用一半的空间,JVM生成的新对象放在一半空间中。当一半空间用完时进行GC,把可到达对象复制到另一半空间,然后把使用过的内存空间一次清理掉。
    优点:按顺序分配内存即可,实现简单、运行高效,不用考虑内存碎片。
    缺点:可用的内存大小缩小为原来的一半,对象存活率高时会频繁进行复制。
  3. 标记-整理算法:
    标记-整理算法 采用和 标记-清除算法 一样的方式进行对象的标记,但后续不直接对可回收对象进行清理,而是将所有的存活对象往一端空闲空间移动,然后清理掉端边界以外的内存空间。
    优点:解决了标记-清理算法存在的内存碎片问题。
    缺点:仍需要进行局部对象移动,一定程度上降低了效率。
  4. 分代收集算法
    当前商业虚拟机都采用分代收集的垃圾收集算法。分代收集算法,顾名思义是根据对象的存活周期将内存划分为几块。一般包括年轻代、老年代和永久代。
    新生代(Young generation)
    绝大多数最新被创建的对象会被分配到这里,由于大部分对象在创建后会很快变得不可达,所以很多对象被创建在新生代,然后消失。对象从这个区域消失的过程我们称之为 minor GC。
    新生代 中存在一个Eden区和两个Survivor区。新对象会首先分配在Eden中(如果新对象过大,会直接分配在老年代中)。在GC中,Eden中的对象会被移动到Survivor中,直至对象满足一定的年纪(定义为熬过GC的次数),会被移动到老年代。
    可以设置新生代和老年代的相对大小。这种方式的优点是新生代大小会随着整个堆大小动态扩展。参数 -XX:NewRatio 设置老年代与新生代的比例。例如 -XX:NewRatio=8 指定 老年代/新生代 为8/1. 老年代 占堆大小的 7/8 ,新生代 占堆大小的 1/8(默认即是 1/8)。
    老年代(Old generation)
    对象没有变得不可达,并且从新生代中存活下来,会被拷贝到这里。其所占用的空间要比新生代多。也正由于其相对较大的空间,发生在老年代上的GC要比新生代要少得多。对象从老年代中消失的过程,可以称之为major GC(或者full GC)。
    永久代(permanent generation)
    像一些类的层级信息,方法数据 和方法信息(如字节码,栈 和 变量大小),运行时常量池(JDK7之后移出永久代),已确定的符号引用和虚方法表等等。它们几乎都是静态的并且很少被卸载和回收,在JDK8之前的HotSpot虚拟机中,类的这些“永久的” 数据存放在一个叫做永久代的区域。
    永久代一段连续的内存空间,我们在JVM启动之前可以通过设置-XX:MaxPermSize的值来控制永久代的大小。但是JDK8之后取消了永久代,这些元数据被移到了一个与堆不相连的称为元空间 (Metaspace) 的本地内存区域。

2.java多线程实现方法

  1. 继承Thread类,启动线程通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。优点:代码简单。缺点:该类无法继承别的类。

  2. 实现Runnable接口,优点:继承其他类。同一实现该接口的实例可以共享资源。缺点:代码复杂

  3. 实现Callable接口,通过FutureTask包装器来创建Thread线程:优点:可以获得异步任务的返回值

  4. 线程池:实现自动化装配,易于管理,循环利用资源。

3.使用锁实现java多线程的安全

在 Java 语言中,使用 Synchronized 是能够实现线程同步的,即加锁。并且实现的是悲观锁,在操作同步资源的时候直接先加锁。

加锁可以使一段代码在同一时间只有一个线程可以访问,在增加安全性的同时,牺牲掉的是程序的执行性能,所以为了在一定程度上减少获得锁和释放锁带来的性能消耗,在 jdk6 之后便引入了“偏向锁”和“轻量级锁”,所以总共有4种锁状态,级别由低到高依次为:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。这几个状态会随着竞争情况逐渐升级。

注意:锁可以升级但不能降级

(1)无锁:
是指没有对资源进行锁定,所有的线程都能访问并修改同一个资源,但同时只有一个线程能修改成功。无锁的特点是修改操作会在循环内进行,线程会不断的尝试修改共享资源。如果没有冲突就修改成功并退出,否则就会继续循环尝试。如果有多个线程修改同一个值,必定会有一个线程能修改成功,而其他修改失败的线程会不断重试直到修改成功。
(2)偏向锁:

  1. 是指当一段同步代码一直被同一个线程所访问时,即不存在多个线程的竞争时,那么该线程在后续访问时便会自动获得锁,从而降低获取锁带来的消耗,即提高性能。当一个线程访问同步代码块并获取锁时,会在 Mark Word 里存储锁偏向的线程 ID。在线程进入和退出同步块时不再通过 CAS 操作来加锁和解锁,而是检测 Mark Word 里是否存储着指向当前线程的偏向锁。轻量级锁的获取及释放依赖多次 CAS 原子指令,而偏向锁只需要在置换 ThreadID 的时候依赖一次 CAS 原子指令即可。
  2. 偏向锁只有遇到其他线程尝试竞争偏向锁时,持有偏向锁的线程才会释放锁,线程是不会主动释放偏向锁的。
  3. 关于偏向锁的撤销,需要等待全局安全点,即在某个时间点上没有字节码正在执行时,它会先暂停拥有偏向锁的线程,然后判断锁对象是否处于被锁定状态。
    如果线程不处于活动状态,则将对象头设置成无锁状态,并撤销偏向锁,恢复到无锁(标志位为01)或轻量级锁(标志位为00)的状态。
  4. 偏向锁在 JDK 6 及之后版本的 JVM 里是默认启用的。可以通过 JVM 参数关闭偏向锁:-XX:-UseBiasedLocking=false,关闭之后程序默认会进入轻量级锁状态。

(3)轻量级锁:
5. 轻量级锁是指当锁是偏向锁的时候,却被另外的线程所访问,此时偏向锁就会升级为轻量级锁,其他线程会通过自旋(关于自旋的介绍见文末)的形式尝试获取锁,线程不会阻塞,从而提高性能。
6. 轻量级锁的获取主要由两种情况:① 当关闭偏向锁功能时;② 由于多个线程竞争偏向锁导致偏向锁升级为轻量级锁。
7. 在代码进入同步块的时候,如果同步对象锁状态为无锁状态,虚拟机将首先在当前线程的栈帧中建立一个名为锁记录(Lock Record)的空间,用于存储锁对象目前的 Mark Word 的拷贝,然后将对象头中的 Mark Word 复制到锁记录中。
8. 拷贝成功后,虚拟机将使用 CAS 操作尝试将对象的 Mark Word 更新为指向 Lock Record 的指针,并将 Lock Record 里的 owner 指针指向对象的 Mark Word。
9. 如果这个更新动作成功了,那么这个线程就拥有了该对象的锁,并且对象 Mark Word 的锁标志位设置为“00”,表示此对象处于轻量级锁定状态。
10. 如果轻量级锁的更新操作失败了,虚拟机首先会检查对象的 Mark Word 是否指向当前线程的栈帧,如果是就说明当前线程已经拥有了这个对象的锁,那就可以直接进入同步块继续执行,否则说明多个线程竞争锁。
11. 若当前只有一个等待线程,则该线程将通过自旋进行等待。但是当自旋超过一定的次数时,轻量级锁便会升级为重量级锁(锁膨胀)。
12. 另外,当一个线程已持有锁,另一个线程在自旋,而此时又有第三个线程来访时,轻量级锁也会升级为重量级锁(锁膨胀)。

(4)重量级锁:
13. 重量级锁是指当有一个线程获取锁之后,其余所有等待获取该锁的线程都会处于阻塞状态。
14. 重量级锁通过对象内部的监视器(monitor)实现,而其中 monitor 的本质是依赖于底层操作系统的 Mutex Lock 实现,操作系统实现线程之间的切换需要从用户态切换到内核态,切换成本非常高。
15. 简言之,就是所有的控制权都交给了操作系统,由操作系统来负责线程间的调度和线程的状态变更。而这样会出现频繁地对线程运行状态的切换,线程的挂起和唤醒,从而消耗大量的系统资源,导致性能低下。

七、设计模式

1.单例模式

在创建一个对象时不向客户暴露内部细节,并提供一个创建对象的通用接口。简单工厂把实例化的操作单独放到一个类中,这个类就成为简单工厂类,让简单工厂类来决定应该用哪个具体子类来实例化。

这样做能把客户类和具体子类的实现解耦,客户类不再需要知道有哪些子类以及应当实例化哪个子类。客户类往往有多个,如果不使用简单工厂,那么所有的客户类都要知道所有子类的细节。而且一旦子类发生改变,例如增加子类,那么所有的客户类都要进行修改。

字节跳动测开实习面经_第4张图片

2.适配器模式

把一个类接口转换成另一个用户需要的接口。鸭子(Duck)和火鸡(Turkey)拥有不同的叫声,Duck 的叫声调用 quack() 方法,而 Turkey 调用 gobble() 方法。要求将 Turkey 的 gobble() 方法适配成 Duck 的 quack() 方法,从而让火鸡冒充鸭子。
字节跳动测开实习面经_第5张图片

八、测试

1.黑盒测试

黑盒测试用例设计方法:基于用户需求的测试、等价类划分方法、边界值分析方法、错误推测方法、因果图方法、判定表驱动分析方法、正交实验法、场景法。依据是用户需求规格说明书,详细设计说明书。

2.白盒测试

白盒测试用例设计有如下方法:语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖和路径覆盖。依据就是代码结构。

3.微信红包功能怎么测试

功能测试:

  1. 在红包钱数,和红包个数的输入框中只能输入数字
  2. 红包里最多和最少可以输入的钱数 200 0.01
  3. 拼手气红包最多可以发多少个红包 100、超过最大拼手气红包的个数是否有提醒
  4. 当红包钱数超过最大范围是不是有对应的提示
  5. 当发送的红包个数超过最大范围是不是有提示
  6. 当余额不足时,红包发送失败
  7. 在红包描述里是否可以输入汉字,英文,符号,表情,纯数字,汉字英语符号,是否可以输入它们的混合搭配
  8. 输入红包钱数是不是只能输入数字
  9. 红包描述里许多能有多少个字符 10个
  10. 红包描述,金额,红包个数框里是否支持复制粘贴操作
  11. 红包描述里的表情可以删除
  12. 发送的红包别人是否可以领取、发的红包自己可不可以领取 2人
  13. 24小时内没有领取的红包是否可以退回到原来的账户、超过24小时没有领取的红包,是否还可以领取
  14. 用户是否可以多次抢一个红包
  15. 发红包的人是否还可以抢红包 多人
  16. 红包的金额里的小数位数是否有限制
  17. 可以按返回键,取消发红包
  18. 断网时,无法抢红包
  19. 可不可以自己选择支付方式
  20. 余额不足时,会不会自动匹配支付方式
  21. 在发红包界面能否看到以前的收发红包的记录
  22. 红包记录里的信息与实际收发红包记录是否匹配
  23. 支付时可以密码支付也可以指纹支付
  24. 如果直接输入小数点,那么小数点之前应该有个0
  25. 支付成功后,退回聊天界面
  26. 发红包金额和收到的红包金额应该匹配
  27. 是否可以连续多次发红包
  28. 输入钱数为0,"塞钱进红包"置灰

性能测试:

  1. 弱网时抢红包,发红包时间
  2. 不同网速时抢红包,发红包的时间
  3. 发红包和收红包成功后的跳转时间
  4. 收发红包的耗电量
  5. 退款到账的时间
    兼容性测试:
  6. 苹果,安卓是否都可以发送红包
  7. 电脑端可以抢微信红包
    界面测试:
  8. 发红包界面没有错别字
  9. 抢完红包界面没有错别字
  10. 发红包和收红包界面排版合理,
  11. 发红包和收到红包界面颜色搭配合理
    安全测试:
  12. 对方微信号异地登录,是否会有提醒 2人
  13. 红包被领取以后,发送红包人的金额会减少,收红包金额会增加
  14. 发送红包失败,余额和银行卡里的钱数不会少
  15. 红包发送成功,是否会收到微信支付的通知
  16. 易用性(有点重复)
  17. 红包描述,可以通过语音输入
  18. 可以指纹支付也可以密码支付

4.微信朋友圈的点赞功能怎么测试

  1. 是否可以点赞、取消点赞
  2. 多次点赞会出现什么情况
  3. 多人点赞时的顺序是否按照时间顺序进行排列
  4. 点赞是否显示头像和名称
  5. 点赞之后能否进行评论
  6. 点赞之后退出该页面,再次进入朋友圈点赞消息是否还存在
  7. 多用户点赞,再次打开朋友圈是是否可以按照顺序看到是谁谁谁赞了我
  8. 弱网络的情况下点赞能否实时更新
  9. 点赞时有短信或电话进来,能否显示点赞情况
  10. 点赞的人是否在可见分组里
  11. 点赞之后共同好友的点赞和评论是否会提醒你

5.抖音的登录功能怎么测试

6.点击登录之后等待时间过长可能是什么原因?如何判断是客户端的问题还是服务端的问题?

你可能感兴趣的:(字节跳动测开实习面经)