包含Java、数据结构、操作系统、计算机网络、算法等相关知识,未分类,按照看到的面经进行解答,可以评论转发喔。
数组和链表都是数据结构中存储数据的容器,数组底层使用连续的地址空间进行数据的存取,链表使用指针(java中使用引用)的方式进行存取。数组的分配需要大量的连续空间,如果大量的使用数组,可能会造成内存碎片严重。链表可以把碎片化的空间通过指针进行连接,从而提高内存的利用率。其次,数组可以随机存取,他的读操作快(直接根据下标),写操作慢(可能涉及到数组的移位)。链表插入数据块,但是读的时候需要遍历,速度较慢。
重写一般是指重写父类已经定义好的方法,而重载呢,是指在一个类中,存在函数名相同,但是参数不同(包括参数列表,参数个数)的函数。通常用来完成相同的功能。重写一般是用于对父类的方法做一个扩充,而我们的重载一般是为了适应不同的参数,比如说max函数,sum函数等等。但是在设计模式中,我们一般不建议重写父类已经写好的方法,这样回违反里氏替换原则。
final:一般修饰类或者不变的量,一般一个类如果不想被继承,通常用final修饰,比如说String,final修饰类的话,其所有的方法都会被隐试的添加final的修饰符。
finally:异常捕获体系中一定会执行的方法,我们在关闭数据库连接,redis连接,socket等连接。
finalize:在垃圾回收的时候需要调用的方法,类似于C++中的析构函数,一般我们可以在这里写一些对类中一些变量的处理,比如DBHelper中关闭连接等操作。还有一个神奇的用法就是在我们垃圾回收的时候,如果在finalize中重新给自身添加引用,可以逃脱垃圾回收。
在jdk1.7 的时候,hashmap使用数组 + 链表的结构,在jdk 1.8 以后,采用的是数组 + 链表 + 红黑树的结构。初始的桶数组的长度为16,当我们的桶数组占用了8个,并且hashmap存储元素超过64个的时候,会从链表转换为红黑树。如果超过了扩容因子,但是元素并未超过64的时候,优选应该是扩容。在桶数组小于6个且存储元素小于64个的时候,会转变成你那表。
在jdk8之前,采用的是头插法,之后是尾插。jdk8之前之所以使用头插,是为了凸显一个热点效应,即后插入的,一般来说先用到,但是这是个伪命题,在进行rehash操作的时候,会颠倒我们的链表。在并发条件下,头插还容易产生死锁。
在java中,流一般分为二进制的字节流,input/outputStrem,和字符流InputStreamReader/OutSteamWriter。
都是在Java并发编程中提到的重要的知识点。volatile可以保证有序性和可见性,而我们的sychornized可以保证原子性。
sychornized可以在方法,代码块上进行一个加锁,在方法上加锁,说明这个方法是一个同步方法。对一段代码加锁,需要传入一个锁对象,我们一般传入一个类或者对象,对象的内存结构会记录锁的状态,只允许一个线程进入。在jdk1.6之前,sychornized使用,会向操作系统申请一个mutex互斥量,但是这样效率很低,所以我们在以后就引入了无锁、偏向锁(只有一个线程)、轻量级锁(自旋锁)、重量级锁。 在不同的情况下,sychornized会在不同的情况使用不同的锁。提高了sychornized的效率,已经达到了和lock锁相当的效率。同时sychornized是非公平锁,由虚拟机决定加锁和解锁,是可重入锁。 一个线程如果尝试获取锁失败之后,会进入一个同步队列中(SynchronizedQueue)阻塞住。
volatile是保持缓存一致性协议(MESI)的重要组成部分,保证我们的一个线程对volatile变量进行修改,马上就可以让别的线程得知,同时加入了内存屏障(读屏障、写屏障)保证了指令的有序性。但是无法保证原子性。
标记清除算法:标记无引用的对象,直接清除,会产生大量的内存碎片。
标记整理算法:标记之后进行整理。
复制算法:每次只用一半的内存空间,然后每次收集,把存活的对象移动到另外的一半内存中。
分代收集算法:把内存划分为新生代和老年代,然后在不同的区域使用不同的垃圾回收算法。
索引底层采用了B+树实现了索引结构。非叶子节点存放索引的值,叶子节点存放索引 + 数据。聚集索引(Innodb),非聚集索引(MyISAM)。在叶子节点,有指向其兄弟节点的指针,又因为有序,树的高度低,可以达到很高的查询效率。
tcp三次握手是tcp连接管理里面的一个重要的知识点,用于在双方发送之前建立链接。首先客户端往服务端发送请求连接的数据(syn = 1, seq = x,ack = 0),发送之后进入“同步以发送”状态,服务端收到消息,返回愿意接受连接的报文(syn = 1, seq = y,ack = x + 1),(服务端进入同步已接收状态),客户端收到消息给服务端返回确认报文。(seq = x + 1 ,(ACK = 1) ack = y + 1),这里传输的x,y就是以后传输的基准。(客户端和服务端进入连接已建立状态)。因为TCP是全双工通信,所以我们必须保证客户端和服务端都有发送数据和接收数据的能力,所以必须要三次握手。
进程是CPU分配资源的最小单位,线程是CPU执行和调度的最小单位。
从并发度来讲: 线程比进程有更高的并发度,因为CPU的大量的任务都是(朝花夕拾的),特别是针对服务端程序来说,处理一个请求开辟一个进程无意义。
从资源方面说: 进程是CPU分配资源的最小单位,线程是利用进程的资源,没有自己独立的资源,但是会有线程隔离的方法栈,程序计数器等。
从调度来说: 进程调度很消耗cpu的资源,但是线程很轻,不存在资源的分配和消耗。
对CPU的利用率上说: 线程对CPU的利用率更高。
从属关系上来说: 一个进程可以有多个线程,一个线程必须从属于一个进程。
Java中的类加载是通过类加载器进行加载的。一般分为四种类加载器。(启动类加载器,扩展类加载器,应用程序类加载器,自定义类加载器),并且采用双亲委派机制进行加载,即委托给父加载器加载,如果父加载器加载了,就不会在加载,如果父加载器不能加载,那么就自身加载。
好处: 沙箱安全机制,可以防止java的核心类库被破坏。
防止类的重复加载。
类加载一般分为:加载、验证、准备、解析、初始化等几个步骤。
加载: 把字节码加载到内存中。
验证: 验证字节码的格式是否符合要求。
准备: 给类变量,静态常量等分配内存,并赋予默认值。
解析: 将符号引用替换为直接引用,相当与页表机制里面产生一个页表,指向实际的内存空间。
初始化: 对类的静态变量初始化为指定的值,执行静态代码块。
(ACID)
原子性: 事务是一组操作,要么成功,要么全部回滚。
一致性: 在执行事务的前后(包括回滚),数据库必须处于数据一致的状态(也可以理解成不会有脏数据)。
隔离性: 事务与事务之间不应该相互影响,是独立的。
持久性: 当事务执行完毕,会直接把修改的信息持久化到数据库。
创建主键的同时会创建一个唯一索引
ARP是地址解析协议,一般用于广播获取mac地址。获取了主机的mac地址才能建立链接。根据IP地址获取物理地址,并缓存起来。可能出现ARP欺骗(正因为ARP协议是广播的,才会出现这种情况。)
操作系统的接口是连接应用软件与操作系统的中间桥梁。接口在程序设计中表现形式就是:函数。操作系统提供的函数就被称为系统调用(system call);
Linux上面在补一下
为了防止用户随意更改系统的文件,导致系统崩溃。操作系统把内存划分成了用户态和内核态,用户态运行用户的程序,内核态运行系统程序、操作硬件。
运行在用户态下的程序不能直接访问操作系统内核数据结构和程序。当我们在系统中执行一个程序时,大部分时间是运行在用户态下的,在其需要操作系统帮助完成某些它没有权力和能力完成的工作时就会切换到内核态(比如操作硬件)。
主要差别:
首先:明白HTTPS是在应用层(HTTP)和传输层(TCP)之间增加了一个SSL层,达到了安全传输的目的;
采用对称加密算法加密数据 + 非对称加密算法交换密钥 + 数字证书验证身份。
磁盘调度:磁盘调度在多道程序设计的计算机系统中,各个进程可能会不断的提出不同的对磁盘进行读/写的请求。由于有时候这些进程的发送请求的速度比磁盘响应快很多,因此我们有必要为每个磁盘设备建立一个等待队列,常用的磁盘调度算法有下面四种。
先来先服务(FCFS): 公平、简单,每个请求都能够一次得到处理。在访问请求比较多的情况下,会降低设备服务的吞吐量。
最短寻道时间优先算法(SSTF): 该算法选择离当前磁头所在磁道最近的磁道进行访问,以每次寻道的时间最短。可以得到较大的吞吐量,但是边缘磁道的请求将会无限期被延迟,得不到处理。
扫描算法(SCAN,电梯调度): 利用电梯的特性,该算法不仅考虑到磁道与当前磁道之间的距离,更加优先考虑的是磁头的移动方向,向外移动到最外,然后向内移动。避免出现饥饿现象。吞吐量大,平均响应时间小,但是由于是摆动的扫描方式,两侧磁道被访问的频率仍然低于中间磁道。
循环扫描算法(CSCAN): 类似于上面的扫描算法,但是扫描算法是摆动扫描,循环扫描是到达一端之后,马上转到另一端,然后继续的扫描,类似于转圈圈。(故称为循环扫描算法)
只要当前JVM实例中尚存任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束,守护线程随着JVM一起结束工作。最典型的就是GC(垃圾回收线程),普通的线程可以调用setDaemon(),编程守护线程。
简言之:守护线程就是后台的线程,用户线程就是前台的线程。
协程是比线程更小的一种执行单元,可以理解为是轻量级的线程,之所以说轻,其中一个方面就是协程持有栈比线程小很多。为什么提出协程?,因为线程切换的成本较高,而协程在这方面很有优势。
线程切换: