2024.05.09瑞幸Java校招一面
2024.05.09瑞幸Java校招一面
算法题
限时十五分钟,给一个字符串n(长度不超过100),和数字m,取出所有可能的字符串组合,如输入abc和2,输出ab,ac,bc,ba,ca,cb
DFS回溯,u表示当前是第几层
#include <bits/stdc++.h>
using namespace std;
const int N = 110;
int st[N];
string res, a;
int n, m;
void dfs(int u) {
if (u >= m) {
cout << res << endl;
return;
}
for (int i = 0; i < n; i++) {
if (!st[i]) {
st[i] = 1;
res.push_back(a[i]);
dfs(u + 1);
st[i] = 0;
res.pop_back();
}
}
}
int main() {
cin >> a >> m;
n = a.size();
dfs(0);
}
输入输出流设计模式
如果实现实现一个Java的输入输出流,你会用什么设计模式
装饰者模式:CSDN
装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地给一个对象添加新的功能,而不改变其原有的结构。
装饰器模式的主要特点如下:
动态地给一个对象添加额外的功能:通过包装对象的方式来扩展对象的功能,而不是通过继承的方式。
避免创建大量子类:如果需要为基础类添加许多额外功能,通过继承会导致类爆炸。装饰器模式可以避免这种情况。
保持对象的核心功能不变:装饰器只是在原有功能的基础上添加新的功能,而不会修改对象的基本功能。
装饰器和被装饰对象实现相同的接口:这样装饰器可以透明地替换被装饰的对象。
装饰器模式的主要结构包括:
- 抽象组件(Component)接口:定义了对象的基本功能。
- 具体组件(ConcreteComponent)类:实现了抽象组件接口。
- 抽象装饰器(Decorator)类:继承或实现了抽象组件接口,并包含了对组件的引用。
- 具体装饰器(ConcreteDecorator)类:扩展抽象装饰器,添加了额外的功能。
通过使用装饰器模式,我们可以在不修改现有类的情况下,灵活地向对象添加新的功能。这种模式提高了代码的可扩展性和可维护性。
Java的IO流分为两类:
- 字节流:传输数据的最小单位为byte(字节)InputStream和OutputStream
- 字符流:传输数据的最小单位char(字符)Reader和Writer
BufferedInputStream是一个具体装饰者。FileInputStream就是一个具体组件。
public class LowerCaseInputStream extends FilterInputStream {
protected LowerCaseInputStream(InputStream in) {
super(in);
}
@Override
public int read() throws IOException {
int read = super.read();
if (read != -1) {
return Character.toLowerCase((char) read);
} else {
return read;
}
}
public static void main(String[] args) {
InputStream in = new LowerCaseInputStream(new BufferedInputStream(System.in));
int c;
try {
while ((c = in.read()) >= 0) {
System.out.print((char) c);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Java的双亲委派机制
双亲委派机制(Parent Delegation Mechanism)是Java中的一种类加载机制。在Java中,类加载器负责加载类的字节码并创建对应的Class对象。双亲委派机制是指当一个类加载器收到类加载请求时,它会先将该请求委派给它的父类加载器去尝试加载。只有当父类加载器无法加载该类时,子类加载器才会尝试加载。
这种机制的设计目的是为了保证类的加载是有序的,避免重复加载同一个类。Java中的类加载器形成了一个层次结构,根加载器(Bootstrap ClassLoader)位于最顶层,它负责加载Java核心类库。其他加载器如扩展类加载器(Extension ClassLoader)和应用程序类加载器(Application ClassLoader)都有各自的加载范围和职责。通过双亲委派机制,可以确保类在被加载时,先从上层的加载器开始查找,逐级向下,直到找到所需的类或者无法找到为止。
这种机制的好处是可以避免类的重复加载,提高了类加载的效率和安全性。同时,它也为Java提供了一种扩展机制,允许开发人员自定义类加载器,实现特定的加载策略。
垃圾回收器G1有没有了解,如何实现的
G1(Garbage First)收集器是 JDK7 提供的一个新收集器,在 JDK9 中更被指定为官方GC收集器,与CMS收集器相比,最突出的改进是:
- 基于 “标记-整理” 算法,收集后不会产生内存碎片。
- 可以非常精确控制停顿时间,在不牺牲吞吐量前提下,实现低停顿垃圾回收。
G1 收集器在后台维护了一个优先列表,每次根据允许的收集时间,优先选择回收价值最大的 Region。这种使用 Region 划分内存空间以及有优先级的区域回收方式,保证了 G1 收集器在有限时间内可以尽可能高的收集效率(把内存化整为零)。
Springboot启动时有哪些步骤
- 初始化SpringApplication对象
- 加载配置文件
- 创建并配置ApplicationContext
- 注册并扫描Bean定义
- 运行Runner
- 启动Web服务器
- 显示启动信息
Spring的AOP是如何实现的
Spring AOP 就是基于动态代理的,如果要代理的对象,实现了某个接口,那么 Spring AOP 会使用 JDK Proxy,去创建代理对象,而对于没有实现接口的对象,就无法使用 JDK Proxy 去进行代理了,这时候 Spring AOP 会使用 Cglib 生成一个被代理对象的子类来作为代理
除了volatile关键字防止指令重排序还有哪些办法
在 Java 中,我们可以使用 volatile 关键字和 synchronized 关键字来禁止指令重排,并实现多线程之间对变量的可见性和有序性的保证
ConcurrentHashMap如何保证线程安全
ConcurrentHashMap 在 JDK 1.7 时,使用的是分段锁也就是 Segment 来实现线程安全的。 然而它在 JDK 1.8 之后,使用的是 CAS + synchronized 或 CAS + volatile 来实现线程安全的。
如果HashMap里有100万条数据,remove掉90万条,HashMap的数组会不会变小
不会变小,这是因为HashMap的设计目的是尽量避免频繁的内部数组大小调整,以提高性能。HashMap会在需要时自动调整其内部数组的大小,以保持合理的装载因子(load factor)。可以手动调用HashMap.trimToSize()方法,HashMap会尝试收缩内部数组的大小,以匹配当前元素的数量。
三次握手和四次挥手
三次握手:
- 客户端向服务端发送初始化序列化+SYN报文
- 服务端收到后发送ACK+SYN+服务端初始化序列号+(客户端初始化序列号+1)给客户端
- 客户端发送(服务端初始化序列号+1)+SYN给服务端
四次挥手:
- 客户端发送FIN报文,表示希望关闭连接
- 服务器收到FIN,发送ACK,告知准备关闭连接
- 服务器发送FIN,表示数据传输完成,希望关闭连接
- 客户端发送ACK,告知关闭连接