随着面试季的来临,越来越多的求职者开始准备面试。本文将分享来自四川大学一位学生的阿里淘天一面的面经,旨在为大家提供一些参考和复习资料。

阿里淘天一面的面试问题相对随意,主要可分为以下三类:

  1. 非技术问题,例如自我介绍。
  2. 笔试题,例如手写单例模式。
  3. 技术问题,即常规的八股文题目,如 GET 和 POST 的区别。

在笔试部分,共有三道题目,难度相对较低,不完全是纯粹的 Leetcode 问题。

图片

内容概览:

图片

非技术问题

自我介绍

自我介绍通常是面试官和应聘者之间的首次正式交流。因此,作为求职者,你应该思考如何有效地介绍自己。理想的自我介绍应该包括以下几个要素:

  1. 用简洁明了的语言阐述你的技术栈和擅长领域;
  2. 强调自己在专业领域的优势和独特之处;
  3. 突出自己的实际能力,比如定位问题的技巧。

请记住,自我介绍不应死记硬背,掌握要点,结合公司的特定情况进行调整也很重要。通常建议准备两种自我介绍:一种是针对 HR,着重突出经历,技术简短提及;另一种是针对技术面试官,详细讲述技术细节和项目经验。

自我介绍模板:

图片

实习经历及遇到的难点

在讲述实习经历时,要避免空话,尽量用具体的数据和指标来量化自己的贡献。示例:

  1. 参与订单模块的开发,负责订单的创建、删除和查询功能。
  2. 解决扣费模块中因同一线程池导致的死锁问题。
  3. 通过使用 CompletableFuture 并行加载后台用户统计数据,响应时间从 3.5 秒降低到 1 秒。
  4. 使用 Redis + Caffeine 多级缓存优化热门数据,解决缓存击穿问题,查询速度达到毫秒级,QPS 超过 30w。
  5. 在实习期间,完成了 10 个需求开发和 5 个问题修复,共编写 2000 行代码和 100 个测试用例,成功通过代码审查和测试验收,运行稳定无故障。

对于实习经历的描述,建议主动承担开发任务或优化现有系统,增强自己的实际能力。

未来发展方向

技术本身并不创造价值,其应用于产品和业务中才能体现其价值。例如,作为技术服务公司,你的技术产品必须有人使用才能产生收益。程序员应当关注技术与业务之间的联系,提升自己的业务理解能力。

刚入职时,接触的业务场景有限,难免对某些细节考虑不周。但随着工作经验的积累,业务能力会逐步提高。培养系统思考和复盘总结的习惯对于提升业务能力尤为重要。

笔试题

笔试采用线上编辑器的形式。

单例模式的三种实现方式

枚举方法(推荐)

public enum Singleton {  
    INSTANCE;  
    public void doSomething(String str) {  
        System.out.println(str);  
    }  
}

静态内部类(推荐)

public class Singleton {  
    private Singleton() {}  
    public static Singleton getInstance() {  
        return SingletonInner.INSTANCE;  
    }  
    private static class SingletonInner {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
}

双重检查锁

public class Singleton {  
    private volatile static Singleton uniqueInstance;  
    private Singleton() {}  
    public static Singleton getUniqueInstance() {  
        if (uniqueInstance == null) {  
            synchronized (Singleton.class) {  
                if (uniqueInstance == null) {  
                    uniqueInstance = new Singleton();  
                }  
            }  
        }  
        return uniqueInstance;  
    }  
}

推荐阅读:Java 并发常见面试题总结(链接)。

循环报数问题

这是标准的约瑟夫环问题。解决方案可通过递归实现,利用公式 f(n,k) = (f(n-1,k) + k) mod n 来求解。

public class Josephus {  
    public static int f(int n, int k) {  
        if (n == 1) {  
            return 0;  
        }  
        return (f(n - 1, k) + k) % n;  
    }  
    public static void main(String[] args) {  
        int n = 10;  
        int k = 3;  
        System.out.println("最后留下的那个人的编号是:" + f(n, k));  
    }  
}

输出结果为:最后留下的那个人的编号是:3。

打印 1-n 的奇偶数

实现方式可以使用线程的 wait()notify() 方法。

class ParityPrinter {  
    private int max;  
    private int number = 1;  
    private boolean odd;  
    public ParityPrinter(int max) {  
        this.max = max;  
    }  
    public synchronized void printOdd() {  
        while (number < 100) {  
            while (odd) {  
                try {  
                    wait();  
                } catch (InterruptedException e) {  
                    Thread.currentThread().interrupt();  
                }  
            }  
            System.out.println(Thread.currentThread().getName() + " : " + number);  
            number++;  
            odd = true;  
            notify();  
        }  
    }  
    public synchronized void printEven() {  
        while (number < 100) {  
            while (!odd) {  
                try {  
                    wait();  
                } catch (InterruptedException e) {  
                    Thread.currentThread().interrupt();  
                }  
            }  
            System.out.println(Thread.currentThread().getName() + " : " + number);  
            number++;  
            odd = false;  
            notify();  
        }  
    }  
}

创建两个线程,分别用于打印奇数和偶数:

ParityPrinter printer = new ParityPrinter(100);  
Thread t1 = new Thread(printer::printOdd);  
Thread t2 = new Thread(printer::printEven);  
t1.start();  
t2.start();  

技术问题

GET 和 POST 的区别

GET 和 POST 是 HTTP 协议中两种常用请求方式,主要区别如下:

  • 语义:GET 用于获取资源,POST 用于创建或修改资源。
  • 幂等性:GET 请求是幂等的,而 POST 请求不是。
  • 格式:GET 请求参数在 URL 中,而 POST 请求参数在请求体中。
  • 缓存性:GET 请求可以被缓存,POST 请求不适合缓存。
  • 安全性:两者通过 HTTP 协议传输不安全,需使用 HTTPS。

MySQL 查询优化

在优化时,应关注慢查询、EXPLAIN 命令、SHOW PROFILESHOW PROFILES 命令。

反射及其应用场景

反射是 Java 框架的灵魂,它使我们能够在运行时分析和操作类及其方法。在很多框架中,如 Spring 和 MyBatis,反射是实现灵活功能的关键。

例如,通过 JDK 实现动态代理的代码示例:

public class DebugInvocationHandler implements InvocationHandler {  
    private final Object target;  
    public DebugInvocationHandler(Object target) {  
        this.target = target;  
    }  
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {  
        System.out.println("before method " + method.getName());  
        Object result = method.invoke(target, args);  
        System.out.println("after method " + method.getName());  
        return result;  
    }  
}

注解的实现也是基于反射,能让我们在使用框架时更加便捷。