大家好,我是二哥呀。
最近大厂的开奖是接二连三,并且很多球友都表示超出了预期,我在 offershow 上统计了一波 B 站的信息给大家做个参考。
硕士 985,后端岗,开到了 32k,大 SSP?准备为爱发电了 本科 985,开发岗,给到了 25k,暑期实习转正,另外还有 1k 的房补,我想说的是,暑期转正开出来的真不如秋招进去的,这年头,还是新鲜的越值钱 硕士 985,开发岗,开了 30k,有点意外,相对来说很高了,可能是因为 B 站终于盈利了,这次真的大方了 测开岗给到了 28k,确实很有诚意了
从 B 站发布的 2024 年 Q3 财报来看,的确是首次实现了单季度盈利,净利润2.4亿元。并且 B 站还官宣了一项新的股份回购计划,将在 24 个月内累计回收价值不超过 2 亿美元的股票。
就我个人的体感来看,明显能看到 B 站的商单多了,我关注的几个up,几乎所有的视频都加入了商业变现,并且 B 站确实也给流量,这让B 站的创作者有更强的动力去更新高质量的内容。
希望小破站持续努力。😄
从今年秋招的结果来看,腰部以上选手的确是赶上了一个好时候,尤其是拿到大厂 offer 的小伙伴,明显比去年和前年好上了很多。
希望明年春招和暑期实习还有这样的好运气。
那接下来,我们就以Java 面试指南中收录的哔哩哔哩同学 1 二面为例来看看B 站今年的面试难度,如果是你遇到这样的题目,该如何作答呢?
1、二哥的 Linux 速查备忘手册.pdf 下载 2、三分恶面渣逆袭在线版:https://javabetter.cn/sidebar/sanfene/nixi.html
哔哩哔哩同学 1 二面
你在系统中解决的难点是什么?
在 PmHub 项目中,印象最深刻的一个难点是集成OpenFeign和Sentinel实现自定义的fallback服务降级。
所谓的服务降级,就是当微服务出现异常的时候,访问者能够从系统中得到一些比较友好的通知,比如:服务繁忙,请稍后再试。
那对于普通的接口来说,可以通过 @SentinelResource 注解来完成,对于需要自定义的接口来说,可以通过实现 UserFeginFallbackFactory 接口来完成。
你在这个项目中承担的什么角色?你是解决方案的提供者还是一个方案的执行者?
我在 PmHub 这个项目中扮演的是主导者角色,不仅提供解决方案,在遇到疑难杂症的时候,也能去花时间去调查,并且通过编写代码去解决问题。
当然了,我也会安排一些宿友能够完成的任务,帮助他们提高,比如说使用 JWT 无状态来完成登录,前后端的跨域问题解决等。
你用的lombok,你用了什么功能,你介绍一下
我用 Lombok,主要是希望借助 Lombok 的注解,在编译的时候自动为 Javabean 的属性生成 getter / setter,不仅如此,还可以生成构造方法、equals方法、hashCode方法,以及 toString方法,让整体项目的代码更加简洁。
springBoot启动机制,启动之后做了哪些步骤;
Spring Boot 的启动由 SpringApplication 类负责:
第一步,创建 SpringApplication 实例,负责应用的启动和初始化; 第二步,从 application.yml 中加载配置文件和环境变量; 第三步,创建上下文环境 ApplicationContext,并加载 Bean,完成依赖注入; 第四步,启动内嵌的 Web 容器。 第五步,发布启动完成事件 ApplicationReadyEvent,并调用 ApplicationRunner 的 run 方法完成启动后的逻辑。
关键的代码逻辑如下:
public ConfigurableApplicationContext run(String... args) {
// 1. 创建启动时的监听器并触发启动事件
SpringApplicationRunListeners listeners = getRunListeners(args);
listeners.starting();
// 2. 准备运行环境
ConfigurableEnvironment environment = prepareEnvironment(listeners);
configureIgnoreBeanInfo(environment);
// 3. 创建上下文
ConfigurableApplicationContext context = createApplicationContext();
try {
// 4. 准备上下文
prepareContext(context, environment, listeners, args);
// 5. 刷新上下文,完成 Bean 初始化和装配
refreshContext(context);
// 6. 调用运行器
afterRefresh(context, args);
// 7. 触发启动完成事件
listeners.started(context);
} catch (Exception ex) {
handleRunFailure(context, ex, listeners);
}
return context;
}
你说一下JVM内存结构?
按照 Java 的虚拟机规范,JVM 的内存区域(JVM 的内存结构/JVM 运行时数据区)可以细分为程序计数器
、虚拟机栈
、本地方法栈
、堆
、方法区
等。
其中方法区
和堆
是线程共享的,虚拟机栈
、本地方法栈
和程序计数器
是线程私有的。
垃圾回收机制是什么?
垃圾回收就是对内存堆中已经死亡的或者长时间没有使用的对象进行清除或回收。
JVM 在做 GC 之前,会先搞清楚什么是垃圾,什么不是垃圾,通常会通过可达性分析算法来判断对象是否存活。
在确定了哪些垃圾可以被回收后,垃圾收集器(如 CMS、G1、ZGC)要做的事情就是进行垃圾回收,可以采用标记清除算法、复制算法、标记整理算法、分代收集算法等。
技术派项目使用的 JDK 8,所以默认采用的是 CMS 垃圾收集器。
你是如何使用jmap,你用过哪些命令?
①、我一般会使用 jmap -heap <pid>
查看堆内存摘要,包括新生代、老年代、元空间等。
②、或者使用 jmap -histo <pid>
查看对象分布。
③、还有生成堆转储文件:jmap -dump:format=b,file=<path> <pid>
。
聊聊你对线程池各个参数的理解?
线程池有 7 个参数,需要重点关注corePoolSize
、maximumPoolSize
、workQueue
、handler
这四个。
①、corePoolSize 定义了线程池中的核心线程数量。即使这些线程处于空闲状态,它们也不会被回收。这是线程池保持在等待状态下的线程数。
②、maximumPoolSize 是线程池允许的最大线程数量。当工作队列满了之后,线程池会创建新线程来处理任务,直到线程数达到这个最大值。
③、workQueue用于存放待处理任务的阻塞队列。当所有核心线程都忙时,新任务会被放在这个队列里等待执行。
④、handler,拒绝策略 RejectedExecutionHandler,定义了当线程池和工作队列都满了之后对新提交的任务的处理策略。常见的拒绝策略包括抛出异常、直接丢弃、丢弃队列中最老的任务、由提交任务的线程来直接执行任务等。
如何知道你设置的线程数多了还是少了?
可以先通过 top 命令观察 CPU 的使用率,如果 CPU 使用率较低,可能是线程数过少;如果 CPU 使用率接近 100%,但吞吐量未提升,可能是线程数过多。
然后再通过 JProfiler、VisualVM 或 Arthas 分析线程运行情况,查看线程的状态、等待时间、运行时间等信息,进一步调整线程池的参数。
通常来说:
对于 CPU 密集型任务,线程数接近 CPU 核心数即可。 对于 IO 密集型任务,线程数可以简单设置为 CPU 核心数 × 2。
给你一个需求,你需要写一个连接池,你现在可以写一下(我直接懵逼。。。。。。)
数据库连接池的核心功能主要包括:
连接的获取和释放 限制最大连接数,避免资源耗尽 连接的复用,避免频繁创建和销毁连接
class SimpleConnectionPool {
// 配置
private String jdbcUrl;
private String username;
private String password;
private int maxConnections;
private BlockingQueue<Connection> connectionPool;
// 构造方法
public SimpleConnectionPool(String jdbcUrl, String username, String password, int maxConnections) throws SQLException {
this.jdbcUrl = jdbcUrl;
this.username = username;
this.password = password;
this.maxConnections = maxConnections;
this.connectionPool = new LinkedBlockingQueue<>(maxConnections);
// 初始化连接池
for (int i = 0; i < maxConnections; i++) {
connectionPool.add(createNewConnection());
}
}
// 创建新连接
private Connection createNewConnection() throws SQLException {
return DriverManager.getConnection(jdbcUrl, username, password);
}
// 获取连接
public Connection getConnection(long timeout, TimeUnit unit) throws InterruptedException, SQLException {
Connection connection = connectionPool.poll(timeout, unit); // 等待指定时间获取连接
if (connection == null) {
throw new SQLException("Timeout: Unable to acquire a connection.");
}
return connection;
}
// 归还连接
public void releaseConnection(Connection connection) throws SQLException {
if (connection != null) {
if (connection.isClosed()) {
// 如果连接已关闭,创建一个新连接补充到池中
connectionPool.add(createNewConnection());
} else {
// 将连接归还到池中
connectionPool.offer(connection);
}
}
}
// 关闭所有连接
public void closeAllConnections() throws SQLException {
for (Connection connection : connectionPool) {
if (!connection.isClosed()) {
connection.close();
}
}
}
// 测试用例
public static void main(String[] args) {
try {
SimpleConnectionPool pool = new SimpleConnectionPool(
"jdbc:mysql://localhost:3306/pai_coding", "root", "", 5
);
// 获取连接
Connection conn = pool.getConnection(5, TimeUnit.SECONDS);
// 使用连接(示例查询)
System.out.println("Connection acquired: " + conn);
Thread.sleep(2000); // 模拟查询
// 归还连接
pool.releaseConnection(conn);
System.out.println("Connection returned.");
// 关闭所有连接
pool.closeAllConnections();
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
ending
一个人可以走得很快,但一群人才能走得更远。二哥的编程星球已经有 6500 多名球友加入了,如果你也需要一个良好的学习环境,戳链接 🔗 加入我们吧。这是一个 编程学习指南 + Java 项目实战 + LeetCode 刷题 + 简历精修 的私密圈子,你可以阅读星球专栏、向二哥提问、帮你制定学习计划、和球友一起打卡成长。
两个置顶帖「球友必看」和「知识图谱」里已经沉淀了非常多优质的学习资源,相信能帮助你走的更快、更稳、更远。
欢迎点击左下角阅读原文了解二哥的编程星球,这可能是你学习求职路上最有含金量的一次点击。
最后,把二哥的座右铭送给大家:没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。共勉 💪。