2024.05.09瑞幸Java校招一面

  • 瑞幸
  • 一面
  • 校招
大约 6 分钟全民制作人ikun

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的输入输出流,你会用什么设计模式

装饰者模式:CSDNopen in new window

装饰器模式(Decorator Pattern)是一种结构型设计模式,它允许在运行时动态地给一个对象添加新的功能,而不改变其原有的结构。

装饰器模式的主要特点如下:

  1. 动态地给一个对象添加额外的功能:通过包装对象的方式来扩展对象的功能,而不是通过继承的方式。

  2. 避免创建大量子类:如果需要为基础类添加许多额外功能,通过继承会导致类爆炸。装饰器模式可以避免这种情况。

  3. 保持对象的核心功能不变:装饰器只是在原有功能的基础上添加新的功能,而不会修改对象的基本功能。

  4. 装饰器和被装饰对象实现相同的接口:这样装饰器可以透明地替换被装饰的对象。

装饰器模式的主要结构包括:

  1. 抽象组件(Component)接口:定义了对象的基本功能。
  2. 具体组件(ConcreteComponent)类:实现了抽象组件接口。
  3. 抽象装饰器(Decorator)类:继承或实现了抽象组件接口,并包含了对组件的引用。
  4. 具体装饰器(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启动时有哪些步骤

参考链接open in new window

  1. 初始化SpringApplication对象
  2. 加载配置文件
  3. 创建并配置ApplicationContext
  4. 注册并扫描Bean定义
  5. 运行Runner
  6. 启动Web服务器
  7. 显示启动信息

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会尝试收缩内部数组的大小,以匹配当前元素的数量。

三次握手和四次挥手

三次握手:

  1. 客户端向服务端发送初始化序列化+SYN报文
  2. 服务端收到后发送ACK+SYN+服务端初始化序列号+(客户端初始化序列号+1)给客户端
  3. 客户端发送(服务端初始化序列号+1)+SYN给服务端

四次挥手:

  1. 客户端发送FIN报文,表示希望关闭连接
  2. 服务器收到FIN,发送ACK,告知准备关闭连接
  3. 服务器发送FIN,表示数据传输完成,希望关闭连接
  4. 客户端发送ACK,告知关闭连接