知识点

面试过程中常见的概念、知识点总结。

一、Linux知识点

\(\color{SlateGray}{1、绝对路径用什么符号表示?当前目录、上层目录用什么表示?主目录用什么表示? 切换目录用什么命令?}\)
  (1)绝对路径用/表示(如/etc/init.d);
  (2)当前目录和上层目录表示为./和../;
  (3)主目录表示为~/;
  (4)切换目录使用cd指令。

\(\color{SlateGray}{2、怎么查看当前进程?怎么执行退出?怎么查看当前路径?}\)
  (1)查看当前进程使用ps命令;
  (2)执行退出是exit命令;
  (3)查看当前路径为命令pwd。

\(\color{SlateGray}{3、怎么清屏?怎么退出当前命令?怎么执行睡眠?怎么查看当前用户 id?查看指定帮助用什么命令?}\)
  (1)清屏执行clear;
  (2)执行ctrl+c彻底退出当前命令;
  (3)crtl+z则挂起当前线程,且fg命令可恢复被中断线程,bg命令则把中断任务放在后台执行;
  (4)id命令可以查看显示目前登陆账户的 uid 和 gid 及所属分组及用户名;
  (5)查看指定帮助可以使用命令man adduser、adduser --help以及info adduesr。

\(\color{SlateGray}{4、Ls 命令执行什么功能?可以带哪些参数,有什么区别?}\)
  (1)ls执行的功能是列出指定目录中的目录以及文件;
  (2)带参数a可以显示所有文件包括隐藏文件(文件名开头带.),带参数l可以显示更详细信息如大小字节数、可读可写可执行的权限等。

\(\color{SlateGray}{5、Linux系统中Inode和块的理解?}\)
  (1)Linux系统中文件由元数据和块组成,数据块是多个连续性扇区(扇区是文件存储的最小单位),存储文件数据和目录数据;
  (2)而元数据则用来记录文件的创建者、创建日期、大小等,存储元数据信息的区域叫做Inode(索引结点),且Inode中包含有inode号,文件的字节数、User ID、Group ID、读、写、执行权限、时间戳、链接数(软硬链接)、数据block的位置。(不包含文件名)

\(\color{SlateGray}{6、什么是软链接(符号链接)和硬链接?建立软链接,以及硬链接的命令?}\)
  (1)在Linux文件系统中,保存在磁盘分区的文件不管什么类型都会分配一个编号(Inode Index),是文件或目录在一个文件系统中的唯一标识。而多个文件名指向同一Inode是允许的,这样其中一个是源文件,其余的文件就是其硬链接。硬链接是相对文件而言,可以防止文件误删,只要文件系统中存在Inode的一个链接,该结点就不会被删除;软链接则类似快捷方式,本质是一个普通文件,文件内容存放的是源文件的路径名执行以便快速定位到源文件,支持文件和目录的创建。软链接和源文件Inode不同,源文件被删,软链接就变成死链接;
  (2)创建软链接指令为ln -s slink source,创建硬链接指令为ln link source。

\(\color{SlateGray}{7、目录创建用什么命令?创建文件用什么命令?复制文件、移动文件用什么命令?}\)
  (1)创建目录使用命令mkdir;
  (2)创建文件可以使用命令touch、vi,一般向一个不存在的文件输出,都会创建文件;
  (3)复制文件使用命令cp,移动文件使用命令mv。

\(\color{SlateGray}{8、文件权限的修改命令以及格式?}\)
  (1)Linux系统中档案的基本权限有9个,分别是owner/group/others三种身份及各自的read/write/execute权限;
  (2)修改文件权限使用chmod命令;常见格式有chmod u+x file(给file的属主增加执行权限)、chmod 751 file(给file的属主分配读、写、执行权限,给file的所在组分配读、执行权限,给其他用户分配执行的权限。也可写成chmod u=rwx,g=rx,o=x file)。

\(\color{SlateGray}{9、Linux中进程有哪几种状态?在ps显示出来的信息中,分别用什么符号表示的?}\)
  (1)不可中断状态(D)指进程处于睡眠状态,但是此刻进程是不可中断的;
  (2)暂停状态/跟踪状态(T)指向进程发送一个SIGSTOP信号,它就会因响应该信号而进入TASK_STOPPED状态。当进程正在被跟踪时,它处于 TASK_TRACED 这个特殊的状态。正在被跟踪指的是进程暂停下来,等待跟踪它的进程对它进行操作;
  (3)可中断睡眠状态处于这个状态的进程因为等待某某事件的发生而被挂起;
  (4)zombie状态(Z)父进程没有通过wait系列的系统调用会顺便将子进程的尸体(task_struct)也释放掉;
  (5)另外还有就绪状态、运行状态、退出状态。

二、Java知识点

\(\color{GoldenRod}{1、JAVA中创建线程用Thread和Runnable的区别?}\)
  (1)避免JAVA单继承的局限性
    Java中一个类只能有一个直接父类,如果一个类已经继承其他的父类,那么当前这个类中假如有需要多线程操作的代码,这时这个类是无法再继承Thread类的。
  (2)把线程代码和任务的代码分离,解耦合
    Thread类是专门负责描述线程本身的。Thread类可以对线程进行各种各样的操作。如果使用第一种方式,那么把线程要执行的任务也交给了Thread类。这样就会导致操作线程本身的功能和线程要执行的任务功能严重的耦合在一起;
    自定义一个类来实现Runnable接口,这样就把任务抽取到Runnable接口中,在这个接口中定义线程需要执行的任务的规则。当需要明确线程的任务时,我们就让这个类实现Runnable接口,只要实现Runnable接口的类,就相当于明确了线程需要执行的任务;
    且Thread类也是实现Runnable接口。

\(\color{GoldenRod}{2、JAVA父子类的构造过程?}\)
  (1)子类构造器没有显式加上this()或super()会默认加上super(),而如果指定this()就不会调用super(),但最终this()里面还是会调用super();
  (2)总执行顺序:
    无父类:初始化static变量,执行static初始化快-->初始化普通成员变量(如果有赋值语句),执行普通初始化块-->构造方法;
    有父类:初始化父类static成员变量,运行父类static初始化块-->初始化子类static成员变量,运行子类static初始化块-->初始化父类实例成员变量(如果有赋值语句),执行父类普通初始化块-->父类构造方法-->初始化子类实例成员变量(如果有赋值语句)及普通初始化块-->子类构造方法。

\(\color{GoldenRod}{3、JAVA中抽象类和接口的区别?}\)
  (1)抽象类:
    用abstract修饰的类叫做抽象类,此类不能有对象。用abstract修饰的方法叫做抽象方法,此方法不能有方法体;
    有抽象方法的类一定是抽象类,但是抽象类中不一定有抽象方法。抽象类中的抽象方法必须在子类中被重写;
    抽象类强调的是“是不是”的问题,相当于提供了一个大的体制。具体实现交给子类。
  (2)接口:
    规范和抽象类比较相似,是某个事物对外提供的一些功能的声明;
    接口属性默认public static final,并且需要给出初始值。方法默认public abstract;
    接口强调“有没有”的问题,一个类具有某个功能,可以实现功能接口。
  (3)区别:
    抽象类描述的是“是不是”的问题,而接口描述的是“有没有”的问题;
    在Java中类的继承是“单继承”,可以“多对一”,但是不允许“一对多”,而一个类却可以同时实现多个接口;
    接口的方法没有实现,抽象类可以有实现方法。
    抽象类是用来捕捉子类的通用特性的,是对整个类整体进行抽象。接口是对行为的抽象,是抽象方法的集合。

\(\color{GoldenRod}{4、JAVA设计模式六大原则?}\)
  (1)单一职责原则:
    即一个类只负责一项职责,防止某职责需求变更,影响另一个职责;
    只有逻辑足够简单,才可以在代码级别上违反单一职责原则。只有类中方法数量足够少,才可以在方法级别上违反单一职责原则;
    降低类的复杂度,一个类只负责一项职责,其逻辑肯定要比负责多项职责简单的多;
    提高类的可读性,提高系统的可维护性;
    变更引起的风险降低,变更是必然的,如果单一职责原则遵守的好,当修改一个功能时,可以显著降低对其他功能的影响。
  (2)里氏替换原则:
    如果对每一个类型为T1的对象o1,都有类型为T2的对象o2,使得以T1定义的所有程序P在所有的对象o1都代换成o2时,程序P的行为没有发生变化,那么类型T2是类型T1的子类型;
    所有引用基类的地方必须能透明地使用其子类的对象;
    使用继承时,遵循里氏替换原则。类B继承类A时,除添加新的方法完成新增功能P2外,尽量不要重写父类A的方法,也尽量不要重载父类A的方法;
    父类中凡是已经实现好的方法,实际上是在设定一系列的规范和契约,虽然它不强制要求所有的子类必须遵从这些契约,但是如果子类对这些非抽象方法任意修改,就会对整个继承体系造成破坏;
    继承会给程序带来侵入性,程序的可移植性降低,增加了对象间的耦合性,如果一个类被其他的类所继承,则当这个类需要修改时,必须考虑到所有的子类,并且父类修改后,所有涉及到子类的功能都有可能会产生故障。
  (3)依赖倒置原则:
    高层模块不应该依赖低层模块,二者都应该依赖其抽象。抽象不应该依赖细节,细节应该依赖抽象;
    相对于细节的多变性,抽象的东西要稳定的多。以抽象为基础搭建起来的架构比以细节为基础搭建起来的架构要稳定的多。使用接口或者抽象类的目的是制定好规范和契约,而不去涉及任何具体的操作,把展现细节的任务交给他们的实现类去完成;
    遵循依赖倒置原则可以降低类之间的耦合性,提高系统的稳定性,降低修改程序造成的风险;
    低层模块尽量都要有抽象类或接口,或者两者都有。变量的声明类型尽量是抽象类或接口。使用继承时遵循里氏替换原则。
  (4)接口隔离原则:
    客户端不应该依赖它不需要的接口,一个类对另一个类的依赖应该建立在最小的接口上;
    如果接口过于臃肿,只要接口中出现的方法,不管对依赖于它的类有没有用处,实现类中都必须去实现这些方法,这显然不是好的设计;
    建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少。也就是说,我们要为各个类建立专用的接口,而不要试图去建立一个很庞大的接口供所有依赖它的类去调用;
    单一职责原则原注重的是职责,而接口隔离原则注重对接口依赖的隔离。其二,单一职责原则主要是约束类,其次才是接口和方法,它针对的是程序中的实现和细节。而接口隔离原则主要约束接口接口,主要针对抽象,针对程序整体框架的构建;
    运用接口隔离原则,一定要适度,接口设计的过大或过小都不好。设计接口的时候,只有多花些时间去思考和筹划,才能准确地实践这一原则。
  (5)迪米特法则:
    一个对象应该对其他对象保持最少的了解,类与类之间的关系越密切,耦合度越大,当一个类发生改变时,对另一个类的影响也越大;
    尽量降低类与类之间的耦合。一个类对自己依赖的类知道的越少越好,对于被依赖的类来说,无论逻辑多么复杂,都尽量地的将逻辑封装在类的内部,对外除了提供的public方法,不对外泄漏任何信息;
    只与直接的朋友通信,也就是说,陌生的类最好不要作为局部变量的形式出现在类的内部;
    迪米特法则的初衷是降低类之间的耦合,由于每个类都减少了不必要的依赖,因此的确可以降低耦合关系。过分的使用迪米特原则,会产生大量的中介和传递类,导致系统复杂度变大。
  (6)开闭原则:
    一个软件实体如类、模块和函数应该对扩展开放,对修改关闭;
    当软件需要变化时,尽量通过扩展软件实体的行为来实现变化,而不是通过修改已有的代码来实现变化;
    开闭原则更像是前面五项原则遵守程度的“平均得分”;
    用抽象构建框架,用实现扩展细节。因为抽象灵活性好,适应性广,只要抽象的合理,可以基本保持软件架构的稳定。而软件中易变的细节,我们用从抽象派生的实现类来进行扩展,当软件需要发生变化时,我们只需要根据需求重新派生一个实现类来扩展就可以了。

\(\color{GoldenRod}{5、JAVA中常见的几种线程池?}\)
  JAVA中有四种基本的线程池:newCacbedThreadPool、newFixedThreadPool、newScheduledThreadPool、newSingleThreadExecutor。四种线程池方法都是由ThreadPoolExecutor实现。
  (1)newCacbedThreadPool:
    为每一个任务创建一个线程,并且也可以重用已有的线程,无核心线程数量,超过60s的空闲线程将弃用,但是会受到系统实际内存的限制;
    按需创建新的线程,如果没有可用线程则创建新的线程,之前用过的线程可能会再次被使用;
    使用SynchronousQueue做阻塞队列。
  (2)newFixedThreadPool:
    核心线程数和最大线程数是相同的,并且无空闲线程,核心线程数无限时要求,就是可以创建固定大小的线程数量,同时阻塞队列是没有大小限制的;
    在任何情况下最多只有nThread个线程工作,多余的Task将会被存放到队列中等待;
    如果线程在执行任务中被终止,终止之前会创建其他的线程代替原来的;
    线程将会一直存在在线程池中,直到调用shutDown()方法;
    使用LinkedBlockingQueue做阻塞队列。
  (3)newSingleThreadExecutor:
    一个单线程化的线程池,只有一个的核心线程,只处理一个线程,多余的任务放在LinkedBlockingQueue中;
    使用LinkedBlockingQueue做阻塞队列。
  (4)newScheduledThreadPool:
    通过参数指定corePoolSize,最大线程数为最大32位整数,超过corePoolSize的线程在执行完任务后即终止;
    核心线程数将会一直存在线程池中,可以设置线程的执行时间;
    使用DelayedWorkQueue做阻塞队列。
  线程池拒绝策略(队列满时):直接丢弃(DiscardPolicy)、丢弃队列中最老的任务(DiscardOldestPolicy)、抛异常(AbortPolicy)、将任务分给调用线程来执行(CallerRunsPolicy);
  线程池相关参数:线程池的大小(corePoolSize)、线程池中创建的最大线程数(maximumPoolSize)、空闲的线程多久时间后被销毁(keepAliveTime)、阻塞队列(workQueue)、用来创建线程的线程工厂(threadFactory)、线程拒绝策略(handler)。

\(\color{GoldenRod}{6、JAVA源代码编译成Class文件、Class文件解释执行的过程?}\)
  Java源代码编译成Class文件,不同编译器有不同的实现:
  (1)解析与填充符号表,解析主要包括词法分析和语法分析两个过程。其中词法分析就是将源代码的字符流转变为标记(Token)集合(标记包括关键字、变量名、字面量、运算符等)。符号表是由一组符号地址和符号信息构成的表格,可以想象成哈希表中K-V值的形式。通常符号表可以用来语义检查和产生中间代码,目标代码生成阶段也是对符号名进行地址分配的依据。语法分析则是根据Token序列构造语法树的过程,抽象语法树是一种用来描述程序代码语法结构的树形表示方式。每个结点都代表程序代码中的一个语法结构(语法结构包括包、类型、修辞符、运算符、接口、返回值等)。
  (2)插入式注解处理器的注解处理过程。主要是JDK1.5后提供了对注解的支持,同时JDK1.6提供了一组插件式注解处理器的标准API,可以实现API自定义注解处理器,干涉编译器的行为。注解处理器可以看作编译器的插件,在编译期间对注解进行处理,可以对语法树进行读取、修改、添加任意元素;但如果有注解处理器修改了语法树,编译器将返回解析及填充符号表的过程,重新处理,直到没有注解处理器修改为止,每一次重新处理循环称为一个Round。
  (3)语义分析与字节码的生成过程。在获得了填充了符号表的抽象语法树列表后,只能表示程序的结构,但无法保证程序的符合逻辑。因此语义分析主要是对结构上正确的源程序进行上下文有关性质的审查,分为标注检查、数据及控制流分析两个步骤。标注检查检查的内容包括变量使用前是否已被声明、变量与赋值的数据类型是否能匹配等,还有比较重要的动作称为常量折叠。数据及控制分析是对程序上下方逻辑更进一步的验证,如检查变量的初始化、方法每个执行分支是否都有返回值、是否所有的异常都被正确处理等。在生成字节码之前,编译器做的另外的工作就是解语法糖。语法糖对语言的功能没有影响,只是简化程序,提高效率,增可读性,减少出错。Java中常用的语法糖有泛型、变长参数、自动装箱/拆箱、遍历循环、内部类、断言等,而这些语法是JVM不支持的。所以解语法糖就是在编译阶段还原回简单的基础语法结构,包括泛型与类型擦除等。最后字节码生成就是把前面生成的语法树、符号表等信息转化成字节码,然后写到磁盘Class文件中。

  Class文件解释执行过程包括了Class类的加载和执行过程:
  (1)JVM只有在第一次主动使用类时才会去加载该类,JVM类加载的过程可以分为加载、链接、初始化三大步骤。加载阶段读取类文件产生二进制流,并转化为特定的数据结构,初步校验cafe babe魔法数、常量池、文件长度、是否有父类等,然后创建对应类的java.lang.Class实例。链接阶段分为验证、准备、解析三个过程,验证是更详细的校验,比如final是否合规、类型是否正确、静态变量是否合理。准备则为类静态变量分配内存,并将其初始化为默认值,给常量分配内存并设置值。解析时解析类和方法确保类与类之间的相互引用正确性,完成内存结构布局。把类型中的符号引用转换为直接引用(符号引用代表我们程序员写的java代码,然后直接引用是将代码转化为指针地址引用)。初始化阶段为类的静态变量赋予正确的初始化值按照从上到下的顺序(类静态变量初始化、类的静态语句块),当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。虚拟机会保证保证一个类的方法在多线程环境中被正确加锁和同步。
  (2)执行阶段和使用就是调用(类变量初始化和类语句块、构造函数)方法实例化出对象来使用,之后JVM根据程序计数器取出字节码进行解释执行(现在多是解释执行和编译执行相结合,如以方法为单位将字节码一次翻译成机器码执行)。

三、网络知识点

\(\color{CornflowerBlue}{1、HTTP报文组成部分?}\)
  (1)请求报文:
    请求行:由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔。比如GET /data/info.html HTTP/1.1;
    请求头部:大多数请求头并不是必需的,但Content-Length除外(常见请求头:Accept、Accept-Charset、Accept-Encoding、Accept-Language、Authorization、Content-Length);
    空行:它的作用是通过一个空行,告诉服务器请求头部到此为止;
    请求数据:若方法字段是GET,则此项为空,没有数据、若方法字段是POST,则通常来说此处放置的就是要提交的数据。
    
  (2)响应报文:
    响应行:响应行一般由协议版本、状态码及其描述组成,比如 HTTP/1.1 200 OK;
    响应头:响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可以通知客户端如何处理等一会儿它回送的数据。(有Content-Encoding、Content-Length、Content- Type等)。
    响应体:响应体就是响应的消息体,如果是纯数据就是返回纯数据,如果请求的是HTML页面,那么返回的就是HTML代码,如果是JS就是JS代码,如此之类。

\(\color{CornflowerBlue}{2、浏览器输入URL向服务器发送请求,浏览器的工作流程?}\)
  HTTP事务指的是从浏览器传给服务器,服务器返回内容给浏览器,整个完整的过程即HTTP的一个事务。
  (1)HTTP请求阶段:
    浏览器把URL发送给DNS服务器;
    DNS服务器根据IP找到对应的服务器;
    服务器接收到请求,客户端和服务器已经产生连接。
  (2)HTTP响应阶段:
    服务器接收到请求后,根据路径找到相应的项目;
    服务器找到之后,服务器立即把一些响应信息放在响应头中,通过HTTP发送给客户端,同时进行数据处理。
    把整理出来的数据通过HTTP发送给客户端,直到客户端接收完毕。
  (3)浏览器渲染阶段:
    浏览器拿到从服务器传输过来的数据文件;
    先遍历HTML形成DOM树;
    代码从上到下解析,形成CSS树;
    DOM树和CSS树重新组成render树;
    浏览器进行描绘和渲染。

\(\color{CornflowerBlue}{2、HTTP协议连接与释放?}\)
  (1)三次握手(握手成功之后,才可以传输数据):
    浏览器需要先发送SYN码,客户端请求和服务器建立连接;
    服务器接收到SYN码,再发送给客户端SYN+ACK码,我可以建立连接;
    客户端接收到ACK码,验证这个ACK是否正确,如果正确则客户端和服务端则建立起数据连接,同时向服务器回ACK报文,这样双方的数据发送通道都将开启。
  (2)四次挥手:
    当客户端无数据要传输了,会发送FIN码告诉服务器,我发送完毕了;
    当服务端接收完毕后,告诉客户端ACK码,告诉客户端你可以把数据通道关闭了;
    当服务器发送完毕之后,也会发送FIN码,告诉浏览器,数据发送完毕;
    当客户端接收完毕之后,同样发送ACK码,告诉服务器,数据接收完毕,你可以关闭(服务器会告诉浏览器数据的长度,浏览器数据长度和响应头数据长度相同,说明数据已经接收完毕了)。

\(\color{CornflowerBlue}{3、为什么HTTP协议要三次握手、四次挥手?}\)
  (1)三次握手可以确认客户端、服务器的收发能力均是正常,防止因为报文丢失而产生的死锁问题。
  (2)四次挥手是因为服务器收到FIN报文先向客户端回复ACK确认收到FIN报文,但是服务器数据可能没有传输完成,因此需等到数据传输完成才向客户端发生FIN报文。客户端收到FIN报文并向服务器回复ACK之后需等待2MSL才真正关闭,可防止最后的ACK报文丢失导致的服务器等待。

四、数据库知识点

\(\color{DarkOliveGreen}{1、数据库事务的四大特性?}\)
  (1)原子性(Atomic)事务是一个不可分割的工作单位,事务中的操作要么都发生,要么都不发生;
  (2)一致性(Consistency)如果事务执行之前数据库是一个完整的状态,那么事务结束后,无论事务是否执行成功,数据库仍然是一个完整的状态;
  (3)隔离性(Isolation)多个用户并发访问数据库时,一个用户的事务不能被其他用户的事务所干扰,多个并发事务之间数据要相互隔离;
  (4)持久性(Durability)一个事务一旦被提交,它对数据库的影响是永久性的。

\(\color{DarkOliveGreen}{2、什么是脏读、不可重复读、幻读?}\)
  (1)脏读指一个事务读取到另一个事务未提交的数据;
  (2)不可重复读指在当前事务中,读取到了另一事务提交的更新和删除的数据;
  (3)幻读指在当前事务中,读取到了另一事务提交的插入的数据。

\(\color{DarkOliveGreen}{3、数据库四大隔离级别?}\)
  (1)Read uncommitted(读未提交)不防止任何隔离型问题,不能防止脏读/不可重复读/幻读问题;(读取数据不加S锁,因此不会和X锁冲突)
  (2)Read commit(读提交)可以防止脏读问题,但是不能防止不可重复读/幻读问题;(oracle默认隔离级别)(X锁存在,则不能加S锁,存在冲突)
  (3)Repeatable read(可重复读)可以防止脏读/不可重复读,不能防止幻读;(mysql默认隔离级别)(S锁事务结束才释放)
  (4)Serializable(序列化)数据库被设计为单线程,可以防止上述所有问题。

\(\color{DarkOliveGreen}{4、mysql的可重复读的实现(MVCC多版本并发控制)?}\)
  基本原理:MVCC的实现,通过保存数据在某个时间点的快照来实现的。这意味着一个事务无论运行多长时间,在同一个事务里能够看到数据一致的视图。根据事务开始的时间不同,同时也意味着在同一个时刻不同事务看到的相同表里的数据可能是不同的。
  基本特征:每行数据都存在一个版本,每次数据更新时都更新该版本。修改时Copy出当前版本随意修改,各个事务之间无干扰。保存时比较版本号,如果成功(commit),则覆盖原记录,失败则放弃copy(rollback)。
  实现策略:在每一行数据中额外保存两个隐藏的列,当前行创建时的版本号和删除时的版本号(可能为空,其实还有一列称为回滚指针,用于事务回滚,不在本文范畴)。这里的版本号并不是实际的时间值,而是系统版本号。每开始新的事务,系统版本号都会自动递增。事务开始时刻的系统版本号会作为事务的版本号,用来和查询每行记录的版本号进行比较。每个事务又有自己的版本号,这样事务内执行CRUD操作时,就通过版本号的比较来达到数据版本控制的目的。
  (1)插入数据:记录的版本号即当前事务的版本号。
  (2)更新数据:采用的是先标记旧的那行记录为已删除,并且删除版本号是事务版本号,然后插入一行新的记录的方式。
  (3)删除数据:就直接把事务版本号作为删除版本号。
  (4)查询数据:查询时要符合两个条件的记录才能被事务查询出来。第一个条件就是删除版本号未指定或者大于当前事务版本号,即查询事务开启后确保读取的行未被删除。另一个便是创建版本号小于或者等于当前事务版本号,就是说记录创建是在当前事务中(等于的情况)或者在当前事务启动之前的其他事物进行的insert。
  通常MVCC只适用于Msyql隔离级别中的读已提交和可重复读,读未提交由于存在脏读,所以不适用MVCC,因此MVCC主要作用于事务性的,有行锁控制的数据库模型。MVCC本质上是乐观锁的实现,但是Mysql读数据是要加排它锁的,因此Mysql的MVCC并非真正意义上的MVCC。

\(\color{DarkOliveGreen}{5、什么是存储引擎?MYSQL中常见的存储引擎与区别?}\)
  数据库存储引擎是数据库底层软件组织,数据库管理系统(DBMS)使用数据引擎进行创建、查询、更新和删除数据。不同的存储引擎提供不同的存储机制、索引技巧、锁定水平等功能,使用不同的存储引擎,还可以获得特定的功能。
  MYSQL中比较重要的两个存储引擎就是InnoDB存储引擎和MYISAM存储引擎。
  (1)InnoDB是事务型数据库的首选引擎,也是目前MYSQL的默认事务型引擎,是目前最重要、使用最广泛的存储引擎。支持事务安全表(ACID),支持行锁定和外键。InnoDB主要特性包括:
    1.InnoDB给MySQL提供了具有提交、回滚和崩溃恢复能力的事物安全(ACID兼容)存储引擎。InnoDB锁定在行级并且也在SELECT语句中提供一个类似Oracle的非锁定读。这些功能增加了多用户部署和性能。在SQL查询中,可以自由地将InnoDB类型的表和其他MySQL的表类型混合起来,甚至在同一个查询中也可以混合。
    2.InnoDB是为处理巨大数据量的最大性能设计。它的CPU效率可能是任何其他基于磁盘的关系型数据库引擎锁不能匹敌的。被用在众多需要高性能的大型数据库站点上。
    3.InnoDB存储引擎完全与MySQL服务器整合,InnoDB存储引擎为在主内存中缓存数据和索引而维持它自己的缓冲池。InnoDB将它的表和索引放在一个逻辑表空间中,表空间可以包含数个文件(或原始磁盘文件)。这与MyISAM表不同,比如在MyISAM表中每个表被存放在分离的文件中。
    4.InnoDB支持外键完整性约束,存储表中的数据时,每张表的存储都按主键顺序存放,如果没有显示在表定义时指定主键,InnoDB会为每一行生成一个6字节的ROWID,并以此作为主键。
    InnoDB不创建目录,使用InnoDB时,MySQL将在MySQL数据目录下创建一个名为ibdata1的10MB大小的自动扩展数据文件,以及两个名为ib_logfile0和ib_logfile1的5MB大小的日志文件。对于InnoDB的使用场景,由于其支持事务处理,支持外键,支持崩溃修复能力和并发控制。如果需要对事务的完整性要求比较高(比如银行),要求实现并发控制(比如售票),那选择InnoDB有很大的优势。如果需要频繁的更新、删除操作的数据库,也可以选择InnoDB,因为支持事务的提交(commit)和回滚(rollback)。
  (2)MyISAM基于ISAM存储引擎,并对其进行扩展。它是在Web、数据仓储和其他应用环境下最常使用的存储引擎之一。MyISAM拥有较高的插入、查询速度,但不支持事物和外键。MyISAM主要特性包括:
    1.在支持大文件的文件系统和操作系统上被支持。
    2.当把删除和更新及插入操作混合使用的时候,动态尺寸的行产生更少碎片。这要通过合并相邻被删除的块,以及若下一个块被删除,就扩展到下一块自动完成。
    3.每个MyISAM表最大索引数是64,这可以通过重新编译来改变。每个索引最大的列数是16,最大的键长度是1000字节,这也可以通过编译来改变,对于键长度超过250字节的情况,一个超过1024字节的键将被用上。
    4.BLOB和TEXT列可以被索引,支持FULLTEXT类型的索引,而InnoDB不支持这种类型的索引。NULL被允许在索引的列中,这个值占每个键的0~1个字节。所有数字键值以高字节优先被存储以允许一个更高的索引压缩。
    5.每个MyISAM类型的表都有一个AUTO_INCREMENT的内部列,当INSERT和UPDATE操作的时候该列被更新,同时AUTO_INCREMENT列将被刷新。所以说,MyISAM类型表的AUTO_INCREMENT列更新比InnoDB类型的AUTO_INCREMENT更快
    6.可以把数据文件和索引文件放在不同目录。
    MyISAM默认使用静态表的存储格式,其字段都是非变长的。存储非常迅速、容易缓存,出现故障容易恢复,但是占用空间通常比动态表多。动态表就是占用的空间相对较少,但是频繁的更新删除记录会产生碎片,需要定期执行optimize table或myisamchk -r命令来改善性能,而且出现故障的时候恢复比较困难。还有压缩表指使用myisampack工具创建,占用非常小的磁盘空间。因为每个记录是被单独压缩的,所以只有非常小的访问开支。使用MyISAM引擎创建数据库,将产生3个文件。文件的名字以表名字开始,扩展名之处文件类型:frm文件存储表定义、数据文件的扩展名为.MYD(MYData)、索引文件的扩展名时.MYI(MYIndex)。MyISAM对于主要用于插入新记录和读出记录的使用场景适用,能实现处理高效率。

\(\color{DarkOliveGreen}{6、数据库范式?}\)
  (1)第一范式(1NF)强调的是列的原子性,即列不能够再分成其它几列;
  (2)第二范式(2NF)首先满足1NF,另外包含两部分内容。一是表必须有一个主键。二是没有包含在主键中的列必须完全依赖于主键,而不能只依赖于主键的一部分;
  (3)第三范式(3NF)满足2NF的特性,另外非主键列必须直接依赖于主键,不能存在传递依赖;
  (4)鲍依斯-科得范式(BCNF)满足3NF,且消除主属性间的部分函数依赖和传递依赖。

五、WEB知识点

\(\color{OrangeRed}{1、Spring的七个事务传播级别?}\)
  (1)PROPAGATION_REQUIRED:如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务;
  (2)PROPAGATION_SUPPORTS:如果存在一个事务,支持当前事务。如果没有事务,则非事务的执行。但是对于事务同步的事务管理器,PROPAGATION_SUPPORTS与不使用事务有少许不同;
  (3)PROPAGATION_MANDATORY:如果已经存在一个事务,支持当前事务。如果没有一个活动的事务,则抛出异常;
  (4)PROPAGATION_REQUIRES_NEW:总是开启一个新的事务。如果一个事务已经存在,则将这个存在的事务挂起。(即新事务不在该事务的执行下受控)
  (5)PROPAGATION_NOT_SUPPORTED:总是非事务地执行,并挂起任何存在的事务。
  (6)PROPAGATION_NEVER:总是非事务地执行,如果存在一个活动事务,则抛出异常;
  (7)PROPAGATION_NESTED:如果一个活动的事务存在,则运行在一个嵌套的事务中。如果没有活动事务,则按TransactionDefinition.PROPAGATION_REQUIRED属性执行。

\(\color{OrangeRed}{2、web会话管理的四种方式?}\)
  (1)基于server端session的管理方式:
    比较安全,sessionid串足够随机,不容易被冒充;
    这种方式将会话信息存储在web服务器里面,所以在用户同时在线量比较多时,这些会话信息会占据比较多的内存;
    当应用采用集群部署的时候,会遇到多台web服务器之间如何做session共享的问题;
    需要在各个应用做好cookie跨域的处理。
  (2)cookie-base的管理方式:
    服务端无状态化。用户信息共享问题处理简易;
    cookie有大小限制,每次传送cookie,增加了请求的数量,对访问性能也有影响。且也有跨域问题。
  (3)token-based的管理方式:
    JWT(json-web-token)标准。可以应用在native app跟web的api之间的传输;
    (解决自动刷新问题)可以在验证ticket或token有效之后,自动把ticket或token的失效时间延长,然后把它再返回给客户端。
  (4)url重写:
    可以在浏览器不支持cookie的情况下使用;
    url重写是通过向url连接添加参数,并把session id作为值包含在连接中。

\(\color{OrangeRed}{3、同步、异步和阻塞、非阻塞IO区别?}\)
  同步、异步是描述被调用方的。阻塞、非阻塞是描述调用方的。
  (1)同步和异步:
    同步和异步关注的是消息通信机制;
    同步就是在发出一个调用时,在没有得到结果之前,该调用就不返回。但是一旦调用返回,就得到返回值了。由调用者主动等待这个调用的结果。
    异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果。换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果。而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用。
  (2)阻塞和非阻塞关注的是程序在等待调用结果(消息,返回值)时的状态:
    阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
    非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

\(\color{OrangeRed}{4、spring中bean的作用域?}\)
  singleton:
    Spring IoC容器中只会存在一个共享的Bean实例,无论有多少个Bean引用它,始终指向同一对象。Singleton作用域是Spring中的缺省作用域。
  prototype:
    每次通过Spring容器获取prototype定义的bean时,容器都将创建一个新的Bean实例,每个Bean实例都有自己的属性和状态,而singleton全局只有一个对象。
  request:
    在一次Http请求中,容器会返回该Bean的同一实例。而对不同的Http请求则会产生新的Bean,而且该bean仅在当前Http Request内有效。
  session:
    在一次Http Session中,容器会返回该Bean的同一实例。而对不同的Session请求则会创建新的实例,该bean实例仅在当前Session内有效。
  global Session:
    在一个全局的Http Session中,容器会返回该Bean的同一个实例,仅在使用portlet context时有效。

\(\color{OrangeRed}{5、spring中bean的生命周期?}\)
  实例化一个Bean,也就是我们通常说的new ——> 按照Spring上下文对实例化的Bean进行配置,也就是IOC注入 ——> 如果这个Bean实现了BeanNameAware接口,会调用它实现的setBeanName(String beanId)方法,此处传递的是Spring配置文件中Bean的ID ——> 如果这个Bean实现了BeanFactoryAware接口,会调用它实现的setBeanFactory(),传递的是Spring工厂本身(可以用这个方法获取到其他Bean) ——> 如果这个Bean实现了ApplicationContextAware接口,会调用setApplicationContext(ApplicationContext)方法,传入Spring上下文 ——> 如果这个Bean关联了BeanPostProcessor接口,将会调用postProcessBeforeInitialization(Object obj, String s)方法,BeanPostProcessor经常被用作是Bean内容的更改,并且由于这个是在Bean初始化结束时调用After方法,也可用于内存或缓存技术 ——> 如果这个Bean在Spring配置文件中配置了init-method属性会自动调用其配置的初始化方法。 ——> 如果这个Bean关联了BeanPostProcessor接口,将会调用postAfterInitialization(Object obj, String s)方法 ——> 当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean接口,会调用其实现的destroy方法 ——> 最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

\(\color{OrangeRed}{5、对spring依赖注入两种方式的认识?}\)
  分别是构造方法注入设值注入
  (1)设值注入与传统的JavaBean的写法更相似,程序员更容易理解、接受,通过setter方式设定依赖关系显得更加直观、明显。
  (2)对于复杂的依赖关系,如果采用构造注入,会导致构造器过于臃肿,难以阅读。Spring在创建Bean实例时,需要同时实例化其依赖的全部实例,因而会产生浪费。而使用设值注入,则避免这下问题。
  (3)在某些属性可选的情况下,多参数的构造器更加笨拙,官方更鼓励使用设值注入。
  (4)构造注入可以在构造器中决定依赖关系的注入顺序,优先依赖的优先注入。
  (5)对于依赖关系无须变化的Bean,构造注入更有用处,因为没有setter方法,所有的依赖关系全部在构造器内设定,因此,不用担心后续代码对依赖关系的破坏
  (6)构造注入使依赖关系只能在构造器中设定,则只有组件的创建者才能改变组件的依赖关系。对组件的调用者而言,组件内部的依赖关系完全透明,更符合高内聚的原则。
  (7)设值注入不会重写构造方法的值。如果我们对同一个变量同时使用了构造方法注入又使用了设置方法注入的话,那么构造方法将不能覆盖由设值方法注入的值。
  (8)建议采用以设值注入为主,构造注入为辅的注入策略。对于依赖关系无须变化的注入,尽量采用构造注入;而其他的依赖关系的注入,则考虑采用设值注入。

\(\color{OrangeRed}{5、spring框架中都用到了哪些设计模式?}\)
  代理模式:在AOP和remoting中被用的比较多。
  单例模式:在spring配置文件中定义的bean默认为单例模式。
  模板方法模式:用来解决代码重复的问题。
  前端控制器模式:Spring提供了DispatcherServlet来对请求进行分发。
  依赖注入模式:贯穿于BeanFactory / ApplicationContext接口的核心理念。
  工厂模式:BeanFactory用来创建对象的实例。

\(\color{OrangeRed}{6、BeanFactory和ApplicationContext的区别?}\)
  BeanFactory和ApplicationContext都是接口,并且ApplicationContext是BeanFactory的子接口。
  BeanFactory是Spring中最底层的接口,提供了最简单的容器的功能,只提供了实例化对象和拿对象的功能。而ApplicationContext是Spring的一个更高级的容器,提供了更多的有用的功能。
  ApplicationContext提供的额外的功能:国际化的功能、消息发送、响应机制、统一加载资源的功能、强大的事件机制、对Web应用的支持等等
  加载方式的区别:BeanFactory采用的是延迟加载的形式来注入Bean;ApplicationContext则相反的,它是在Ioc启动时就一次性创建所有的Bean,好处是可以马上发现Spring配置文件中的错误,坏处是造成浪费

六、程序知识点

\(\color{cyan-blue}{1、什么是可重入函数和不可重入函数?}\)
  (1)可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。因此可重入函数内要么只使用局部变量,要么使用全局变量时予以保护。
  (2)对于不可重入函数,当不同任务调用这个函数时可能修改其他任务调用这个函数的数据,从而导致不可预料的后果。所以不可重入函数在实时任务系统中又被视为不安全函数。另外不可重入函数由于使用了一些系统资源,如全局变量、中断向量表等,所以如果被中断,可能出现问题。因此把不可重入函数改为可重入函数就可以用可重入规则重写。如不要使用全局变量,因为别的代码很可能覆盖这些变量值。和硬件发生交互的时候,切记执行类似disinterrupt()之类的操作,就是关闭硬件中断。完成交互记得打开中断,在有些系列上,这叫做进入/退出核心或者用OS_ENTER_KERNAL/OS_EXIT_KERNAL来描述(临界区保护)。内部不能调用任何不可重入的函数。谨慎使用堆栈。最好在使用前先OS_ENTER_KERNA。总的来说就是保证中断的安全。

\(\color{cyan-blue}{2、编译型语言和解释型语言的区别?}\)
  (1)编译型语言先要进行编译,然后转为特定的可执行文件,这个可执行文件是针对平台的。而需要运行代码时,不需要重新编译代码,只需要运行该可执行的二进制文件。即一次编译,永久执行。但是对于不同的平台需要重新编译生成新的可执行文件,即跨平台能力差。
  (2)解释型语言需要一个解释器,在源代码执行的时候被解释器翻译为一个与平台无关的中间代码,因此解释型语言没执行一次就要被解释器翻译因此。所以解释型语言可以跨平台执行,但是运行时需要源代码,知识产权保护性差,运行效率低。

\(\color{cyan-blue}{3、编译型语言源代码生成可执行文件的过程?}\)
  (1)首先是预处理,在正式的编译阶段之前进行。根据已放置在文件中的预处理指令来修改源文件的内容,包括根据预处理指令#include进行头文件内容的添加,为编译之前修改源文件的方式提供了很大的灵活性,以适应不同计算机和操作系统环境的限制。还有宏定义指令的处理、条件编译指令处理、特殊符号的处理等。
  (2)预处理后便是编译和优化阶段,经过预编译得到的输出文件中只有常量,如数字、字符串、变量的定义以及一些关键字。编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。优化一部分是对中间代码的优化,另一种优化则主要针对目标代码的生成而进行的。
  (3)之后就是汇编过程,汇编实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码,目标文件由段组成,包含代码段、数据段。在UNIX环境下主要有三种类型的目标文件分别是可重定位文件、共享的目标文件和可执行文件。
  (4)最后便是链接过程指由汇编程序生成的目标文件并不能立即就被执行。如某个源文件中的函数可能引用了另一个源文件中定义的某个符号,在程序中可能调用了某个库文件中的函数。因此链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。通常链接处理又可分为静态链接和动态链接,静态链接时函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中,这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。动态链接时函数的代码被放到称作是动态链接库或共享对象的某个目标文件中,链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

posted @ 2024-07-02 23:41  Idempotent  阅读(1)  评论(0编辑  收藏  举报