文章目录
-
-
- [1.List 有哪些实现,Arraylist 和LinkedList之间的区别是啥?](#1.List 有哪些实现,Arraylist 和LinkedList之间的区别是啥?)
- 2Redis的数据类型
- [3 Redis的内存淘汰策略](#3 Redis的内存淘汰策略)
- 4.TCP和HTTP的区别,网络模型是哪七层模型
- 5.Exception和Error有什么区别?![](https://i-blog.csdnimg.cn/img_convert/7935fead2608766d4629d6be67fbc00e.png)
- 6如何处理异常
- 7创建线程的方式
- 8涉及模式有哪些
-
- [**创建型模式(Creational Patterns)**](#创建型模式(Creational Patterns))
- [**结构型模式(Structural Patterns)**](#结构型模式(Structural Patterns))
- [**行为型模式(Behavioral Patterns)**](#行为型模式(Behavioral Patterns))
- 9.Cookie和Session的区别是什么
- 10.数据库中索引的种类
- 11.聚簇索引和非簇索引的区别
- [12.左连接的关键字是什么 左连接和右连接有什么区别](#12.左连接的关键字是什么 左连接和右连接有什么区别)
- 13.HashMap底层的数据结构
-
1.List 有哪些实现,Arraylist 和LinkedList之间的区别是啥?
List是一个接口,继承了Collection。 实现类包括了三个,Vector、LinkedList、ArrayList 三个实现类。
三个类的创建方式
plain
Vector<Integer> vector = new Vector<>();
ArrayList<Integer> arrayList = new ArrayList<>();
LinkedList<Integer> linkedList = new LinkedList<>();
从线程安全角度考虑:
Vector是线程安全的,其他两个都不是线程安全的。
Vector 方法上面都加上了synchroniozed关键字。是线程安全的
常用的是后面两个。
ArrayList 是基于数组实现了,LinkedList是基于双向链表实现的。
ArrayList: 插入效率高,但是需要扩容。 查找效率很高。 删除效率低
LinkedList 是基于双向链表的,插入效率高。 查找效率低
在多线程的情况下面,ArrayList中添加了1000个数字,但是最终只有995个,中间有五个数字消失了。
plain
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
// 创建多个线程同时向 ArrayList 添加元素
for (int i = 0; i < 1000; i++) {
new Thread(() -> {
list.add(1);
}).start();
}
// 等待所有线程执行完成
try {
Thread.sleep(2000); // 简单等待2秒,实际项目中应使用更好的同步机制
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出 ArrayList 的大小,期望是1000,但由于线程不安全,结果可能不正确
System.out.println("ArrayList size: " + list.size());
}
为什么会出现不一致的问题呢? 这个涉及到原子性的问题了,add方法 需要判断当前容量、插入到数组中下一个位置、修改当前的size。可以认为是散步,如果当前容量还有一个,两个线程同时进行判断容量之后,都插入到数组中的位置,此时其实就发生了问题。会出现覆盖或者遗漏现象。
解决线程安全问题
使用 Collections.synchronizedList
方法将 ArrayList
包装为一个线程安全的列表。
java
List<Integer> list = Collections.synchronizedList (new ArrayList<>());
使用 CopyOnWriteArrayList
,它是 java.util.concurrent
包的一部分,专为并发环境设计。
java
List<Integer> list = new CopyOnWriteArrayList<>();
2Redis的数据类型
Redis的基本数据类型,Redis有八种数据类型
基础的五种数据类型
string :用来存储字符串或者二进制的数据包括 片、音频等信息,存储验证码
set: 去重的set集合 ,去重校验、用户标签、共同好友
列表: list 列表 ,用来存储消息队列
Zset: 有序集合,给每一个成员关联一个分数,根据分数进行有序排列。 场景:排行榜
hash: 存储对象的多个属性。
bitmap: 基于字符串实现的位数组,可以进行位操作。 存储用户的点击、网页的浏览量
HyperLogLog: 用来处理大量数据的去重计算
- 统计网站的独立访客数(UV)。
Geospatial: 地理空间,支持地理坐标和地理相关的查询。查找附近的餐馆、加油站。
3 Redis的内存淘汰策略
Redis的内存是有限制的,可以在配置文件中设置内存的大小。
java
maxmemory 100mb //指定最大内存为100mb
对于内存淘汰有下面几种策略:
1.不淘汰,最简单的不淘汰内存。
- 所有的键都使用LRU淘汰策略
- 带过期时间的使用LRU淘汰策略
- 所有键随机淘汰
- 带过期时间的键随机淘汰
- 带过期时间的优先淘汰
- 所有键使用LFU算法
- 带过期时间的键使用LFU算法
4.TCP和HTTP的区别,网络模型是哪七层模型
TCP是传输控制协议,在传输层,在两个端点之间提供了可靠的、面向连接、基于字节流的服务,保证数据按序到达并且无误。
HTTP是应用层的协议,HTTP是超文本传输协议,用来传输图片、文字、音频的协议。
TCP 需要通过三次握手建立连接、四次挥手释放连接。 对于可靠传输、流量控制、拥塞控制 还得复习。
HTTP 客户端发送请求,服务器返回HTTP响应。基本请求 get、delete、put、post请求。 这边又可以衍生出来好多关于计算机网络的问题。
网络模型包括了
应用层、 表示层、会话层、传输层、网络层、数据链路层、物理层。一共七层
- 应用层处理具体的应用程序任务(如网页浏览、文件传输等)。
- 表示层负责数据格式的转换、加解密等。
- 会话层管理会话并确保对话的可靠性。
- 传输层保证端到端的数据传输可靠性。
- 网络层负责数据包的路由和转发。
- 数据链路层确保数据在物理网络上传输时无误。
- 物理层负责通过硬件实现比特流的传输。
5.Exception和Error有什么区别?
都是基础与Throwable,标识程序允许出现了异常。
exception: 标识程序出现是逻辑上面的错误,可以被捕获进行处理,后序还是可以继续执行的。
受检 编译的时候就会被检查 sqlexception、ClassNotFoundException
非受检异常 允许时候会跑出来的,不需要显式的捕获或者声明。 通常是程序逻辑错误,比如 空指针、下标越界。
Error 是系统级别的错误,程序无法再运行的,比如 内存不足、栈移除、虚拟机错误等。
OutOfMemoryError
:内存不足。StackOverflowError
:栈溢出。VirtualMachineError
:虚拟机错误(例如JVM崩溃)
6如何处理异常
在开发中使用全局异常处理器+自定义异常来处理。
设置全局异常处理器。
使用RestControllerAdvice 实现全局异常处理。
plain
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public Result handleException(Exception ex) {
return Result.error(ex.getMessage());
}
}
自定义一个登录异常:
plain
public class LoginException extends RuntimeException{
public LoginException() {
}
public LoginException(String message) {
super(message);
}
public LoginException(String message, Throwable cause) {
super(message, cause);
}
public LoginException(Throwable cause) {
super(cause);
}
public LoginException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
7创建线程的方式
第一种创建线程的方式 继承Thread类,有个缺点是继承Thread之后就不能继承其他类了,因为Java是单继承机制。
java
public class MyThread extends Thread{
@Override
public void run() {
System.out.println("aaa");
}
public static void main(String[] args) {
Thread thread=new MyThread();
thread.start();
}
}
第二种可以实现Runnable接口。
java
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("aa");
}
public static void main(String[] args) {
Runnable runnable=new MyRunnable();
Thread thread=new Thread(runnable);
thread.start();
}
}
通过线程池创建线程
java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("aa");
}
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(10);
for (int i = 0; i < 5; i++) {
executorService.submit(new MyRunnable());
}
executorService.shutdown();
}
}
**ExecutorService executorService = Executors.newFixedThreadPool(3);**
:- 创建一个固定大小的线程池,大小为 3。即线程池中最多只能同时有 3 个线程在执行任务。
**executorService.submit(new MyTask("Task-" + i));**
:- 使用
submit()
方法提交任务。任务会被线程池中的线程执行,执行结果会异步返回。
8涉及模式有哪些
涉及模式分类 创建型模式 、结构型模式 和行为型模式
常见的一些涉及
创建型模式(Creational Patterns)
这些模式主要关注对象的创建,提供了一种创建对象的方式,增加了代码的灵活性和复用性。
- 单例模式****(Singleton Pattern)
- 目的:确保一个类只有一个实例,并提供一个全局访问点。
- 应用场景:配置管理器、日志记录器等需要全局唯一实例的场景。
- 工厂方法模式****(Factory Method Pattern)
- 目的:定义一个用于创建对象的接口,让子类决定实例化哪一个类。
- 应用场景:需要在运行时决定创建哪个对象的场景。
- 抽象工厂模式****(Abstract Factory Pattern)
- 目的:提供一个创建一系列相关或相互依赖对象的接口,而无需指定它们具体的类。
- 应用场景:需要创建一系列相关对象的场景,如UI组件的不同风格。
- 建造者模式(Builder Pattern)
- 目的:将一个复杂对象的构建过程与其表示分离,使得同样的构建过程可以创建不同的表示。
- 应用场景:对象构建过程复杂,或者需要不同的表示方式的场景。
- 原型模式(Prototype Pattern)
- 目的:通过复制现有的实例来创建新的实例,而不是通过新建操作。
- 应用场景:创建对象的成本较高,或者需要大量类似对象的场景。
结构型模式(Structural Patterns)
这些模式主要关注类和对象的组合,通过继承和组合实现更大的结构。
- 适配器模式(Adapter Pattern)
- 目的:将一个类的接口转换成客户希望的另一个接口,使得原本由于接口不兼容而无法一起工作的类可以一起工作。
- 应用场景:需要使用现有类,但其接口与需求不符的场景。
- 装饰器模式(Decorator Pattern)
- 目的:动态地给一个对象添加一些额外的职责,就增加功能来说,装饰器模式比生成子类更为灵活。
- 应用场景:需要动态地给对象添加功能,而不改变其结构的场景。
- 代理模式(Proxy Pattern)
- 目的:为其他对象提供一种代理以控制对这个对象的访问。
- 应用场景:需要在访问对象时进行控制或增强功能,如懒加载、安全控制等。
- 外观模式(Facade Pattern)
- 目的:为子系统中的一组接口提供一个一致的高层接口,使子系统更易使用。
- 应用场景:需要为复杂子系统提供简单接口的场景。
- 桥接模式(Bridge Pattern)
- 目的:将抽象部分与实现部分分离,使它们可以独立变化。
- 应用场景:抽象和实现需要独立扩展的场景。
行为型模式(Behavioral Patterns)
这些模式主要关注对象之间的通信和职责分配。
- 观察者模式(Observer Pattern)
- 目的:定义对象间的一种一对多的依赖关系,当一个对象状态发生改变时,所有依赖于它的对象都会收到通知并自动更新。
- 应用场景:事件处理系统、订阅-发布系统。
- 策略模式(Strategy Pattern)
- 目的:定义一系列算法,并使它们可以互相替换,让算法独立于使用它的客户端。
- 应用场景:需要在运行时选择不同算法的场景。
- 命令模式(Command Pattern)
- 目的:将请求封装为对象,从而使你可以用不同的请求对客户进行参数化。
- 应用场景:需要对操作进行排队、记录或撤销的场景。
- 责任链模式(Chain of Responsibility Pattern)
- 目的:使多个对象都有机会处理请求,避免请求的发送者和接收者之间的耦合关系。
- 应用场景:需要多个对象处理同一个请求的场景,如事件处理。
- 状态模式(State Pattern)
- 目的:允许一个对象在其内部状态改变时改变其行为,对象看起来好像修改了它的类。
- 应用场景:对象的行为依赖于其状态,且状态会改变的场景。
- 中介者模式(Mediator Pattern)
- 目的:用一个中介对象来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散。
- 应用场景:对象之间关系复杂,直接交互会导致高度耦合的场景。
- 迭代器模式(Iterator Pattern)
- 目的:提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露该对象的内部表示。
- 应用场景:需要遍历不同集合的场景。
Spring中的设计模式包括了
1.单列模式,每一个Bean的作用域都是单列的,确保容器中只有一个bean。
2.工厂模式: 管理和创建对象实例,使得创建逻辑与使用逻辑分离。
3.代理模式 AOP切面编程,使用JDK的动态代理实现。
plain
@Service
public class UserService {
@Transactional
public void createUser(User user) {
// 用户创建逻辑
}
}
Spring通过代理在createUser
方法执行前后插入事务管理逻辑。
4.模板方法模式
Spring的JdbcTemplate :JdbcTemplate
简化了JDBC操作,封装了连接管理、异常处理等步骤,开发者只需关注具体的数据库操作逻辑。
9.Cookie和Session的区别是什么
在Web开发中,Cookie 和Session都是用于在客户端和服务器之间维护状态信息的重要机制。由于HTTP协议本身是无状态的(每个请求都是独立的,服务器不会记住之前的请求),因此需要借助Cookie和Session来实现用户的会话管理和状态保持。
-
Cookie:
-
客户端存储:Cookie数据存储在用户的浏览器中,每次请求都会自动携带相关的Cookie信息发送到服务器。
-
Session:
-
服务器端存储:Session数据存储在服务器内存或持久化存储(如数据库)中,客户端只保存一个唯一的Session ID(通常通过Cookie传递)。
-
Cookie:
-
容量限制:每个Cookie的大小通常限制在4KB左右,不适合存储大量数据。
-
Session:
-
容量较大:Session存储在服务器端,可以存储较大和复杂的数据,几乎不受容量限制(受服务器资源约束)。
-
Cookie:
-
相对不安全 :由于Cookie存储在客户端,用户可以通过浏览器查看和修改Cookie内容,存在被篡改或盗用的风险。尽管可以使用
HttpOnly
和Secure
等标志来增强安全性,但仍需谨慎处理敏感数据。 -
Session:
-
相对安全:Session数据存储在服务器端,用户无法直接访问或修改,降低了敏感数据被泄露的风险。唯一暴露给客户端的是Session ID,虽然Session ID本身也需要保护(如防止会话劫持),但总体安全性更高。
-
Cookie:
-
可控的过期时间 :可以通过设置
Expires
或Max-Age
属性来定义Cookie的有效期,过期后自动删除。也可以设置为会话Cookie(关闭浏览器后自动删除)。 -
Session:
-
服务器端管理:Session的生命周期由服务器控制,通常基于用户活动的超时机制(如30分钟无操作则失效)。当用户关闭浏览器时,Session并不会立即失效,除非服务器配置了会话终止策略。
-
Cookie:
-
适用于:
- 存储少量非敏感数据,如用户偏好设置、记住用户名等。
- 实现"记住我"功能,保持用户登录状态。
- 跟踪用户行为(需注意隐私和合规性)。
-
Session:
-
适用于:
- 存储敏感数据,如用户认证信息、购物车内容等。
- 需要在多个页面之间共享复杂的数据结构。
- 实现用户会话管理,保持用户在整个访问过程中的状态。
分布式系统中使用session较为复杂。现在一般都是使用JWT令牌。
10.数据库中索引的种类
数据库中的索引主要有
B+树: 大部分都是支持b+树索引的,三层就可以存储千万级别的数据,查找效率很高。
hash索引 只支持精确查找,不支持范围查找。
R-tree(空间索引) 空间索引是MyISAM引擎的一个特殊索引类型,主要用于地理空间数据类
型,通常使用较少
Full-text索引:是一种通过建立倒排索引,快速匹配文档的方式。
11.聚簇索引和非簇索引的区别
聚簇索引决定了数据在磁盘上的物理存储顺序,每一张表只能有一个聚簇索引。
**叶子节点包含数据:在聚簇索引的B树结构中,叶子节点直接存储数据行。 **
非聚簇索引不决定数据在磁盘上的物理存储顺序。它们是独立于数据表的结构,包含索引列的值和指向实际数据行的指针(如行地址或聚簇索引键)。
在非聚簇索引的B树结构中,叶子节点存储索引列的值和指向数据行的指针。 这个指针就是主键ID,这个时候就需要发生回表查询
name是非聚簇索引,b+树中叶子节点存储的是id值,还需要到聚簇索引中去查找一遍,这个过程叫做回标。
12.左连接的关键字是什么 左连接和右连接有什么区别
左外连接的关键字是 left join
左外连接相当于查询表1(左表)的所有数据,当然也包含表1和表2交集部分的数据。
SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ... ;
外连接相当于查询表2(右表)的所有数据,当然也包含表1和表2交集部分的数据。
SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ... ;
- 左连接(LEFT JOIN) :保留左表(
<font style="color:rgb(52,73,94);">FROM</font>
子句中指定的第一个表)的所有记录,即使右表中没有匹配的记录。 - 右连接(RIGHT JOIN) :保留右表(
<font style="color:rgb(52,73,94);">JOIN</font>
子句中指定的第二个表)的所有记录,即使左表中没有匹配的记录。
13.HashMap底层的数据结构
hashmap也是经常考察的内容
https://blog.csdn.net/qq_37084904/article/details/109243886
14.如何遍历一个HashMap