Java面试

Java基础

JDK,JRE ,JVM 分别是什么,有什么区别?

从小到大的范围开始说:
jvm英文名字(Java virtual machine)中文名字(Java虚拟机)
首先我们先了解虚拟机,虚拟机是在计算机上模仿计算机各种功能。其本意就是我在计算机里面创建了一个计算机,而jvm是运行Java程序的抽象计算机,运行Java代码的假想计算机,负责将Java文件编译成字节码文件,并将字节码文件翻译成机器码(相当于Java程序和操作系统的中间人)
jre(JAVA Runtime Environment):可以让计算机系统运行的Java的应用程序,换句话说就是可以运行Java的一个软件。
jdk(Java developm kit):既包括Java运行环境(jvm),也包括编译器javac等开发工具和Java标准库。
总体来说表示范围为:JDK>JRE>JVM

什么是字节码?采用字节码的最大好处是什么?

先了解机器码,相对计算机而言,机器码是用于指挥计算机做出操作和操作数地址的二进制数,直接而言就是机器码和机器打交道。
字节码:字节码是由Java程序通过编译后生成的一种代码,这种代码可以被JVM理解,文件后扩展名为(.class)这种代码称之为字节码。
字节码最大的好处:字节码文件由虚拟机执行,虽然操作系统或平台不同,但其实现的虚拟机是相同的,所以使用字节码可以让java程序无需编译就在多种不同的计算机上运行
在这里插入图片描述

Java 和 C++、Go 语言的区别,各自的优缺点?
嵌入式系统:硬件和软件组成.是能够独立进行运作的器件。其软件内容只包括软件运行环境及其操作系统。
操作系统:一组主管并控制计算机操作、运用和运行硬件、软件资源和提供公共服务来组织用户交互的相互关联的系统软件程序。

Java 是一种面向对象的编程语言,最初被设计用于开发嵌入式系统,现在已广泛应用于企业应用、Web 应用和移动应用开发等领域。
优点:跨平台性强,安全性高,生态系统完备

系统软件:控制和协调计算机及外部设备,支持应用软件开发和运行的系统,是无需用户干预的各种程序的集合,
主要功能是调度,监控和维护计算机系统;负责管理计算机系统中各种独立的硬件。
应用软件(Application)是和系统软件相对应的,是用户可以使用的各种程序设计,以及用各种程序设计语言编制的应用程序的集合。

C++ 是一种系统级编程语言,最初被设计用于操作系统和系统软件的开发,现在也广泛应用于游戏、图形图像处理、嵌入式系统等领域。
优点:性能高,控制能力强,应用领域广泛
Go 是一种开源的编程语言,由 Google 公司开发,被设计用于开发高并发、分布式系统。
优点:并发能力强,内存管理高效,语言特性简单
不同的编程语言适用于不同的应用场景,开发者可以根据项目的需求、自己的经验和偏好选择合适的语言。

Java中final关键字有什么用?

final可以修饰类,方法,变量。
(1)修饰类:表示该类不能被继承;
(2)修饰方法:表示方法不能被重写;
(3)修饰变量:表示变量只能一次赋值以后值不能被修改(常量)。

Java 访问修饰符 public、private、protected,以及无修饰符(默认)的区别

JDK 动态代理和 CGLIB 动态代理的区别是什么?

学完Spring基本原理会好点
JDK的动态代理是需要接口的(被代理类需要实现接口),接口中写着被代理类能做到的事情,代理类代理执行方法是需要知道被代理类能做到事情,那么就将被代理的接口传入代理类即可,JDK的动态代理可以使用Proxy.newProxyInstance()方法获取代理类,其方法必须传入被代理类的接口。
CGLIB动态代理的被代理类是不需要实现接口的,其使用cglib的Enhancer增强器,向增强其器中设置需要增强的类(被代理类)和回调即可生成代理类对象,这个代理类对象是继承于被代理类的,从而指导被代理类能做的事情。
1.底层实现方式不同:JDK 动态代理是通过反射实现的,而CGLib采用了底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。其中采用的是继承,所以不能对final修饰的类进行代理。
2.被代理类的不同:JDK 动态代理是基于接口实现的,被代理类对象必须有实现接口;当被代理类对象没有实现接口,使用CGLIB动态代理,用CGLIB动态代理是基于继承实现的。
3.创建对象的耗时不同:CGLib创建的动态代理对象比JDK创建的动态代理对象快,但是CGLIB创建代理对象比JDK动态代理创建对象慢很多,因此对于单例对象这种不需要频繁创建对象的情形而言用CGLIB更好。
为了便于理解,可以参考这篇博客:
对于动态代理和CGLIB代理

什么是反射机制?说说反射机制的优缺点、应用场景?

Java 反射机制是指在运行时动态地获取类的信息、创建对象以及调用对象的属性和方法的机制。Java 反射机制提供了运行时检查 Java 类型信息的能力,让 Java 程序可以通过程序获取其本身的信息。
反射机制(properties)
Java 反射机制的优点:
● 可以动态地获取类的信息,不需要在编译时就知道类的信息。
● 可以动态地创建对象,不需要在编译时就知道对象的类型。
● 可以动态地调用对象的属性和方法,可以在运行时动态地改变对象的行为。
Java 反射机制的缺点:
● 由于反射是动态的,所以它的运行效率较低,不如直接调用方法或属性。
● 由于反射是动态的,所以它会破坏 Java 的封装性,可能会使代码变得复杂和不稳定。
应用场景:
● 动态代理。动态代理可以使用反射机制在运行时动态地创建代理对象,而不需要在编译时就知道接口的实现类。
● 单元测试。JUnit 等单元测试框架可以使用反射机制在运行时动态地获取类和方法的信息,实现自动化测试。
● 配置文件加载。许多框架(如 Spring)使用反射机制来读取和解析配置文件,从而实现依赖注入和面向切面编程等功能。

Java 中 hashCode 和 equals 方法是什么?它们和 == 各有什么区别?

在这里插入图片描述

什么是消息队列?消息队列有哪些应用场景?

消息队列是一种异步通信机制,用于在应用程序之间传递消息。它可以将消息暂时存储在队列中,然后按照一定的顺序和条件将消息传递给消费者。总之,消息队列是一种非常重要的中间件技术,可以在各种场景下发挥作用,提高系统的可靠性、性能和扩展性。
1.异步处理:将请求放入消息队列,由消费者异步处理,提高系统的吞吐量和并发能力。
2.解耦合:消息队列可以解耦合应用程序中不同模块之间的依赖关系,每个模块可以独立进行开发和维护。
3.缓存:消息队列可以作为缓存层,将数据缓存到消息队列中,提高数据读取的速度和性能。
4.流量削峰:将大量的请求放入消息队列中,然后通过消费者进行处理,可以有效地削峰填谷,保证系统的稳定性。

并发和并行有什么区别?同步和异步有什么区别?

并发(Concurrency):指的是系统中同时存在多个正在执行的任务,并且这些任务之间可能会相互影响。并发通常用来处理多个任务共享资源的情况。在单核 CPU 上,多个任务会轮流使用 CPU 时间片,表现为看似同时执行的情况,但实际上只有一个任务正在执行。
并行(Parallelism):指的是系统中同时存在多个并且相互独立的任务,并且这些任务可以在多个处理器上同时执行,真正意义上的同时处理多个任务。
同步(Synchronous):指的是程序按照代码的顺序执行,一行一行地执行,直到当前行执行完成后才能继续执行下一行。同步通常会阻塞调用者,直到任务完成才能返回。
异步(Asynchronous):指的是程序在执行某个任务时,不会一直等待任务完成,而是继续执行下一行代码,当任务完成后再进行相应的处理。异步通常不会阻塞调用者,可以提高系统的并发性能。
总的来说,"并发"和"并行"是针对多个任务的执行方式,"同步"和"异步"是针对任务执行的阻塞方式和返回方式。在实际应用中,可以根据不同的需求来选择合适的并发和同步方式,以提高系统的性能和可靠性。

String 和 StringBuffer、StringBuilder 的区别是什么?

String 和 StringBuffer/StringBuilder 是 Java 中两种不同的字符串处理方式,主要的区别在于 String 是不可变的(immutable)对象,而 StringBuffer 和 StringBuilder 则是可变的(mutable)对象。
String 对象一旦被创建,就不可修改,任何的字符串操作都会返回一个新的 String 对象,这可能导致频繁的对象创建和销毁,影响性能。而 StringBuffer 和 StringBuilder 允许进行修改操作,提供了一种更高效的字符串处理方式。
StringBuffer 和 StringBuilder 的主要区别在于线程安全性和性能方面。StringBuffer 是线程安全的,所有方法都是同步的,因此可以被多个线程同时访问和修改。而 StringBuilder 不是线程安全的,适用于单线程环境下的字符串处理,但是相比于 StringBuffer,StringBuilder 具有更高的性能。
因此,当字符串处理需要频繁修改时,建议使用 StringBuffer 或 StringBuilder;而当字符串处理不需要修改时,可以使用 String。

什么是 BIO、NIO、AIO?

BIO、NIO、AIO 都是 Java 中关于网络编程的不同实现方式,它们的主要区别在于 IO 操作的方式和实现方式。
BIO(Blocking IO):同步阻塞 IO,指在 IO 操作时,必须等待 IO 完成才能进行下一步操作。在服务器端,每个连接都需要独立的线程完成数据的读取和处理,当连接数较多时,会导致系统资源消耗严重,且性能下降。在客户端,如果连接远程服务器时,服务器响应时间过长,客户端会一直等待,造成阻塞。
NIO(Non-Blocking IO):同步非阻塞 IO,指在 IO 操作时,不需要等待 IO 完成才能进行下一步操作,而是通过轮询机制来实现 IO 操作的异步执行。在服务器端,可以使用一个线程处理多个连接的数据读写,大大降低了系统资源的消耗,提高了系统的扩展性和性能。在客户端,连接远程服务器时,如果服务器响应时间过长,客户端可以进行其他操作,不会造成阻塞。
AIO(Asynchronous IO):异步非阻塞 IO,指在 IO 操作时,无需等待 IO 完成才能进行下一步操作,而是由操作系统内核完成 IO 操作,当操作完成后,操作系统会通知应用程序。在服务器端,AIO 的性能较 NIO 更高,但 AIO 目前仅支持 Linux 系统,而且 API 更加复杂,使用较少。

Ngnix 是什么?它有哪些应用场景?

Nginx(发音为“engine-x”)是一个高性能的开源Web服务器和反向代理服务器,可以处理大量的并发连接和请求。它使用事件驱动的异步架构和多线程设计,可以高效地处理并发请求,同时也支持反向代理、负载均衡、动态HTTP缓存、SSL/TLS终止、基于模块的扩展等功能。
Nginx 的应用场景非常广泛,以下是其中的几个:
Web 服务器:Nginx 可以作为 HTTP 服务器,处理并发的静态请求和动态请求,并且可以支持负载均衡和缓存,为网站提供高性能和高可用性。
反向代理服务器:Nginx 可以作为反向代理服务器,接收客户端请求并将其转发到后端服务器,同时也可以支持负载均衡和缓存。
邮件代理服务器:Nginx 可以作为邮件代理服务器,支持 POP3、IMAP 和 SMTP 协议,并且可以支持 SSL/TLS 加密和反垃圾邮件功能。
流媒体服务器:Nginx 可以作为流媒体服务器,支持 RTMP、HLS 和 DASH 协议,可以用于实现直播、点播和视频-on-demand(VoD)等场景。
总之,Nginx 具有高性能、可扩展性和可靠性等特点,被广泛应用于大型网站、互联网公司、云计算、视频流媒体、游戏等领域。

synchronized 关键字是什么,有什么作用?

synchronized 是 Java 中的一个关键字,用于实现线程同步。具体来说,synchronized 用于修饰方法或代码块,使得同一时刻只能有一个线程访问被修饰的代码,其他线程需要等待当前线程执行完毕后才能访问。
synchronized 主要用于解决多线程并发访问共享资源时出现的线程安全问题。如果多个线程同时访问一个共享资源,就会出现多个线程同时修改这个资源的情况,从而导致数据不一致等问题。而使用 synchronized 可以保证同一时刻只有一个线程访问该资源,从而避免了线程安全问题。

什么是内部类? 内部类的分类有哪些 ?内部类的优点 ,内部类有哪些应用场景?

在这里插入图片描述
内部类的优点:
1.可以访问外部类的私有成员变量和方法。
2。可以隐藏实现细节。
3.便于编写和维护,提高代码的可读性和可维护性。
4.内部类可以很好地解决Java中单继承的问题。
内部类的应用场景:
1.需要访问外部类的私有成员变量和方法。
2.需要定义一个回调函数或者监听器。
3.需要实现多重继承。
4.需要对外部类进行扩展。

MySQL

什么是数据库事务?讲一下事务的 ACID 特性?

数据库事务是指由一个或多个操作所组成的逻辑工作单元,这些操作要么全部执行成功,要么全部执行失败。
事务的 ACID 特性是指:
原子性(Atomicity):事务中的所有操作要么全部执行成功,要么全部执行失败,不会出现部分执行成功和部分执行失败的情况。如果其中一个操作失败,整个事务将被回滚到之前的状态,所有操作都将被撤销。
一致性(Consistency):事务执行前后,数据库都必须处于一致的状态。这意味着,事务执行前和执行后,数据库都必须满足一些约束条件,例如数据类型、唯一性、参照完整性、触发器等等。
隔离性(Isolation):事务之间是相互隔离的,即一个事务的执行不应该受到其他事务的干扰。事务的隔离性可以通过锁机制来实现。
持久性(Durability):事务执行成功后,其结果将永久保存在数据库中,即使发生系统故障或者机器宕机,数据也不会丢失。
**作用:**这些数据保证了事务在执行过程中的正确性和可靠性,从而保证了数据的一致性和完整性。

MySQL 日志有了解过吗?binlog、redolog、undolog 分别有什么作用

MySQL 是一款流行的关系型数据库,其日志是其关键功能之一。MySQL 包括三种类型的日志,分别是 binlog、redolog 和 undolog,它们分别有不同的作用和特点。

  1. binlog ,binlog(Binary log)是 MySQL 中的二进制日志文件,用于记录 MySQL 服务器上的所有更新和修改操作。它可以记录所有的 DDL(Data Definition Language)和 DML(Data Modification Language)操作,包括对表结构的更改、数据的插入、修改、删除等等。binlog是在事务提交后生成的,因此可以用于恢复数据库。
  2. redolog ,redolog(Redo log)用于恢复数据,保证数据的一致性和持久性。当 MySQL 发生修改时,redolog 会将这些操作记录下来,并写入磁盘。这样,当 MySQL 发生宕机或崩溃时,通过重放 redolog 就可以恢复数据。
  3. undolog,undolog(Undo log)用于回滚操作。当 MySQL 发生事务回滚时,undolog 会记录这些操作并将其写入磁盘。这样,当 MySQL 需要回滚时,通过重放 undolog 就可以回滚事务。
    区别:
    binlog 和 redolog 都是 MySQL 中的二进制日志,但是它们的作用和实现方式有所不同。binlog 是 MySQL 记录所有的操作,而 redolog 则是用于保证数据的一致性和持久性。此外,binlog 是逻辑日志,redolog 是物理日志。binlog 记录的是SQL语句,而 redolog 记录的是数据页的修改,所以 binlog 可以跨平台使用,而 redolog 不能。undolog 和 redolog 的区别是,undolog 是用于回滚操作的,而 redolog 是用于恢复数据的。
数据库索引是什么,有什么作用,什么场景适合使用索引?

数据库索引就是一种数据结构,用于提高数据库表的查询效率。索引可以帮助数据库快速定位和检索存储在表中的数据,从而加快数据查询的速度。索引可以被看作是一本书的目录,它可以帮助我们快速找到所需的内容。
索引在数据库中的作用主要有:

  1. 提高查询效率。索引可以将查询的时间复杂度从O(n)降低到O(logn),从而提高查询的效率。
  2. 提高数据唯一性。索引可以保证数据库中数据的唯一性,避免数据冲突和重复。
  3. 提高数据完整性。索引可以对数据库中的数据进行约束和限制,从而保证数据的完整性和正确性。
    适合使用索引的场景包括:
  4. 数据量较大的表。对于数据量较大的表,使用索引可以提高查询效率,减少查询时的数据扫描量。
  5. 频繁进行查询的表。对于频繁进行查询的表,使用索引可以减少查询响应时间,提高系统的性能。
  6. 经常需要排序或分组的表。对于需要排序或分组的表,使用索引可以提高排序和分组的效率。
  7. 需要保证数据唯一性的表。对于需要保证数据唯一性的表,可以使用唯一索引或主键索引来实现。
  8. 经常需要进行连接查询的表。对于需要进行连接查询的表,可以使用外键索引来提高查询效率。
    需要注意的是,虽然索引可以提高查询效率,但过多的索引会占用大量的磁盘空间,同时也会降低写操作的性能,因此需要根据实际情况进行选择和优化。
你是怎么做 MySQL 数据备份的?比如怎么恢复半个月前的数据?

一、备份 MySQL 数据库
我们可以使用 mysqldump 工具来备份 MySQL 数据库,该工具可以生成 SQL 脚本文件,包含数据库中所有表和数据的语句。在终端中运行以下命令:

mysqldump -u [username] -p [database_name] > [backup_file].sql

其中,[username] 是 MySQL 用户名,[database_name] 是需要备份的数据库名称,[backup_file].sql 是备份的文件名。
该命令会将 SQL 脚本文件导出到当前目录下。
二、恢复 MySQL 数据库
如果需要恢复之前备份的数据,可以运行以下命令:

mysql -u [username] -p [database_name] < [backup_file].sql

其中,[username] 是 MySQL 用户名,[database_name] 是需要恢复的数据库名称,[backup_file].sql 是备份的文件名。
该命令会将备份文件中的 SQL 语句执行,从而将数据恢复到指定的数据库中。

一条 SQL 语句在 MySQL 中的执行过程是怎样的?

在这里插入图片描述
大体上就两层数据存储和Service层
在 MySQL 中,一条 SQL 语句的执行过程通常可以分为以下几个步骤:
1.词法分析和语法分析:MySQL 的 SQL 解析器会对输入的 SQL 语句进行词法分析和语法分析,以确定语句的结构和语法是否正确。
2.查询优化:MySQL 会对 SQL 语句进行优化,以确定最优的执行计划。在这个过程中,MySQL 会考虑许多因素,例如索引、表连接、统计信息等,以找到执行查询的最有效方式。
3.查询执行:在查询优化后,MySQL 开始执行查询,读取和处理数据。在执行过程中,MySQL 会根据查询中所涉及的表和列等信息,从磁盘中读取相应的数据,并进行计算和过滤操作。
4.结果返回:最后,MySQL 会将查询结果返回给客户端,完成整个查询过程。
需要注意的是,实际的执行过程可能会因为多种因素而不同,例如数据量、硬件配置等。另外,在并发环境下,多个查询可能会同时进行,需要使用锁和事务等机制来保证数据的一致性和正确性。

事务:在计算机术语中是指访问并可能更新数据库中各种数据项的一个程序执行单元(unit)
MySQL 中的索引是怎么实现的?B+ 树是什么,B 树和 B+ 树的区别,为什么 MySQL 要用 B+ 树?

MySQL 中的索引是通过 B+ 树实现的。B+ 树是一种多叉树,它可以将数据按照一定的顺序组织起来,从而提高查询效率。
B+ 树与 B 树的区别在于,B 树的所有数据都存储在叶子节点上,而非叶子节点只存储索引,这样可以提高数据查询效率。B+ 树的叶子节点之间使用指针相连,这样可以实现区间查找,也就是说,可以快速定位某个区间内的数据。
MySQL 之所以采用 B+ 树作为索引的实现方式,主要是因为 B+ 树具有以下优点:
1.能够支持高效的范围查找和排序。
2.叶子节点之间使用指针相连,能够支持高效的区间查询。
3.B+ 树具有较高的数据密度,可以减少磁盘 I/O 次数,提高查询效率。
4.B+ 树对于插入和删除操作也比较高效。
在 MySQL 中,B+ 树的实现主要是通过 InnoDB 存储引擎来实现的。InnoDB 存储引擎中的索引主要有聚簇索引和辅助索引两种类型,聚簇索引是根据主键创建的索引,而辅助索引是根据非主键列创建的索引。对于辅助索引,MySQL 中会同时创建一个对应的聚簇索引,这样可以提高查询效率。

MySQL 事务有哪些隔离级别、分别有什么特点,以及 MySQL 的默认隔离级别是什么?

MySQL 事务有四种隔离级别:
读未提交(Read Uncommitted):事务可以读取未提交的数据,可能会读到脏数据,会导致幻读、不可重复读、脏读等问题;
读已提交(Read Committed):只能读取已经提交的数据,可以避免脏读问题,但是可能会遇到不可重复读、幻读问题;
可重复读(Repeatable Read):保证同一个事务中多次读取同一数据的结果是一致的,避免了脏读和不可重复读问题,但是可能会遇到幻读问题;
序列化(Serializable):最高的隔离级别,可以避免所有并发问题,但是并发性能非常低,开销很大。
MySQL 的默认隔离级别是可重复读(Repeatable Read)。
其中,脏读指一个事务读到了另一个事务未提交的数据,不可重复读指同一个事务多次读取同一数据得到不同结果,幻读指同一个事务前后读取的数据集合不一致。
在实际使用中,应该根据具体情况选择合适的隔离级别,权衡数据的一致性和并发性能。
在这里插入图片描述

脏读:脏读又称无效数据的读出,脏读一般是针对于update操作的。
幻读:是指当事务不是独立执行时发生的一种现象。事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行了修改,比如这种修改涉及到表中的“全部数据行”。同时,第二个事务也修改这个表中的数据,这种修改是向表中插入“一行新数据”。
意向锁是什么?有什么作用?它是表级锁还是行级锁?

意向锁是一种 MySQL 数据库中的锁,用于表级锁协调多个行级锁的使用。在表级锁定一个表之前,MySQL 需要先获得一个意向锁,以表明要获取的锁类型(读锁或写锁),避免其他事务锁定整个表或锁定一部分表时引发死锁。
意向锁是一种轻量级锁,它不会影响其他事务的读操作,只有在某个事务要对表进行写操作时才会加上意向锁,而其他事务在读取表时只需要获取读锁,不需要等待意向锁的释放。
意向锁可以提高数据库并发性能,防止死锁的发生。它是表级锁,而不是行级锁。

MVCC 是什么?InnoDB 是如何实现 MVCC 机制的?

MVCC 是指多版本并发控制(Multiversion Concurrency Control),是一种并发控制机制,常用于数据库系统中,用于实现事务的并发控制。它允许在同一时间多个事务对同一个数据集合进行读取操作,同时防止数据不一致和其他并发问题。
InnoDB 是 MySQL 中最常用的存储引擎之一,它的 MVCC 实现是通过在每行记录中添加两个隐藏的列,分别记录行的创建时间和过期时间,以此来判断事务对该行记录的可见性。当一个事务需要读取一行记录时,InnoDB 首先读取这行记录的创建时间和过期时间,并根据这些信息判断该行记录是否可见。如果创建时间早于当前事务的开始时间,且过期时间晚于当前事务的开始时间,那么该行记录对当前事务可见。
在 InnoDB 中,MVCC 主要是通过实现以下几个机制来实现的:
事务版本号:每个事务都有一个唯一的版本号,用来标识该事务的创建时间。
读取视图:每个事务在开始时都会创建一个读取视图,记录该事务开始时间和其他信息。在事务执行期间,所有读取操作都要检查该视图,以确定读取哪些版本的数据。
undo 日志:在事务执行期间,如果对数据进行修改,那么会先将原始数据复制一份到 undo 日志中。这样,在回滚操作时就可以使用 undo 日志中的数据来还原原始数据。
快照读取:在某些情况下,事务需要读取一个数据的历史版本,而不是当前版本。这时可以使用快照读取来实现,即在读取时根据事务开始时间和 undo 日志来读取历史版本的数据。

覆盖索引和联合索引是什么?讲一下索引的最左前缀匹配原则。

覆盖索引指的是在查询语句中只需要使用到了索引列,而不需要回表查询数据页,即可以直接从索引中获取需要的数据,避免了回表操作,提高了查询效率。
联合索引指的是在一个索引中包含多个列,可以同时按照多个列进行排序,减少了排序时的磁盘 I/O 操作和排序时间,提高了查询效率。
最左前缀匹配原则
最左前缀匹配原则是指在使用联合索引时,可以按照索引中的列顺序进行查询,但只能使用最左侧的列或列的组合进行查询,不能跳过中间的列进行查询。
例如,如果索引包含 A、B、C 三列,那么可以使用 A、A+B、A+B+C 进行查询,但不能使用 B、B+C 进行查询。
最左前缀匹配原则是因为联合索引是以多列为键值建立的,而每个键值都是由多个列组成的,因此查询时只能使用最左侧的列或列的组合进行查询。这种原则提高了查询效率,避免了无效的索引扫描,但也需要根据实际情况合理地设计索引,以满足查询需求。

什么是 MySQL 执行计划?如何获取执行计划并对其进行分析?

MySQL 执行计划是 MySQL 用于执行 SQL 查询语句时所生成的一个详细计划,它描述了 MySQL 在查询过程中使用了哪些索引、如何连接表以及如何处理查询条件等信息。通过分析 MySQL 执行计划,可以确定查询语句是否有效,以及如何对查询语句进行优化,提高查询效率。
获取 MySQL 执行计划有多种方法,其中最常用的是使用 EXPLAIN 关键字。可以在查询前加上 EXPLAIN 关键字,并以 SELECT 语句的形式提交它来获取执行计划。

EXPLAIN SELECT * FROM mytable WHERE id = 100;

分析计划执行
1.检查 MySQL 是否使用了索引。如果 MySQL 不使用索引,则可能会导致慢查询。
2.确定是否存在全表扫描。全表扫描通常会导致性能下降,特别是对于大型数据集。
3.查看连接类型和连接顺序。连接过多或连接的表中没有足够的索引可能会导致性能问题。
4.检查是否存在排序、分组和聚合操作。这些操作可能会占用大量 CPU 和内存资源。

MySQL 支持哪些存储引擎?默认使用哪个?MyISAM 和 InnoDB 引擎有什么区别,如何选择?

MySQL支持多种存储引擎,包括MyISAM、InnoDB、Memory、CSV、Archive、Blackhole等等。默认情况下,MySQL使用的是InnoDB存储引擎。
MyISAM和InnoDB是MySQL中最常用的两种存储引擎,二者主要区别如下:
1.事务支持:MyISAM不支持事务,而InnoDB支持ACID事务;
2.锁机制:MyISAM采用表级锁定(table-level locking),在加锁时会锁定整个表,而InnoDB采用行级锁定(row-level locking),可以实现更好的并发性;
3.外键支持:MyISAM不支持外键,而InnoDB支持外键;
4.性能方面:在读取频繁的情况下,MyISAM比InnoDB更快,而在写入频繁的情况下,InnoDB比MyISAM更快;
5.其他特点:MyISAM支持全文索引,而InnoDB不支持;MyISAM适合于读密集型应用,如Web应用程序,而InnoDB适合于写密集型应用,如高并发的OLTP系统。
对于选择MyISAM还是InnoDB存储引擎,要根据具体应用场景来选择。如果应用程序需要频繁地进行插入、更新、删除操作,建议选择InnoDB存储引擎,因为它提供了更好的事务支持和锁机制;如果应用程序主要以查询为主,而且需要全文索引功能,建议选择MyISAM存储引擎。

Redis

什么是 Redis?Redis 有哪些特点?Redis 有哪些常见的应用场景?
NoSQL数据库:非关系型数据库,去掉关系数据库的关系型特性,数据之间无关系。MySQL:关系型数据库。

Redis是一种基于键值对的NoSQL数据库,而键值对的值是由多种数据结构和算法组成的。Redis的数据都存储于内存中,因此它的速度惊人,读写性能可达10万/秒,远超关系型数据库。

  1. Redis支持5种核心的数据类型,分别是字符串、哈希、列表、集合、有序集合;
  2. Redis还提供了Bitmap、HyperLogLog、Geo类型,但这些类型都是基于上述核心数据类型实现的;
  3. Redis在5.0新增加了Streams数据类型,它是一个功能强大的、支持多播的、可持久化的消息队列。
中间件:介于应用系统和系统软件之间的一类软件,它使用系统软件所提供的基础服务(功能),
衔接网络上应用系统的各个部分或不同的应用,能够达到资源共享、功能共享的目的。

● Redis最常用来做缓存,是实现分布式缓存的首先中间件;
● Redis可以作为数据库,实现诸如点赞、关注、排行等对性能要求极高的互联网需求;
● Redis可以作为计算工具,能用很小的代价,统计诸如PV/UV、用户在线天数等数据;
● Redis还有很多其他的使用场景,例如:可以实现分布式锁,可以作为消息队列使用

讲一下Redis 的单线程模型,IO 多路复用是什么?

Redis是一个基于内存的高性能键值数据库,其单线程模型是Redis的核心之一。Redis采用单线程模型来处理客户端请求,即一个Redis进程中只有一个线程来执行所有的请求,所有的请求都按照顺序执行。这种单线程模型的好处是可以避免多线程间的上下文切换,同时避免了多线程带来的竞争条件和死锁等问题,从而提高Redis的性能。
单线程模型的优点是可以避免多线程并发访问共享数据时的竞争和死锁问题,简化了系统的设计和调试。此外,由于 Redis 的内存访问速度非常快,因此单线程处理请求也能够保证足够的性能。
IO 多路复用是指在一个线程中同时监听多个文件描述符,一旦某个文件描述符就绪,就立即处理对应的事件。在 Redis 中,采用的是基于 epoll 的 IO 多路复用技术,可以实现高效的事件监听和响应。
在 Redis 中,客户端的请求是由一个单线程来处理的,而 IO 操作却是通过 epoll 多路复用技术实现的。这种设计方式既能充分利用 CPU 的计算能力,又能够保证足够的 IO 处理能力,从而实现了高效的键值存储服务。

Redis 基础类型中的 String 底层实现是什么?

在 Redis 中,String 是一种基本的数据类型,它可以存储字符串、整数或者浮点数等类型的数据。
String 的底层实现是一个字节数组,Redis 会根据存储的内容来判断该字符串是一个普通的字符串、整数或者浮点数,并相应地进行编码和存储。
在存储字符串时,Redis 会根据字符串的长度来选择不同的编码方式,可以选择使用普通的字符串编码,也可以使用一种叫做 intset 的紧凑编码方式,intset 可以节省空间,但是仅适用于存储较小的整数。
在存储整数或浮点数时,Redis 会将其转换成二进制格式并存储在字节数组中,这样可以节省存储空间,并且提高访问速度。
除了普通的字符串操作外,Redis 还提供了一些针对 String 类型的特殊操作,如增加或减少一个整数、获取一个子串等。

如何使用 Redis 实现一个排行榜?

使用 Redis 可以很方便地实现一个排行榜,以下是一种实现方法:
使用有序集合(Sorted Set)来存储排行榜数据,以用户得分作为分数(score),用户 ID 作为成员(member)。
当用户得分改变时,使用 Redis 的 ZADD 命令将用户的分数更新到有序集合中。
获取排行榜数据时,使用 Redis 的 ZREVRANGE 命令按分数倒序获取有序集合中的成员。
可以使用 Redis 的 ZSCORE 命令获取某个用户的分数,或使用 ZREVRANK 命令获取某个用户的排名。
以下是一个简单的 Node.js 实现:

const redis = require('redis');
const client = redis.createClient();

// 将用户得分更新到排行榜中

function updateScore(userId, score) {
  client.zadd('leaderboard', score, userId, (err, res) => {
    if (err) {
      console.error('Error updating score:', err);
    }
  });
}

// 获取排行榜数据
function getLeaderboard() {
  return new Promise((resolve, reject) => {
    client.zrevrange('leaderboard', 0, -1, 'WITHSCORES', (err, res) => {
      if (err) {
        reject(err);
      } else {
        const leaderboard = [];
        for (let i = 0; i < res.length; i += 2) {
          leaderboard.push({
            userId: res[i],
            score: parseInt(res[i + 1])
          });
        }
        resolve(leaderboard);
      }
    });
  });
}

// 获取某个用户的分数
function getScore(userId) {
  return new Promise((resolve, reject) => {
    client.zscore('leaderboard', userId, (err, res) => {
      if (err) {
        reject(err);
      } else {
        resolve(parseInt(res));
      }
    });
  });
}

// 获取某个用户的排名
function getRank(userId) {
  return new Promise((resolve, reject) => {
    client.zrevrank('leaderboard', userId, (err, res) => {
      if (err) {
        reject(err);
      } else {
        resolve(res === null ? null : res + 1);
      }
    });
  });
}

// 示例代码
updateScore('user1', 100);
updateScore('user2', 200);
updateScore('user3', 300);

getLeaderboard().then((leaderboard) => {
  console.log('Leaderboard:', leaderboard);
});

getScore('user1').then((score) => {
  console.log('User1 score:', score);
});

getRank('user1').then((rank) => {
  console.log('User1 rank:', rank);
});

在实际应用中,还可以根据需要进行排行榜数据的缓存和更新策略等优化。

Redis 的持久化机制有哪些?说说各自的优缺点和应用场景?

Redis 的持久化机制有两种,分别是 RDB(Redis DataBase)和 AOF(Append-Only File)。
RDB:RDB 是一种快照持久化方式,可以将 Redis 内存中的数据定期保存到磁盘上。RDB 机制是通过将 Redis 内存中的数据按照指定格式转换成二进制并保存到磁盘上,以便在 Redis 重启后重新加载。RDB 机制的优点是快速、高效,可以在 Redis 需要备份或迁移数据时使用,对于大规模数据的恢复来说也比较快速。其缺点是,由于是定期持久化,所以如果 Redis 宕机或意外关闭,就可能会丢失最近一次快照之后的所有数据。
AOF:AOF 是一种增量持久化方式,可以将 Redis 执行的所有写命令保存到一个追加文件中。AOF 持久化机制是通过将 Redis 内存中执行的写命令追加到 AOF 文件中,以便在 Redis 重启后重新执行。AOF 机制的优点是安全、可靠,可以保证 Redis 重启后数据的完整性,能够实现较小的数据损失。其缺点是,由于是追加写入,所以 AOF 文件会变得很大,如果不定期进行压缩和重写操作,会占用大量磁盘空间,并且对 Redis 的性能会产生一定的影响。
选择 RDB 还是 AOF,主要取决于对数据完整性、性能和存储空间的需求:
对于数据完整性要求较高的场景,建议使用 AOF,因为它可以最大程度地保证数据的完整性。
对于性能要求较高的场景,建议使用 RDB,因为它可以快速备份和恢复大量数据。
对于存储空间要求较高的场景,建议使用 RDB,因为它占用的磁盘空间比 AOF 小。
在实际应用中,通常可以结合使用 RDB 和 AOF 两种持久化方式,以充分利用它们的优点,提高系统的可靠性和性能。例如,可以使用 AOF 持久化方式保证数据的完整性,同时使用 RDB 定期进行备份和恢复,以提高系统的性能和可靠性。

如何用 Redis 实现分布式 Session?

在分布式系统中,通常会将 Session 存储在 Redis 中来实现分布式 Session,这样就可以在多台服务器之间共享 Session 数据。实现分布式 Session 的方式有多种,其中一种常用的方式是使用 Redis 的数据结构 Hash。具体实现步骤如下:
1.在用户登录成功后,将 Session 数据存储在 Redis 中。
2.将 Redis 中的 Session 数据的 Key 设置为一个全局唯一的 ID,一般使用类似于“session:token”这样的格式,其中 token 是一个随机生成的字符串,用来标识这个 Session 数据。
3.在客户端返回响应的同时,将 Session ID(即 token)以 Cookie 的形式返回给客户端。客户端在后续的请求中都会携带这个 Cookie。
4.在后续的请求中,服务器会从客户端传递过来的 Cookie 中获取 Session ID,然后根据这个 ID 从 Redis 中获取对应的 Session 数据。如果 Redis 中没有找到对应的 Session 数据,那么就表示这个请求无法通过认证。
5.在用户退出登录或 Session 失效时,需要将 Redis 中的对应 Session 数据删除。
6.可以使用 Redis 的 EXPIRE 命令来设置 Session 数据的过期时间,这样可以自动删除已经过期的 Session 数据。同时,还需要注意保护 Redis 中的 Session 数据不被恶意攻击者窃取,一般可以通过设置 Session 数据的前缀和使用随机的 Session ID 等方式来提高安全性。

讲一下 Redis 中的内存淘汰机制、有哪些内存淘汰策略?

Redis 是一种基于内存的键值数据库,由于内存有限,当 Redis 占用的内存达到上限时,就需要进行内存淘汰,以腾出一些内存空间。
Redis 中的内存淘汰机制包括:
1.定期删除:Redis 可以设置一个定时器,定期扫描键空间中的键,并删除已经过期的键。
2.惰性删除:当一个键过期时,Redis 不会立即删除该键,而是等到该键被访问时再删除。
3.内存淘汰策略:当 Redis 内存占用达到上限时,会根据内存淘汰策略来选择一些键进行删除,以腾出更多的内存空间。
Redis 中的内存淘汰策略包括:
noeviction:禁止删除键,即不做任何操作。
allkeys-lru:从所有的键中选择最近最少使用的键进行删除。
allkeys-random:从所有的键中随机选择一些键进行删除。
volatile-lru:从已设置过期时间的键中选择最近最少使用的键进行删除。
volatile-random:从已设置过期时间的键中随机选择一些键进行删除。
volatile-ttl:从已设置过期时间的键中选择剩余时间最短的键进行删除。
其中,noeviction 策略是最简单的策略,但可能会导致 Redis 内存占满,并导致 Redis 无法正常工作。其他策略则会根据不同的算法进行键的选择和删除,以尽可能地保留重要的键。
总之,Redis 中的内存淘汰机制是保证 Redis 正常运行的重要机制之一,内存淘汰策略则根据不同的场景选择合适的策略来删除不必要的键,以腾出更多的内存空间。

Redis 6.0 之后为何引入了多线程?6.0 之前为什么不使用多线程?

Redis 6.0之前的版本采用单线程架构,主要是考虑到了以下几点:
1.简单高效:单线程模型非常简单,对于简单的读写操作,无需进行多线程同步和锁竞争,因此能够获得更高的性能。
2.数据一致性:由于单线程模型不需要考虑锁竞争等复杂情况,因此可以避免数据一致性问题。
扩展性:在分布式部署场景下,采用单线程模型可以快速实现节点间的数据同步,避免了分布式锁和数据同步等问题。
然而,在大规模应用场景下,单线程模型也存在一些瓶颈。例如在处理大量短连接请求时,单线程模型的阻塞I/O机制会导致CPU利用率过低,影响系统处理能力。此外,单线程模型不能充分利用多核 CPU 的优势,导致系统整体性能上限受限。
因此,Redis 6.0引入了多线程支持,通过使用多个工作线程来提高吞吐量和并发度,并且保证数据的一致性和正确性。同时,Redis 6.0中的多线程实现采用了“每个客户端一个线程”的思路,将多个客户端的请求映射到不同的工作线程中,并且通过共享内存实现数据同步和通信,极大地提高了系统的并发能力和扩展性。

Redis 有哪些数据类型?基础数据结构有几种?你还知道哪些 Redis 的高级数据结构?

1.String字符串:字符串是二进制安全的,可以存储任何东西,512 M为上限,主要的还是操作键值对。
2.Hash散列表:Redis 的哈希值是字符串字段和字符串值之间的映射,因此它们被用来表示对象,还有用户信息之类的,经常变动的信息。
3.List链表:链表结构,可以在头部或尾部插入元素。
4.Set集合:在Set集合当中,是不允许有重复的。
5.SortedSet有序集合zset:Redis 中的一种有序集合类型,可以存储多个元素,每个元素都有一个分值,支持根据分值进行排序和查询等操作。
Redis高级的数据结构
Geospatial 地理位置
Hyperloglog 基数统计
Bitmap位图场景

Redis 为什么快?

redis是单线程处理所有的请求,避免了多线程的切换开销

如何使用 Redis 实现分布式锁?

使用Redis实现分布式锁的基本思路是使用Redis的原子性操作来确保在多个客户端之间只有一个客户端可以成功获取锁。以下是使用Redis实现分布式锁的步骤:
1.获取锁:客户端尝试获取锁,可以使用Redis的SET命令结合NX和EX选项来实现。SET命令用于设置一个键值对,NX选项表示只有在键不存在时才设置,EX选项表示设置一个过期时间(单位为秒)。 示例:SET lock_key unique_value NX EX 30。这个命令表示如果lock_key不存在,就设置它的值为unique_value,并设置30秒的过期时间。如果设置成功,表示获取锁成功;如果设置失败,表示锁已被其他客户端持有,需要等待或重试。
2.持有锁:在持有锁的客户端执行临界区代码。在此期间,其他客户端无法获取锁。
3.释放锁:在临界区代码执行完毕后,需要释放锁以供其他客户端使用。为了避免误解锁的情况(例如,由于执行时间过长导致锁过期,然后被其他客户端获取),在释放锁时需要检查锁的值是否与获取锁时设置的值相同。这可以通过Redis的EVAL命令来实现,使用Lua脚本进行原子性操作。 示例:EVAL “if redis.call(‘get’, KEYS[1]) == ARGV[1] then return redis.call(‘del’, KEYS[1]) else return 0 end” 1 lock_key unique_value。这个命令表示检查lock_key的值是否为unique_value,如果是,则删除lock_key以释放锁;否则不执行任何操作。
通过以上步骤,可以实现一个基本的Redis分布式锁。在实际应用中,还需要考虑一些其他因素,例如获取锁的重试策略、锁的续期等。

计算机网络

简述计算机网络七层模型和各自的作用?

计算机网络七层模型是一个把网络通信协议分为七个层次的标准模型,其目的是为了让计算机网络的设计和管理更加灵活和模块化。
目的:每个层次都有自己的独立功能和责任,这种分层的方式使得每个层次都可以独立工作,同时还能够很好地协调上下层之间的数据传输,而不需要依赖于其他层次的实现细节。
物理—>数据—>网络—>传输—>会话—>表示—>应用

  1. 物理层:主要负责通过物理媒介传输比特流,如电缆、光纤、无线电波等。物理层规定了物理连接的规范,包括电缆的类型、接口的规范等。
  2. 数据链路层:主要负责把数据分成数据帧进行传输,并对错误进行检测和纠正。数据链路层还负责物理地址的分配、数据流量控制、错误校验等。
  3. 网络层:主要负责数据在网络中的传输,包括路由选择、分组转发、数据报文的封装等。网络层还处理数据包的寻址和控制流量等。
  4. 传输层:主要负责数据传输的可靠性和流量控制等,同时还包括分段、组装、连接建立和断开等功能。传输层的最重要的两个协议是TCP和UDP。
    TCP(传输控制协议):是一种面向连接的,可靠的,基于字节流的传输层通信协议。
  5. 会话层:主要负责建立、管理和终止会话,提供会话控制和同步等服务。会话层还负责处理多个应用程序之间的数据交换。
  6. 表示层:主要负责数据格式转换、加密解密、压缩解压等服务。表示层使得应用程序可以使用不同的数据格式和编码,同时还提供了数据的安全性和完整性保护等服务。
  7. 应用层:主要提供各种服务和应用程序,如电子邮件、文件传输、远程登录、Web浏览等。应用层服务可以使用不同的协议实现,如HTTP、SMTP、FTP、TELNET等。
    现在比较常用的是TCP/IP模型,它只有四层:应用层、传输层、网络层和数据链路层
HTTP 是哪一层的协议?简述它的作用?

HTTP 是应用层协议,主要用于在 Web 浏览器和 Web 服务器之间传递数据。
HTTP 协议的作用是规定了 Web 应用程序中客户端和服务器之间的通信方式和数据传输格式,是支持 Web 应用程序开发的基础协议。
HTTP 协议主要作用包括:

  1. 建立连接:客户端与服务器建立 TCP 连接,然后发送 HTTP 请求,服务器接收请求并处理。
  2. 发送请求:客户端发送HTTP请求到服务器,包括请求方法(GET、POST、PUT等)、请求头(如User-Agent、Accept等)和请求正文(可选)等信息。
  3. 处理请求:服务器接收并解析 HTTP 请求,执行请求操作(如查询数据库等),并将处理结果返回给客户端。
  4. 返回响应:服务器返回HTTP响应,包括响应状态码(如200 OK、404 Not Found等)、响应头(如Content-Type、Cache-Control等)和响应正文(可选)等信息。
  5. 关闭连接:客户端接收到响应后,关闭TCP连接。
    HTTP的状态码指示了服务器对请求的处理结果。200(请求成功),301(永久重定向),404(未找到资源)和500(服务器内部错误)等等。
HTTP 有哪些常见的状态码?

200 ok
301 资源已永久重定向
302 资源临时重定向
400 请求报文语法错误
401 请求未被认证(未登录)
403 请求的资源被拒绝
404 资源不存在
500 服务器内部出现问题
503 服务器已停机,无法响应

TCP 和 UDP 协议有什么区别,分别适用于什么场景?

1.连接性:TCP 是一种面向连接的协议,UDP 是一种非连接性的协议。在使用 TCP 协议进行通信时,发送数据前需要先建立连接,而 UDP 则不需要。
2.可靠性:TCP 提供可靠的数据传输服务,通过重传机制、校验和等手段可以保证数据传输的完整性和正确性。而 UDP 则不提供这样的服务,因此在传输过程中可能会出现数据丢失或者乱序。
3.速度:由于 TCP 需要进行连接的建立和数据传输的校验等工作,因此比 UDP 慢一些。而 UDP 不需要进行这些操作,因此传输速度更快。
根据以上特点,TCP 适用于要求数据传输可靠、稳定的场景,例如文件传输、邮件和网页浏览等;而 UDP 适用于要求实时性较高、速度较快的场景,例如音视频传输、游戏等。

HTTP 协议中 GET 和 POST 有什么区别?分别适用于什么场景?

HTTP协议中GET和POST是两种不同的请求方法,主要区别如下:
GET请求:浏览器向服务器请求在URL指定位置获取资源。GET请求使用URL传递参数,参数会被附加在URL后面,以问号(?)分隔参数和URL。例如:http://www.example.com/index.html?name=value。GET请求的数据量不能超过URL的最大长度,一般不超过2KB。
POST请求:通常用于向服务器提交表单数据,将用户输入的数据放在HTTP请求体中进行传输。POST请求没有数据大小限制,且比GET请求更为安全。
适用场景:
GET请求:适用于获取数据操作,例如查询文字信息、搜索结果、商品列表等。由于GET请求参数会被包含在URL中,因此可以通过浏览器的回退功能来重新执行请求,也可以被缓存起来,这对于需要频繁读取不变的资源来说非常有利。
POST请求:适用于向服务器提交数据的操作,例如注册、登录、评论、发帖等,或者上传文件等数据量较大的操作。由于POST请求将数据放在请求体中,因此安全性更高,也更适合传输大量数据。
总结:GET和POST是HTTP协议中的两种常见请求方法,GET请求适用于查询数据等读取操作,而POST请求适用于向服务器提交数据的操作,如新增、修改、删除等写入操作。

简述 TCP 三次握手、四次挥手的流程?为什么需要三次握手?为什么需要四次挥手?

TCP(Transmission Control Protocol)是一种面向连接的协议,为了保证数据传输的可靠性,TCP 使用了三次握手和四次挥手的过程。
三次握手的过程如下:
第一次握手:客户端向服务器发送 SYN 报文,请求建立连接。
第二次握手:服务器收到客户端的 SYN 报文,向客户端发送 SYN+ACK 报文,表示可以建立连接。
第三次握手:客户端收到服务器的 SYN+ACK 报文,向服务器发送 ACK 报文,表示连接已经建立。
为什么需要三次握手?
三次握手的目的是为了确认双方的收发能力和同步初始序列号。
四次挥手的过程如下:
第一次挥手:客户端向服务器发送 FIN 报文,请求关闭连接。
第二次挥手:服务器收到客户端的 FIN 报文,向客户端发送 ACK 报文,表示收到关闭请求。
第三次挥手:服务器向客户端发送 FIN 报文,请求关闭连接。
第四次挥手:客户端收到服务器的 FIN 报文,向服务器发送 ACK 报文,表示收到关闭请求。
为什么需要四次挥手?
四次挥手的目的是为了保证数据的完整性和可靠性。在关闭连接之前,双方需要确保所有数据都已经传输完毕,因此需要通过四次挥手的过程进行确认和处理。
总结:三次握手的本质是确认通信双方收发数据的能力 ,四次挥手的目的是关闭一个连接。

操作系统

什么是进程和线程?它们有哪些区别和联系?

在操作系统中,进程是指一个正在执行中的程序,而线程是进程的一部分,是一个程序中执行的代码片段。
进程是操作系统资源分配的最小单位,一个进程至少包括一个线程,进程拥有自己的内存空间、文件句柄、环境变量等系统资源。进程间相互独立,互不干扰,每个进程都拥有自己的地址空间。进程通信需要通过进程间通信机制(IPC)来实现。
线程是程序执行的最小单位,一个进程中可以包含多个线程,它们共享进程的内存空间和系统资源。多个线程可以并发执行,从而提高了程序的运行效率,同时也会带来线程安全等问题。线程之间的通信可以通过共享内存、信号量等机制实现。
有一个很形象的比喻,进程是一列火车,线程是每个车厢;
进程比线程范围大
● 资源分配:进程拥有自己的内存空间等系统资源,而线程共享进程的资源;
● 独立性:进程之间相互独立,互不干扰,而线程是进程的一部分,线程之间共享进程的资源;
● 调度:进程间调度的开销比线程大,线程的调度开销小,可以并发执行;
● 并发性:多个进程之间相互独立,多个线程可以并发执行;
● 同步:进程间通信需要通过IPC机制,线程间同步可以通过共享内存、信号量等机制实现。

线程间有哪些通信方式?

1.共享内存
多个线程共享同一块内存区域,通过读写共享内存来进行通信。这种方式通常需要使用同步机制来协调不同线程的访问。
2.消息队列
消息队列是一种可以将数据进行缓存和传递的通信机制,不同的线程可以向同一队列发送或接收消息。消息队列有两种类型:阻塞式和非阻塞式。
3.信号量
信号量是一种计数器,用于控制对资源的访问。在多线程中,可以使用信号量来控制线程的访问和使用共享资源的数量。
4.互斥量
互斥量用于保护共享资源的访问,通过给共享资源加锁来确保只有一个线程可以访问该资源。在访问共享资源时,线程需要先获得互斥量的锁,使用完后再释放锁,让其他线程可以继续访问该资源。
5.条件变量
条件变量用于在多线程中实现线程之间的等待和通知机制。当某个条件不满足时,线程可以调用条件变量的等待函数,使线程进入等待状态。当条件满足时,线程可以调用条件变量的通知函数,通知其他线程可以继续执行。

线程的生命周期是什么,线程有几种状态,什么是上下文切换?

线程的生命周期通常包括五种状态:新建(New)、就绪(Runnable)、运行(Running)、阻塞(Blocked)和终止(Terminated)。
1.新建状态是指当线程对象创建后,就进入了新建状态。此时它已经有了相应的内存空间和其他资源,但还没有开始运行。
2.就绪状态是指当线程对象调用 start() 方法后,线程进入了就绪状态。此时它已经具备了运行的条件,只等 CPU 分配资源,让其开始执行。
3.运行状态是指当线程对象获得 CPU 资源后,就开始执行 run() 方法中的代码,线程处于运行状态。
4.阻塞状态是指线程在某些特定情况下会被挂起,暂时停止执行。当线程处于阻塞状态时,它并不会占用 CPU 资源。
5.终止状态是指当线程的 run() 方法执行完毕或者因异常退出时,线程进入了终止状态。此时,该线程不再占用 CPU 资源,也不再执行任何代码。
上下文切换
线程的状态变化通常是由操作系统进行管理和控制的,当线程状态发生变化时,需要进行上下文切换。
上下文切换是指将当前线程的状态保存下来,并将CPU资源切换到另一个线程上运行的过程。上下文切换需要花费一定的时间和系统资源,因此,线程的上下文切换次数要尽量减少,以提高系统的性能。

什么是零拷贝?说一说你对零拷贝的理解?

零拷贝(Zero-Copy)是一种高效的数据传输技术,它可以将数据从内核空间直接传输到应用程序的内存空间中,避免了不必要的数据拷贝,从而提高了数据传输的效率和性能。
在传统的数据传输方式中,当应用程序需要从磁盘、网络等外部设备中读取数据时,操作系统需要先将数据从外部设备拷贝到内核空间的缓冲区,然后再将数据从内核空间拷贝到应用程序的内存空间中,这个过程中需要进行两次数据拷贝,浪费了大量的 CPU 时间和内存带宽。
而使用零拷贝技术,数据可以直接从外部设备复制到应用程序的内存空间中,避免了中间的内核空间缓冲区,减少了不必要的数据拷贝,提高了数据传输的效率和性能。
在网络编程中,零拷贝技术可以用于大文件的传输、网络文件系统的读写、数据库查询等场景中,提高数据传输的效率和响应速度。同时,零拷贝技术也可以减少系统内存的开销,提高系统的稳定性和可靠

Spring

Spring 框架是什么?使用 Spring 框架有哪些好处?

spring是一个开源框架,为简化企业级开发而生,开源框架,分层管理各个模块。
优点:
高内聚,低耦合,spring就是一个大工厂,可以将所有对象的创建和依赖管理都交给spring来管理。
aop编程支持,spring提供面向切面编程,可以很方便的实现对程序进行权限拦截,运行监控等。

Spring 的两大核心概念是什么?简单讲一下你对它们的理解

Spring两大核心概念就是控制反转和面向切面编程
控制反转:将对象的创建和依赖注入由应用代码转移到Spring容器中进行,由Spring容器负责创建对象和管理它们之间的依赖关系。让Spring去管理对象的创建于对象之间的复杂依赖(创建的对象都从放在ioc容器),这样降低了代码的层与层之间的耦合度,也方便以后对组件的替换。
面向切面编程:将业务逻辑无关的代码(如日志,安全,事务等)从业务逻辑中剥离出来,以便于统一管理和维护。

什么是 IOC,简单讲一下 Spring IOC 的实现机制?

基本概念
IOC(Inverse Of Controll,控制反转):就是原来代码里面需要自己手动创建的对象,依赖,反转给Spring来帮忙实现。我们需要创建一个容器,同时需要一种描述来让容器知道要创建的对象与对象之间的关系。
在Spring中BeanFactory就是IOC容器,在Spring初始化的时候,创建容器,并将需要创建对象和对象的关系(xml,注解)通过BeanDefinitionReader加载到BeanDefinition中并保存在BeanDefinitionMap中,然后再由IOC容器创建bean对象.
两种bean的注册方式
方法1:通过@Bean+@Configuration的方式直接定义要创建的对象与对象的关系
方式2:通过@Component定义类,这种方式必须使用@ComponetScan定位Bean扫描路径
IOC的创建
在Spring中BeanFactory就是IOC容器,在Spring初始化的时候,创建容器,并将需要创建对象和对象的关系(xml,注解)通过BeanDefinitionReader加载到BeanDefinition中并保存在BeanDefinitionMap中,在这个过程中会让BeanDefinitionProcesser(Bean的定义信息的后置处理器)进行增强,然后再由IOC容器创建bean对象.
Bean的生命周期(面试官顺着问题往下问的拓展)
bean的实例化:spring启动后,会查找和加载需要被spring管理的Bean,并且实例化
bean的属性注入(bean的初始化):bean被实例化后将Bean的引入河值注入到bean的属性中
查看是否调用一些aware接口,比如BeanFactoryAware,BeanFactoryAware,ApplicationContextAware接口,分别会将Bean的名字,BeanFactory容器实例,以及Bean所在用的上下文引用传入给Bean
在初始化之前,会查看是否调用了BeanPostProcessor的预初始化方法,可以对bean进行扩展
调用InitializingBean的afterPropertiesSet()方法:如果Bean实现了InitializingBean接口,spring将调用他们的afterPropertiesSet()方法,类似的,如果Bean使用init-method生命了初始化方法的话,这个方法也会被调用。
初始化成功之后,会查看是否调用BeanPostProcessor的初始化后的方法:如果Bean实现了BeanPostProcessor接口,spring就将调用他们的postprocessAfterInitialization()方法。可以对bean进行扩展
bean的正常使用:可以被应用程序正常使用了,他们将驻留在上下文中,直到应用的上下文被销毁
bean的销毁:调用DisposableBean的destory()方法:如果Bean实现DisposableBean接口,spring将用他的destory()方法,相同的,如果Bean使用了destory-method生命销毁方法,该方法也会被调用。(但由于bean也分为单例和多例,单例bean会随着IOC容器的销毁而销毁,多例的bean不会随着IOC容器的销毁而销毁,他是通过JVM里面的垃圾回收器负责回收)

Spring 框架中都用到了哪些设计模式?

Spring 框架中使用了许多设计模式,以下列举一些比较重要的:
单例模式:Spring 的 Bean 默认是单例模式,通过 Spring 容器管理 Bean 的生命周期,保证每个 Bean 只被创建一次,并在整个应用程序中重用。
工厂模式:Spring 使用工厂模式通过 BeanFactory 和 ApplicationContext 创建并管理 Bean 对象。
代理模式:Spring AOP 基于动态代理技术,使用代理模式实现切面编程,提供了对 AOP 编程的支持。
观察者模式:Spring 中的事件机制基于观察者模式,通过 ApplicationEventPublisher 发布事件,由 ApplicationListener 监听事件,实现了对象间的松耦合。
模板方法模式:Spring 中的 JdbcTemplate 使用了模板方法模式,将一些固定的流程封装在父类中,子类只需实现一些抽象方法即可。
策略模式:Spring 中的 HandlerInterceptor 和 HandlerExecutionChain 使用了策略模式,允许开发者自定义处理器拦截器,按照一定顺序执行。
责任链模式:Spring 中的过滤器和拦截器使用了责任链模式,多个过滤器和拦截器按照一定顺序执行,每个过滤器和拦截器可以拦截请求或者响应并做出相应的处理。
总之,Spring 框架中充分利用了许多设计模式,提供了良好的扩展性和灵活性,降低了代码的耦合度,提高了代码的可维护性。

Spring、SpringMVC、SpringBoot 三者之间是什么关系?

Spring、SpringMVC、SpringBoot 是三个独立的框架,它们之间的关系是:
Spring 是一个 Java 的轻量级应用框架,提供了基于 IoC 和 AOP 的支持,用于构建企业级应用。Spring 有多个模块,包括 Spring Core、Spring Context、Spring JDBC、Spring Web 等,每个模块提供了不同的功能。
SpringMVC 是 Spring 框架的一部分,是基于 MVC 设计模式的 Web 框架,用于构建 Web 应用程序。它提供了控制器、视图解析器、数据绑定、异常处理等功能,使得开发 Web 应用变得更加简单。SpringMVC 还支持 RESTful 架构。
SpringBoot 是基于 Spring 框架的一个开发框架,用于快速构建独立的、生产级别的 Spring 应用程序。它通过自动配置和约定优于配置的方式,简化了 Spring 应用程序的配置和开发过程。SpringBoot 集成了很多常用的第三方库和工具,例如 Spring Data、Spring Security、Thymeleaf、Logback 等,可以极大地提高开发效率。
因此,SpringBoot 可以看作是在 Spring 的基础上,通过自动配置和约定优于配置的方式,提供了更加简单、快速的开发体验。而 SpringMVC 则是 Spring 框架中用于构建 Web 应用程序的模块。

有哪些注解可以注入 Bean?@Autowired 和 @Resource 的区别?

在 Spring 框架中,常用的注入 Bean 的注解包括
@Autowired:自动注入,按照类型自动装配,如果有多个同类型的 Bean,则需要通过 @Qualifier 指定具体的 Bean。
@Resource:Java 自带的注入方式,按照名称自动装配,默认是按照属性名称进行匹配,如果需要按照 Bean 的名称进行匹配,可以使用 @Resource(name=“beanName”)。
@Autowired 注解是 Spring 框架提供的。
@Value:用于注入配置文件中的属性值,可以指定默认值。
@Component:用于声明一个 Bean,作用类似于 XML 中的 标签。
以上注解都可以用于注入 Bean,不同的注解之间的区别主要在于注入方式和实现方式的不同。@Autowired 和 @Resource 最常用,其中 @Autowired 按照类型自动装配更为常用,而 @Resource 按照名称自动装配则比较适合需要明确指定 Bean 名称的情况。
需要注意的是,注入 Bean 的时候最好使用接口类型作为注入对象,这样可以避免因为具体实现类变更导致注入失败的问题。

Spring 如何处理线程并发问题,ThreadLocal 你了解过吗?

Spring 框架中处理线程并发问题的方式包括以下几种:
同步关键字 synchronized:使用 synchronized 关键字可以对共享资源进行加锁,从而保证多线程访问时的同步性。但是,synchronized 对性能会产生一定的影响,并且容易导致死锁等问题。
Lock 接口:Lock 接口提供了比 synchronized 更加灵活的加锁方式,并且可以防止死锁问题的发生。但是,Lock 接口的使用相对较复杂,需要手动进行加锁和解锁操作。
ThreadLocal 类:ThreadLocal 类提供了线程本地变量的功能,可以让每个线程拥有自己的变量副本,从而避免了多个线程之间的共享问题。但是,ThreadLocal 类的使用需要注意内存泄漏问题。
关于 ThreadLocal,它是 Java 中一个非常重要的类,用于实现线程本地存储,即让每个线程都拥有自己的变量副本,从而避免多个线程之间的共享问题。在 Spring 框架中,ThreadLocal 类经常用于存储一些与当前线程相关的数据,例如请求上下文、用户信息等。通过将这些数据存储到 ThreadLocal 对象中,可以方便地在整个应用程序中进行访问和传递,同时避免了多个线程之间的共享问题。
ThreadLocal 类的使用需要注意内存泄漏问题,因为线程本地变量只有在对应的线程被回收时才会被回收,如果没有及时清理,就可能导致内存泄漏问题。因此,在使用 ThreadLocal 类时,需要注意在不需要存储数据时及时调用 remove() 方法清理 ThreadLocal 对象中的数据。

Spring 中的 BeanFactory 和 ApplicationContext 有什么区别和联系?

BeanFactory和ApplicationContext是Spring中两个核心接口,都负责管理Java Bean实例的创建、组装和管理。
BeanFactory是Spring容器的超级接口。ApplicationContext是BeanFactory的子接口。
区别:
1.BeanFactory:Spring IoC容器的顶级对象,BeanFactory被翻译为“Bean工厂”,在Spring的IoC容器中,“Bean工厂”负责创建Bean对象。BeanFactory是工厂。提供了基本的依赖注入和控制反转功能,它可以加载配置文件中定义的bean,并管理它们的生命周期。
2.ApplicationContext是BeanFactory的一个子接口,它在BeanFactory的基础上增加了更多的企业级功能,例如事件发布、资源处理、AOP等。ApplicationContext还可以自动识别Spring框架内置的bean,并对它们进行预处理。

Spring框架中Bean的生命周期

在 Spring 框架中,每个 Bean 都有其生命周期,包括创建、初始化、使用和销毁。以下是 Spring Bean 生命周期的各个阶段:
1.实例化:当 Spring 容器接收到 Bean 的定义信息后,它将实例化 Bean。这是通过调用默认构造函数或提供的工厂方法来完成的。
2.设置属性值:在实例化之后,Spring 容器会通过调用 setter 方法或直接设置属性值来为 Bean 注入属性。
调用 BeanPostProcessor 的 postProcessBeforeInitialization() 方法:如果 Bean 中实现了 BeanPostProcessor 接口,则在 Bean 初始化之前,Spring 将调用其 postProcessBeforeInitialization() 方法。
3.初始化:在所有属性都被设置之后,如果指定了 init-method,Spring 将调用该方法来完成 Bean 的初始化。
调用 BeanPostProcessor 的 postProcessAfterInitialization() 方法:如果 Bean 中实现了 BeanPostProcessor 接口,则在 Bean 初始化之后,Spring 将调用其 postProcessAfterInitialization() 方法。
4.使用:此时,Bean 可以被应用程序使用。
5.销毁:当应用程序关闭时,Spring 容器将调用 destroy-method 方法来销毁 Bean。
总而言之,Spring Bean 的生命周期可以概括为:实例化、设置属性值、初始化、使用和销毁。在这个过程中,需要注意的是,Spring 允许开发人员控制 Bean 的生命周期,并且可以通过实现一些特定的接口或使用注释等方式来自定义 Bean 的行为。

Spring 支持哪几种事务管理类型,Spring 的事务实现方式和实现原理是?

Spring 支持的事务管理类型包括:
编程式事务管理:在代码中显式地编写事务管理相关代码,如开启事务、提交事务、回滚事务等。
声明式事务管理:使用 AOP 技术,在代码中通过配置进行声明,从而实现对事务管理的控制。
注解式事务管理:基于声明式事务管理,使用注解的方式进行事务的管理。
Spring 的事务实现方式采用了模板模式,通过模板模式实现了事务的封装。Spring 事务管理实现原理主要是通过 AOP 和代理模式来实现的。在使用声明式事务管理时,Spring 通过拦截器和代理对象来实现对方法调用的拦截和控制。当方法被调用时,Spring 会在方法调用前开启一个新的事务,如果方法执行成功,Spring 会提交事务,否则回滚事务。在使用注解式事务管理时,Spring 会扫描带有事务注解的方法,并在运行时使用动态代理为这些方法生成代理对象,在代理对象的方法调用前后进行事务管理的操作。

什么是 Spring 的依赖注入,依赖注入的基本原则以及好处?

依赖注入(Dependency Injection,简称DI)是Spring框架中的一种设计模式,用于实现控制反转(Inversion of Control,简称IoC)。它是一种将对象之间的依赖关系从硬编码中解耦的方法。通过依赖注入,Spring框架可以在运行时自动为类的属性、构造函数或方法参数提供所需的依赖对象,从而实现对象之间的松耦合。
依赖注入的基本原则:
1.高层模块不应该依赖低层模块。它们都应该依赖抽象。
2.抽象不应该依赖具体实现。具体实现应该依赖抽象。
依赖注入的好处:
1.解耦:依赖注入降低了组件之间的耦合度,使得组件可以独立地进行开发、测试和维护。通过将组件的创建和管理交给IoC容器,组件之间的依赖关系变得更加清晰,有助于提高代码的可维护性。
2.提高代码的可测试性:依赖注入使得对组件进行单元测试变得更加容易。通过使用依赖注入,我们可以轻松地为组件提供模拟(Mock)的依赖对象,从而实现组件在隔离环境中的测试。
3.更好的代码重用:由于组件之间的依赖关系变得更加清晰,这有助于提高代码的可重用性。组件可以在不同的上下文中重用,而无需对其进行修改。
4.更简洁的代码:依赖注入使得代码更加简洁,因为组件不再需要直接处理依赖对象的创建和管理。这使得开发人员可以更加专注于组件的核心功能,从而提高开发效率。
5.更容易进行配置管理:依赖注入允许我们将组件的配置与实现代码分离,从而使得配置管理变得更加容易。通过使用外部配置文件或注解,我们可以在不修改代码的情况下,调整组件之间的依赖关系。

什么是 AOP?Spring AOP 和 AspectJ AOP 有什么区别?有哪些实现 AOP 的方式?

AOP(Aspect-Oriented Programming,面向切面编程)是一种编程范式,它旨在解决软件开发中的横切关注点(cross-cutting concerns)问题。横切关注点是那些分布于多个模块或对象的功能,例如日志记录、安全检查、事务管理等。AOP通过将横切关注点与业务逻辑分离,从而提高了代码的模块化程度,使得开发更加简洁、易于维护。
Spring AOP和AspectJ AOP是两种不同的AOP实现。
1.Spring AOP:是Spring框架中的AOP实现,基于动态代理实现。Spring AOP主要用于解决Spring容器中Bean的横切关注点问题。由于它使用了动态代理,所以只支持方法级别的切面(即横切关注点只能织入方法的执行)。Spring AOP的性能略逊于AspectJ,但对于大部分应用来说,性能影响不大。
2.AspectJ AOP:是一个独立的、功能更强大的AOP实现,不仅支持方法级别的切面,还支持字段、构造器等其他切面。AspectJ可以通过编译时织入(编译时修改字节码)或加载时织入(在类加载时修改字节码)的方式实现AOP。Spring可以与AspectJ结合使用,以提供更强大的AOP功能。
实现AOP的方式主要有以下几种:
1.动态代理:通过代理模式,为目标对象生成一个代理对象,然后在代理对象中实现横切关注点的织入。动态代理可以分为JDK动态代理(基于接口)和CGLIB动态代理(基于类)。
2.编译时织入:在编译阶段,通过修改字节码实现AOP。AspectJ的编译时织入就是这种方式。
3.类加载时织入:在类加载阶段,通过修改字节码实现AOP。AspectJ的加载时织入就是这种方式。
4,AOP的实现方式取决于具体需求和技术选型。对于Spring应用来说,通常可以使用Spring AOP满足大部分需求,如果需要更强大的AOP功能,可以考虑使用AspectJ。

Spring 如何处理线程并发问题,ThreadLocal 你了解过吗?

Spring 中处理线程并发问题的主要方式是使用线程安全的对象和并发包中提供的类来避免线程安全问题。
例如,Spring 中的单例 Bean 是线程安全的,因为 Spring 容器在创建单例 Bean 时会确保只有一个实例存在。Spring 还提供了对多线程的支持,例如使用 @Async 注解实现异步方法调用等。
在 Spring 中处理线程并发问题,常用的方法有以下几种:
1.Synchronized关键字:使用 synchronized 关键字可以锁定某个对象或类,使得多个线程无法同时进入该代码块,从而保证数据的安全性。
2.ReentrantLock:与 synchronized 相比,ReentrantLock 提供了更加灵活的加锁方式,可以控制锁的获取和释放的时机,提供了更多的扩展功能。
3.Atomic 包:Java 的 Atomic 包提供了一些原子性操作,例如 AtomicLong、AtomicInteger 等,可以保证某个操作的原子性。
4.ThreadLocal:ThreadLocal 可以使得每个线程拥有自己的变量副本,避免了多个线程之间共享变量所带来的线程安全问题。
1.ThreadLocal 是 Java 中的一个线程局部变量,它可以为每个线程提供一个独立的变量副本,避免了多线程之间的数据共享和数据竞争问题。
2.ThreadLocal 可以在每个线程中存储一个对象,并且每个线程只能访问自己的对象,而不会影响其他线程中的对象。
3.ThreadLocal 主要用于解决线程安全问题,例如在 Web 应用中,可以使用 ThreadLocal 存储用户的会话信息,这样每个线程就可以独立地访问自己的会话信息,避免了多个线程之间的数据共享和数据竞争问题。
4.在 Spring 中,ThreadLocal 通常用于存储和传递一些与线程相关的上下文信息,例如当前登录用户的信息、请求的 IP 地址等。可以将 ThreadLocal 对象定义为一个 Bean,然后在需要使用时注入到其他 Bean 中使用。
5.Spring 还提供了一些与 ThreadLocal 相关的类和工具,例如 SimpleThreadScope,用于实现线程范围内的 Bean,以及 TaskExecutor,用于在多线程环境中执行任务。

设计模式

设计模式是什么?为什么要学习和使用设计模式?

设计模式是一套被反复使用、经过验证的、通用的解决特定问题的设计思想,是一种被设计师反复使用,经过时间验证的、解决特定问题的一种技术方案。
设计模式的主要作用在于:
1.提高代码的可维护性、可扩展性、可读性,提高代码的质量;
2.通过共享经验,提高开发人员的设计能力,缩短学习时间,增强团队合作效率;
3.提高开发效率,缩短开发周期。
常见的模式与业务场景:
1.工厂模式:当需要创建多种具有相同特征的对象时,使用工厂模式可以将对象的创建与业务逻辑分离,降低代码的耦合度。 例如,在电商平台上,不同种类的商品都需要进行库存管理和订单管理,可以使用工厂模式来创建对应的库存管理器和订单管理器。
2.单例模式:当需要确保系统中某个类只有一个实例时,可以使用单例模式,保证全局唯一性,避免资源的浪费。 例如,在 Web 应用中,有时需要保证所有请求都使用同一个数据库连接,可以使用单例模式来实现数据库连接池。
3.观察者模式:当一个对象的状态发生改变需要通知其他对象时,可以使用观察者模式,将对象的状态与业务逻辑分离,提高系统的灵活性和可扩展性。 例如,在多人在线游戏中,玩家的行为会影响其他玩家的状态,可以使用观察者模式来实现游戏中的事件处理和状态同步。
4.策略模式:当需要在运行时根据不同的情况采用不同的算法时,可以使用策略模式,将算法与业务逻辑分离,提高代码的可维护性和扩展性。 例如,在电商平台上,可以使用策略模式来实现不同的促销策略,例如满减、打折等。

设计模式可以分为哪几类?一共有多少种主流的设计模式?

设计模式通常被分为三类:
创建型模式:用于创建对象的模式,主要解决对象的实例化问题。
结构型模式:用于处理类或对象的组合,以获得更大的结构,主要解决不同对象之间的关系问题。
行为型模式:用于处理对象之间的交互以及职责划分,主要解决对象的行为问题。
总的来说,主流的设计模式有23种。其中,创建型模式包括工厂方法模式、抽象工厂模式、单例模式、建造者模式和原型模式;结构型模式包括适配器模式、桥接模式、装饰器模式、组合模式、外观模式、享元模式和代理模式;行为型模式包括责任链模式、命令模式、解释器模式、迭代器模式、中介者模式、备忘录模式、观察者模式、状态模式、策略模式、模板方法模式和访问者模式。这些模式都是经过长期实践证明可行的,可以帮助开发人员提高代码的可重用性、灵活性和可维护性。

什么是工厂模式?使用工厂模式有什么好处?工厂模式有哪些分类?各自的应用场景是什么?

工厂模式(Factory Pattern)是一种创建型设计模式,它提供了一种封装对象创建过程的方法。工厂模式将对象的创建和使用分离,让一个专门的工厂类负责创建对象实例,而不是在代码中直接使用new操作符。这有助于降低代码的耦合度,提高可维护性和可扩展性。
使用工厂模式的好处:
1.降低耦合度:工厂模式将对象的创建与使用分离,使得客户端代码不直接依赖具体的类,降低了耦合度。
2.提高可扩展性:当需要添加或修改产品类时,只需修改工厂类,而不需要修改客户端代码,提高了系统的可扩展性。
3.提高可维护性:通过集中管理对象的创建,提高了代码的可维护性。
4.提高代码复用性:工厂类可以被多个客户端代码复用,减少了重复代码。
工厂模式可以分为以下几类:
简单工厂模式(Simple Factory Pattern):一个工厂类根据传入的参数决定创建哪个具体产品类的实例。简单工厂模式适用于产品种类较少且不易变化的场景。 应用场景:例如,一个简单的图形绘制工具,可以根据传入的参数创建不同类型的图形(如圆形、矩形等)。
工厂方法模式(Factory Method Pattern):定义一个接口或抽象类来创建对象,将实际创建对象的工作推迟到子类中。工厂方法模式适用于产品种类较多且可能增加的场景。 应用场景:例如,一个日志记录器,可以根据不同的需求(如文件日志、数据库日志等)使用不同的工厂子类创建相应的日志记录器实例。
抽象工厂模式(Abstract Factory Pattern):提供一个接口,用于创建一系列相关或相互依赖的对象,而无需指定它们具体的类。抽象工厂模式适用于产品族的场景。 应用场景:例如,跨平台UI框架,可以为不同平台(如Windows、macOS等)提供不同的UI控件实现,通过抽象工厂来创建对应平台的一系列UI控件。
各种工厂模式的应用场景取决于实际需求,需要根据具体问题来选择合适的工厂模式。

什么是单例模式?使用单例模式有什么好处?有哪些常用的单例模式实现方式?各自的应用场景是什么?请你举例说明哪些地方用到了单例模式?

单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来访问该实例。单例模式的目的是确保类的一个唯一实例,因此其他类可以轻松地从一个可知的地方访问它。
单例模式有以下好处:
1.节省系统资源:在系统中,如果有多个实例会造成资源浪费,而使用单例模式可以减少这种浪费。
2.简化了对象访问:单例模式提供了一个全局的访问点,因此可以简化访问过程。
常用的单例模式实现方式有以下几种:
1.饿汉式单例模式:在类加载时创建单例对象。缺点是不支持延迟加载。
2.懒汉式单例模式:在第一次使用时才创建单例对象。缺点是需要考虑线程安全问题。
3.双重检查锁单例模式:在第一次使用时创建单例对象,并使用双重检查锁定来确保线程安全。
4.枚举单例模式:在枚举类型中创建单例对象,可以防止反射和序列化攻击。
应用场景:
1.数据库连接池:通过单例模式,可以确保系统中只有一个数据库连接池。
2.日志记录器:可以使用单例模式记录系统日志,这样可以确保系统中只有一个日志记录器。
3.配置文件管理器:可以使用单例模式来管理应用程序的配置文件,这样可以避免重复读取配置文件的开销。
4.线程池:可以使用单例模式来确保系统中只有一个线程池。
一个例子是 Spring 框架中的 ApplicationContext,它是一个全局访问点,提供了一个管理 Bean 的中央注册表。由于 Spring 中的 Bean 只需创建一次,因此 ApplicationContext 使用单例模式确保只有一个实例。

分布式

什么是分布式?为什么需要分布式?

分布式是指在多台计算机上协同工作的系统,这些计算机通过网络连接在一起,共同完成一个任务。
1.数据量
单点部署的方式处理的数据量有上限
分布式有更多的计算机,处理的数据量更大并且 " 没有上限 "
2.安全容错
单点部署,一挂全挂
分布式系统出现故障,可以保证系统仍能够正常运行
3.拓展性
单点部署,资源用完就用完了,想拓展心有余而力不足
分布式系统只需要在系统基础上,继续增加新的计算机,可以很方便拓展
4.并发性
单点部署:都访问一个服务端
分布式系统,相同功能服务端有N个,并发量高

什么是网关,网关有哪些作用?

网关(Gateway)是在计算机网络中用于连接两个独立的网络的设备,它能够在两个不同协议的网络之间传递数据。在互联网中,网关是一个可以连接不同协议的网络的设备,比如说可以连接局域网和互联网,它可以把局域网的内部网络地址转换成互联网上的合法地址,从而使得局域网内的主机可以与外界通信。
作用:
应用网关:用于应用层协议的处理,如 HTTP、SMTP 等。
数据库网关:用于数据库访问的控制和管理。
通信网关:用于不同通信协议之间的数据交换,如 TCP/IP、UDP/IP 等。
API 网关:用于管理和转发 API 请求,实现 API 的授权、限流、监控等功能。
总的来说,网关可以为不同网络提供连接和通信的功能,同时也可以提供安全、性能、可靠性等方面的增强功能,是现代计算机系统中不可或缺的一部分。

Dubbo 是什么?是否了解过它的架构设计?

Dubbo是一个高性能、轻量级的开源Java RPC框架(RPC是远程过程调用(Remote Procedure Call)),它提供了完整的RPC协议栈,包括服务发布、服务引用、负载均衡、容错、服务治理和服务监控等功能,同时提供了可扩展的RPC协议、数据模型、序列化和网络传输等组件,支持多语言和多协议。
Dubbo的架构设计主要包括服务提供者、服务消费者、注册中心和监控中心四个角色。其中,服务提供者提供服务的实现,并通过注册中心将自己注册到服务治理中心;服务消费者则通过注册中心发现可用的服务,并通过负载均衡策略选择一个服务提供者进行调用;注册中心主要负责服务的注册、发现和路由;监控中心则负责服务的统计和监控。
Dubbo的架构设计采用了分层架构模式,将不同的功能模块进行分离,以达到模块化和可扩展的目的。同时,Dubbo还提供了丰富的扩展点和插件机制,用户可以通过自定义扩展点和插件来满足不同的业务需求。

什么是分布式的 CAP 理论?

CAP 理论是分布式系统中一个重要的理论,它指出在分布式系统中,一致性(Consistency)、可用性(Availability)和分区容错性(Partition Tolerance)这三个目标不可能同时满足。
一致性:指的是多节点数据的一致性,即多个节点读取同一个数据时,应该获得相同的结果。这个目标要求所有节点都能够看到最新的数据。
可用性:指的是系统在部分组件失效的情况下仍然可以对外提供服务,即系统具有24/7的高可用性。
分区容错性:指的是系统在网络分区的情况下仍然能够正常运行,即系统不会因为网络故障而停止工作。
CAP 理论认为,在分布式系统中,任何两个目标只能同时满足其中的两项,无法同时满足三项。例如,当网络出现分区时,必须选择放弃一致性或可用性中的一个,否则系统将无法正常工作。
因此,在实际应用中,需要根据具体的业务需求来选择合适的权衡方案。例如,某些应用场景可能更注重高可用性,而不是一致性,因此可能选择放弃一致性以保证系统可用;而另一些场景则可能更注重数据的一致性,因此可能会选择放弃可用性来保证数据的一致性。

什么是 RPC?目前有哪些常见的 RPC 框架?实现 RPC 框架的核心原理是什么?

RPC(Remote Procedure Call,远程过程调用)是一种在分布式计算环境中,允许一个进程调用另一个进程的过程。它通过网络请求和响应来实现两个进程之间的通信和数据传输。
目前常见的 RPC 框架包括: gRPC、Dubbo、Thrift、Apache Avro、Java RMI 等。这些框架都提供了方便易用的接口和工具,可以帮助我们快速构建分布式系统。
实现 RPC 框架的核心原理是序列化和反序列化。当客户端请求服务器时,客户端将对象序列化为二进制数据并发送到服务器。服务器收到请求后,对二进制数据进行反序列化,并根据请求内容执行相应的操作。服务器执行完操作后,再将结果序列化并发送回客户端,客户端再将结果反序列化并处理。因此,序列化与反序列化是 RPC 框架中最基础的部分,也是实现 RPC 框架的重要技术之一。除此之外,还需要考虑协议定义、网络传输和负载均衡等问题。

有哪些主流的消息队列,它们分别有什么优缺点、各自的适用场景是什么?

在这里插入图片描述

什么是注册中心?如何实现一个注册中心?

注册中心(Service Registry)是微服务架构中的一个关键组件,负责管理服务实例的信息,包括服务实例的地址、端口、元数据等。它允许服务实例在启动时注册自己,并在关闭时注销。服务消费者可以通过查询注册中心来发现可用的服务提供者,并根据负载均衡策略选择合适的服务实例进行调用。
实现一个注册中心需要以下几个关键功能:
1.服务注册:允许服务实例在启动时将自己的信息注册到注册中心,包括地址、端口、元数据等。
2.服务注销:允许服务实例在关闭时从注册中心注销自己。
3.服务发现:允许服务消费者查询可用的服务实例列表,以便找到合适的服务实例进行调用。
4.健康检查:注册中心需要定期检查已注册的服务实例的健康状况,以确保服务实例列表的准确性。如果发现某个服务实例不再可用,注册中心应将其从列表中移除。
实现一个简单的注册中心可以参考以下步骤:
选择一个合适的数据结构(例如哈希表)存储服务实例的信息。
1.实现一个HTTP API,允许服务实例在启动时向注册中心发送注册请求,将自己的信息添加到数据结构中。
2.实现一个HTTP API,允许服务实例在关闭时向注册中心发送注销请求,将自己的信息从数据结构中移除。
3.实现一个HTTP API,允许服务消费者查询可用的服务实例列表。
4.实现一个定时任务,定期检查已注册的服务实例的健康状况,如果发现某个服务实例不再可用,将其从数据结构中移除。
然而,实现一个可靠、高性能、可扩展的注册中心是一个复杂的任务。在实际应用中,通常推荐使用现有的成熟的注册中心组件,例如Eureka、Consul、Zookeeper等。这些注册中心提供了丰富的功能,包括高可用性、数据持久化、动态配置等。

什么是分布式的 BASE 理论,它与 CAP 理论有什么联系?

BASE 理论是对分布式系统的一种更加宽松的理解,它认为分布式系统无法避免出现某些不可预期的情况,如网络延迟、节点故障等,因此系统的可用性、可靠性、性能等指标需要按照实际需求进行平衡。具体来说,BASE 理论主要包含以下三个要素:
1.Basically Available(基本可用):系统需要保证基本的可用性,允许部分节点故障或数据丢失,但是不能影响整个系统的正常运行。
2.Soft state(软状态):系统中的数据不需要实时同步,而是允许存在一定的延迟或者不一致性,只要最终趋于一致即可。
3.Eventually consistent(最终一致性):系统最终能够保证数据的一致性,但是在某些时刻可能出现短暂的不一致情况,需要在一定的时间范围内达成一致。
与 CAP 理论相比,BASE 理论强调系统的可用性和性能,而不是强一致性。CAP 理论认为分布式系统无法同时满足一致性(Consistency)、可用性(Availability)和分区容错性(Partition tolerance)三个条件,只能在其中选择两个。因此,BASE 理论可以看作是 CAP 理论的一种扩展和实现方式,它强调在分布式系统中尽量保证可用性和性能,并通过最终一致性来达成数据一致性。

JVM

请你介绍下 JVM 内存模型,分为哪些区域?各区域的作用是什么?

JVM 内存模型分为以下几个区域:
1.程序计数器(Program Counter Register):每个线程都有自己的程序计数器,用于指示当前线程执行的字节码指令的行号,以便线程执行时能够回到正确的位置。
2.虚拟机栈(JVM Stack):也称为 Java 方法栈,用于存储方法执行时的局部变量表、操作数栈、动态链接、方法出口等信息。每个线程在执行一个方法时,都会为该方法分配一个栈帧,并将该栈帧压入虚拟机栈,当方法执行完毕后,虚拟机会将其出栈。
3.本地方法栈(Native Method Stack):与虚拟机栈类似,用于存储本地方法的执行信息。
4.堆(Heap):用于存储对象实例,是 JVM 中最大的一块内存区域。堆是被所有线程共享的,当创建一个新对象时,对象实例存储在堆中,堆中存储的对象实例都有一个标记用于标记对象是否存活。垃圾回收器会周期性地回收那些没有被标记为存活的对象。
5.方法区(Method Area):用于存储已经被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。方法区也是被所有线程共享的。
6.运行时常量池(Runtime Constant Pool):是方法区的一部分,用于存储编译期间生成的各种字面量和符号引用,这些内容在类加载后进入常量池中。
其中,程序计数器、虚拟机栈、本地方法栈是线程私有的,堆、方法区、运行时常量池是线程共享的。

Linux

Linux 中的硬链接和软连接是什么,二者有什么区别?

在 Linux 文件系统中,硬链接(hard link)和软链接(symbolic link)都是一种文件链接的方式,可以用于将一个文件链接到另一个文件上。它们的主要区别在于创建方式、所占空间和使用限制等方面。
硬链接是通过在文件系统中创建一个新的目录项(directory entry)指向同一文件 inode 的位置来实现的。因为硬链接实际上是指向同一 inode,所以如果原文件被删除,硬链接依然能够访问到原文件的内容。硬链接的使用范围比较受限,因为硬链接只能指向同一个文件系统内的文件,不能跨文件系统创建。
软链接是通过在文件系统中创建一个新的文件来实现的,该文件中包含指向另一个文件的路径。软链接可以跨文件系统创建,并且可以指向任何类型的文件。但是,当原文件被删除时,软链接将会失效。
总的来说,硬链接更加高效,因为它只是添加了一个新的目录项,所以对磁盘空间的消耗比软链接要小。但是,硬链接不能跨文件系统,所以在实际应用中需要根据具体的需求来选择使用哪种链接方式。

系统设计

如何设计一个点赞系统?

设计一个点赞系统可以分为以下几个步骤:
确定需求:需要明确点赞的对象是什么,是否需要计数等信息,同时需要考虑点赞的业务场景,如用户点赞、文章点赞等。
数据库设计:需要设计点赞相关的数据表,可以包含点赞者 ID、被点赞对象 ID、点赞时间等字段。
接口设计:需要设计点赞相关的接口,包括点赞、取消点赞、查询点赞数等操作。
业务逻辑实现:在接口中实现点赞相关的业务逻辑,包括判断点赞状态、更新点赞数、更新点赞状态等操作。
安全性考虑:需要考虑并发访问的情况,可以使用分布式锁来保证数据一致性和安全性。
性能优化:如果点赞系统的访问量很高,可以使用缓存来提高性能,比如使用 Redis 来缓存点赞数等信息。
监控和日志:需要对点赞系统进行监控和日志记录,以便及时发现和排查问题。

总之,设计一个点赞系统需要综合考虑需求、数据库设计、接口设计、业务逻辑实现、安全性、性能优化等方面,同时需要不断优化和完善。

如何在 10 亿个数据中找到最大的 1 万个?(提示:最小堆)

根据对应的MySQL的B树索引,先以先1w个数建立一个小根堆,然后遍历后面的数,比较每个数与根节点的大小,大于根节点则替换再跟新树结构,小于根节点则直接丢弃

云原生

什么是云原生?它有哪些优缺点?

云原生是一种基于云计算的软件开发、部署和运行方式,旨在提高应用程序的可移植性、弹性、可伸缩性和可靠性。其包含多个组件,如容器化、微服务架构、自动化管理等技术。
优点:
1.高可移植性:云原生应用程序可以在不同的云环境中运行,而不需要对代码进行重写或修改。
2.弹性和可伸缩性:云原生应用程序可以根据负载和需求自动扩展和缩小资源,从而提供更好的性能和资源利用率。
3.可靠性和容错性:云原生应用程序的组件可以自动协调和管理,从而提高系统的可靠性和恢复能力。
缺点:
1.学习成本:采用云原生技术需要学习多个新的技术和工具,这可能需要额外的培训和时间投入。
2.部署复杂性:采用云原生技术需要配置和管理多个组件和服务,这可能增加部署和维护的复杂性。
3.安全风险:由于云原生应用程序通常由多个组件和服务组成,因此存在安全风险,其中一个组件出现漏洞可能会导致整个系统受到攻击。

前端

(稍微了解一下前端的基本知识)

什么是 CSS 盒子模型?

CSS 盒子模型(CSS box model)指的是用于布局和设计网页的 CSS 样式规范。CSS 盒子模型将每个 HTML 元素表示为一个矩形的盒子,这个盒子包括内容(content)、内边距(padding)、边框(border)和外边距(margin)四个部分。
● 内容(content):元素的实际内容,由 width 和 height 属性决定。
● 内边距(padding):内容和边框之间的空间,由 padding 属性决定。
● 边框(border):内容和外边距之间的边框,由 border 属性决定。
● 外边距(margin):盒子边缘和其它元素之间的空间,由 margin 属性决定。
注意:盒子的大小由这四个部分加起来决定。
在盒子模型中,有两种模式:标准模式和怪异模式。标准模式下,元素的宽度和高度不包括内边距和边框;怪异模式下,元素的宽度和高度包括内边距和边框。可以通过CSS中的box-sizing属性来控制盒子模型的属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值