1.JDK和JRE 有什么区别?

jvm是java虚拟机。JRE有核心类库:java.lang 包。JDK里包含JRE,给java开发人员使用

2.==和equals的区别是什么?

==是判断两个对象的地址是不是相等(基本数据类型==比较的是值相等,引用行数据类型==比较的是内存地址,即是否来自同一个对象)
equals也是判断两个对象是否相等:
类没有覆盖equals方法比较的是两个对象是否为同一对象,等价于通过==比较两个对象
类覆盖方法的话比较的是两对象的内容是否相等(一般是两个对象)
String中的equals方法是被重写过了的

== : 它的作用是判断两个对象的地址是不是相等。即,判断两个对象是不是同 一个对象。(基本数据类型 == 比较的是值,引用数据类型 == 比较的是内存地址) equals() : 它的作用也是判断两个对象是否相等。但它一般有两种使用情况: 情况1:类没有覆盖 equals() 方法。则通过 equals() 比较该类的两个对象时, 等价于通过“==”比较这两个对象。 情况2:类覆盖了 equals() 方法。一般,我们都覆盖 equals() 方法来两个对象 的内容相等;若它们的内 容相等,则返回 true (即,认为这两个对象相等)。 举个例子:

image-20230817135932905
说明:
String中的equals方法是被重写过的,因为object的equals方法是比较的对象的 内存地址,而 String的equals方法比较的是对象的值。 当创建String类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要 创建的值相同的对 象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建 一个String对象。

3.String 类的常用方法都有那些?

equals:比较字符串
split:分割字符串
subString:截取字符串
length:返回字符串长度
indexOf:返回指定字符的索引
charAt:返回指定索引处的字符
replace:字符串的替换
trim:去除字符串两端的空白
getBytes:返回字符串的byte类型数组
toLowerCase:将字符串转换成小写字母
toUpperCase:将字符串转换成大写字母

4.接口和抽象类的区别?

  • 接口默认是public的
  • 方法在接口中不能实现
  • 接口不能new实例化,只能声明
  • 一个类实现接口需要实现接口中所有的方法,而抽象类不一定
  • 一个类可以实现多个接口,但只能实现一个抽象类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
// 接口
public interface Vehicle {
void start();
void stop();
int getSpeed();
}

// 抽象类
public abstract class Animal {
private String name;

public Animal(String name) {
this.name = name;
}

public abstract void makeSound();

public void sleep() {
System.out.println(name + " is sleeping.");
}

public String getName() {
return name;
}
}

在上面的代码中,Vehicle 是一个接口,它定义了 start()stop()getSpeed() 三个方法。接口中的方法默认是抽象的,因此没有具体的实现。

Animal 是一个抽象类,它包含了一个私有的字段 name 和一个构造方法用于初始化该字段。抽象类中可以包含抽象方法和具体方法。在上面的例子中,makeSound() 是一个抽象方法,没有具体的实现,而 sleep() 是一个具体方法,它有一个默认的实现。抽象类可以有字段、构造方法、具体方法和抽象方法的组合。

接口和抽象类都是用来定义类的结构和行为的,但它们有一些区别。接口只能定义抽象方法和常量,而抽象类可以包含字段和具体方法。一个类可以实现多个接口,但只能继承一个抽象类。接口用于描述类的行为,而抽象类通常用于作为其他具体类的基类,提供一些通用的方法和字段。

5.Java 中i0 流分为几种?

按功能来分:输入流 (input)、输出流 (output)。
按类型来分:字节流和字符流。
字节流和字符流的区别是:字节流按8位传输以字节为单位输入输出数据,宇符流按16位传输以字符为单位输入输出数据。

6.Java 容器(集合)都有哪些?

list,set,map。
list有序的,可重复,允许有多个null。set是无序的,不可重复,只能有一个null。map只能在key上有一个null,value可以有多个null

7.HashMap 和Hashtable 有什么区别?

  • HashTable是线程同步,HashMap非线程同步
  • HashTable不允许<key,value>有空值,HashMap允许<key,value>有空值
  • HashTable使用Enumeration,HashMap使用Iterator。
  • TreeMap能够把它保存的记录根据键排序,默认是按升序排序。
  • HashTable中hash数组的默认大小是11,增加方式的old*2+1,HashMap中hash数组的默认大小 是16,增长方式一定是2的指数倍。

8.有哪些方式可以遍历一个 Map?

keySet,entrySet,迭代器

  1. 使用 keySet() 方法遍历键:

    1
    2
    3
    4
    5
    6
    7
    Map<K, V> map = ...; // 假设为要遍历的 Map
    for (K key : map.keySet()) {
    // 获取键对应的值
    V value = map.get(key);
    // 处理键值对
    // ...
    }
  2. 使用 entrySet() 方法遍历键值对:

    1
    2
    3
    4
    5
    6
    7
    Map<K, V> map = ...; // 假设为要遍历的 Map
    for (Map.Entry<K, V> entry : map.entrySet()) {
    K key = entry.getKey();
    V value = entry.getValue();
    // 处理键值对
    // ...
    }
  3. 使用迭代器遍历键值对:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    Map<K, V> map = ...; // 假设为要遍历的 Map
    Iterator<Map.Entry<K, V>> iterator = map.entrySet().iterator();
    while (iterator.hasNext()) {
    Map.Entry<K, V> entry = iterator.next();
    K key = entry.getKey();
    V value = entry.getValue();
    // 处理键值对
    // ...
    }

9.线程和进程的区别?

线程是进程内部,

举个例子,可以将进程比作一个运行的程序,而线程则是程序中的一个执行路径。假设你在电脑上同时打开了多个应用程序,比如浏览器、音乐播放器和文本编辑器。每个应用程序都是一个进程,它们独立运行,并且拥有各自的内存空间和资源。而每个应用程序内部可能会有多个线程,比如浏览器中的渲染线程、网络请求线程和 JavaScript 执行线程等。这些线程共享浏览器进程的内存空间和资源,协同工作来完成各种任务,例如同时下载多个文件、渲染网页内容和响应用户交互等。在这个例子中,每个应用程序是一个进程,而应用程序内部的线程是在进程内部进行调度和执行的。

当我们试图理解进程和线程的区别时,可以通过比喻来简化这些概念。一个常用的比喻是将进程比作工厂,而线程则比作工厂里的工人。

进程比作工厂

想象一个进程像是一个工厂。每个工厂(进程)拥有它自己的资源:建筑物(内存空间)、电力供应(CPU时间)、原料(程序代码和数据)等。每个工厂独立运作,生产产品(执行程序)。工厂之间互不干扰,它们各自管理自己的资源。如果一个工厂发生故障(进程崩溃),它不会影响到其他工厂的运作。

线程比作工人

在每个工厂内部,有许多工人(线程)在工作。这些工人共享工厂的资源:他们在同一建筑内工作,使用相同的电力供应和原料。但每个工人负责完成特定的任务(执行特定的代码段)。一些工人可能在装配线上工作(执行计算任务),而其他工人可能在包装部门(处理输入/输出任务)。

工人之间可以轻松地互相沟通(线程间通信),因为他们处于同一个工厂内。他们可以直接交谈,交换工具或材料,而不需要通过外部邮递服务(进程间通信需要更复杂的机制)。

如果一个工人遇到问题(线程崩溃),这可能会影响到同一工厂内的其他工人,因为他们共享同样的工作环境。在最坏的情况下,这可能导致整个工厂停工(进程崩溃)。

创建和管理

创建一个新工厂(进程)需要大量的资源和时间,因为你需要建造建筑物,安装设备,招聘员工等。相比之下,雇佣一个新工人(创建一个线程)成本较低,因为工厂的基础设施已经就绪,只需要为新工人提供一些个人工具和工作空间即可。

总结

通过将进程比作工厂,线程比作工人,我们可以更容易地理解它们之间的关系和区别。进程是资源分配和执行的独立单位,拥有自己的地址空间和资源。线程是进程内部的执行单位,共享进程的资源,但是能独立执行任务,提高工作效率。

10.守护线程是什么?

在 Java 中,守护线程(Daemon Thread)是一种特殊类型的线程,它是为其他线程提供服务的线程。与普通线程(用户线程)相比,守护线程具有以下特点:

  1. 生命周期依赖:守护线程的生命周期依赖于其他非守护线程。当所有非守护线程都结束时,虚拟机会自动退出,不会等待守护线程执行完毕。
  2. 后台执行:守护线程在后台默默地执行任务,不会阻止程序的终止。当所有非守护线程结束后,守护线程会被自动终止。
  3. 资源释放:守护线程主要用于执行一些后台任务,如垃圾回收、自动保存等。它们通常不处理关键业务逻辑,因此在程序终止时,不需要手动释放守护线程所占用的资源。

在 Java 中,可以通过 setDaemon(true) 方法将线程设置为守护线程。默认情况下,线程是非守护线程。

下面是一个简单的示例,演示了守护线程的使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class DaemonThreadExample {
public static void main(String[] args) {
Thread daemonThread = new Thread(() -> {
while (true) {
System.out.println("Daemon thread is running.");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});

daemonThread.setDaemon(true); // 将线程设置为守护线程

daemonThread.start(); // 启动守护线程

System.out.println("Main thread ends.");
}
}

在上面的示例中,创建了一个守护线程,它会每隔一秒输出一条信息。主线程启动守护线程后,立即结束,而守护线程会在后台一直运行,直到程序终止。

11.创建线程有哪几种方式?

12.什么是反射?

什么是反射机制?
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个 对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为 java语言的反射机制。
静态编译和动态编译
静态编译:在编译时确定类型,绑定对象
动态编译:运行时确定类型,绑定对象
反射机制优缺点
优点: 运行期类型的判断,动态加载类,提高代码灵活度。
缺点: 性能瓶颈:反射相当于一系列解释操作,通知 JVM 要做的事情,性能 比直接的java代码要慢很多。
反射机制的应用场景有哪些?
反射是框架设计的灵魂。
在我们平时的项目开发过程中,基本上很少会直接使用到反射机制,但这不能说明反射机制没有用,实 际上有很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码;动态代理设计模式也采用了反射机制,还有我们日常使用的 Spring/Hibernate 等框架也大量使用到了反射机制。
举例:①我们在使用JDBC连接数据库时使用Class.forName()通过反射加载数据库的驱动程序;
②Spring框架也用到很多反射机制, 经典的就是xml的配置模式。Spring 通过 XML 配置模式装载 Bean 的过程:

1)将程序内所有 XML 或 Properties 配置文件加载入内存中;
2)Java类里面解析xml或 properties里面的内容,得到对应实体类的字节码字符串以及相关的属性信息;
3)使用反射机制,根据这 个字符串获得某个类的Class实例;
4)动态配置实例的属性

Java获取反射的三种方法
1.通过new对象实现反射机制 2.通过路径实现反射机制 3.通过类名实现反射机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
public class Student {

private int id;

String name;

protected boolean sex;

public float score;

}

public class Get {

//获取反射机制三种方式

public static void main(String[] args) throws ClassNotFoundException {

//方式一(通过建立对象)

Student stu = new Student();

Class classobj1 = stu.getClass();

System.out.println(classobj1.getName());

//方式二(所在通过路径-相对路径)

Class classobj2 = Class.forName("fanshe.Student");

System.out.println(classobj2.getName());

//方式三(通过类名)

Class classobj3 = Student.class;

System.out.println(classobj3.getName());

}

}

13.什么是Java 序列化?什么情况下需要序列化?

14.如何实现对象克隆?

15.深拷贝和浅拷贝区别是什么?

浅拷贝(shallowCopy)只是增加了一个指针指向已存在的内存地址, 深拷贝(deepCopy)是增加了一个指针并且申请了一个新的内存,使这个增加 的指针指向这个新的内 存, 使用深拷贝的情况下,释放内存的时候不会因为出现浅拷贝时释放同一个内存的 错误。 浅复制:仅仅是指向被复制的内存地址,如果原地址发生改变,那么浅复制出来 的对象也会相应的改变。 深复制:在计算机中开辟一块新的内存地址用于存放复制的对象。

  • 存储位置:session是存放在服务器端的,cookie是存放在浏览器里
    • 安全性:cookie可以被用户随意篡改,是不安全的。session将数据存放在数据库或者内存中,用户无法直接访问修改,是更加安全的
      • 跨域支持:cookie支持跨域访问,多个域名之间共享数据。而session通常与单个域名相关,不支持跨域
        • 生命周期:cookie的生命周期可以设置过期时间,在浏览器关闭后可以不销毁。而session的生命周期一般是伴随着浏览器的关闭或者超过一定时间不用了就会自动销毁。
          • 存储位置:cookie是存在浏览器的cookie文件中。而session是存在内存或者数据库中
            • 存储容量:cookie的存储容量是比较小的,session容量可以更大
              • 性能开销:session存储在服务器中,它是一个用户对应一个session,随着用户的数量增加,对服务器的负载也会变大。cookie存储在客户端中,对服务器的负载小

17.get 和post 请求有哪些区别?

GET和POST是HTTP协议中两种常用的请求方法,它们有以下区别:

  1. 数据传输方式:
    • GET请求:通过URL参数传递数据,数据附加在URL的查询字符串中,可以在浏览器地址栏中看到。GET请求的数据传输是明文的,不适合传输敏感信息。
    • POST请求:通过请求体传递数据,数据不会显示在URL中,对传输的数据进行了加密处理,更适合传输敏感信息。
  2. 数据长度限制:
    • GET请求:由于数据附加在URL中,URL长度有限制,不同浏览器和服务器对URL长度的限制不同,通常在2048个字符左右。
    • POST请求:没有长度限制,可以传输大量数据。
  3. 安全性:
    • GET请求:数据在URL中可见,容易被截获和篡改,不适合传输敏感信息。
    • POST请求:数据在请求体中,不会显示在URL中,相对于GET请求更安全。
  4. 幂等性:
    • GET请求:对服务器的资源操作是幂等的,即多次请求同一个URL返回的结果是相同的,不会对服务器产生副作用。
    • POST请求:对服务器的资源操作可能是非幂等的,多次请求同一个URL可能会产生不同的结果,可能对服务器产生副作用。
  5. 缓存:
    • GET请求:可以被浏览器缓存,当再次请求相同URL时,浏览器可以直接从缓存中获取响应结果。
    • POST请求:不会被浏览器缓存,每次请求都会向服务器发送请求。
  6. 使用场景:
    • GET请求:适合获取资源、查询数据,对于幂等操作和不涉及敏感信息的请求。
    • POST请求:适合提交数据、修改资源,对于非幂等操作和涉及敏感信息的请求。

需要根据具体的需求和场景选择合适的请求方法,合理使用GET和POST可以提高系统的性能和安全性。

假设你在一家餐厅(服务器)点餐(发送请求)。你可以通过两种方式提交你的订单(数据):

  1. GET请求比喻:通过大声告诉服务员(URL)你想要的食物,例如:“我想要一个汉堡和一份薯条。” 这种方式类似于GET请求,因为你的订单(数据)对周围的人来说是可见的,容易被人听到(数据通过URL传递,容易被拦截)。此外,如果你的订单非常长,你可能会因为太多信息而让服务员(服务器)难以记住,或者服务员根本听不过来(URL长度限制)。
  2. POST请求比喻:你写下你的订单(数据)在一张纸上,然后递给服务员(HTTP消息体)。这样,你的订单对其他客人来说是不可见的(数据不在URL中,更安全)。同时,你可以在纸上写下尽可能多的特殊要求,因为纸张容纳的信息量远大于你能大声说出的(没有数据大小限制)。

18.如何避免 SQL注入?

19.try-catch-finally 中哪个部分可以省略?

catch可以省略

原因 :
更为严格的说法其实是:try只适合处理运行时异常,try+catch适合处理运行时异常+普通异常。也就是说,如果你只用try去处理普通异常却不加以catch处 理,编译是通不过的,因为编译器硬性规定,普通 异常如果选择捕获,则必须用 catch显示声明以便进一步处理。而运行时异常在编译时没有如此规定, 所以 catch可以省略,你加上catch编译器也觉得无可厚非。 理论上,编译器看任何代码都不顺眼,都觉得可能有潜在的问题,所以你即使对 所有代码加上try,代码 在运行期时也只不过是在正常运行的基础上加一层皮。 但是你一旦对一段代码加上try,就等于显示地承 诺编译器,对这段代码可能抛 出的异常进行捕获而非向上抛出处理。如果是普通异常,编译器要求必须 用 catch捕获以便进一步处理;如果运行时异常,捕获然后丢弃并且+finally扫尾 处理,或者加上catch 捕获以便进一步处理。 至于加上finally,则是在不管有没捕获异常,都要进行的“扫尾”处理。


第一家:与工厂对接的、20人左右、转正后薪资 4.5k-5k

问了以下问题:

1.Spring 与 Spring Boot 的区別

Spring和Spring Boot是两个相关的框架,它们有以下区别:

  1. 复杂性:Spring是一个全功能的企业级开发框架,提供了广泛的功能和组件,如依赖注入、AOP、事务管理、MVC框架等。Spring框架需要进行显式的配置和集成,因此在配置和启动上可能需要更多的工作。而Spring Boot是建立在Spring框架之上的工具,旨在简化Spring应用程序的初始化和开发过程。Spring Boot通过自动配置和约定优于配置的原则,大大减少了开发人员的配置工作,使得快速构建和部署应用变得更加容易。
  2. 配置方式:在Spring中,开发人员需要显式地进行配置,通常使用XML配置文件或Java注解来定义和管理Bean、依赖关系、事务等。而Spring Boot采用了约定优于配置的原则,通过自动配置和默认值,减少了大部分的配置工作。Spring Boot使用基于Java的配置(如注解配置)和属性文件来管理应用程序的配置。
  3. 依赖管理:Spring框架的依赖管理相对灵活,开发人员可以根据需要选择和配置所需的库和组件。而Spring Boot通过提供一组预定义的“Starter”依赖简化了依赖管理。这些Starter依赖包含了常用的库和组件,可以根据应用程序的需求进行选择和引入。
  4. 内嵌服务器:Spring Boot内置了一个嵌入式的Servlet容器(如Tomcat、Jetty等),可以方便地将应用程序打包为可执行的JAR文件,并直接运行。这样可以简化部署过程,不再需要独立安装和配置外部的Servlet容器。

总的来说,Spring Boot是建立在Spring框架之上的工具,旨在简化Spring应用程序的初始化、配置和部署过程。它通过自动配置、约定优于配置的原则、依赖管理和内嵌服务器等特性,提供了更简单、更快速的开发体验。Spring框架则提供了更多的灵活性和自定义能力,适用于更复杂和定制化的企业级应用开发。

Spring Boot 的核心注解是哪个?它主要由哪几个注解组成的?
启动类上面的注解是@SpringBootApplication,它也是 Spring Boot 的核心注解,主要组合包含了以下 3 个注解:
@SpringBootConfiguration:组合了@Configuration 注解,实现配置文件的功能。
@EnableAutoConfiguration:打开自动配置的功能,也可以关闭某个自动配置的选项,如关闭数据源 自动配置功能: @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })。

@ComponentScan:Spring组件扫描。

2.Spring Bean 的生命周期

实例化,属性赋值,初始化,使用,销毁

在 Spring 框架中,Bean 的生命周期包括以下几个阶段:

  1. 实例化(Instantiation):在这个阶段,Spring 容器会根据配置信息或注解创建一个 Bean 的实例。可以通过构造函数实例化或使用工厂方法创建 Bean。
  2. 属性赋值(Population):在实例化之后,Spring 容器会将配置的属性值或依赖注入到 Bean 实例中。这可以通过 XML 配置、注解或自动装配等方式完成。
  3. 初始化(Initialization):在属性赋值完成后,Spring 容器会调用 Bean 的初始化方法,进行一些自定义的初始化逻辑。可以通过实现 InitializingBean 接口或使用 @PostConstruct 注解来定义初始化方法。
  4. 使用(In Use):在初始化完成后,Bean 可以被应用程序使用。它可以响应应用程序的请求,提供所需的功能。
  5. 销毁(Destruction):在应用程序关闭或容器销毁时,Spring 容器会调用 Bean 的销毁方法,执行一些清理操作。可以通过实现 DisposableBean 接口或使用 @PreDestroy 注解来定义销毁方法。

需要注意的是,Bean 的生命周期可以通过配置或注解进行控制,例如使用 XML 配置的 init-method 和 destroy-method 属性,或使用 @PostConstruct 和 @PreDestroy 注解。

另外,Spring 还提供了 BeanPostProcessor 接口,它允许在 Bean 的初始化前后进行一些自定义的处理。通过实现 BeanPostProcessor 接口,可以在 Bean 的初始化前后对 Bean 进行修改或增强。

总结起来,Spring Bean 的生命周期包括实例化、属性赋值、初始化、使用和销毁这几个阶段。通过配置或注解,可以控制 Bean 在每个阶段的行为,并在需要时执行自定义的初始化和销毁逻辑。

实例化,属性赋值,初始化对象,使用,销毁

3.Spring MVC 的执行流程

Spring MVC 是一种基于 Spring 框架的 Web 应用程序开发框架,它通过 MVC(Model-View-Controller)的设计模式来组织和处理请求。下面是 Spring MVC 的执行流程:

  1. 客户端发送请求:客户端(如浏览器)发送一个 HTTP 请求到服务器。
  2. DispatcherServlet 接收请求:DispatcherServlet 是 Spring MVC 的核心控制器,它拦截所有的请求,并作为前端控制器来处理请求。DispatcherServlet 接收到请求后,根据配置的 URL 映射规则将请求分发给相应的处理器。
  3. 处理器映射器(Handler Mapping)进行映射:处理器映射器根据配置的 URL 映射规则,将请求映射到一个具体的处理器(Controller)。
  4. 处理器执行请求:处理器(Controller)是一个用于处理请求的组件,它根据业务逻辑处理请求,并生成一个 ModelAndView 对象,包含处理结果和视图信息。
  5. 处理器适配器(Handler Adapter)执行处理器:处理器适配器负责执行处理器,并将处理结果封装成 ModelAndView 对象。
  6. 视图解析器(View Resolver)解析视图:视图解析器根据 ModelAndView 中的视图名称解析出真正的视图对象(View),可以是 JSP、Thymeleaf、Freemarker 等。
  7. 视图渲染:视图对象负责将模型数据渲染成最终的响应结果,可以是 HTML 页面、JSON 数据等。
  8. 响应结果返回给客户端:DispatcherServlet 将渲染后的响应结果返回给客户端,完成请求处理过程。

在整个流程中,Spring MVC 提供了一系列的组件来处理请求,包括 DispatcherServlet、处理器映射器、处理器适配器、视图解析器等。通过配置和扩展这些组件,可以实现灵活的请求处理和视图渲染。同时,Spring MVC 还提供了拦截器、数据绑定、表单验证等功能,以支持更复杂的 Web 应用程序开发。

4.MySQL 的内外连接

在 MySQL 中,内连接(INNER JOIN)和外连接(LEFT JOIN、RIGHT JOIN、FULL JOIN)是用于连接多个表的操作。

  1. 内连接(INNER JOIN):内连接返回两个表中匹配的行。只有当连接条件满足时,才会返回结果。语法如下:
1
2
3
SELECT 列名
FROM1
INNER JOIN2 ON 连接条件;
  1. 左外连接(LEFT JOIN):左外连接返回左表中的所有行,以及右表中满足连接条件的行。如果右表中没有匹配的行,则以 NULL 值填充。语法如下:
1
2
3
SELECT 列名
FROM1
LEFT JOIN2 ON 连接条件;
  1. 右外连接(RIGHT JOIN):右外连接返回右表中的所有行,以及左表中满足连接条件的行。如果左表中没有匹配的行,则以 NULL 值填充。语法如下:
1
2
3
SELECT 列名
FROM1
RIGHT JOIN2 ON 连接条件;
  1. 全外连接(FULL JOIN):全外连接返回左表和右表中的所有行,如果左表或右表中没有匹配的行,则以 NULL 值填充。语法如下:
1
2
3
SELECT 列名
FROM1
FULL JOIN2 ON 连接条件;

在连接条件中,可以使用比较运算符(如 =、<、>)或其他逻辑运算符(如 AND、OR)来指定连接条件。

这些连接操作可以帮助在多个表之间建立关联,并根据连接条件返回合适的结果集。根据业务需求和数据关系,选择适当的连接类型来获取所需的数据。

5.Redis 是如何使用的,有哪些基本数据类型

Redis 是一个开源的高性能键值存储系统,常用于缓存、消息队列、实时统计等场景。它支持多种数据结构和数据类型,包括以下基本数据类型:

  1. 字符串(String):字符串是 Redis 最基本的数据类型,可以存储任意类型的数据,如文本、数字等。字符串类型支持丰富的操作,如设置值、获取值、追加、计数器操作等。
  2. 列表(List):列表是一个有序的字符串集合,可以在列表的两端进行元素的插入和删除操作。列表类型支持根据索引获取元素、获取列表长度、修剪列表等操作,还提供了一些特殊的操作,如弹出元素、阻塞式弹出等。
  3. 哈希(Hash):哈希是一个键值对的集合,类似于关联数组。哈希类型适用于存储对象,每个对象可以包含多个字段和对应的值。哈希类型支持设置字段值、获取字段值、获取所有字段和值等操作。
  4. 集合(Set):集合是一个无序的字符串集合,不允许重复的元素。集合类型支持添加元素、删除元素、判断元素是否存在、计算交集、并集、差集等操作。
  5. 有序集合(Sorted Set):有序集合是一个有序的字符串集合,每个元素都关联一个分数,用于排序。有序集合类型支持添加元素、删除元素、根据分数范围获取元素、根据分数排序等操作。

除了以上基本数据类型,Redis 还提供了一些其他的数据结构和数据类型,如位图(Bitmap)、超级日志(HyperLogLog)、地理位置(Geospatial)等,以满足不同场景下的需求。

6.Nginx 做什么用的

7.Docker 怎么构建镜像的,怎么push 到仓库

  1. 你是怎么引入配置的
    总结:没有问八股文类 java 基础的问题,视频面试的,面完直接让我去公司谈谈

第二家:物流仓促(个人觉得也做外包)、全栈、转正后薪资 4k-4.5k

问了以下问题:

1.java 中八种数据类型,假如有一个类中有属性为身高,那么使用那种数据类型?

int,short,long,byte
float,double
char
boolean

2.有哪几种集合容器,有什么区别,list 和 set 的区别。你怎么看到list 中的有序?

list,set,map

  • list是有序的,允许重复元素,可以插入多个null。
  • set是无序的,set不可以重复元素,只允许一个null
  • map不是collection的子接口,map可以有任意个null值,但是只允许有一个null键,key不能重复,value可以重复

3.前端 form 表単怎么提交数据的

4.Form 传参数怎么传,id 还是 name,后端怎么接收

5.String 类型的日期,怎么在前段显示为日期(这个我没懂没回答出来)

6.Mysql的语句考察(语句挺简单的,好久没写我给忘了,没答出来)

7.前段用的什么东西?

8.你对框架有什么看法?

总结:基础问的多,主要在试探会不会开发

第三家:外包、纯后端、转正后薪资 4.5-5k

有做题目,String、集合的关系图、有哪些运行中异常、算法、sql语句问了以下问题:

你对上一个面试者有什么看法?

Nginx 做什么用的?配置在那个文件,怎么配置的?

Docker 命令有哪些?

RabbitMQ、FastDFS、Elasticsearch 是做什么的?

Git 会不会?

controller 中都有什么注解

@RestController,@Controller,@RequestMapping,@PostMapping,@RequestParam,@RequestBody,@PathVariable

在Spring框架中,尤其是在开发Spring MVC应用时,Controller层使用了多种注解来简化Web应用的开发。这些注解提供了定义请求路由、请求数据处理、响应数据封装等功能。以下是一些常见的与Controller相关的注解:

@Controller

  • @Controller:标识一个类为Spring MVC Controller处理器。Spring框架的控制反转(IoC)容器会自动检测使用了此注解的类,并将它们注册为Controller Bean。

请求映射注解

  • @RequestMapping:用于映射Web请求到具体的处理函数。它可以声明在类或方法上,用于设置请求的URL、HTTP方法等信息。
  • @GetMapping:是@RequestMapping的一个简化版,专门用于处理GET类型的请求。
  • @PostMapping:用于处理POST类型的请求,也是@RequestMapping的简化版。
  • @PutMapping、**@DeleteMapping@PatchMapping**:分别用于处理PUT、DELETE、PATCH类型的HTTP请求。

请求数据处理注解

  • @RequestParam:用于将请求参数绑定到控制器的方法参数上。
  • @PathVariable:用于将URL中的模板变量绑定到控制器的方法参数上。
  • @RequestBody:用于将HTTP请求的body部分绑定到一个对象上,通常用于处理POST请求的内容。
  • @RequestHeader:用于将请求头信息绑定到控制器的方法参数上。

响应数据处理注解

  • @ResponseBody:指示方法的返回值应该被直接在响应体中返回,而不是解释为视图名。
  • @ResponseStatus:用于指定方法完成后应该返回的HTTP状态码。
  • @RestController:是@Controller和@ResponseBody的组合注解,用于简化创建RESTful服务的Controller。使用@RestController,所有方法默认都会使用@ResponseBody。

其他有用的注解

  • @ModelAttribute:用于将方法参数或方法返回值绑定到指定的模型属性上,通常用于表单数据的提交处理。
  • @SessionAttributes:用于在会话中存储模型属性,通常用于跨请求之间共享模型属性。
  • @RequestMapping的变体(如@GetMapping, @PostMapping等)提供了一种便捷的方式来处理特定类型的请求。

总结:基础和框架都有,很实在

第四家:不清楚

有做题目,八股文,抽象类和普通类的区别什么的
问了以下问题:

一、

1.Spring 的文件上传怎么实现的?

在Spring框架中,文件上传可以通过多种方式实现,其中最常用的方法是使用Spring的MultipartResolver接口和MultipartFile类。

以下是文件上传的基本步骤:

  1. 配置MultipartResolver:在Spring配置文件中配置MultipartResolver,用于处理文件上传请求。可以选择使用Spring提供的CommonsMultipartResolver或StandardServletMultipartResolver,具体根据项目所使用的Servlet容器来选择。
  2. 创建文件上传表单:在前端页面中创建一个包含文件上传字段的表单。确保表单的enctype属性设置为”multipart/form-data”,以支持文件上传。
  3. 处理文件上传请求:在控制器中处理文件上传请求。可以通过在方法参数中添加MultipartFile类型的参数来接收上传的文件。例如:
1
2
3
4
5
6
@PostMapping("/upload")
public String handleFileUpload(@RequestParam("file") MultipartFile file) {
// 文件上传处理逻辑
// ...
return "upload-success";
}
  1. 处理上传文件:在处理文件上传的方法中,可以使用MultipartFile对象的方法来获取文件的相关信息和内容。例如,可以使用file.getOriginalFilename()获取原始文件名,使用file.getSize()获取文件大小,使用file.getInputStream()获取文件内容的输入流等。
  2. 保存上传文件:根据业务需求,可以将上传的文件保存到本地文件系统、数据库或其他存储介质中。可以使用Java的IO操作或其他框架(如Apache Commons IO)来实现文件的保存。

需要注意的是,文件上传可能会涉及到文件大小限制、文件类型限制、文件重命名、文件存储路径等问题。可以通过配置MultipartResolver的属性或在控制器中添加相应的验证逻辑来处理这些问题。

此外,Spring还提供了更高级的文件上传功能,例如支持同时上传多个文件、限制文件大小、处理文件上传进度等。可以根据具体需求,使用Spring提供的相关类和方法来实现更复杂的文件上传功能。

2.sql 语句

3.项目中都有什么包名

4.controller 中都有什么注解

@RestController,@Controller,@RequestMapping,@PostMapping,@RequestParam,@RequestBody,@PathVariable

二、

1.String 能不能 用==和equals ?

String可以用重写后的equals方法,用于进行字符串内容的比较
String用==比较两对象的内存地址是否相等

2.技术栈的都问了一边是干什么的?里面有什么用法?

3.Docker 命令

4.AJAX 做什么用的

5.你是怎么引入配置的?

6.mybatis 中$和#的区别

当id=1时传进的参数为
#{id=?}
${id=1}

问题

redis数据类型

Redis 是一个开源的高性能键值存储系统,常用于缓存、消息队列、实时统计等场景。它支持多种数据结构和数据类型,包括以下基本数据类型:

  1. 字符串(String):字符串是 Redis 最基本的数据类型,可以存储任意类型的数据,如文本、数字等。字符串类型支持丰富的操作,如设置值、获取值、追加、计数器操作等。
  2. 列表(List):列表是一个有序的字符串集合,可以在列表的两端进行元素的插入和删除操作。列表类型支持根据索引获取元素、获取列表长度、修剪列表等操作,还提供了一些特殊的操作,如弹出元素、阻塞式弹出等。
  3. 哈希(Hash):哈希是一个键值对的集合,类似于关联数组。哈希类型适用于存储对象,每个对象可以包含多个字段和对应的值。哈希类型支持设置字段值、获取字段值、获取所有字段和值等操作。
  4. 集合(Set):集合是一个无序的字符串集合,不允许重复的元素。集合类型支持添加元素、删除元素、判断元素是否存在、计算交集、并集、差集等操作。
  5. 有序集合(Sorted Set):有序集合是一个有序的字符串集合,每个元素都关联一个分数,用于排序。有序集合类型支持添加元素、删除元素、根据分数范围获取元素、根据分数排序等操作。

除了以上基本数据类型,Redis 还提供了一些其他的数据结构和数据类型,如位图(Bitmap)、超级日志(HyperLogLog)、地理位置(Geospatial)等,以满足不同场景下的需求。

使用 Redis,可以通过 Redis 客户端(如 Redis CLI、Redisson、Lettuce 等)或编程语言的 Redis 客户端库来与 Redis 服务器进行交互,执行各种数据操作和命令。通过合理地选择和使用 Redis 的数据类型,可以实现高效的数据存储和处理。

消息队列的作用——我这里想

mysql 掌握程度,sql优化

服务器部署spring项目吗?

git

服务器

docker


图锐

1.说一下链表

和数组是统一等级,LinkedList是链表,且插入和删除有优势,但循环遍历的效率不高

2.什么是栈

能从栈顶进行进栈和出栈操作,后进先出

3.说一下String是什么

底层是一个char类型的数组,不可继承

4.String StringBuffer 和 StringBuilder 的区别是什么

StringBuffer对方法加了同步锁,或者对调用方法加了同步锁,线程是安全的
StringBuilder是没有对方法加同步锁,线程是不安全的
大批量数据单线程用StringBuilder
大批量数据多线程用StringBuffer

5.字符串拼接的方法

用+进行拼接
stringBuffer的方法进行拼接
stringBuilder的方法进行拼接

6.判断String字符串中是否有abc,字符串的截取

判断是否有abc:string.contains
字符串的截取:string.subString

7.String类中的常用方法:

indexOf():返回指定字符的索引。
charAt():返回指定索引处的字符。
replace():字符串替换。
trim():去除字符串两端空白。
split():分割字符串,返回一个分割后的字符串数组。
getBytes():返回字符串的 byte 类型数组。
length():返回字符串长度。
toLowerCase():将字符串转成小写字母。
toUpperCase():将字符串转成大写字符。
substring():截取字符串。
equals():字符串比较。

递归算法

20

反转链表

图片说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
 /*
public class ListNode {
int val;
ListNode next = null;

ListNode(int val) {
this.val = val;
}
}*/
public class Solution {
public ListNode ReverseList(ListNode head) {
//pre指针:用来指向反转后的节点,初始化为null
ListNode pre = null;
//当前节点指针
ListNode cur = head;
//循环迭代
while(cur!=null){
//Cur_next 节点,永远指向当前节点cur的下一个节点
ListNode Cur_next = cur.next;
//反转的关键:当前的节点指向其前一个节点(注意这不是双向链表,没有前驱指针)
cur.next = pre;
//更新pre
pre = cur;
//更新当前节点指针
cur = Cur_next ;
}
//为什么返回pre?因为pre是反转之后的头节点
return pre;
}
}

冒泡排序

图片说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
    public int[] MySort (int[] arr) {
if(arr.length<2){
return arr;
}

// for(int end=arr.length-1; end>0; end--){//每次选出最大的放到最后,重复;
// for(int i=0;i<end;i++){
// if(arr[i]>arr[i+1]){
// swap(arr,i,i+1);
// }
// }
// }

for(int i=0;i<arr.length-1;i++){
for(int j=0;j<arr.length-i-1;j++){
if(arr[j]>arr[j+1]){
swap(arr,j,j+1);
}
}
}
return arr;
}

public void swap(int[]arr,int i, int j){
int tmp;
tmp=arr[i];
arr[i]=arr[j];
arr[j]=tmp;
}
/* public static void swap(int[] arr, int i, int j) {
arr[i] = arr[i] ^ arr[j];
arr[j] = arr[i] ^ arr[j];
arr[i] = arr[i] ^ arr[j];
}
*/


面向对象

面向对象和面向过程的区别

2.说说面向对象的理解?

面向过程
优点:性能比面向对象高,因为类调用时需要实例化,开销比较大,比较消耗资源;比如单片机、嵌入式 开发、Linux/Unix等一般采用面向过程开发,能是最重要的因素。
缺点:没有面向对象易维护、易复用、易扩展
面向对象: 优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系 统,使系统更加灵活、更加易于维护
缺点:性能比面向过程低 面向过程是具体化的,流程化的,解决一个问题,你需要一步一步的分析,一步 一步的实现。

面向对象是模型化的,你只需抽象出一个类,这是一个封闭的盒子,在这里你拥有数据也拥有解决问题 的方法。需要什么功能直接使用就可以了,不必去一步一步的实现,至于这个功能是如何实现的,管我们什么事?我们会用就可以了。 面向对象的底层其实还是面向过程,把面向过程抽象成类,然后封装,方便我们使用的就是面向对象了。

3.java 内存模型?

4.垃圾回收的目的?

垃圾回收的主要目的是自动管理内存,以减轻程序员的负担,并提高程序的可靠性和性能。以下是垃圾回收的几个目标:

  1. 释放不再使用的内存:垃圾回收器负责检测和回收不再使用的内存空间。当对象不再被引用时,垃圾回收器可以自动将其标记为可回收,并释放其占用的内存。这减少了内存泄漏和内存溢出的风险,确保可用内存供其他对象使用。
  2. 简化内存管理:手动管理内存是一项复杂而容易出错的任务。垃圾回收机制使程序员从手动释放内存的责任中解放出来。程序员不再需要显式地跟踪和释放每个对象的内存,而是可以专注于业务逻辑的实现。这提高了开发效率,并降低了出现内存管理错误的可能性。
  3. 提高程序的可靠性:垃圾回收可以防止一些常见的内存错误,如空指针异常和野指针访问。通过自动检测和清理不再使用的对象,垃圾回收器减少了悬空引用和访问已释放内存的风险,从而提高了程序的稳定性和可靠性。
  4. 优化性能:垃圾回收器可以在运行时选择最佳时间来执行垃圾回收操作。它们通常在应用程序空闲时进行工作,以最小化对应用程序性能的影响。通过自动释放不再使用的内存,垃圾回收器可以减少内存碎片化并提高内存的利用率,从而改善程序的整体性能。

总的来说,垃圾回收的目的是提供一种自动管理内存的机制,减轻程序员的负担,提高程序的可靠性和性能。它使内存管理变得更简单,减少了内存错误的风险,并优化了程序的执行效率。

5.什么时候会进行垃圾回收?

当对象不再被引用时,垃圾回收器可以自动将其标记为可回收,并释放其占用的内存。

6.常用那些集合?有什么特点?

  1. ArrayList:基于数组实现的动态数组。它可以自动扩容,并支持随机访问元素。特点包括高效的随机访问和尾部插入/删除操作,但在中间插入/删除元素时效率较低。
  2. LinkedList:基于链表实现的双向链表。它支持高效的插入/删除操作,但访问任意位置的元素需要遍历链表。特点包括高效的插入/删除操作,但随机访问的效率较低。
  3. HashSet:基于哈希表实现的无序集合。它使用哈希函数来存储元素,具有快速的插入/删除/查找操作。特点包括无序性、不允许重复元素和高效的插入/删除/查找操作。
  4. TreeSet:基于红黑树实现的有序集合。它可以按照元素的自然顺序或自定义比较器进行排序,并支持高效的插入/删除/查找操作。特点包括有序性、不允许重复元素和高效的插入/删除/查找操作。
  5. HashMap:基于哈希表实现的无序键值对集合。它使用键的哈希值来存储和查找值,具有快速的插入/删除/查找操作。特点包括无序性、键唯一性和高效的插入/删除/查找操作。
  6. TreeMap:基于红黑树实现的有序键值对集合。它可以按照键的自然顺序或自定义比较器进行排序,并支持高效的插入/删除/查找操作。特点包括有序性、键唯一性和高效的插入/删除/查找操作。

这些集合类型具有不同的特点和适用场景:

  • ArrayList 和 LinkedList 适用于需要频繁访问元素的场景。ArrayList 在随机访问和尾部插入/删除操作上效率更高,而 LinkedList 在插入/删除元素时效率更高。
  • HashSet 和 TreeSet 适用于需要存储唯一元素的场景。HashSet 提供了快速的插入/删除/查找操作,但不保证元素的顺序。TreeSet 提供了有序性和更多的检索功能,但插入/删除操作稍慢。
  • HashMap 和 TreeMap 适用于存储键值对的场景。HashMap 提供了快速的插入/删除/查找操作,但不保证键的顺序。TreeMap 提供了有序性和更多的检索功能,但插入/删除操作稍慢。

根据具体的需求和性能要求,选择合适的集合类型可以提高程序的效率和可读性。

7.Map可以存储重复数据么?

map的key不能重复,value可以重复

8.list 集合如何去重?

  1. 使用Set集合:将List集合转换为Set集合,因为Set集合不允许重复元素,重复元素会被自动去除。可以使用HashSetTreeSet来存储去重后的元素,并将List集合中的元素逐个添加到Set集合中。例如:
1
2
3
List<Integer> list = Arrays.asList(1, 2, 3, 2, 4, 3, 5);
Set<Integer> set = new HashSet<>(list);
List<Integer> deduplicatedList = new ArrayList<>(set);

9.Redis工作原理?

  1. redis的五中基本数据类型: 答:string、hash、set、zset、list。
    string,set,zset,list,hash

10.Redis如何优化登录流程?目的是什么? 达到什么效果?

MySQL怎么映射

  1. 手动映射:最简单的方式是手动编写代码来处理数据表和对象之间的映射关系。通过使用SQL语句执行查询和更新操作,并将结果映射到对象的属性上,可以实现手动的数据映射。这种方式需要开发人员自行编写映射逻辑,比较灵活,但也需要更多的代码开发工作。
  2. ORM框架:ORM(对象关系映射)框架可以自动处理数据表和对象之间的映射关系,提供了一种更高级的映射方式。在MySQL中,有一些常用的ORM框架可供选择,如Hibernate、Spring Data JPA、Sequelize等。这些框架提供了一套API和配置方式,可以简化数据库操作,并自动将数据表映射到对象上。
  3. 注解:一些ORM框架允许使用注解(Annotation)来标记对象和数据表之间的映射关系。通过在对象的属性或方法上添加注解,可以指定对应的数据表字段名、关联关系等信息,框架会根据注解来自动进行映射。这种方式可以在代码中直观地表示映射关系,减少了配置文件的使用。
  4. XML配置:一些ORM框架使用XML配置文件来定义对象和数据表之间的映射关系。通过在配置文件中指定对象和数据表的对应关系、字段映射规则等信息,框架可以根据配置文件来进行映射。这种方式适用于需要更灵活配置的场景,但相对来说配置较为繁琐。

SpringBoot的工作原理

  1. 自动配置:Spring Boot通过自动配置机制来简化应用程序的配置。它根据应用程序的依赖和类路径上的资源,自动配置Spring框架的各个模块和组件。例如,它会根据类路径上的数据库驱动依赖自动配置数据源和JPA,根据类路径上的Web依赖自动配置Web服务器和Servlet容器等。自动配置可以减少开发人员的配置工作,提高开发效率。
  2. 起步依赖:Spring Boot使用起步依赖(Starter Dependencies)来简化依赖管理。起步依赖是一组预配置的依赖项集合,它们提供了常见场景下所需的依赖,例如Web应用程序、数据库访问、安全性等。通过引入适当的起步依赖,开发人员可以轻松地获取所需的功能和依赖,而不需要手动配置和管理各个依赖项。
  3. 内嵌服务器:Spring Boot支持内嵌服务器,可以将应用程序打包为可执行的JAR文件,并直接运行在内嵌的Web服务器中,如Tomcat、Jetty等。这样可以简化应用程序的部署和运行,不需要额外安装和配置外部的Web服务器。
  4. 约定优于配置:Spring Boot遵循约定优于配置的原则,通过一些默认的约定来减少配置。例如,它会根据类路径上的资源和注解自动扫描组件,并进行相应的自动配置。开发人员只需要按照一些约定来编写代码和组织项目结构,就能够获得一些默认的配置和行为。
  5. 外部化配置:Spring Boot支持外部化配置,可以将应用程序的配置信息放在外部的配置文件中,如application.properties或application.yml。通过使用这些配置文件,开发人员可以在不修改代码的情况下修改应用程序的配置,例如数据库连接信息、日志级别等。
  6. Actuator:Spring Boot提供了Actuator模块,用于监控和管理应用程序。Actuator可以提供有关应用程序运行状况、健康状况、性能指标等方面的信息,可以通过HTTP接口或JMX进行访问。通过Actuator,开发人员可以方便地监控和管理应用程序。

什么是事务的隔离级别?MySQL的默认隔离级别是什么?

为了达到事务的四大特性,数据库定义了4种不同的事务隔离级别,由低到高依次为Read uncommitted、Read committed、Repeatable read、Serializable,这四个级别可以逐个解决脏读、 不可重复读、幻读这几类问题。
image-20230817175317428

SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED(读取未提交): 最低的隔离级别,允许读取尚未提交的数据变更,可能会导 致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交): 允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读 或不可重复读仍有可能发生。 REPEATABLE-READ(可重复读): 对同一字段的多次读取结果都是一致的,除非数据是被本身事务 自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化): 最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执 行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
这里需要注意的是:Mysql 默认采用的 REPEATABLE_READ隔离级别 Oracle默认采用的 READ_COMMITTED隔离级别
事务隔离机制的实现基于锁机制和并发调度。其中并发调度使用的是MVVC(多版本并发控制),通过 保存修改的旧版本信息来支持并发一致性读和回滚等特性。
因为隔离级别越低,事务请求的锁越少,所以大部分数据库系统的隔离级别都是READ-COMMITTED(读 取提交内容):,但是你要知道的是InnoDB 存储引擎默认使用 REPEATABLE-READ(可重读)并不会有 任何性能损失。
InnoDB 存储引擎在 分布式事务 的情况下一般会用到SERIALIZABLE(可串行化)隔离级别。

String、StringBuffer、StringBuilder区别?

  1. 不可变性:

    • String类是不可变的,即一旦创建了String对象,就不能修改它的值。每次对String对象进行修改时,都会创建一个新的String对象。
    • StringBuffer和StringBuilder类是可变的,可以对其进行修改和操作,而不会创建新的对象。
  2. 线程安全性:

    • String类是线程安全的,可以在多线程环境下共享和操作String对象。
    • StringBuffer类是线程安全的,提供了同步方法,可以在多线程环境下安全地进行字符串操作。
    • StringBuilder类不是线程安全的,不提供同步方法,适用于单线程环境下的字符串操作。
  3. 性能:

    • 由于String类的不可变性,每次对String对象进行修改都会创建一个新的对象,频繁的字符串拼接操作会导致大量的对象创建和内存开销。
    • StringBuffer类在进行字符串操作时,会在原有对象的基础上进行修改,避免了对象的频繁创建,因此在字符串拼接操作较多或需要线程安全时,使用StringBuffer会更高效。
    • StringBuilder类与StringBuffer类类似,但不提供同步方法,因此在单线程环境下,StringBuilder的性能比StringBuffer稍高。

    Redis.了解穿透,击穿,雪崩现象及其解决方案

    1. Redis 穿透(Cache Penetration)

    现象: 当一个请求查询一个不存在于缓存中的数据时,每次请求都会直接访问数据库,这可能导致数据库负载过高。

    原因: 请求查询的数据不存在于缓存中,但却在数据库中也不存在。

    解决方案:

    • 布隆过滤器(Bloom Filter): 在请求到达缓存之前,使用布隆过滤器快速地判断查询的数据是否存在于数据库中,如果不存在,则直接返回,避免对数据库的查询。
    • 缓存空对象: 在缓存中存储那些不存在于数据库中的数据的键,但是值为空对象,这样可以防止针对同一无效数据的重复查询。

    2. Redis 击穿(Cache Breakdown)

    现象: 在一个高并发的场景下,某个缓存失效时,大量的请求同时涌入,导致请求直接击穿到数据库上,造成数据库压力激增。

    原因: 缓存中某个热门数据过期后,大量的请求同时访问数据库。

    解决方案:

    • 互斥锁(Mutex Lock): 在查询缓存失效时,使用互斥锁来保护对数据库的访问,只允许一个线程查询数据库,其他线程等待查询结果。
    • 预先加载(Preloading): 在缓存失效前,提前加载该数据的新值到缓存中,或者设置较短但有一定冗余的过期时间,以减少缓存失效时的并发请求。

    3. Redis 雪崩(Cache Avalanche)

    现象: 在某个时间点,大量的缓存同时失效,导致大量请求涌入数据库,造成数据库瞬时压力过大。

    原因: 缓存中的大量数据在同一时间段内过期失效,导致大量请求同时涌入数据库。

    解决方案:

    • 分布式部署: 将缓存服务器进行分布式部署,使得不同缓存的失效时间不一致,减少因缓存同时失效而引发雪崩效应的概率。
    • 缓存失效时间随机化: 对缓存的失效时间进行随机化设置,使得缓存失效的时间分布更加均匀,减少集中失效的可能性。
    • 热点数据永不过期: 对于一些热点数据,设置其永不过期,或者采用异步更新的方式来更新热点数据,避免热点数据的集中失效。

    1. 基本数据类型(Primitive Data Types):

    Java 中的基本数据类型包括以下八种:

    1. byte: 8 位,有符号,范围为 -128 到 127。
    2. short: 16 位,有符号,范围为 -32768 到 32767。
    3. int: 32 位,有符号,范围为 -2^31 到 2^31-1。
    4. long: 64 位,有符号,范围为 -2^63 到 2^63-1。
    5. float: 32 位,单精度浮点数。
    6. double: 64 位,双精度浮点数。
    7. char: 16 位,Unicode 字符。
    8. boolean: 表示逻辑值的类型,只有两个取值:true 和 false。