第一次面滴滴,没想到凉透了!

沉默王二

共 7493字,需浏览 15分钟

 ·

2024-05-21 14:04

大家好,我是二哥呀。

滴滴这两年低调多了,原因大家都知道,但滴滴这家互联网公司本身的技术还是非常不错的,尤其是网约车这块的核心业务,能进组基本上以后再跳槽就很有含金量了。

我去滴滴的官网看了一眼,好家伙,25 届秋招储备实习生已经开招了,我滴妈呀,互联网的招聘时间线真的是越来越靠前了,招聘的花样是真的越来越多了。

叫暑期实习或者秋招提前批我都能理解,这来一个秋招储备实习生,确实有点难绷。只能说和秋招无缝衔接了,只能说兄弟姐妹们早点准备吧,只能说有坑早点占吧。

这次我们以《Java 面试指南-滴滴面经》同学 1 为例, 来看看滴滴的面试官都喜欢问哪些问题,好做到知彼知己百战不殆~

让天下所有的面渣都能逆袭 😁

能看得出,仍然是围绕着二哥一直强调的 Java 后端四大件展开,所以大家在学习的时候一定要有的放矢,效率就会高很多。

滴滴面经(八股吟唱开始)

列表的底层实现

  • ArrayList 基于数组实现
  • LinkedList 基于链表实现
三分恶面渣逆袭:ArrayList和LinkedList的数据结构

多数情况下,ArrayList 更利于查找,LinkedList 更利于增删

①、由于 ArrayList 是基于数组实现的,所以 get(int index) 可以直接通过数组下标获取,时间复杂度是 O(1);LinkedList 是基于链表实现的,get(int index) 需要遍历链表,时间复杂度是 O(n)。

当然,get(E element) 这种查找,两种集合都需要遍历通过 equals 比较获取元素,所以时间复杂度都是 O(n)。

②、ArrayList 如果增删的是数组的尾部,直接插入或者删除就可以了,时间复杂度是 O(1);如果 add 的时候涉及到扩容,时间复杂度会提升到 O(n)。

但如果插入的是中间的位置,就需要把插入位置后的元素向前或者向后移动,甚至还有可能触发扩容,效率就会低很多,O(n)。

LinkedList 因为是链表结构,插入和删除只需要改变前置节点、后置节点和插入节点的引用就行了,不需要移动元素。

如果是在链表的头部插入或者删除,时间复杂度是 O(1);如果是在链表的中间插入或者删除,时间复杂度是 O(n),因为需要遍历链表找到插入位置;如果是在链表的尾部插入或者删除,时间复杂度是 O(1)。

三分恶面渣逆袭:ArrayList和LinkedList中间插入
三分恶面渣逆袭:ArrayList和LinkedList中间删除

注意,这里有个陷阱,LinkedList 更利于增删不是体现在时间复杂度上,因为二者增删的时间复杂度都是 O(n),都需要遍历列表;而是体现在增删的效率上,因为 LinkedList 的增删只需要改变引用,而 ArrayList 的增删可能需要移动元素。

单例的底层实现

单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取该实例。单例模式主要用于控制对某些共享资源的访问,例如配置管理器、连接池、线程池、日志对象等。

refactoringguru.cn:单例模式

01、饿汉式如何实现单例?

饿汉式单例(Eager Initialization)在类加载时就急切地创建实例,不管你后续用不用得到,这也是饿汉式的来源,简单但不支持延迟加载实例。

public class Singleton {
    private static final Singleton instance = new Singleton();

    private Singleton() {}

    public static Singleton getInstance() {
        return instance;
    }
}

02、懒汉式如何实现单例?

懒汉式单例(Lazy Initialization)在实际使用时才创建实例,“确实懒”(😂)。这种实现方式需要考虑线程安全问题,因此一般会带上 synchronized 关键字。

public class Singleton {
    private static Singleton instance;

    private Singleton() {}

    public static synchronized Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

03、双重检查锁定如何实现单例?

双重检查锁定(Double-Checked Locking)结合了懒汉式的延迟加载和线程安全,同时又减少了同步的开销,主要是用 synchronized 同步代码块来替代同步方法。

public class Singleton {
    private static volatile Singleton instance;

    private Singleton() {}

    public static Singleton getInstance() {
        if (instance == null) {
            synchronized (Singleton.class{
                if (instance == null) {
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

当 instance 创建后,再次调用 getInstance 方法时,不会进入同步代码块,从而提高了性能。

在 instance 前加上 volatile 关键字,可以防止指令重排,因为 instance = new Singleton() 并不是一个原子操作,可能会被重排序,导致其他线程获取到未初始化完成的实例。

04、静态内部类如何实现单例?

利用 Java 的静态内部类(Static Nested Class)和类加载机制来实现线程安全的延迟初始化。

public class Singleton {
    private Singleton() {}

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getInstance() {
        return SingletonHolder.INSTANCE;
    }
}

当第一次加载 Singleton 类时并不会初始化 SingletonHolder,只有在第一次调用 getInstance 方法时才会导致 SingletonHolder 被加载,从而实例化 instance。

05、枚举如何实现单例?

使用枚举(Enum)实现单例是最简单的方式,不仅不需要考虑线程同步问题,还能防止反射攻击和序列化问题。

public enum Singleton {
    INSTANCE;
    // 可以添加实例方法
}

redis消息队列

可以使用 Redis 的 zset(有序集合)来实现延时队列。

三分恶面渣逆袭:zset实现延时队列

第一步,将任务添加到 zset 中,score 为任务的执行时间戳,value 为任务的内容。

ZADD delay_queue 1617024000 task1

第二步,定期(例如每秒)从 zset 中获取 score 小于当前时间戳的任务,然后执行任务。

ZREMRANGEBYSCORE delay_queue -inf 1617024000

第三步,任务执行后,从 zset 中删除任务。

ZREM delay_queue task1

mysql事务

事务是一个或多个 SQL 语句组成的一个执行单元,这些 SQL 语句要么全部执行成功,要么全部不执行,不会出现部分执行的情况。事务是数据库管理系统执行过程中的一个逻辑单位,由一个有限的数据库操作序列构成。

事务的主要作用是保证数据库操作的一致性,即事务内的操作,要么全部成功,要么全部失败回滚,不会出现中间状态。这对于维护数据库的完整性和一致性非常重要。

事务具有四个基本特性,也就是通常所说的 ACID 特性,即原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持久性(Durability)。

三分恶面渣逆袭:事务四大特性

什么是原子性?

原子性子性意味着事务中的所有操作要么全部完成,要么全部不完成,它是不可分割的单位。如果事务中的任何一个操作失败了,整个事务都会回滚到事务开始之前的状态,如同这些操作从未被执行过一样。

什么是一致性?

一致性确保事务从一个一致的状态转换到另一个一致的状态。

比如在银行转账事务中,无论发生什么,转账前后两个账户的总金额应保持不变。假如 A 账户(100 块)给 B 账户(10 块)转了 10 块钱,不管成功与否,A 和 B 的总金额都是 110 块。

什么是隔离性?

隔离性意味着并发执行的事务是彼此隔离的,一个事务的执行不会被其他事务干扰。就是事务之间是井水不犯河水的。

隔离性主要是为了解决事务并发执行时可能出现的问题,如脏读、不可重复读、幻读等。

数据库系统通过事务隔离级别(如读未提交、读已提交、可重复读、串行化)来实现事务的隔离性。

什么是持久性?

持久性确保事务一旦提交,它对数据库所做的更改就是永久性的,即使发生系统崩溃,数据库也能恢复到最近一次提交的状态。通常,持久性是通过数据库的恢复和日志机制来实现的,确保提交的事务更改不会丢失。

简短一点的回答可以是:

  • 原子性:事务的所有操作要么全部提交成功,要么全部失败回滚,对于一个事务中的操作不能只执行其中一部分。
  • 一致性:事务应确保数据库的状态从一个一致状态转变为另一个一致状态。一致性与业务规则有关,比如银行转账,不论事务成功还是失败,转账双方的总金额应该是不变的。
  • 隔离性:多个并发事务之间需要相互隔离,即一个事务的执行不能被其他事务干扰。
  • 持久性:一旦事务提交,则其所做的修改将永久保存到数据库中。即使发生系统崩溃,修改的数据也不会丢失。

内容来源

  • 星球嘉宾三分恶的面渣逆袭:https://javabetter.cn/sidebar/sanfene/nixi.html
  • 二哥的 Java 进阶之路(GitHub 已有 12000+star):https://javabetter.cn

ending

一个人可以走得很快,但一群人才能走得更远。二哥的编程星球已经有 5300 多名球友加入了,如果你也需要一个良好的学习环境,戳链接 🔗 加入我们吧。这是一个编程学习指南 + Java 项目实战 + LeetCode 刷题的私密圈子,你可以阅读星球专栏、向二哥提问、帮你制定学习计划、和球友一起打卡成长。

两个置顶帖「球友必看」和「知识图谱」里已经沉淀了非常多优质的学习资源,相信能帮助你走的更快、更稳、更远

欢迎点击左下角阅读原文了解二哥的编程星球,这可能是你学习求职路上最有含金量的一次点击。

最后,把二哥的座右铭送给大家:没有什么使我停留——除了目的,纵然岸旁有玫瑰、有绿荫、有宁静的港湾,我是不系之舟。共勉 💪。

浏览 588
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报