JAVA面试高频知识点突击手册(30分钟实现高效突击)
原文链接:
https://mp.weixin.qq.com/s/laf4T5goSkMwbN0dc63jUA
前言:
作者前些天参加了两次 腾讯 公司的后台开发面试,由于已经很长时间没有面过试刷过题了,为了应对这次面试,作者根据曾经的面试经验总结了一些高频技术点列举在本文中,以方便作者本人以及找实习找工作的小伙伴们突击使用。
本文将分为JVM虚拟机、Java核心技术、数据库、网络四大模块。最后会附上这两次面试题。
一、JVM虚拟机
关于JVM虚拟机的面试题主要涉及到java内存区域、对象引用类型、垃圾回收算法、垃圾收集器。
1. java内存区域
java内存区域主要包含方法区、程序计数器、Java虚拟机栈、本地方法栈、Java堆、运行时常量池、直接内存。
Java虚拟机栈:线程私有;和本地方法栈相似,但区别是虚拟机栈为虚拟机执行Java方法服务,本地方法栈为虚拟机使用到的Native方法服务。每个方法在执行的同时都会创建一个栈帧用于存储局部变量表、操作数栈、动态链接、方法出口等信息。每个方法从调用到执行完成的过程对应栈帧在虚拟机中入栈到出栈的过程。
Java堆:线程共享;java堆是垃圾收集器管理的主要区域,也叫GC堆,唯一目的是存放对象实例。Java堆可分为:新生代和老年代-Eden空间、From Survivor空间、To Survivor空间、Tentired空间等。
方法区:线程共享;方法区用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据,存放Class的相关信息例如类名、访问修饰符、常量池、字段描述、方法描述等。别名为Non-Heap(非堆)、Hotspot:永久代Perm。
栈用于服务java方法,方法区主要存储类信息,java堆存储对象实例信息。GC主要针对java堆进行。分代的目的是更好地回收内存,或者更快地分配内存。
两种异常情况:
StackOverFlowError-线程请求的栈深度大于虚拟机所允许的深度。
OutOfMemoryError-若虚拟机栈可以动态扩展,但扩展时申请不到足够内存。
模拟方法:不断调用递归可导致SOF,不断建立线程可导致OOM,
2. 对象引用类型
2.1 对象的生死
判断法-引用计数器法:给对象添加一个引用计数器,每当被引用时加1,失效时减1,任何时刻计数器为0的对象是不可能再被引用的。此方法很难解决对象之间相互循环引用的问题。
判断法-可达性分析算法:通过一系列的称为“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径成为引用链,当一个对象到GC Roots没有任何引用链相连(不可达)时,则此对象不可用。
可作为GC Roots的对象:虚拟机栈中引用的对象、方法区中类静态属性引用的对象、方法区中常量引用的对象、本地方法栈中JNI(即Native方法)引用的对象。
2.2 引用类型
JDK1.2之后,将引用分为强引用、软引用、弱引用、虚引用,强度依次减弱。
强引用:普遍存在,类似Object obj = new Object(),只要强引用还存在,GC就无法回收它。
软引用:描述一些还有用但非必须的对象。系统快发生溢出前,会把这些对象列进回收范围之中进行第二次回收。SoftReference类来实现。
弱引用:描述非必须对象,此类对象只能生存到下一次垃圾回收发生之前,无论内存是否够,都会回收。WeakReference类实现。
虚引用:唯一目的是能在这个对象被收集器回收时收到一个系统通知,不会影响对象生存时间也不会获得对象实例。PhantomReference类实现。
强引用只要还在就不会回收,软引用会在溢出前回收,弱引用在下次回收时回收,虚引用仅会在回收时发送通知。
3. 垃圾收集算法
垃圾收集算法主要包括标记-清除算法、复制算法、标记-整理算法、分代收集算法。
标记-清除算法:分为“标记”和“清除”阶段:首先标记出所有需要回收的对象,在标记完成后统一回收所有被标记的对象。存在效率问题和空间问题(标记清除后会产生大量不连续的碎片)。
复制算法:将内存分为大小相同的两块,每次使用其中的一块。当这一块的内存使用完后,就将还存活的对象复制到另一块去,然后再把使用的空间一次清理掉。这样就使每次的内存回收都是对内存区间的一半进行回收。
标记-整理算法:根据老年代的特点提出的一种算法,标记过程与“标记-清除”算法一样,但后续步骤不是直接对可回收对象回收,而是让所有存活的对象向一端移动,然后直接清理掉端边界以外的内存。可解决碎片问题。
分代收集算法:
新生代 GC(Minor GC):指发生新生代的的垃圾收集动作,Minor GC 非常频繁,回收速度一般也比较快。
老年代 GC(Major GC/Full GC):指发生在老年代的 GC,出现了 Major GC 经常会伴随至少一次的 Minor GC(并非绝对),Major GC 的速度一般会比 Minor GC 的慢 10 倍以上。
大部分情况对象都会首先在 Eden 区域分配,在一次新生代垃圾回收后,如果对象还存活,则会进入Survivor 区,对象年龄加 1,当年龄增加到一定默认 15 岁时,就会被晋升到老年代中。同时,大对象将直接进入老年代。
4. 垃圾收集器
垃圾收集器主要包括Serial、ParNew、Parallel Scavenge、CMS、G1
Serial:
单线程;简单而高效(与其他收集器的单线程相比);新生代采用复制算法,老年代采用标记-整理算法。进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The World" )。
ParNew:
ParNew 收集器其实就是 Serial 收集器的多线程版本。
Parallel Scavenge:
几乎和ParNew一样,关注点是吞吐量(高效率的利用 CPU,吞吐量是 CPU 中用于运行用户代码的时间与 CPU 总消耗时间的比值)。可设置Parallel 收集器+ 老年代串行/老年代并行。
CMS:
是一种以获取最短回收停顿时间为目标的并发收集器,实现了让垃圾收集线程与用户线程(基本上)同时工作。基于“标记-清除”算法。
CMS运作分为4个步骤:
- 初始标记:暂停所有的其他线程,并记录下直接与 root 相连的对象,速度很快。
- 并发标记:同时开启 GC 和用户线程,记录可达对象,但并不能保证包含当前所有的可达对象。因为用户线程可能会不断的更新引用域,所以 GC 线程无法保证可达性分析的实时性。所以这个算法里会跟踪记录这些发生引用更新的地方。
- 重新标记:重新标记阶段修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,导致短暂停顿。
- 并发清除:开启用户线程,同时 GC 线程开始对为标记的区域做清扫。
CMS主要优缺点:并发收集、低停顿;对 CPU 资源敏感、无法处理浮动垃圾、使用“标记-清除”算***导致收集结束时会有大量空间碎片产生。
G1:
G1 (Garbage-First) 主要针对配备多处理器及大容量内存的机器. 具备低停顿和高吞吐量特征。G1 从整体来看是基于“标记整理”算法实现的收集器,从局部上来看是基于“复制”算法实现的。
G1 在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region(这也就是它的名字 Garbage-First 的由来)。
G1主要有并行与并发、分代收集、空间整合、可预测的停顿的特点。运作大致分为初始标记、并发标记、最终标记、筛选回收这四个过程。
二、Java核心技术
关于Java核心技术的面试题主要涉及到Java并发、I/O、容器、static关键字。
1. Java并发
1.1 Java多线程
线程池:
建议用ThreadPoolExecutor而不是Executors去创建,否则可能会导致OOM。
池化技术的思想主要是为了减少每次获取资源的消耗,提高对资源的利用率。
ThreadPoolExecutor 3 个最重要的参数:
- corePoolSize : 核心线程数线程数定义了最小可以同时运行的线程数量。
- maximumPoolSize : 当队列中存放的任务达到队列容量的时候,当前可以同时运行的线程数量变为最大线程数。
- workQueue: 当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。
下图展示了线程池的工作原理:
ThreadPoolExecutor饱和策略:
- AbortPolicy:拒绝新任务的处理并抛出RejectedExecutionException
- CallerRunsPolicy-调用执行自己的线程运行任务
- DiscardPolicy-直接丢弃新任务;DiscardOldestPolicy-丢弃最早的未处理的任务请求。
多线程的实现方式:
- 继承Thread类:重写run方法,用new 类名.start()方法启动线程。
- 实现Runnable接口:重写run方法,用new Thread(Runnable对象).start()方法启动线程。
- 实现Callable接口:类似Runnable,但支持异常和返回值。需要创建一个FutureTask,指定Callable对象,做为线程任务,再创建线程new Thread(task).start()来执行。
线程和进程的区别与联系:
进程是程序的一次执行过程,是系统运行程序的基本单位,是动态的。
线程与进程相似,但线程是一个比进程更小的执行单位。
一个进程在其执行的过程中可以产生多个线程。
同类的多个线程共享进程的堆和方法区资源,但每个线程有自己的程序计数器、虚拟机栈和本地方法栈,所以系统在产生一个线程,或是在各个线程之间作切换工作时,负担要比进程小得多,也正因为如此,线程也被称为轻量级进程。
多个线程共享进程的堆和方法区。程序计数器、虚拟机栈和本地方法栈是线程私有的。
1.2 Java内存模型(JMM)
指令序列的重排序:执行程序时,为了提高性能,编译器和处理器常常会对指令做重排序。
内存屏障:内存屏障分为LoadLoad、StoreStroe、LoadStore、StoreLoad四种,用于保证某些情况下指令按顺序执行。
volatile:
作用:
- 保证变量的可见性、防止指令重排序。
特性:
- 可见性:对一个volatile变量的读,总数能看到(任意线程)对这个volatile变量最后的写入。
- 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性。
写-读的内存语义:
- volatile写:当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量值刷新到主内存。
- volatile读:当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来从主内存中读取共享变量。
实现方法:
- volatile写:在每个volatile写操作前面插入一个SS屏障,后面插入一个SL屏障。
- volatile读:在每个volatile读操作后面插入一个LL屏障和一个LS屏障。
数据竞争:在一个线程中写一个变量,在另一个线程中读同一个变量,而且写和读没有通过同步来排序,就会出现数据竞争。
1.3 锁
悲观锁:共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程(总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁)
乐观锁:总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量。
CAS算法:compare and swap(比较与交换),是一种有名的无锁算法。假设有需要读写的内存值V、进行比较的值A、拟写入的新值B,当V的值等于A时,CAS通过原子方式用新值B来更新V的值,否则不会执行任何操作(比较和替换是一个原子操作)。一般情况下是一个自旋操作,即不断的重试。
CAS算法存在三个问题:ABA 问题(读取时是A,期间被修改为B后又被改为A,CAS在赋值时可能会认为这个变量没有被改过。ABA 问题也是乐观锁一个常见的问题)、循环时间长开销大、只能保证一个共享变量的原子操作。
synchronized:synchronized属于重量级锁,主要用于修饰实例方法、静态方法、代码块三种场景。能保证数据的可见性和原子性。
volatile:synchronized属于轻量级锁,只能用于变量,性能好。能保证数据的可见性,不能保证原子性。
volatile关键字主要用于解决变量在多个线程之间的可见性,而 synchronized关键字解决的是多个线程之间访问资源的同步性。
ReentrantLock:
synchronized和ReentrantLock的区别与联系:
(1)两者都是可重入锁
(2)synchronized 依赖于 JVM 而 ReentrantLock 依赖于 API
(3)ReentrantLock 比 synchronized 增加了一些高级功能(①等待可中断;②可实现公平锁;③可实现选择性通知(锁可以绑定多个条件))
双重校验锁:
public class Singleton { private static Singleton Instance; private Singleton() {} public static Singleton getInstance() { if (Instance == null) { synchronized (Singleton.class) { if (Instance == null) { Instance = new Singleton(); } } } return Instance; } }
单例模式,构造方法为私有,用双重校验锁防止被多线程创建为多个实例。
注意:如今,如今双重校验锁已损坏,在优化编译器或共享内存多处理器的情况下,双重校验锁无法正常工作。
三种解决方法:
- 给getInstance方法加上synchronized。
- 静态内部类。
- 结合volatile使用。
2. I/O
BIO(Blocking I/O):
同步阻塞I/O模式,数据的读取写入必须阻塞在一个线程内等待其完成。
NIO(New I/O):
NIO中的N可以理解为Non-blocking,不单纯是New。
NIO与IO的区别:
(1)Non-blocking:NIO流非阻塞,IO流是阻塞的。
(2)Buffer:IO面向流,NIO面向缓冲区。
(3)Channel:NIO 通过Channel(通道)进行读写,是双向的,可读也可写,而流的读写是单向的。
(4)Selector:NIO有选择器,而IO没有。
NIO 读数据和写数据方式:
通常来说NIO中的所有IO都是从 Channel(通道) 开始的。
- 从通道进行数据读取:创建一个缓冲区,然后请求通道读取数据。
- 从通道进行数据写入:创建一个缓冲区,填充数据,并要求通道写入数据。
AIO(Asynchronous I/O):基于事件和回调机制实现,操作之后会直接返回,不会堵塞在那里,当后台处理完成。目前应用不是很广泛,windows操作系统就是典型的AIO。
BIO:一次链接(connection,没发数据)一个线程。NIO:一次请求一个线程。AIO:一次有效请求一个线程。
3. 容器
Arraylist与LinkedList区别:Arraylist底层基于Object数组,LinkedList底层基于双向链表,因此Arraylist支持随机访问,LinkedList不支持。
ArrayList与Vector区别:Vector类的所有方法都是线程安全的,单线程会花费多余的时间在同步上。而Arraylist不是线程安全的,建议单线程使用。
HashMap和Hashtable的区别:
(1)HashMap是非线程安全的,HashTable是线程安全的。
(2)HashTable中的键值不能为null,HashMap可以。
(3)初始容量大小和每次扩充容量大小的不同。
HashTable已基本被淘汰
HashMap的底层实现:HashMap底层是散列链表,通过拉链法解决冲突,JDK1.8后当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。
ConcurrentHashMap:
底层数据结构:
JDK1.7的ConcurrentHashMap底层采用分段的数组+链表实现,JDK1.8采用的数据结构跟HashMap1.8的结构一样,数组+链表/红黑二叉树。
实现线程安全的方式:
JDK1.7时,ConcurrentHashMap(分段锁)对整个桶数组进行了分割分段(Segment),每一把锁只锁容器其中一部分数据,多线程访问容器里不同数据段的数据,就不会存在锁竞争,提高并发访问率。JDK1.8时摒弃了Segment的概念,而是直接用Node数组+链表+红黑树的数据结构来实现,并发控制使用synchronized和CAS来操作。
JDK1.7:
JDK1.8:
4. static关键字
四种使用场景:
- 修饰成员变量和成员方法
- 静态代码块
- 修饰类(只能修饰内部类)
- 静态导包(用来导入类中的静态资源,1.5之后的新特性)
修饰成员变量和成员方法:被static修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,可以并且建议通过类名调用。被static声明的成员变量属于静态成员变量,静态变量存放在Java内存区域的方法区。
静态代码块:静态代码块定义在类中方法外,静态代码块在非静态代码块之前执行(静态代码块—非静态代码块—构造方法)。该类不管创建多少对象,静态代码块只执行一次。
静态内部类:非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。意味着静态内部类的创建是不需要依赖外围类的创建,且不能使用任何外围类的非static成员变量和方法。
三、数据库
关于数据库的面试题主要涉及到事务和索引。
1. 事务
事物的四大特性(ACID):
- 原子性(Atomicity):事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用。
- 一致性(Consistency):执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的。
- 隔离性(Isolation):并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的。
- 持久性(Durability):一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
并发事务带来的问题:
- 脏读(Dirty read): 当一个事务正在访问数据并且对数据进行了修改,且未提交,这时另外一个事务也访问了这个数据,然后使用了这个数据,那么该事务读到的这个数据就是“脏数据”。
- 丢失修改(Lost to modify): 指在一个事务读取一个数据时,另外一个事务也访问了该数据,那么在第一个事务中修改了这个数据后,第二个事务也修改了这个数据。这样第一个事务内的修改结果就被丢失,因此称为丢失修改。
- 不可重复读(Unrepeatableread): 指在一个事务内多次读同一数据。在这个事务还没有结束时,另一个事务也访问该数据。那么,在第一个事务中的两次读数据之间,由于第二个事务的修改导致第一个事务两次读取的数据可能不太一样。这就发生了在一个事务内两次读到的数据是不一样的情况,因此称为不可重复读。
- 幻读(Phantom read): 幻读与不可重复读类似。它发生在一个事务读取了几行数据,接着另一个并发事务插入了一些数据时。在随后的查询中,第一个事务就会发现多了一些原本不存在的记录,就好像发生了幻觉一样,所以称为幻读。
不可重复读指两次读取的某一行数据的结果不同,幻读则是两次读取的记录的行数不同。
事务隔离级别:
- 读取未提交(READ-UNCOMMITTED):最低的隔离级别,允许读取尚未提交的数据变更。
- 读取已提交(READ-COMMITTED):允许读取并发事务已经提交的数据,可以阻止脏读。
- 可重复读(REPEATABLE-READ):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读。
- 可串行化(SERIALIZABLE):最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,可以阻止脏读、不可重复读和幻读。
MySQL默认的隔离级别是可重复读(REPEATABLE-READ)
2.索引
数据结构:
MySQL索引使用的数据结构主要有BTree索引和哈希索引。绝大多数需求为单条记录查询的时候,可以选择哈希索引,否则使用BTree索引。
主要索引类型:
- PRIMARY KEY(主键索引)
- UNIQUE(唯一索引)
- INDEX(普通索引)
- FULLTEXT(全文索引)
- 组合索引
普通索引:
最基本的索引,唯一任务是加快对数据的访问速度。
唯一索引:
与普通索引类似,不同的是唯一索引列的值必须唯一,但允许有空值,如果是组合索引,则列值的组合必须唯一。
主键索引:
是一种特殊的唯一索引,不允许有空值。一个表只能有一个主键。
组合索引:
建立语句:
ALTER TABLE mytable ADD INDEX name_city_age (name(10),city,age);
假设建表时,usernname长度为16,这里用10。这是因为一般情况下名字的长度不会超过10,这样会加速索引查询速度,还会减少索引文件的大小,提高INSERT的更新速度。
根据最左前缀原则,相当于分别建立了下面三组组合索引:
usernname,city,age
usernname,city
usernname
此处有一道阿里二面面试题:A,B,C,D四个字段,A+B,B+C,A+C,A+D,C+D检索条件比较常用,怎么建立索引最高效?可以在留言区回答。
B+Tree:每一个叶子节点都包含指向下一个叶子节点的指针,从而方便叶子节点的范围遍历。
建立索引会产生占用磁盘空间的索引文件,表中增删改也时索引也会动态维护,因此对于中到大型表索引都是非常有效的,但特大型表的话维护开销会很大,不适合建索引。
索引设计:
- 索引字段尽量使用数字型(简单的数据类型)
- 尽量不要让字段的默认值为NULL
- 前缀索引和索引选择性
- 使用唯一索引
- 使用组合索引代替多个列索引
- 注意重复/冗余的索引、不使用的索引
四、网络
关于网络的面试题主要涉及到TCP/IP协议族。
1. TCP
TCP三次握手:
• 客户端–发送带有SYN标志的数据包–一次握手–服务端
• 服务端–发送带有SYN/ACK标志的数据包–二次握手–客户端
• 客户端–发送带有带有ACK标志的数据包–三次握手–服务端
TCP四次挥手:
• 客户端-发送一个FIN,用来关闭客户端到服务器的数据传送
• 服务器-收到这个FIN,它发回一个ACK,确认序号为收到的序号加1。和SYN一样,一个FIN将占用一个序号
• 服务器-关闭与客户端的连接,发送一个FIN给客户端
• 客户端-发回ACK报文确认,并将确认序号设置为收到序号加1
TCP与UDP区别:
TCP面向连接,可靠,一般用于文件传输、发送和接收邮件、远程登录等场景。
UDP不面向连接,高效,不可靠,适用于直播、视频等即时通信场景。
TCP流量控制: TCP连接的每一方都有固定大小的缓冲空间,TCP的接收端只允许发送端发送接收端缓冲区能接纳的数据。当接收方来不及处理发送方的数据,能提示发送方降低发送的速率,防止包丢失。
TCP利用滑动窗口实现流量控制。
TCP拥塞控制:慢开始、拥塞避免、快重传和快恢复。
- 慢开始:拥塞窗口cwnd初始值为1,每经过一个传播轮次,cwnd加倍。
- 拥塞避免:拥塞窗口cwnd缓慢增大,每经过一个往返时间RTT就把发送放的cwnd加1。
- 快重传和快恢复:发送方收到三次重复确认后立即重传丢失数据段。
TCP粘包:
原因:发送端需要等缓冲区满才发送出去,造成粘包;接收方不及时接收缓冲区的包,造成多个包接收。
解决:
(1)发送固定长度的消息
(2)把消息的尺寸与消息一块发送
(3)使用特殊标记来区分消息间隔
(4)接收方创建一预处理线程,对接收到的数据包进行预处理,将粘连的包分开。
UDP不会出现粘包,因为它有消息边界。
2. HTTP和HTTPS
HTTP状态码:
HTTP1.0和HTTP1.1的主要区别:长连接(HTTP1.1是长连接)、错误状态响应码、缓存处理、带宽优化及网络连接的使用
HTTP和HTTPS的区别:①默认端口不同,http是80,https是443。②HTTPS基于SSL/TLS,更加安全,但耗费更多服务器资源。
HTTPS的加密方式:所有传输的内容都经过加密,加密采用对称加密,但对称加密的密钥用服务器方的证书进行了非对称加密。
HTTPS的加密过程:认证服务器并从https证书中获取公钥->利用公钥与服务器协商会话秘钥->使用会话秘钥加密传输。
3. 补充
在浏览器中输入url地址 ->> 显示主页的过程:
图解(图片来源:《图解HTTP》):
DNS解析 -> TCP连接 -> 发送HTTP请求 -> 服务器处理请求并返回HTTP报文 -> 浏览器解析渲染页面 -> 连接结束
DNS默认端口:53
DNS工作原理:
五、写在最后
如果你已经掌握本文中的这些技术点,那么恭喜你,每场面试你已经可以应付30%-45%的面试题了。
剩下的,主要还是看你的项目经验、思维能力和coding能力。
比如你项目中运用过SSM技术,那么你需要清楚Spring的AOP、IOC原理,以及SpringMVC的Dispacher过程。或者你可能做过一些分布式项目,那么你必须清楚RPC原理以及zookeeper的机制和算法。能讲清楚这些,你就可以应付75%以上的面试题了。
最后25%的面试题,考验coding和思维能力,需要掌握数据结构和算法,并进行集中刷题,可以看《剑指offer》、《程序员面试宝典》、《Cracking the Coding Interview》这几本书,同时可以在nowcoder、leetcode、lintcode等网站上刷题。
重要的是那75%的面试题,这些面试题涉及的知识点的复习性价比也是最高,如果时间紧张,就多花些时间在这些基础知识上吧。
六、腾讯面试
面试-1
1.自我介绍
2.你负责项目中的哪一块
3.TCP、UDP的区别与联系
4.TCP拥塞控制怎么实现的
5.线程和进程的区别与联系
6.线程共享了哪些系统资源
7.n+1个数字,其中n个是成对的,最终剩下一个落单,如何快速筛选出这个落单的。
8.有一篇英文文章,如何快速对文章中所有单词进行逆序操作?
面试-2
1.自我介绍
2.TCP、UDP区别
3.TCP流量控制怎么进行的
4.TCP拥塞控制有哪些策略
5.c++中struct、class、union的区别
6.进程与线程的区别
7.客户端用UDP向服务器发送10、20、30字节的3个包,服务端有没有可能同时收到这3个包,也就是一次性收到一个60字节的包,或收到10和50字节的2个包?TCP会出现这种情况吗?为什么?
8.在浏览器地址栏输入url访问一个网站,会涉及哪些协议?
9.DNS的原理、工作流程、默认端口是多少?
10.HTTP、HTTPS的区别?默认端口?
11.HTTPS的加密过程?用非对称还是对称?全部用的非对称吗?
12.linux如果要执行目录A下的脚本B,对于A是什么权限,B是什么权限?
腾讯十分倾向于问数据结构、操作系统、数据库和计算机网络这四门专业课的知识。