JAVA后端开发工程师面试题
- 没答好部分
- 什么是面向对象编程?请举例说明一个类、对象、方法和属性的概念
- 编写一个程序,打印出从1到100的奇数序列并倒序排列
- Java提供了哪些集合类?请列举并简单描述它们的用途
- 什么是线程?如何在Java中创建多线程?
- 如何使用 Java编写一个简单的HTTP客户端和服务器
- 使用Java连接到数据库的基本步骤是什么?请给出一段简单的代码示例
- 在Java中如何读写文件?请给出一个简单的文件读取和写入的代码示例**
- Java有哪些高级特性?例如,解释一下接口、抽象类、泛型和反射的概念
- 如何在Java中实现MVC(模型-视图-控制器)设计模式?请给出一个简单的示例
- http和https的区别
- == 和 === 的区别
- MySQL分页查询的关键字
- 左连接的结果集是怎样的
- 什么是值传递和引用传递以及区别?
- Mysql数据库和Oracle数据库的区别
- 前端怎么获取一个标签里的属性,有哪些方法?
- vue中v-if和v-show的区别
- Mybatis-plus中怎么设置主键自动递增
- MySQL中怎么查询除了左连接结果集后剩下的部分(也就是剩余的右表部分)
- arraylist中间的一个元素删除后,后面的元素会自动向前补位吗?还是会怎么样?
- 在java8里面为什么要加个default(方法体)
- 你怎么理解类的单一继承?有什么作用?
- String、StringBuilder和StringBuffer的区别
- java中的字符串可以通过什么拼接
- 实现多线程的几种方式之间的区别
- 介绍一下Mybatis-plus里面的QueryWrapper以及里面常用的方法(like...)
- 讲讲设计数据库的思路
- Spring Boot 应用程序的启动过程
- SpringBoot中怎么解决循环依赖的问题
- List和Set的区别
- 什么是静态变量?什么是常量?
- Tomcat为什么要打破双亲委派机制?
- @Component和@Service的区别(性能上)
- 说一下java的异常体系,继承结构
- 什么是运行时异常和非运行时异常以及两者的区别
- 为什么会出现空指针异常
- Mybatis常用的注解和Xml中的标签
- Mybatis中怎么做查询结果的嵌套
- 介绍SpringBoot的自动装配
- 同一个SpringBoot项目怎么启动两个服务
- SpringBoot和SpringCloud的关系
- 为什么需要SpringCloud?有什么作用
- 介绍SpringCloud必要的组件
- A服务查询B服务用到什么组件
- Spring怎么管理事务
- 事务的传播机制
- 为什么需要事务
- 单表操作需要事务吗
- Redis的常见数据类型
- 索引用的是什么数据结构,可以用B树吗
- 中软国际面试题
- 什么是Java?介绍面向对象及四大特征概念?
- 增删改查基本语句是什么?
- servlet生命周期是什么?
- JDBC连接步骤是什么?
- 线程和进程的区别
- 多线程有几种实现方法?分别是什么?
- 如何实现线程安全?
- 描述关系型数据库中三范式的概念
- 列举你了解的设计模式
- 怎么理解MVC、SpringMVC及两者的区别
- 列举几种表连接方式
- 你知道的排序算法有哪些?
- 运行时异常和非运行时异常的区别,或者是异常和错误的区别
- 表的自连接怎么实现?
- JSP九大内置对象(必考)
- 什么是主键?外键?
- Spring是什么?
- Spring的优点?
- 对Spring的AOP理解
- 对Spring的IoC理解
- Spring支持哪几种bean的作用域?
- Spring的单例Beans是线程安全的么?
- Spring如何处理线程并发问题?
- Spring基于xml注入bean(依赖注入)有几种方式?
- Spring的自动装配(自动注入)是什么?
- Spring事务的实现方式和实现原理
- Spring框架中有哪些不同类型的事件?
- Spring的AOP通知有哪些类型?
- 什么是 Spring Boot?
- 为什么要用 Spring Boot?
- Spring Boot 的配置文件有哪几种格式以及区别?
- Spring Boot 的核心注解是哪个?主要由哪几个注解组成?
- 开启 Spring Boot 特性有哪几种方式?
- Spring Boot 需要独立的容器运行吗?
- 运行 Spring Boot 有哪几种方式?
- Spring Boot 自动配置原理是什么?
- 你如何理解 Spring Boot 中的 Starters?
- 如何在 Spring Boot 启动的时候运行一些特定的代码?
- Spring Boot 有哪几种读取配置的方式?
- Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?
- SpringBoot 实现热部署有哪几种方式?
- 如何理解 Spring Boot 的配置加载顺序?
- Spring Boot 如何定义多套不同的环境配置?
- Spring Boot 可以兼容老 Spring 项目吗?如何做?
- 保护 Spring Boot 应用有哪些方法?
- Spring Boot 2.X 有什么新特性?与 1.X 有的区别?
- SpringMVC常用注解
- SpringMVC怎样设定转发和重定向?
- 转发和重定向的区别
- SpringMVC怎么和AJAX相互调用?
- SpringMVC的优点
- Spring MVC的主要组件
- SpingMVC中的控制器注解一般用哪个?有没有别的注解可以替代?
- 如何在拦截请求中拦截 GET 方式提交的方法?
- 怎样在方法里面得到Request或者Session?
- 怎么在拦截的方法里得到从前台传入的参数?
- 如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象?
- SpringMVC中函数的返回值是什么?
- SpringMVC用什么对象从后台向前台传递数据?
- SpringMVC中Controller怎么向JSP返回值?
- Controller怎么接收前端的参数?
- Jquery常用选择器有哪些?
- 什么是Mybatis?
- Mybaits的优点
- MyBatis的缺点
- MyBatis适用场合
- MyBatis与Hibernate的区别?
- #{}和${}的区别?
- 实体类中属性名和表中字段名不一样 时怎么办 ?
- 模糊查询like语句怎么写?
- Xml对应的Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
- Mybatis是如何进行分页的?分页插件的原理是什么?
- Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
- 如何执行批量插入?
- 如何获取自动生成的(主)键值?
- session和cookie的区别?
- LocalStorage和SessionStorage的区别
- 常用Linux命令
- 介绍下Dubbo
- Dubbo特点
- Dubbo支持的协议
- 应用之间如何通讯?
- 应用之间的调用如何规划和监控?
- 介绍下SpringCloud
- 字节流和字符流的区别
- 接口和抽象类的区别
- 介绍Java的反射机制
- XML和Json的特点
- 关系型数据库、nosql数据库和缓存数据库的区别
- Zookeeper的主要功能
- 代码中事务是如何控制的?
- 介绍一下Mybatis缓存
- 介绍下索引
- 介绍下存储过程
- 项目怎么进行重构,代码怎么进行优化?
- ArrayList和LinkedList的比较
- list与map的底层如何实现?
- 说说对 MySQL 常见的两种存储引擎MyISAM与InnoDB的理解
- 为什么索引能提高查询速度
- 什么是事务
- 为什么要用 redis 缓存
- 数据库优化应该从哪些方法考虑?
- HashMap的底层原理是什么
- 当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,介绍一些常见的优化措施
- 项目的开发流程是什么
- 在项目中如何设计数据库
- 拦截器和过滤器的区别
- 序列化和反序列化
没答好部分
什么是面向对象编程?请举例说明一个类、对象、方法和属性的概念
答:面向对象编程(Object-Oriented Programming,OOP)是一种程序设计的方法,通过将问题拆分成对象并通过对象之间的交互来解决问题。在面向对象编程中,类是对象的蓝图,它定义了对象的属性和行为。对象是类的一个实例,它具有类定义的属性和行为。方法是类中定义的可以执行的操作,用于实现对象的行为。属性是类中定义的用于描述对象状态的变量。
举例:
- 类:一个 “Person” 类可以定义人的属性(如姓名、年龄、性别)和行为(如说话、走路)
- 对象:基于 “Person” 类创建的一个人对象,具有具体的姓名、年龄和性别
- 方法:“Person” 类中的一个方法可以是 speak(),用于让人说话
- 属性: “Person” 类中的一个属性可以是 name,用于表示人的姓名
编写一个程序,打印出从1到100的奇数序列并倒序排列
public class OddNumbers {
public static void main(String[] args) {
for (int i = 100; i >= 1; i--) {
if (i % 2 != 0) {
System.out.println(i);
}
}
}
}
Java提供了哪些集合类?请列举并简单描述它们的用途
- ArrayList:基于动态数组实现的可变长度列表。用于存储和操作元素的集合
- LinkedList:基于链表实现的可变长度列表。适用于频繁的插入和删除操作
- HashSet:基于哈希表实现的无序集合。用于存储唯一的元素
- HashMap:基于哈希表实现的键值对映射。用于存储和检索键值对
- TreeSet:基于红黑树实现的有序集合。用于存储有序的唯一元素
什么是线程?如何在Java中创建多线程?
线程是程序执行中的一个独立单位,它可以并发执行。在 Java 中,可以通过继承 Thread 类或实现 Runnable 接口来创建线程。下面是两种创建线程的示例:
使用 Thread 类:
public class MyThread extends Thread {
public void run() {
// 线程执行的代码
}
}
// 创建并启动线程
MyThread thread = new MyThread();
thread.start();
使用 Runnable 接口:
public class MyRunnable implements Runnable {
public void run() {
// 线程执行的代码
}
}
// 创建并启动线程
Thread thread = new Thread(new MyRunnable());
thread.start();
如何使用 Java编写一个简单的HTTP客户端和服务器
使用 Java 编写简单的 HTTP 客户端和服务器可以使用 java.net 包中的类。下面是一个简单的示例:
HTTP 客户端:
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpClientExample {
public static void main(String[] args) throws Exception {
URL url = new URL("http://example.com");
HttpURLConnection con = (HttpURLConnection) url.openConnection();
con.setRequestMethod("GET");
BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer content = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
content.append(inputLine);
}
in.close();
System.out.println(content.toString());
}
}
HTTP 服务器:
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServerExample {
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("Listening on port 8080...");
while (true) {
Socket clientSocket = serverSocket.accept();
OutputStream out = clientSocket.getOutputStream();
out.write("HTTP/1.1 200 OK\r\n\r\nHello, World!".getBytes());
out.flush();
out.close();
clientSocket.close();
}
}
}
另外一个例子:
HTTP客户端代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
public class HttpClientExample {
public static void main(String[] args) {
try {
// 创建URL对象
URL url = new URL("http://example.com");
// 打开URL连接
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 设置请求方法
connection.setRequestMethod("GET");
// 发送请求并获取响应
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
// 读取响应内容
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 关闭连接
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
HTTP服务器代码:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.ServerSocket;
import java.net.Socket;
public class HttpServerExample {
public static void main(String[] args) {
try {
// 创建ServerSocket对象并指定监听的端口号
ServerSocket serverSocket = new ServerSocket(8080);
while (true) {
// 等待客户端连接
Socket clientSocket = serverSocket.accept();
// 获取客户端请求信息
BufferedReader reader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
// 解析请求信息并处理
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
// 构造响应并发送给客户端
PrintWriter writer = new PrintWriter(clientSocket.getOutputStream());
writer.println("HTTP/1.1 200 OK");
writer.println("Content-Type: text/html");
writer.println();
writer.println("<h1>Hello, World!</h1>");
writer.flush();
// 关闭连接
reader.close();
writer.close();
clientSocket.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
以上示例代码分别实现了一个简单的HTTP客户端和服务器。在客户端代码中,我们创建了一个URL对象,打开URL连接,并发送GET请求获取响应。在服务器代码中,我们创建了一个ServerSocket对象并指定监听的端口号,然后在一个无限循环中等待客户端连接,获取客户端请求信息并处理,然后构造响应并发送给客户端。
使用Java连接到数据库的基本步骤是什么?请给出一段简单的代码示例
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class DatabaseExample {
public static void main(String[] args) {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
try {
// 加载数据库驱动程序
Class.forName("com.mysql.jdbc.Driver");
// 建立数据库连接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mydatabase", "username", "password");
// 创建 SQL 语句
String sql = "SELECT * FROM mytable";
// 创建 Statement 对象
statement = connection.createStatement();
// 执行查询并获取结果集
resultSet = statement.executeQuery(sql);
// 处理结果集
while (resultSet.next()) {
String column1 = resultSet.getString("column1");
int column2 = resultSet.getInt("column2");
// 处理获取到的数据
}
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
} finally {
try {
// 关闭结果集
if (resultSet != null) {
resultSet.close();
}
// 关闭语句
if (statement != null) {
statement.close();
}
// 关闭连接
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
在Java中如何读写文件?请给出一个简单的文件读取和写入的代码示例**
在 Java 中,可以使用 java.io 包中的类来进行文件读写。下面是一个简单的文件读取和写入的示例:
文件读取:
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderExample {
public static void main(String[] args) {
try (BufferedReader reader = new BufferedReader(new FileReader("input.txt"))) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
文件写入:
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterExample {
public static void main(String[] args) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter("output.txt"))) {
writer.write("Hello, world!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java有哪些高级特性?例如,解释一下接口、抽象类、泛型和反射的概念
- 接口(Interface)是一种定义了一组需要实现的方法的抽象类型。它定义了行为的契约,实现接口的类必须提供接口中定义的方法实现
- 抽象类(Abstract Class)是一种不能被实例化的类,它可以包含抽象方法和具体方法。抽象类用于作为其他类的基类,并通过继承来共享通用的属性和方法
- 泛型(Generics)是一种参数化类型的机制,它允许在编译时指定类或方法的类型。通过使用泛型,可以增加代码的类型安全性和重用性
- 反射(Reflection)是一种在运行时检查和修改类、对象和方法的能力。它允许程序在运行时获取类型信息、访问和修改对象的属性和方法
如何在Java中实现MVC(模型-视图-控制器)设计模式?请给出一个简单的示例
在 Java 中实现 MVC(模型-视图-控制器)设计模式可以将代码分为三个主要部分:
- 模型(Model)负责处理应用程序的数据逻辑和业务规则
- 视图(View)负责显示用户界面,并与用户交互
- 控制器(Controller)负责处理用户输入、更新模型和管理视图
以下是一个简单的示例:
Model(模型):
public class UserModel {
private String username;
private String password;
// getters and setters
}
View(视图):
public class UserView {
public void displayUserDetails(String username) {
System.out.println("Username: " + username);
}
public String getUsernameFromUser() {
// 获取用户输入的用户名
}
public String getPasswordFromUser() {
// 获取用户输入的密码
}
}
Controller(控制器):
public class UserController {
private UserModel model;
private UserView view;
public UserController(UserModel model, UserView view) {
this.model = model;
this.view = view;
}
public void updateUser() {
String username = view.getUsernameFromUser();
String password = view.getPasswordFromUser();
model.setUsername(username);
model.setPassword(password);
view.displayUserDetails(model.getUsername());
}
}
主程序:
public class Main {
public static void main(String[] args) {
UserModel model = new UserModel();
UserView view = new UserView();
UserController controller = new UserController(model, view);
controller.updateUser();
}
}
http和https的区别
- 信息传输方式不同:http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议
- 连接方式不同:http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
- 安全性不同:http连接是无状态的,https协议是由ssl+http协议构建的可进行加密传输、身份认证的网络协议,安全性高于http协议
- 费用不同:https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用,http则没有这种费用
== 和 === 的区别
" == " 运算符用于比较两个对象的引用是否相等。也就是说,当两个变量指向内存中的同一个对象时,即两个变量引用同一个对象,那么使用"=="运算符比较它们会返回真。例如:
在这个例子中,a和b都是指向同一个新创建的Object对象的引用,所以a == b返回真
Object a = new Object();
Object b = a;
if (a == b) {
System.out.println("a == b"); // 输出 "a == b"
}
“===” 运算符是Java 7中引入的严格比较运算符,它不仅比较两个对象的引用是否相等,还比较两个对象的类型是否相同。例如:
String s1 = "Hello";
String s2 = new String("Hello");
if (s1 === s2) {
System.out.println("s1 === s2"); // 不会输出
}
在这个例子中,s1和s2虽然内容相同,但是它们在内存中的对象类型不同(一个是字面量字符串,一个是通过new关键字创建的字符串对象),所以s1 === s2返回假。
需要注意的是," === " 运算符只适用于原始类型和String类型,对于其他对象类型,Java虚拟机会自动将其转化为 " == " 运算符。所以在实际编程中," === “的使用并不多见,更多时候我们使用的还是” == "
MySQL分页查询的关键字
MySQL分页查询的关键字是LIMIT和OFFSET
- LIMIT关键字用于限制查询结果集的返回行数,可以设置查询从哪里开始,以及返回多少行数据
- OFFSET关键字用于指定结果集的偏移量,通常与LIMIT关键字一起使用,用于实现分页效果。
可以通过LIMIT和OFFSET的组合来实现分页查询,例如:
SELECT * FROM table LIMIT offset, rows //其中offset表示从哪里开始查询,rows表示要查询的行数。
左连接的结果集是怎样的
- 左连接是以左表为主,结果集包含在左表的基础上加上左右表都匹配的共同部分
- 右连接是以右表为主,结果集包含在右表的基础上加上左右表都匹配的共同部分
- 内连接的结果集为左右表都匹配的中间部分
什么是值传递和引用传递以及区别?
值传递和引用传递是Java等编程语言中两种不同的参数传递方式
- 值传递是指方法调用时,实际参数拷贝一份传递到方法中,这样在方法中如果对参数进行修改,将不会影响到实际参数
- 引用传递是指方法调用时,实际参数的地址直接传递到方法中,那么在方法中对参数所进行的修改,将影响到实际参数
Mysql数据库和Oracle数据库的区别
- MySQL是开源的数据库,而Oracle是商业数据库,需要购买许可证才能使用
- Oracle在处理大规模企业级数据库时表现更好,具有更高的性能和可扩展性。MySQL则更适合小型和中型应用,处理较小规模的数据库
- MySQL主要以表级锁为主,对资源锁定的粒度很大,Oracle使用行级锁,对资源锁定的粒度要小很多
- MySQL没有表空间、角色管理、快照、同义词和包以及自动存储管理等特性
前端怎么获取一个标签里的属性,有哪些方法?
- 使用 getAttribute()方法:该方法可以获取指定属性名的属性值
- 使用元素对象的属性:例如,element.id可以获取标签的id属性值,element.className可以获取标签的class属性值
- 使用dataset属性:该属性可以获取标签的data-*属性值,例如,element.dataset.name可以获取标签的data-name属性值
- 使用属性选择器:可以使用CSS选择器来获取标签的属性值,例如,使用[attribute=value]选择器获取指定属性名和属性值的标签
- 使用jQuery库:jQuery库提供了attr()方法来获取指定属性名的属性值,例如,$(element).attr(‘id’)可以获取标签的id属性值
vue中v-if和v-show的区别
- v-show的作用与v-if一致,但区别在于v-if的html元素会消失在body中,而v-show只是隐藏,元素还在!因此在元素的隐藏和显示之间切换次数比较高的时候使用v-show会比较好
Mybatis-plus中怎么设置主键自动递增
- 在实体类中,使用 @TableId 注解来设置主键,并指定 type 为 IdType.AUTO,表示自动递增
- Mybatis-plus 的全局配置文件 mybatis-plus-config.xml 中,使用 GlobalConfig 配置对象来设置主键自动递增,例如:
<configuration>
<global-config>
<db-config>
<!-- 主键自动递增 -->
<key-generator type="com.baomidou.mybatisplus.incrementer.OracleKeyGenerator" />
</db-config>
</global-config>
</configuration>
MySQL中怎么查询除了左连接结果集后剩下的部分(也就是剩余的右表部分)
使用 NOT IN:
SELECT *
FROM right_table
WHERE right_table.id NOT IN (
SELECT left_table.id
FROM left_table
LEFT JOIN right_table ON left_table.id = right_table.id
WHERE left_table.id IS NULL
);
使用 NOT EXISTS:
SELECT *
FROM right_table
WHERE NOT EXISTS (
SELECT 1
FROM left_table
LEFT JOIN right_table ON left_table.id = right_table.id
WHERE left_table.id IS NULL
);
arraylist中间的一个元素删除后,后面的元素会自动向前补位吗?还是会怎么样?
- 后面的元素会自动向前补位,之前的下标会重新排列,数组大小变小
在java8里面为什么要加个default(方法体)
- 在Java 8中引入了接口的默认方法,主要是为了向现有的接口添加新的方法,而不会影响已有的实现类。在Java 8之前,一旦一个接口添加了新的方法,所有实现该接口的类都需要实现新方法,这可能会导致一些问题。通过在接口中添加默认方法,可以在不影响已有实现类的情况下,为接口添加新的方法。使得实现类可以选择性地覆盖默认方法或者直接使用默认实现。这样可以减少代码的重复性,并提高代码的可维护性和灵活性。因此,在Java 8中加入default方法体的功能是为了增强接口的灵活性和扩展性。
小例子:
当我们在Java 8中定义一个接口时,可以使用default关键字来定义一个默认方法。例如,我们定义一个简单的接口Greeting,其中包含一个默认方法defaultGreet:
public interface Greeting {
void greet(String name);
default void defaultGreet() {
System.out.println("Hello, how are you?");
}
}
然后我们可以创建一个实现该接口的类GreetingImpl,并选择性地覆盖默认方法:
public class GreetingImpl implements Greeting {
@Override
public void greet(String name) {
System.out.println("Hello, " + name);
}
// 不覆盖默认方法,直接使用默认实现
}
public class Main {
public static void main(String[] args) {
GreetingImpl greeting = new GreetingImpl();
greeting.greet("Alice"); // Output: Hello, Alice
greeting.defaultGreet(); // Output: Hello, how are you?
}
}
你怎么理解类的单一继承?有什么作用?
- 避免类的多重继承带来的复杂性。如果一个类可以继承多个父类,那么就会带来多个父类中可能存在的重名方法、变量等问题,从而增加了代码的复杂度和维护难度
String、StringBuilder和StringBuffer的区别
- String 是不可变的(immutable)类,一旦创建就不能被修改。每次对String进行操作(例如拼接、替换等),都会创建一个新的String对象。由于不可变性,String在多线程环境下是安全的。适用于字符串不经常改变的情况。
- StringBuilder是可变的(mutable)类,可以通过调用方法来修改其中的字符序列,而不创建新的对象。由于可变性,StringBuilder 的性能比String高,特别是在需要频繁进行字符串操作的情况下。适用于字符串经常需要改变的情况,但StringBuilder不是线程安全的。
- StringBuffer 与 StringBuilder 类似,但它是线程安全的
java中的字符串可以通过什么拼接
- 使用加号 (+) 拼接: 这是最直观的方法,也是最常用的。例如:
String str1 = "Hello";
String str2 = "World";
String str3 = str1 + ", " + str2;
- 使用 concat() 方法: 这个方法是 String 类中的一个方法,例如:
String str1 = "Hello";
String str2 = "World";
String str3 = str1.concat(", ").concat(str2);
- 使用 StringBuilder 或 StringBuffer: 当你需要拼接大量的字符串时,使用 StringBuilder 或 StringBuffer 是一个更好的选择,因为它们是可变的,不会为每次拼接创建新的字符串对象。例如:
StringBuilder sb = new StringBuilder(); //或StringBuffer sb = new StringBuffer();
sb.append("Hello");
sb.append(", ");
sb.append("World");
String str = sb.toString();
- 使用 String.format() 方法: 这个方法可以按照指定的格式拼接字符串。例如:
String str1 = "Hello";
String str2 = "World";
String str3 = String.format("%s, %s", str1, str2); //%s 是一个占位符,表示将替换为一个字符串,按参数的顺序替换
- 使用文本块(JDK 13 新增): 如果你使用的是 JDK 13 或更高版本,你还可以使用文本块来拼接字符串。例如:
String str1 = "Hello";
String str2 = "World";
String str3 = """
%s,
%s""".formatted(str1, str2);
实现多线程的几种方式之间的区别
在Java中,实现多线程的几种方式包括继承Thread类、实现Runnable接口、使用Callable和Future、使用线程池等,区别如下:
- 继承Thread类:创建一个继承自Thread类的子类,并重写run()方法来定义线程的执行逻辑。这种方式简单直观,但由于Java是单继承的,因此如果已经继承了其他类,就无法再使用这种方式创建线程。实现Runnable接口:创建一个实现了Runnable接口的类,并实现其中的run()方法。这种方式避免了单继承的限制,因为一个类可以实现多个接口,因此更灵活。
- 使用Callable和Future:Callable接口允许线程执行并返回结果,并且可以抛出异常。Future接口可以用来获取Callable的返回结果,还可以取消任务、查询任务是否完成等。这种方式相比Runnable更加强大,但也更加复杂
- 使用线程池:线程池可以重用已创建的线程,减少线程创建和销毁的开销,提高系统的性能。通过线程池,可以更好地控制并发线程的数量,避免系统资源被耗尽。Java提供了Executors工厂类来创建不同类型的线程池。
介绍一下Mybatis-plus里面的QueryWrapper以及里面常用的方法(like…)
- Mybatis-Plus 是一个Mybatis 的增强工具,在Mybatis的基础上只做增强不做改变,简化开发、提高效率,而且不用写SQL。Query是Mybatis-Plus中的查询工具类,用于执行各种查询操作。Wrapper是查询条件封装类,通常用于封装复杂的查询条件。
like(String column, Object value)方法:用于构建模糊查询条件,其中column为数据库表字段名,value为要匹配的值。
示例:
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("username", "admin");
上述示例中,构建了一个模糊查询条件,要求username字段包含"admin"的值。
除了like方法,QueryWrapper还提供了一系列其他常用的方法,如eq、ne、gt、ge、lt、le等,用于构建等值查询、不等值查询、大于、大于等于、小于、小于等于等各种查询条件。
讲讲设计数据库的思路
- 需求分析:首先需要明确业务需求,了解系统要支持的功能和数据处理需求。这包括收集用户需求、业务流程、数据处理流程等,以便确定数据库需要存储哪些数据以及数据之间的关系
- 实体建模:根据需求分析,识别出系统中的各种实体,并分析它们之间的关系。这些实体可以转化为数据库中的表,实体之间的关系可以转化为表之间的关系(如一对一、一对多、多对多关系)
- 属性定义:为每个实体确定需要存储的属性(字段),包括属性的数据类型、长度、约束条件等
- 范式设计:考虑数据库的范式化设计,以确保数据的一致性和有效性。范式设计包括将数据组织成符合第一范式、第二范式、第三范式等范式的形式,以减少数据冗余和提高数据的完整性
- 性能考虑:考虑性能需求,包括数据的读写频率、数据量的大小等。根据性能需求进行索引设计、分区设计等优化措施
- 安全性考虑:包括对敏感数据的加密、权限控制、防止SQL注入等
- 可维护性设计:包括可读性、可扩展性、可维护性等。这需要采用合理的命名规范、文档编写、代码注释等方法
- 备份与恢复策略:为了应对意外情况,需要制定备份与恢复策略,包括定期备份数据、使用日志文件进行恢复等
Spring Boot 应用程序的启动过程
- 加载启动类SpringBootApplication,Spring Boot通过扫描启动类所在的包和子包,自动装配配置类和Bean
- 加载配置文件,默认从 application.properties 文件中加载,也可以通过在启动类上使用@PropertySource注解指定其他的属性文件
- 通过 SpringApplication来创建 Spring容器
- 加载自动配置,根据 classpath中的 jar包和 Bean的装配情况,自动装配相应的Bean
- 当Spring容器准备就绪后,Spring Boot就会启动内嵌的Tomcat服务器并运行应用程序。如果使用的是外部Web服务器,Spring Boot就会将应用程序打包成一个可执行的 jar文件,并启动外部Web服务器。
SpringBoot中怎么解决循环依赖的问题
List和Set的区别
什么是静态变量?什么是常量?
Tomcat为什么要打破双亲委派机制?
@Component和@Service的区别(性能上)
说一下java的异常体系,继承结构
什么是运行时异常和非运行时异常以及两者的区别
为什么会出现空指针异常
- 因为代码中使用了一个空对象,而这个空对象没有进行初始化或者已经被释放了
Mybatis常用的注解和Xml中的标签
Mybatis中怎么做查询结果的嵌套
介绍SpringBoot的自动装配
同一个SpringBoot项目怎么启动两个服务
SpringBoot和SpringCloud的关系
为什么需要SpringCloud?有什么作用
介绍SpringCloud必要的组件
A服务查询B服务用到什么组件
Spring怎么管理事务
事务的传播机制
为什么需要事务
单表操作需要事务吗
Redis的常见数据类型
- 字符串(string):最基本的数据类型,可以存储任意类型的数据,例如数字、文本等
- 列表(list):按照插入顺序存储一系列的元素,可以在列表的两端进行插入、删除操作,支持对列表进行分片操作
- 集合(set):无序的唯一元素的集合,可以进行元素的添加、删除、判断某元素是否存在等操作
- 有序集合(sorted set):类似于集合,但每个元素都关联了一个分数(score),可以根据分数对元素进行排序,支持按照分数范围获取元素
- 哈希表(hash):类似于字典或散列表,可以存储多个键值对,每个键对应一个值,支持对单个键值对的增、删、改、查操作
- Redis还支持一些其他的数据类型,例如位图(bitmap)、超级日志(hyperloglog)等。不同的数据类型在Redis中有不同的存储结构和操作方式,可以根据具体的需求选择适合的数据类型
索引用的是什么数据结构,可以用B树吗
中软国际面试题
什么是Java?介绍面向对象及四大特征概念?
Java概念: Java是一种广泛应用于软件开发的编程语言,具有跨平台性和面向对象的特性。它由Sun公司1995年推出,后被Oracle公司收购。
面向对象思想: 是一种编程思想,将程序中的数据和操作数据的方法组合成一个对象,通过调用对象的方法来实现功能
面向对象四大特征:
- 封装:将数据和操作封装在对象中,隐藏内部实现细节,只通过对象接口与外界交互
- 继承:通过继承已有类的属性和方法,可以减少代码的重复性,提高代码的复用性
- 多态:同一个方法在不同的对象上产生不同的行为和操作
- 抽象:定义抽象类和抽象方法,由子类实现具体细节
增删改查基本语句是什么?
- 增:insert
- 删:drop/delete
- 改:update/alter(设置)
- 查:select
servlet生命周期是什么?
- servlet生命周期:在第一次访问时调用init初始化,每次请求使用service服务,在服务器关闭时调用destory销毁
JDBC连接步骤是什么?
- 加载驱动(使用Class.forName()方法加载数据库驱动)
- 获得连接
- 创建Statement对象
- 发送sql语句
- 处理结果集
- 关闭连接
线程和进程的区别
- 线程是程序执行的最小单位,而进程是操作系统分配资源和调度的最小单位;线程是进程的一部分,一个进程可以包含多个线程
多线程有几种实现方法?分别是什么?
目前我知道的有四种:
- 继承Thread类:重写run()方法,并通过调用start()方法启动线程
- 实现Runnable接口:实现run()方法,并通过创建Thread对象,将Runnable对象作为参数传入,再调用start()方法启动线程
- 实现Callable接口
- 使用线程池:通过Executor框架的线程池来管理和执行线程
如何实现线程安全?
- 互斥锁:通过使用synchronized关键字或ReentrantLock类来确保同一时间只有一个线程可以访问共享资源
- 读写锁:ReadWriteLock接口提供了读写锁,允许多个线程同时读取共享资源,但只允许一个线程写入共享资源
- 使用Lock接口:通过Lock对象的lock()和unlock()方法来实现线程的同步
- 使用原子类:使用java.util.concurrent.atomic包下的原子类来保证对变量的原子操作
- 信号量:Semaphore类可以用来限制对共享资源的访问权限,确保同时只有一个线程可以访问共享资源
- 倒计时门闩(shuān):CyclicBarrier类可以让一定数量的线程互相等待,当所有线程都准备好后,门闩才会打开,允许所有线程同时访问共享资源
- 循环栅栏:CountDownLatch类可以让一个线程等待其他线程都完成操作后才能继续执行
描述关系型数据库中三范式的概念
范式是关系型数据库中的一种设计规范,三范式用于提高数据的一致性、完整性和减少数据冗余:
- 第一范式(1NF):确保每个列都是原子性的,即每个列都不可再分;不允许多值属性或重复的属性,每个属性只能包含一个值
- 第二范式(2NF):在满足第一范式的基础上,要求非主键列完全依赖于主键
- 第三范式(3NF):在满足第二范式的基础上,要求非主键列之间没有传递依赖关系,而是直接依赖于主键
当然还有更高级的范式,如BCNF(巴斯-科德范式)和第四范式(4NF),但在实际设计中并不常用
列举你了解的设计模式
Spring运用了很多设计模式: IOC容器应用工厂设计模式、Bean的管理应用单例设计模式、AOP切面编程应用代理设计模式、HandlerAdapter应用适配器设计模式、不同的HandlerMapping找寻对应的Handler应用策略设计模式、参数的获取以及转换AgurmentResovler应用责任链设计模式、监听器应用了观察者设计模式等
怎么理解MVC、SpringMVC及两者的区别
-
MVC(Model-View-Controller)是一种软件设计模式,将应用程序分为三个部分:模型(数据和业务逻辑)、视图(用户界面)和控制器(处理用户输入和调度模型和视图)。MVC模式可以提高代码的可维护性和可扩展性
-
Spring MVC是一个实现了MVC模式的轻量级Web框架,它基于Java平台,提供了丰富的Web开发工具和API,用于快速开发Web应用程序
列举几种表连接方式
- 内连接(INNER JOIN):返回两个表中都匹配的行
- 左连接(LEFT JOIN):返回左表中的所有行以及与右表中匹配的行
- 右连接(RIGHT JOIN):返回右表中的所有行以及与左表中匹配的行
- 全连接(FULL JOIN):返回两个表中的所有行
各自的详解如下:
注意“inner join”两边的表的位置可以互换,结果都一样
-- inner join
select * from course c inner join teacher t on c.t_id = t.t_id
-- 逗号的连表方式也是内连接
select * from course c , teacher t where c.t_id = t.t_id
注意“left join”两边的表位置不可以互换,交换后结果可能不一样。需要考虑好哪个是主表,哪个是从表。写在前面的是主表
select * from course c left join teacher t on c.t_id = t.t_id
select * from course c right join teacher t on c.t_id = t.t_id
注意中间部分包括了左右表两部分,需要去重掉其中的一部分
-- Oracle的全连接
select * from a full join b on a.id = b.id
-- Mysql的全连接没有full join,mysql可以使用union实现全连接
select * from a left join b on a.id = b.id
union
select * from a right join b on a.id = b.id
你知道的排序算法有哪些?
冒泡排序:
- 实现逻辑:比较相邻的两个元素,如果前面的元素大于后面的元素,就交换它们的位置,重复这个过程直到整个数组排序完成
- 时间复杂度:最好情况下为O(n),最坏情况下为O(n^ 2),平均情况下为O(n^2)
插入排序:
- 实现逻辑:将数组分为已排序和未排序两部分,每次从未排序部分中取出一个元素,插入到已排序部分的适当位置,重复这个过程直到整个数组排序完成
- 时间复杂度:最好情况下为O(n),最坏情况下为O(n^ 2),平均情况下为O(n^2)
选择排序:
- 实现逻辑:将数组分为已排序和未排序两部分,每次从未排序部分中选择最小(或最大)的元素,将其放到已排序部分的末尾,重复这个过程直到整个数组排序完成
- 时间复杂度:最好情况下为O(n^ 2),最坏情况下为O(n^ 2),平均情况下为O(n^2)
快速排序:
- 实现逻辑:选择一个基准元素,将数组分为两部分,左边的元素都小于基准元素,右边的元素都大于基准元素,然后对左右两部分分别进行递归排序
- 时间复杂度:最好情况下为O(nlogn),最坏情况下为O(n^2),平均情况下为O(nlogn)
归并排序:
- 实现逻辑:将数组递归地分成两半,对每一半进行排序,然后将两个有序的子数组合并成一个有序的数组
- 时间复杂度:最好情况下为O(nlogn),最坏情况下为O(nlogn),平均情况下为O(nlogn)
堆排序:
- 实现逻辑:将数组构建成一个最大堆(或最小堆),然后将堆顶元素与最后一个元素交换,再重新调整堆,重复这个过程直到整个数组排序完成
- 时间复杂度:最好情况下为O(nlogn),最坏情况下为O(nlogn),平均情况下为O(nlogn)
运行时异常和非运行时异常的区别,或者是异常和错误的区别
- 运行时异常:是指在程序运行过程中可能发生的异常,通常是由程序逻辑错误引起的,如空指针异常、数组越界异常等。运行时异常不需要在代码中显式处理,可以选择捕获或抛出
- 非运行时异常:是指在程序编译过程中就能够预测到的异常,需要在代码中显式处理,否则编译不通过。非运行时异常包括IO异常、数据库异常等
- 异常和错误的区别在于异常是可以被程序处理的,而错误是无法被程序处理的严重问题,错误需要通过调试和修复来解决
表的自连接怎么实现?
表的自链接是指将表与自身进行连接操作。实现自链接可以使用表的别名来区分不同的表实例,然后使用连接条件将表的不同实例进行连接
例如,假设有一个员工表,包含员工的ID、姓名和上级ID等字段。可以使用自链接查询出每个员工的上级姓名
SELECT e1.姓名 AS 员工姓名, e2.姓名 AS 上级姓名
FROM 员工表 e1, 员工表 e2
WHERE e1.上级ID = e2.ID;
JSP九大内置对象(必考)
- request 请求,封装HTTP请求的信息
- response 响应,封装HTTP响应的信息
- session 会话,封装用户会话的信息
- application 应用,封装Web应用程序的全局信息
- page 页面,当前JSP页面的实例
- pageContext 页面上下文,封装页面上下文的信息
- config 配置,封装Servlet配置的信息
- exception 异常,封装页面抛出的异常信息
- out 输出,用于输出响应到客户端的输出流
什么是主键?外键?
- 主键(Primary Key)是数据库表中用于唯一标识记录的字段,它的值是唯一的,且不能为空
- 外键(Foreign Key)是用于建立表与表之间的关联关系的字段,它引用了另一个表的主键值
Spring是什么?
Spring是一个开源的Java应用开发框架,用于开发企业级应用程序。Spring框架提供了一系列的模块,包括依赖注入(IoC)、面向切面编程(AOP)、数据访问、事务管理、Web开发等,帮助开发者更简单、高效地构建应用程序
Spring的优点?
- 轻量级:Spring框架的体积较小,易于学习和使用,不会给开发带来额外的负担
- 面向切面编程:Spring提供了面向切面编程的支持,可以方便地实现日志记录、性能统计等功能,提高了代码的可重用性和可维护性
- 依赖注入:Spring通过依赖注入机制,实现了组件之间的解耦,使得代码更加灵活和可配置
- 模块化:Spring框架采用了模块化的设计,各个模块之间相互独立,可以灵活地组合使用,提高了开发效率
- 强大的整合能力:Spring可以与各种流行的Java技术进行整合,如Hibernate、Struts等,可以快速构建企业级应用程序
- 事务管理:Spring提供了统一的事务管理接口,支持编程式和声明式的事务管理,简化了事务管理的开发工作
- 简化开发:Spring提供了大量的开发工具和集成支持,简化了开发过程,提高了开发效率
- 测试支持:Spring提供了丰富的测试支持,可以方便地进行单元测试和集成测试
对Spring的AOP理解
- Spring的面向切面编程是一种编程思想,它允许开发者定义跨多个服务和方法的横切关注点。通过AOP,开发者可以将公共功能(如日志记录、性能统计等)抽取出来,封装成一个独立的模块,将其应用到其他模块中。这样可以将关注点从业务逻辑中分离出来,提高了代码的可重用性和可维护性
对Spring的IoC理解
- Spring的依赖注入是一种设计模式,它允许开发者通过配置文件或注解等方式,将组件之间的依赖关系从硬编码中解耦出来,将其交给Spring容器来管理。
Spring支持哪几种bean的作用域?
- singleton:默认的作用域,每个Bean都是单例的,在Spring容器中只会存在一个实例,所有请求都共享同一个实例
- prototype:每次获取Bean时都会创建一个新的实例
- request:每个HTTP请求都会创建一个新的Bean实例,不同请求之间的实例是独立的
- session:每个会话都会创建一个新的Bean实例,不同会话之间的实例是独立的
- global-session:在整个应用程序中只创建一个实例,但只在基于portlet的web应用程序中有效
Spring的单例Beans是线程安全的么?
- 是的,在Spring容器中,每个Bean都是单例的,且在初始化时会进行依赖注入,Spring还提供了对线程安全的支持,例如使用synchronized关键字来保证线程安全
Spring如何处理线程并发问题?
- 使用单例Beans,Spring的单例Beans默认是线程安全的,可以在多线程环境下安全地访问
- 可以使用ThreadLocal来存储线程私有的数据,保证线程安全
- 可以使用同步关键字synchronized或锁来保证多个线程对共享资源的访问的互斥性
- 可以使用线程池来管理线程,控制并发访问的数量
Spring基于xml注入bean(依赖注入)有几种方式?
- Setter注入
- 构造器注入
- 工厂注入
- 接口注入
Spring的自动装配(自动注入)是什么?
Spring的自动装配(Autowired)是指Spring容器根据类型或名称自动将匹配的依赖注入到相应的属性、构造函数或方法参数中。自动装配可以减少手动配置的工作量,提高开发效率。可以通过@Autowired和@Resource注解来实现自动装配,还可以通过@Qualifier注解指定具体的bean名称进行装配
Spring事务的实现方式和实现原理
- 实现方式:Spring事务的实现方式有两种,分别是编程式事务管理和声明式事务管理
- 实现原理:Spring事务的实现原理是通过AOP(面向切面编程)和代理模式来实现的。在编程式事务管理中,通过编写代码手动控制事务的开启、提交和回滚;在声明式事务管理中,通过在方法或类上添加事务注解来实现事务的自动管理
Spring框架中有哪些不同类型的事件?
- ApplicationEvent:这是一个泛型事件,可以用于实现自定义事件
- HttpRequestEvent:处理HTTP请求时触发的事件
- TransactionalEvent:用于事务中的事件
- ContextRefreshedEvent:容器刷新完成后触发的事件
- ContextStartedEvent:当ApplicationContext启动时触发的事件
- ContextStoppedEvent:当ApplicationContext停止时触发的事件
- ContextClosedEvent:当ApplicationContext关闭时触发的事件
- NotificationEvent:用于发送通知的事件
Spring的AOP通知有哪些类型?
- Before:在目标方法执行之前执行通知(使用场景:记录日志)
- After:在目标方法执行之后执行通知,无论是否发生异常(使用场景:记录日志)
- AfterReturning:在目标方法执行之后执行通知,仅在方法成功完成时执行
- AfterThrowing:在目标方法抛出异常后执行通知(使用场景:异常处理、控制事务)
- Around:在目标方法执行前后执行通知,可以自定义目标方法的执行(使用场景:控制事务、权限控制)
什么是 Spring Boot?
- Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它简化了Spring应用程序的配置和部署过程,并提供了一种约定优于配置的方式
为什么要用 Spring Boot?
- 因为它能够大大简化Spring应用程序的开发和部署过程。它提供了自动配置、起步依赖和可嵌入的容器等特性,使开发人员能够更专注于业务逻辑而不是繁琐的配置
Spring Boot 的配置文件有哪几种格式以及区别?
主要有以下三种:
- application.properties:键值对格式
- application.xml:XML格式
- application.yml(还有.yaml):缩进的简洁的键值对格式
Spring Boot 的核心注解是哪个?主要由哪几个注解组成?
Spring Boot 的核心注解是 @SpringBootApplication
它主要由以下三个注解组成:
- @Configuration:用于标记一个配置类
- @EnableAutoConfiguration:用于启用自动配置功能,让 Spring Boot 能够根据需要自动配置相关的组件
- @ComponentScan:这个注解用于扫描指定包下的组件,让 Spring 能够找到并注册这些组件
开启 Spring Boot 特性有哪几种方式?
主要有以下几种方式:
- 使用 @SpringBootApplication 注解
- 使用@EnableAutoConfiguration 注解
- 使用 SpringApplication.run() 方法
Spring Boot 需要独立的容器运行吗?
- 不需要,因为它可以使用内置的Tomcat、Jetty或Undertow等容器运行
运行 Spring Boot 有哪几种方式?
- 直接通过IDE运行主类
- 使用命令行运行:使用 Maven 或 Gradle 构建JAR 包,使用命令 mvn spring-boot:run 或 gradle bootRun 来运行
Spring Boot 自动配置原理是什么?
- 约定优于配置:Spring Boot 会根据项目的结构和已有的配置,自动地配置你的应用程序。例如项目中的 application.properties 文件,Spring Boot 会自动地读取这个文件并使用其中的配置
- 类的路径扫描:Spring Boot 会扫描项目中的类路径,寻找可以自动配置的组件。例如一个类实现了某个特定的接口或注解,Spring Boot 就会自动地将其作为一个 Bean 注册到应用程序中
- Bean 的依赖注入:Spring Boot 会根据 Bean 的依赖关系,将它们组装在一起。例如一个类依赖于另一个类,Spring Boot 就会自动地创建一个前者实例并将其注入到后者中
你如何理解 Spring Boot 中的 Starters?
- Starters 是预先配置好的项目模板,可以帮助开发者快速地创建和启动一个 Spring Boot 项目。它们包含了项目的基本结构和依赖关系,以及一些常用的库和工具。使用 Starters可以大大减少创建和配置一个 Spring Boot 项目所需的时间
如何在 Spring Boot 启动的时候运行一些特定的代码?
通过实现ApplicationRunner接口或CommandLineRunner接口,并重写里面的run()方法
@Component
public class MyStartupRunner implements ApplicationRunner {
@Override
public void run(ApplicationArguments args) throws Exception {
// 在这里编写你需要在启动时运行的代码
}
}
Spring Boot 有哪几种读取配置的方式?
- 通过 application.properties 文件读取
- 通过环境变量读取。可以在启动应用程序时设置环境变量,或者在应用程序中使用 Environment 对象来访问环境变量
- 通过命令行参数读取。可以在启动应用程序时传递命令行参数,或者在应用程序中使用 @Value 注解来访问命令行参数
Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个?
- Spring Boot 支持多种日志框架,包括 SLF4J,Logback,Log4j2,JUL(Java Util Logging)等。推荐和默认的日志框架是 Logback
SpringBoot 实现热部署有哪几种方式?
热部署概念:程序无需手动重启,会自动重新加载类、配置文件和静态资源的更改,从而实现热部署
- 使用 Spring Boot DevTools
- 使用Maven 提供的 hot-deploy 插件
- 使用 JRebel插件
如何理解 Spring Boot 的配置加载顺序?
- 是按照特定的优先级来加载的,首先会加载内置的默认配置。然后会加载外部的配置文件(如application.properties)。最后会加载通过命令行参数或环境变量传递的配置
Spring Boot 如何定义多套不同的环境配置?
- 可以使用不同的配置文件来定义多套环境配置。例如,可以使用 application.properties 文件作为默认配置文件,然后为不同的环境创建额外的配置文件,例如 dev.properties 文件用于开发环境,prod.properties 文件用于生产环境。在启动应用程序时,可以通过设置 spring.profiles.active 环境属性来选择加载哪个配置文件。此外还可以使用 @Profile 注解来限制特定的 Bean 只在特定的环境下使用
Spring Boot 可以兼容老 Spring 项目吗?如何做?
- 可以,可以将老 Spring 项目作为一个普通的 Maven 或 Gradle 项目导入到 Spring Boot 中,然后使用 Spring Boot 的自动配置和约定优于配置的原则来简化配置和启动过程。同时也可以根据需要启用或禁用某些自动配置项,以避免与老项目的兼容性问题
保护 Spring Boot 应用有哪些方法?
- 使用安全框架:例如 Spring Security,它可以为应用程序提供身份验证和授权机制
- 编码和加密:对敏感数据进行编码或加密,以保护数据的安全性
- 防止 SQL 注入:使用参数化查询或 ORM 框架提供的查询构建器来防止 SQL 注入攻击
- 使用 HTTPS:通过使用 HTTPS 来保护数据传输过程中的安全性
- 日志和监控:对应用程序进行日志记录和监控,以便及时发现并应对潜在的安全威胁
Spring Boot 2.X 有什么新特性?与 1.X 有的区别?
- 支持 Java 8 和新的版本进行升级
- 支持 Spring Data Redis
- 支持 Spring Session 和响应式数据库连接
- 支持响应式编程模型(Reactive Programming)
- 提供新的 Security 相关 API
- 提供 WebFlux 控制器注解 @WebFluxController
- 对自定义starter支持进行了重构与提升等
SpringMVC常用注解
- @Controller:用于标识一个类为控制器。表明这个类是一个处理 HTTP 请求的类
- @RequestMapping:用于映射 HTTP 请求到特定的处理方法上,在类上使用时,会映射该类所有的处理方法;在方法上使用时,会映射该方法
- @RequestParam:用于获取请求参数
- @RequestBody:用于获取 HTTP 请求体的内容
- @RequestHeader:用于获取 HTTP 请求头的内容
- @PathVariable:用于获取路径变量/参数
- @ResponseEntity:用于返回 HTTP 响应,它可以在方法的返回值中使用
- @Autowired:用于自动装配依赖
- @Qualifier:用于指定依赖的名称
SpringMVC怎样设定转发和重定向?
- 可以使用 request.getRequestDispatcher().forward() 方法进行转发
- 可以使用 response.sendRedirect() 方法进行重定向
转发和重定向的区别
- 转发的url地址栏不会发生改变;重定向的url地址栏会发生改变
- 转发实际上是用上一次的请求做的;而重定向是要求浏览器重新发送一次新的请求
- 转发可以携带上一次请求的参数;而重定向无法携带上一次请求的参数
SpringMVC怎么和AJAX相互调用?
- SpringMVC可以通过使用@ResponseBody注解将方法返回的对象转换为JSON格式,从而与AJAX进行相互调用
SpringMVC的优点
- Spring MVC 提供了丰富的文档和示例,使得使用和配置比较容易
- 支持多种视图技术,包括 JSP、Thymeleaf、FreeMarker 等
- 提供了强大的数据绑定和验证功能
- 支持国际化和本地化,可以轻松地创建多语言应用程序
- 提供了良好的可测试性和可维护性,可以使用单元测试和集成测试来确保代码的质量
Spring MVC的主要组件
- DispatcherServlet
- Controller
- View Resolver
- Model
- 验证器(Validator):用于验证命令对象是否符合特定的规则和约束
- 命令对象(Command Object):用于接收来自表单的参数,通常用于处理表单提交
SpingMVC中的控制器注解一般用哪个?有没有别的注解可以替代?
- 在 Spring MVC 中,@Controller 是最常用的注解之一,还可以使用 @RestController 注解,它结合了 @Controller 和 @ResponseBody 的功能
如何在拦截请求中拦截 GET 方式提交的方法?
- 可以在拦截器中配置拦截的URL路径,并在拦截器中通过HttpServletRequest的getMethod()方法获取请求的方法类型,然后进行判断和处理
怎样在方法里面得到Request或者Session?
- 在 Spring MVC 中,控制器方法可以通过注入 HttpServletRequest 和 HttpSession 对象来获取请求和会话信息。例如,在一个控制器方法中添加 HttpServletRequest 和 HttpSession 类型的参数,Spring 框架会自动将相应的对象注入到方法中
怎么在拦截的方法里得到从前台传入的参数?
- 可以直接在方法的参数列表中使用@RequestParam注解来获取指定名称的参数值
如果前台有很多个参数传入,并且这些参数都是一个对象的,那么怎么样快速得到这个对象?
- 在控制器方法中,使用 @ModelAttribute 注解来指定参数的名称,然后将从前台传入的参数值绑定到该参数上。Spring MVC 会自动将参数值转换为目标对象类型,并将其注入到控制器方法中
SpringMVC中函数的返回值是什么?
- 可以是一个字符串,表示返回的视图名称;也可以是一个ModelAndView对象,包含了返回的视图名称和模型数据;还可以是一个@ResponseBody注解的方法,返回JSON格式的数据;可以是任何合法的 Java 类型,包括基本数据类型、对象、集合等
SpringMVC用什么对象从后台向前台传递数据?
- 可以使用ModelAndView对象来向前台传递数据,也可以使用Model对象或者Map对象,这些数据会被自动放入到请求的作用域中
SpringMVC中Controller怎么向JSP返回值?
- 可以将返回值设置到ModelAndView对象中,然后通过设置视图名称为JSP文件的路径来返回。在 JSP 中可以使用 EL表达式或 Jstl 标签来访问这些数据并进行显示
Controller怎么接收前端的参数?
- 可以使用@RequestParam、@PathVariable、@RequestBody注解等方式来接收
Jquery常用选择器有哪些?
- 元素选择器:如$(“div”)选择所有的div元素
- ID选择器:如$(“#myDiv”)选择id为"myDiv"的元素
- 类选择器:如$(“.myClass”)选择所有class为"myClass"的元素
- 属性选择器:如$(“[name=‘myName’]”)选择所有name属性值为"myName"的元素
- 后代选择器:如$(“div p”)选择所有div元素下的p元素
- 子元素选择器:如$(“div > p”)选择所有div元素下的直接子元素为p的元素
- 兄弟元素选择器:选择与指定元素相邻的元素,如$(“div + p”)选择所有紧接在div元素后的p元素
- 筛选器选择器:如$(“div:first”)选择第一个div元素
什么是Mybatis?
- MyBatis是一个数据持久层ORM映射框架,把实体类和SQL语句之间建立了映射关系,是一种半自动化的ORM实现
Mybaits的优点
- 灵活性高,可以自定义SQL语句;易于学习和使用;提供了强大的动态SQL功能;
MyBatis的缺点
- 需要手动编写SQL语句,对开发人员的要求较高;配置较为繁琐,需要编写XML文件或注解;对于复杂的关联查询支持较弱
MyBatis适用场合
- 适用于需要灵活控制SQL语句,对性能要求较高的项目,以及对数据库操作较为熟悉的开发人员,适用于需要执行复杂SQL语句的业务场景(如金融领域需要大量分页查询等操作)
MyBatis与Hibernate的区别?
- Mybatis是半自动化的ORM,可以自定义SQL语句,更加灵活
- Hibernate是全自动的ORM,会自动生成SQL语句,更加面向对象,通过ORM实现数据的映射
#{}和${}的区别?
- #{}用于预编译SQL语句中的参数,会将参数值进行安全地替换,防止SQL注入攻击;主要用于查询中的参数绑定,如传入的参数是变量或者是表达式
- ${}用于拼接SQL语句,会将参数直接替换到SQL语句中,但存在SQL注入攻击的风险;主要用于已知变量的插入,不建议用于查询参数的绑定
实体类中属性名和表中字段名不一样 时怎么办 ?
- 可以在SQL语句中使用别名
- 可以在XML文件中的resultMap元素中使用column属性来指定字段名,使用property属性来指定属性名
- 可以在实体类中使用@Result注解来指定映射关系
模糊查询like语句怎么写?
- 示例使用了CONCAT函数将%符号与查询参数拼接起来,实现了模糊查询:
SELECT * FROM users WHERE name LIKE CONCAT('%', #{name}, '%')
Xml对应的Dao接口的工作原理是什么?Dao接口里的方法,参数不同时,方法能重载吗?
- MyBatis通过动态代理机制将XML映射文件中的SQL语句与DAO接口的方法关联起来。当调用DAO接口的方法时,MyBatis会根据方法名和参数类型生成相应的SQL语句并执行
- Dao接口里的方法可以重载,但是需要使用@Param注解来区分不同的方法
Mybatis是如何进行分页的?分页插件的原理是什么?
- 可以在XML中使用 rowBounds 属性来指定分页参数,例如:
<select id="selectUsers" resultType="User" parameterType="map" rowBounds="offset,limit">
SELECT * FROM users WHERE 1=1 ORDER BY id LIMIT #{offset}, #{limit}
</select>
offset 是偏移量,表示要跳过的记录数;limit 是每页记录数。当调用selectUsers方法时,MyBatis会自动根据传入的分页参数生成相应的SQL语句并执行
- 分页插件的原理是通过拦截器机制,在查询SQL语句执行前,修改SQL语句的内容,添加分页的逻辑
Mybatis是如何将sql执行结果封装为目标对象并返回的?都有哪些映射形式?
Mybatis将sql执行结果封装为目标对象并返回的过程主要通过以下步骤完成:
- Mybatis执行sql后,获取到结果集(ResultSet)
- 结果集经过 Mybatis的 ResultSetHandler进行处理,根据XML映射文件中的映射配置,将结果集转化为Java对象
- 最后将转化后的Java对象返回给调用者
Mybatis的映射形式主要有以下几种:
- 单行映射(ResultMap):适用于结果集中只有一行数据的情况
- 多行映射(ResultMap):适用于结果集中有多行数据的情况
- 使用@Result和@Results进行映射:可以在Java类中使用注解进行映射配置,适用于简单的情况
- 使用对象映射(ObjectMapping):将结果集映射到一个Map中,然后根据key将数据取出
如何执行批量插入?
- 在Mybatis中,可以使用insertList方法来一次性插入多条数据。该方法接受一个列表参数,列表中的每个元素表示一条待插入的数据
- 也可以使用foreach来循环插入多条数据,在每次遍历时执行插入操作
- 还可以调用PreparedStatement对象的addBatch()方法,将数据都添加到批处理中,再执行executeBatch()方法执行批处理插入操作
如何获取自动生成的(主)键值?
- 可以调用PreparedStatement对象的getGeneratedKeys()方法获取
- 可以在 Mybatis映射文件或注解中设置 useGeneratedKeys=“true”,然后在目标对象的属性上使用 @Options(useGeneratedKeys = true, keyProperty = “id”) 注解来指定自动生成的主键属性
session和cookie的区别?
- 存储位置:Session是存储在服务器端,Cookie是存储在客户端
- 存储容量:Session可以存储较大的数据量,而Cookie的存储容量有限,通常只能存储几KB的数据
- 安全性:Session相对较安全,因为数据存储在服务器端,客户端无法修改;Cookie相对较不安全,因为数据存储在客户端,可以被篡改
- 使用方式:Session需要在服务器端进行管理,通过Session ID来识别和管理用户的会话状态;Cookie可以直接在客户端使用,通过键值对的方式存储数据
- 生命周期:都是在浏览器关闭时失效,但如果设置了过期时间就在该过期时间时才失效
LocalStorage和SessionStorage的区别
- 生命周期:localStorage中的数据没有过期时间,除非手动删除,否则会一直保存在客户端;而sessionStorage中的数据只在当前会话有效,当用户关闭浏览器窗口或标签页时,数据会被删除
- 存储容量:localStorage的存储容量较大,一般为5MB或更多;而sessionStorage的存储容量较小,一般为5MB或更少
- 数据共享:localStorage中的数据在同一个域名下的所有页面之间共享;而sessionStorage中的数据只在同一个窗口或标签页下共享
- 数据安全性:localStorage和sessionStorage中的数据都是保存在客户端,不会被发送到服务器,因此相对较安全。但注意不要将敏感信息存储在localStorage或sessionStorage中,以防止被恶意获取
常用Linux命令
- ls:列出目录内容
- cd:切换目录
- pwd:显示当前目录
- mkdir:创建目录
- rm:删除文件或目录
- cp:复制文件或目录
- mv:移动文件或目录
- cat:查看文件内容
- grep:搜索文件内容
- find:查找文件
- tar:打包和解压文件
介绍下Dubbo
- Dubbo是一个高性能、轻量级的开源Java RPC框架,由阿里巴巴开发并开源。它提供了一种基于服务的架构,用于解决分布式系统中的服务治理问题,广泛应用于大规模分布式系统中
Dubbo特点
- 高性能:Dubbo使用 Netty作为底层通信框架,支持高性能、高吞吐量的RPC调用
- 轻量级:Dubbo框架简洁、易于理解和使用,对应用侵入性小
- 丰富的服务治理能力:Dubbo提供了多种服务治理策略,如服务发现、负载均衡、熔断、链路追踪等,帮助开发者构建稳定、高效的分布式应用
- 易于扩展:Dubbo支持自定义扩展,方便开发者根据需求进行扩展和定制
- 透明化的远程调用:Dubbo对远程调用进行了封装,使得开发者可以像调用本地方法一样调用远程服务,屏蔽了底层的通信细节
- 智能负载均衡:Dubbo提供了多种负载均衡策略,如随机、轮询、一致性哈希等,根据实际情况选择合适的负载均衡策略
Dubbo支持的协议
- Dubbo协议:它自定义的协议,默认使用Netty进行通信,采用单一长连接,适用于传输大量小数据的场景
- RMI协议:基于Java的远程方法调用协议,适用于Java环境
- Hessian协议:基于二进制的远程方法调用协议,适用于各种语言之间的通信
- HTTP协议:基于HTTP的远程方法调用协议,适用于各种环境和语言
- Thrift协议:Apache 的 Thrift 跨语言的远程服务调用框架
应用之间如何通讯?
- 基于HTTP协议的通信:应用之间通过HTTP协议进行通信,可以使用HTTP的GET、POST等方法进行数据的传输
- 基于RPC框架的通信:应用之间通过RPC框架(如Dubbo)进行远程调用,实现应用之间的通信和数据交互
- 基于消息队列的通信:应用之间通过消息队列进行异步通信,生产者将消息发送到消息队列,消费者从消息队列中获取消息进行处理
- 基于TCP/IP的Socket通信:应用之间通过Socket建立连接,通过读写数据流进行通信
应用之间的调用如何规划和监控?
可以通过服务治理来规划和监控,常见的方式包括:
- 服务注册和发现:应用将自己的服务注册到注册中心,其他应用从注册中心获取服务的地址,实现服务的自动发现和调用
- 负载均衡:通过负载均衡策略,将请求均匀地分发到多个服务提供者,实现负载均衡和高可用
- 监控和追踪:通过监控和追踪系统,可以对服务的调用情况进行监控和统计,包括调用次数、调用耗时等指标,以便及时发现和解决问题
介绍下SpringCloud
- Spring Cloud是一个基于Spring Boot的微服务框架,用于构建分布式系统中的微服务架构。它提供了一系列的组件和工具,用于实现服务注册和发现、负载均衡、断路器、配置管理、消息总线等功能,简化了微服务架构的开发和部署
字节流和字符流的区别
- 都是用于读写数据的,但是字符流是以字符(char)为单位进行读写,主要用于处理文本数据;而字节流是以字节(byte)为单位进行读写,主要用于处理二进制数据,字节流读取文件速度较快
接口和抽象类的区别
- 接口没有方法体,而抽象类可以包含抽象方法和普通方法
- 一个类可以实现多个接口,但只能继承一个抽象类
- 接口中的方法必须都是public修饰的,而抽象类可以根据需要设置不同的访问控制级别
- 接口主要用于定义行为,而抽象类主要用于提供实现细节和继承关系
介绍Java的反射机制
- Java反射机制是指在运行时动态地获取类的信息,包括类名、成员变量、方法、注解等,并通过反射调用类的方法和属性
- 优点是可以访问私有方法和属性,增加了程序的灵活性和可维护性;可以将对象序列化为字节流,以便于存储和传输
- 缺点是性能较低,因为反射需要动态地获取类信息并调用方法,比直接调用方法的性能要低;由于可以访问私有属性和方法,破坏代码的安全性;代码复杂和难以阅读
XML和Json的特点
- XML是一种标记语言,可以用于描述结构化的数据。主要特点是良好的可读性,支持自定义标签和属性,可以很好地扩展和适应新的数据结构
- JSON是一种轻量级的数据交换格式,使用键值对的形式来描述数据内容。主要特点是可读性好、体积小、解析速度快等。可以使用数组和嵌套对象来表示复杂的数据结构。
关系型数据库、nosql数据库和缓存数据库的区别
- 关系型数据库以表格为单位,可以使用SQL语言进行数据的查询和操作,支持事务处理。通常用于需要复杂查询和数据一致性的应用
- NoSQL数据库一般使用键值对、文档等来存数据。适用于需要处理大量数据、对一致性要求不高或需要灵活的数据模型的应用
- 缓存数据库用于提高数据访问速度和性能,将经常使用的数据存储在内存中;具有快速读写性能和较低的数据持久化要求。适用于需要快速响应和高并发访问的应用中
Zookeeper的主要功能
Zookeeper是一个分布式协调服务,主要用于管理和协调分布式系统中的各种服务和进程。有以下主要功能:
- 提供分布式命名服务,可以用于唯一标识节点
- 可以集中管理和维护分布式系统的配置信息
- 可以用来同步分布式节点之间的状态
- 可以用来管理分布式系统的集群,包括节点监控、故障转移等
- 可以用来实现分布式系统之间的同步操作
代码中事务是如何控制的?
- 手动控制事务:通过在代码中显式地开启和提交或回滚事务来控制事务。这种方式需要手动编写事务的开启、提交和回滚代码,适用于较小的事务场景
- 声明式事务管理:通过使用注解或XML配置等方式来声明事务的边界和行为。这种方式可以让事务的管理更加简洁和直观,适用于较复杂的事务场景
- 自动事务管理:通过ORM框架或数据库连接池等自动管理事务的开启和提交或回滚。这种方式可以减少开发者的负担,适用于简单的事务场景
- 编程式事务管理:通过编程方式来控制事务的开启、提交和回滚。这种方式适用于需要自定义事务行为或需要高级控制的事务场景
介绍一下Mybatis缓存
- 一级缓存是指在同一个SqlSession中共享的缓存。当执行一个查询语句时,MyBatis会将查询结果缓存到一级缓存中。如果接下来执行相同的查询语句,MyBatis会先检查一级缓存中是否存在该结果,如果存在,则直接从缓存中获取结果,而不再访问数据库。一级缓存的作用范围是SqlSession级别的,当SqlSession关闭时,一级缓存也会被清空
- 二级缓存是指在多个SqlSession之间共享的缓存。当执行一个查询语句时,MyBatis会将查询结果缓存到二级缓存中。如果接下来执行相同的查询语句,MyBatis会先检查二级缓存中是否存在该结果,如果存在,则直接从缓存中获取结果,而不再访问数据库。二级缓存的作用范围是Mapper级别的,可以被多个SqlSession共享。需要注意的是,二级缓存默认是关闭的,需要在配置文件中进行配置开启
介绍下索引
- 索引是数据库管理系统中用于加快数据检索速度的数据结构。它可以根据指定的列或列的组合,对数据进行预排序,从而使得查询速度大大提高。索引的原理类似于图书的目录,通过索引可以快速定位到所需数据的位置
介绍下存储过程
- 存储过程是一段预编译的SQL代码,可以在数据库服务器上执行。它通常用于封装复杂的查询逻辑,以提高性能并减少网络流量。存储过程可以通过参数传递数据,并可以接受输入和产生输出
示例:
CREATE PROCEDURE GetEmployeeName(IN employeeId INT, OUT employeeName VARCHAR(255))
BEGIN
SELECT name INTO employeeName FROM employees WHERE id = employeeId;
END;
这个存储过程名为GetEmployeeName,它接受一个整数类型的参数employeeId作为输入,并将一个字符串类型的变量employeeName作为输出。在存储过程的主体中,使用SELECT语句从employees表中查询名字,并将结果赋值给employeeName变量。
要调用这个存储过程,可以使用CALL语句:
CALL GetEmployeeName(1, @name);
SELECT @name;
这里的1是作为输入参数传递给存储过程的员工ID,@name是一个用户定义的变量,用于接收存储过程的输出结果。通过SELECT语句可以查看存储过程返回的结果。
项目怎么进行重构,代码怎么进行优化?
- 重构是指对代码进行修改以提高其质量和可读性,但不改变其外部行为的过程。重构的目标包括消除重复代码、简化条件语句和循环、优化算法、减少冗余代码等
- 代码优化可以通过减少数据库查询次数、缓存结果、减少I/O操作等
ArrayList和LinkedList的比较
- ArrayList和LinkedList都是Java中的List接口的实现,ArrayList是基于动态数组实现的,它在内存中以连续的方式存储元素,因此访问元素的速度很快。LinkedList是基于双向链表实现的,它在内存中以非连续的方式存储元素,因此插入和删除元素的速度很快
list与map的底层如何实现?
- List接口主要包括ArrayList和LinkedList,它们的底层实现方式如上所述。Map接口主要包括HashMap和TreeMap,HashMap底层基于哈希表实现,哈希表是通过哈希函数将键映射到桶中的位置,从而快速定位到元素的位置。而TreeMap底层基于红黑树实现,红黑树是一种自平衡的二叉搜索树,它通过调整树的结构来保持平衡,从而保证查找、插入和删除的时间复杂度为O(log n)
说说对 MySQL 常见的两种存储引擎MyISAM与InnoDB的理解
- MyISAM存储引擎的特点是简单、快速,但不支持事务和行级锁定。它适用于读多写少的系统,因为读操作是不需要加锁的,而写操作则需要整个表锁定。
- InnoDB存储引擎的特点是支持事务、行级锁定和外键约束,适用于高并发读写的情况。它通过使用多版本并发控制(MVCC)来提高并发性能,并使用缓冲池来缓存磁盘上的数据,从而减少I/O操作
为什么索引能提高查询速度
- 原因在于它对数据进行预排序,从而使得查询时不需要全表扫描,而是直接定位到满足条件的数据行。索引可以显著减少需要扫描的行数,特别是在数据量很大的情况下,没有索引可能需要扫描整个表,而有了索引只需要扫描索引本身和满足条件的行即可
什么是事务
- 事务是一系列操作组成的工作单元,该工作单元内的操作是不可分割的,即要么所有操作都做,要么所有操作都不做。事务的目的是确保多个操作的一致性和完整性。在数据库中,事务通常用于保护数据的完整性和一致性,避免数据出现不一致的情况
为什么要用 redis 缓存
- Redis是一种内存数据库,将数据存储在内存中,可以快速地访问和操作数据,从而提高应用程序的性能
- Redis支持多种数据结构,如字符串、哈希表、列表、集合和有序集合等,可以方便地存储和操作数据
- Redis提供了持久化机制,可以将数据保存到磁盘中,保证数据不会因为程序崩溃而丢失
- Redis支持分布式缓存,可以用于构建高可用的应用程序
缓存的作用是将计算结果临时存储在高速存储介质中,以减少对底层数据源的访问,提高系统性能和吞吐量;可以减少IO操作和网络传输的开销,加快数据访问速度;可以大大提高系统的响应速度和并发能力
数据库优化应该从哪些方法考虑?
- 索引优化:合理地使用索引可以显著提高查询速度。对于常用的查询条件和排序字段,应该建立合适的索引
- SQL语句优化:编写高效的SQL语句可以提高查询速度。应该尽量避免全表扫描,合理地使用索引和优化查询语句
- 数据库分区:将数据库按照功能和数据类型进行分区,可以更好地组织和管理数据,提高查询效率
- 数据库复制:使用数据库复制可以提高系统的可用性和性能。当主库出现故障时,可以快速地切换到备用库
- 数据分片:对于海量数据和高并发场景,可以考虑使用数据分片技术,将数据分散到多个数据库实例中,提高系统的负载能力和可用性
- 控制事务的粒度:尽量减少事务的范围,避免长时间持有锁,以提高并发性能
- 适当使用缓存:将热点数据缓存起来,减少对数据库的访问次数,提高系统的响应速度
HashMap的底层原理是什么
- HashMap的底层原理是基于哈希表实现的。哈希表是一种根据键的哈希值来快速定位值的数据结构。在HashMap中,键值对被存储在一个数组中,通过对键的哈希值进行计算,得到一个索引,然后将键值对存储在对应索引的位置上。当需要获取值时,再根据键的哈希值计算出索引,并在数组中找到对应位置的值
当MySQL单表记录数过大时,数据库的CRUD性能会明显下降,介绍一些常见的优化措施
- 分表分库:将单表拆分为多个表或多个库,可以分散数据压力和提高并发处理能力。可以使用中间件来实现分表分库的操作
- 索引优化:针对常用的查询条件和排序字段建立合适的索引,可以提高查询效率。但要注意不要过度索引,否则会增加维护成本和降低写入性能
- SQL语句优化:编写高效的SQL语句可以提高查询速度。应该尽量避免全表扫描和减少锁竞争,合理地使用索引和优化查询语句
- 缓存数据:将经常访问的数据缓存到内存中,可以减少对数据库的访问次数,提高系统的性能和可用性。可以使用Redis等内存数据库来实现缓存功能
项目的开发流程是什么
- 项目规划:明确项目的目标、范围、时间计划、预算等
- 需求分析:对项目需求进行详细的理解和分析,确定项目的功能、性能、质量等要求
- 系统设计:根据需求分析结果,进行系统的总体设计,包括架构设计、数据库设计、界面设计等
- 编码开发:根据系统设计,进行编码开发工作,实现相应的功能和性能
- 测试验收:对开发完成的系统进行测试和验收,确保系统的质量符合要求
- 部署上线:将系统部署到生产环境中,进行上线操作
- 维护升级:对系统进行维护和升级,保证系统的稳定性和可用性
在项目中如何设计数据库
- 确定数据需求:明确项目中对数据的具体需求,包括数据的种类、格式、关系等
- 设计数据模型
- 选择合适的数据库管理系统:如MySQL、Oracle、SQL Server等
- 创建数据库表
- 定义字段和约束:约束包括主键约束、外键约束、非空约束等
- 创建索引:索引可以加快查询速度,但过多的索引会增加写操作的开销
- 设计关联关系:一对一、一对多、多对多
- 考虑事务处理,如果项目需要
- 测试和优化:验证设计的正确性和性能
- 备份和恢复设计:以防止数据丢失和灾难恢复
- 文档记录:编写数据库设计文档,记录设计的详细信息,以便于后续维护和使用
拦截器和过滤器的区别
过滤器(Filter)是Servlet容器提供的一种组件,用于对所有请求进行预处理和后处理;拦截器(Interceptor)是应用程序框架或容器提供的一种组件,用于对特定请求或方法进行预处理和后处理。过滤器具有较好的扩展性,而拦截器通常只能在特定框架或容器中使用
- 实现方式:过滤器是基于Servlet规范的一种组件,由Servlet容器负责调用
拦截器是基于AOP的一种组件,由应用程序框架或容器负责调用 - 作用范围:过滤器可以作用于Web应用中的所有请求,包括静态资源请求和动态请求
拦截器通常只作用于特定的Controller或Handler方法 - 功能:过滤器可以对请求进行预处理和后处理,如身份验证、日志记录等。可以修改请求和响应的内容
拦截器主要用于在Controller或Handler方法执行前中后进行一些额外的处理,如权限验证、日志记录等。也可以修改请求和响应的内容,也可以中断请求的执行 - 调用顺序:都是根据配置顺序来确定的,先配置的先被调用
- 扩展性:过滤器是基于Servlet规范的,可以与任何Servlet容器兼容,具有较好的扩展性
拦截器通常是特定框架或容器提供的功能,只能在该框架或容器中使用,扩展性相对较差 - 实现原理:过滤器是基于函数回调实现的,而拦截器则是基于Java的反射机制(动态代理)实现的
- 使用范围:过滤器实现的是javax.servlet.Filter接口,这个接口是在Servlet规范中定义的,因此过滤器的使用依赖于Tomcat等容器,通常仅限于web程序中使用。
而拦截器则是一个Spring组件,由Spring容器管理,并不依赖Tomcat等容器,可以单独使用 - 拦截的请求范围:过滤器执行了两次,而拦截器只执行了一次
序列化和反序列化
- 序列化:序列化是将对象转化为字节流的过程。在序列化过程中,对象的状态信息被转化为字节序列,可以被存储在文件中、传输到网络中或通过其他方式进行持久化。
序列化可以将对象的状态信息包括属性值、字段值、关联关系等保存下来,以便在需要时进行恢复。
序列化可以通过Java的ObjectOutputStream类或其他序列化框架(如JSON、XML等)来实现。 - 反序列化:反序列化是将字节流转化为对象的过程。在反序列化过程中,字节序列被转化为对象的状态信息,可以重新创建出原始的对象。
反序列化可以将存储在文件中、传输到网络中或通过其他方式持久化的对象重新恢复到内存中。
反序列化可以通过Java的ObjectInputStream类或其他反序列化框架(如JSON、XML等)来实现。 - 应用场景:分布式系统:在分布式系统中,对象可以在不同的节点之间进行传输和共享,通过序列化和反序列化可以将对象转化为字节流进行传输,再在接收端进行反序列化恢复成对象。
缓存和持久化:将对象序列化后存储在缓存或数据库中,以便在需要时进行快速读取和恢复。
远程调用:在远程调用中,可以将参数对象进行序列化后传输到远程服务器,再在服务器端进行反序列化后调用相应的方法。
需要注意的是,不是所有的对象都可以被序列化,只有实现了Serializable接口的对象才能进行序列化和反序列化。同时,序列化和反序列化的过程也可能存在版本兼容性的问题,需要注意版本的管理和控制。1